-
Notifications
You must be signed in to change notification settings - Fork 248
Expand file tree
/
Copy pathtutorials_en.html
More file actions
1228 lines (1226 loc) · 144 KB
/
Copy pathtutorials_en.html
File metadata and controls
1228 lines (1226 loc) · 144 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0">
<title>STNodeEditor - Tutorials</title>
<link rel="stylesheet" type="text/css" href="./css/stdoc.css"/>
<script type="text/javascript" src="./js/jquery-1.10.2.min.js"></script>
<script type="text/javascript" src="./js/stdoc.js"></script>
</head>
<body>
<div id="div_body">
<div id="div_left">
<div id="div_left_list">
<ul class='ul_group_root'>
<li>
<a class='a_node_root anchor_btn' anchor='a_a'>Foreword</a>
<ul>
<li class='li_node_sub'><a class='anchor_btn' anchor='a_b'>Introduction</a></li>
</ul>
</li>
<li>
<a class='a_node_root anchor_btn' anchor='a_c'>[Basic]STNode</a>
<ul>
<li class='li_node_sub'><a class='anchor_btn' anchor='a_d'>Create Node</a></li>
<li class='li_node_sub'><a class='anchor_btn' anchor='a_e'>STNodeOption</a></li>
<li class='li_node_sub'><a class='anchor_btn' anchor='a_f'>STNodeOption.Empty</a></li>
<li class='li_node_sub'><a class='anchor_btn' anchor='a_g'>STNode.AutoSize</a></li>
<li class='li_node_sub'><a class='anchor_btn' anchor='a_h'>e.g. - ClockNode</a></li>
<li class='li_node_sub'><a class='anchor_btn' anchor='a_i'>STNode.SetOptionXXX</a></li>
<li class='li_node_sub'><a class='anchor_btn' anchor='a_j'>About TransferData</a></li>
<li class='li_node_sub'><a class='anchor_btn' anchor='a_k'>STNodeHub</a></li>
</ul>
</li>
<li>
<a class='a_node_root anchor_btn' anchor='a_l'>[basic]STNodeControl</a>
<ul>
<li class='li_node_sub'><a class='anchor_btn' anchor='a_m'>add a control</a></li>
<li class='li_node_sub'><a class='anchor_btn' anchor='a_n'>Customize a Button</a></li>
<li class='li_node_sub'><a class='anchor_btn' anchor='a_o'>e.g. - Image info</a></li>
</ul>
</li>
<li>
<a class='a_node_root anchor_btn' anchor='a_p'>[basic]STNodeEditor</a>
<ul>
<li class='li_node_sub'><a class='anchor_btn' anchor='a_q'>Save canvas</a></li>
<li class='li_node_sub'><a class='anchor_btn' anchor='a_r'>Load canvas</a></li>
<li class='li_node_sub'><a class='anchor_btn' anchor='a_s'>useful event</a></li>
<li class='li_node_sub'><a class='anchor_btn' anchor='a_t'>useful function</a></li>
</ul>
</li>
<li>
<a class='a_node_root anchor_btn' anchor='a_u'>STNodePropertyGrid</a>
<ul>
<li class='li_node_sub'><a class='anchor_btn' anchor='a_v'>how to use</a></li>
<li class='li_node_sub'><a class='anchor_btn' anchor='a_w'>STNodePropertyAttribute</a></li>
<li class='li_node_sub'><a class='anchor_btn' anchor='a_x'>STNodeAttribute</a></li>
<li class='li_node_sub'><a class='anchor_btn' anchor='a_y'>Show help</a></li>
<li class='li_node_sub'><a class='anchor_btn' anchor='a_z'>e.g. - Add number</a></li>
<li class='li_node_sub'><a class='anchor_btn' anchor='b_a'>ReadOnlyModel</a></li>
</ul>
</li>
<li>
<a class='a_node_root anchor_btn' anchor='b_b'>STNodePropertyDescriptor</a>
<ul>
<li class='li_node_sub'><a class='anchor_btn' anchor='b_c'>e.g. - ColorTestNode</a></li>
<li class='li_node_sub'><a class='anchor_btn' anchor='b_d'>about properoty discriptor</a></li>
<li class='li_node_sub'><a class='anchor_btn' anchor='b_e'>GetValueFromString()</a></li>
<li class='li_node_sub'><a class='anchor_btn' anchor='b_f'>GetStringFromValue()</a></li>
<li class='li_node_sub'><a class='anchor_btn' anchor='b_g'>e.g. - ColorTestNode</a></li>
<li class='li_node_sub'><a class='anchor_btn' anchor='b_h'>e.g. - ColorTestNode</a></li>
</ul>
</li>
<li>
<a class='a_node_root anchor_btn' anchor='b_i'>STNodeTreeView</a>
<ul>
<li class='li_node_sub'><a class='anchor_btn' anchor='b_j'>how to use</a></li>
<li class='li_node_sub'><a class='anchor_btn' anchor='b_k'>Load assembly</a></li>
</ul>
</li>
<li>
<a class='a_node_root anchor_btn' anchor='b_l'>Save nodes</a>
<ul>
<li class='li_node_sub'><a class='anchor_btn' anchor='b_m'>Save</a></li>
<li class='li_node_sub'><a class='anchor_btn' anchor='b_n'>OnSaveNode(dic)</a></li>
<li class='li_node_sub'><a class='anchor_btn' anchor='b_o'>GetBytesFromValue()</a></li>
<li class='li_node_sub'><a class='anchor_btn' anchor='b_p'>OnLoadNode(dic)</a></li>
<li class='li_node_sub'><a class='anchor_btn' anchor='b_q'>OnEditorLoadCompleted</a></li>
</ul>
</li>
<li>
<a class='a_node_root anchor_btn' anchor='b_r'>THE END</a>
<ul>
</ul>
</li>
</ul><span class='span_time'>2021-04-29</span>
</div>
</div>
<div id="div_right">
<h1 class='h_title anchor_point' anchor='a_a'>Foreword</h1>
<div><h2 class='h_option anchor_point' anchor='a_b'>Introduction</h2></div>
<p>It was a winter. The author who was studying radio security used <a target='_bank' href='https://www.gnuradio.org/'>GNURadio</a>. That was the first time the author used the node editor.</p>
<p>-> What? Excuse me... What"s this?.. What the hell is this?...</p>
<p>It was a spring season, and I don"t know why the whole world has changed after the Chinese New Year. Everyone was forced to stay at home. The extremely boring author learned <a target='_bank' href='https://www.blender.org/'>Blender</a>. That was the second time the author used the node editor.</p>
<p>-> Wo...It turns out that this one is really convenient to use.</p>
<p>So some ideas gradually emerged in the author"s mind, making the author want to make one.</p>
<p>It was a summer, I don’t know why the author started to learn <a target='_bank' href='http://www.blackmagicdesign.com/cn/products/davinciresolve/'>Davinci</a> again. That was the third time the author used the node editor. The use of this time has doubled the author"s favor with the node editor. The author instantly felt that as long as it is a program that can be modularized and streamlined, everything can be nodeized.</p>
<p>So <span class='span_mark'>STNodeEditor</span> appeared.</p>
<img width=990 src='./images/page_top.png'/>
<hr/>
<h1 class='h_title anchor_point' anchor='a_c'>[Basic]STNode</h1>
<p><span class='span_mark'>STNode</span> is the core of the whole framework. If <span class='span_mark'>STNodeEditor</span> is regarded as <span class='span_mark'>Desktop</span>, then an <span class='span_mark'>STNode</span> can be regarded as an <span class='span_mark'>application</span> on the desktop. It is very necessary to develop a strong <span class='span_mark'>STNode</span>.</p>
<hr/>
<div><h2 class='h_option anchor_point' anchor='a_d'>Create Node</h2></div>
<hr/>
<p>The base class <span class='span_mark'>STNode</span> of the node is modified by <span class='span_mark'>abstract</span>, so the node must be extended to <span class='span_mark'>STNode</span></p>
<p><span class='span_mark'>STNode</span> contains a large number of <span class='span_mark'>virtual</span> functions for developers to overload. For more information, please refer to <a target='_bank' href='./doc.html'>API Documentation</a></p>
<div class='div_code'>
<pre class='pre_code'><span class='span_code_line'></span><span class='code_key'>using</span> ST.Library.UI.NodeEditor;
<span class='span_code_line'></span>
<span class='span_code_line'></span><span class='code_key'>namespace</span> WinNodeEditorDemo
<span class='span_code_line'></span>{
<span class='span_code_line'></span> <span class='code_key'>public</span> <span class='code_key'>class</span> <span class='code_class'>MyNode</span> : <span class='code_class'>STNode</span>
<span class='span_code_line'></span> {
<span class='span_code_line'></span> <span class='code_key'>public</span> <span class='code_class'>MyNode</span>() { <span class='code_note'>//Equivalent to OnCreate()</span>
<span class='span_code_line'></span> <span class='code_key'>this</span>.Title = <span class='code_string'>"TestNode"</span>;
<span class='span_code_line'></span> }
<span class='span_code_line'></span> <span class='code_note'>//protected override void OnCreate() {</span>
<span class='span_code_line'></span> <span class='code_note'>// base.OnCreate();</span>
<span class='span_code_line'></span> <span class='code_note'>//}</span>
<span class='span_code_line'></span> }
<span class='span_code_line'></span>}
<span class='span_code_line'></span><span class='code_note'>//Add to STNodeEditor</span>
<span class='span_code_line'></span>stNodeEditor1.Nodes.Add(<span class='code_key'>new</span> <span class='code_class'>MyNode</span>());</pre>
</div>
<img width=208 src='./images/tu_testnode.png'/>
<p>You will find that you only see a title and nothing. Because no input and output options are added to it, let's add some code.</p>
<div class='div_code'>
<pre class='pre_code'><span class='span_code_line'></span><span class='code_key'>public</span> <span class='code_key'>class</span> <span class='code_class'>MyNode</span> : <span class='code_class'>STNode</span>
<span class='span_code_line'></span>{
<span class='span_code_line'></span> <span class='code_key'>protected</span> <span class='code_key'>override</span> <span class='code_key'>void</span> OnCreate() {
<span class='span_code_line'></span> <span class='code_key'>base</span>.OnCreate();
<span class='span_code_line'></span> <span class='code_key'>this</span>.Title = <span class='code_string'>"TestNode"</span>;
<span class='span_code_line'></span> <span class='code_note'>//get the index of STNodeOption that added</span>
<span class='span_code_line'></span> <span class='code_key'>int</span> nIndex = <span class='code_key'>this</span>.InputOptions.Add(<span class='code_key'>new</span> <span class='code_class'>STNodeOption</span>(<span class='code_string'>"IN_1"</span>, <span class='code_key'>typeof</span>(<span class='code_key'>string</span>), <span class='code_key'>false</span>));
<span class='span_code_line'></span> <span class='code_note'>//get the STNodeOption that added</span>
<span class='span_code_line'></span> <span class='code_class'>STNodeOption</span> op = <span class='code_key'>this</span>.InputOptions.Add(<span class='code_string'>"IN_2"</span>, <span class='code_key'>typeof</span>(<span class='code_key'>int</span>), <span class='code_key'>true</span>);
<span class='span_code_line'></span>
<span class='span_code_line'></span> <span class='code_key'>this</span>.OutputOptions.Add(<span class='code_string'>"OUT"</span>, <span class='code_key'>typeof</span>(<span class='code_key'>string</span>), <span class='code_key'>false</span>);
<span class='span_code_line'></span> }
<span class='span_code_line'></span>}</pre>
</div>
<img width=208 src='./images/tu_testnode_adop.png'/>
<p>In this way, the added option will be displayed on the node, but this is not enough. You can see that there are two data types <span class='span_mark'>string</span> and <span class='span_mark'>int</span>. Color should be added to the data type to distinguish different data types.</p>
<div class='div_code'>
<pre class='pre_code'><span class='span_code_line'></span><span class='code_key'>public</span> <span class='code_key'>class</span> <span class='code_class'>MyNode</span> : <span class='code_class'>STNode</span>
<span class='span_code_line'></span>{
<span class='span_code_line'></span> <span class='code_key'>protected</span> <span class='code_key'>override</span> <span class='code_key'>void</span> OnCreate() {
<span class='span_code_line'></span> <span class='code_key'>base</span>.OnCreate();
<span class='span_code_line'></span> <span class='code_key'>this</span>.Title = <span class='code_string'>"TestNode"</span>;
<span class='span_code_line'></span> <span class='code_key'>int</span> nIndex = <span class='code_key'>this</span>.InputOptions.Add(<span class='code_key'>new</span> <span class='code_class'>STNodeOption</span>(<span class='code_string'>"IN_1"</span>, <span class='code_key'>typeof</span>(<span class='code_key'>string</span>), <span class='code_key'>false</span>));
<span class='span_code_line'></span> <span class='code_class'>STNodeOption</span> op = <span class='code_key'>this</span>.InputOptions.Add(<span class='code_string'>"IN_2"</span>, <span class='code_key'>typeof</span>(<span class='code_key'>int</span>), <span class='code_key'>true</span>);
<span class='span_code_line'></span> <span class='code_note'>//The highest priority, the color information in the container will be ignored</span>
<span class='span_code_line'></span> <span class='code_note'>//this.SetOptionDotColor(op, Color.Red);</span>
<span class='span_code_line'></span> <span class='code_key'>this</span>.OutputOptions.Add(<span class='code_string'>"OUT"</span>, <span class='code_key'>typeof</span>(<span class='code_key'>string</span>), <span class='code_key'>false</span>);
<span class='span_code_line'></span> }
<span class='span_code_line'></span> <span class='code_note'>//Occurs when the owner changes, submit the color</span>
<span class='span_code_line'></span> <span class='code_key'>protected</span> <span class='code_key'>override</span> <span class='code_key'>void</span> OnOwnerChanged() {
<span class='span_code_line'></span> <span class='code_key'>base</span>.OnOwnerChanged();
<span class='span_code_line'></span> <span class='code_key'>if</span> (<span class='code_key'>this</span>.Owner == <span class='code_key'>null</span>) <span class='code_key'>return</span>;
<span class='span_code_line'></span> <span class='code_key'>this</span>.Owner.SetTypeColor(<span class='code_key'>typeof</span>(<span class='code_key'>string</span>), <span class='code_class'>Color</span>.Yellow);
<span class='span_code_line'></span> <span class='code_note'>//will replace old color</span>
<span class='span_code_line'></span> <span class='code_key'>this</span>.Owner.SetTypeColor(<span class='code_key'>typeof</span>(<span class='code_key'>int</span>), <span class='code_class'>Color</span>.DodgerBlue, <span class='code_key'>true</span>);
<span class='span_code_line'></span> }
<span class='span_code_line'></span>}</pre>
</div>
<img width=208 src='./images/tu_testnode_adopclr.png'/>
<p>Such a node is created, but this node does not have any functions. In the next case, functions will be added.</p>
<p class='p_hightlight'>In any case, developers should try to keep an empty parameter constructor for the extended <span class='span_mark'>STNode</span>, otherwise there will be unnecessary troubles in many functions.</p>
<hr/>
<div><h2 class='h_option anchor_point' anchor='a_e'>STNodeOption</h2></div>
<hr/>
<p>From the above case, we can see that <span class='span_mark'>STNodeOption</span> is the connection option of <span class='span_mark'>STNode</span>. The connection option can be <span class='span_mark'>multi-connection</span> and <span class='span_mark'>single-connection</span> mode.</p>
<div class='div_code'>
<pre class='pre_code'><span class='span_code_line'></span><span class='code_key'>public</span> <span class='code_key'>class</span> <span class='code_class'>MyNode</span> : <span class='code_class'>STNode</span> {
<span class='span_code_line'></span> <span class='code_key'>protected</span> <span class='code_key'>override</span> <span class='code_key'>void</span> OnCreate() {
<span class='span_code_line'></span> <span class='code_key'>base</span>.OnCreate();
<span class='span_code_line'></span> <span class='code_key'>this</span>.Title = <span class='code_string'>"MyNode"</span>;
<span class='span_code_line'></span> <span class='code_key'>this</span>.TitleColor = <span class='code_class'>Color</span>.FromArgb(200, <span class='code_class'>Color</span>.Goldenrod);
<span class='span_code_line'></span> <span class='code_note'>//multi-connection</span>
<span class='span_code_line'></span> <span class='code_key'>this</span>.InputOptions.Add(<span class='code_string'>"Single"</span>, <span class='code_key'>typeof</span>(<span class='code_key'>string</span>), <span class='code_key'>true</span>);
<span class='span_code_line'></span> <span class='code_note'>//single-connection</span>
<span class='span_code_line'></span> <span class='code_key'>this</span>.OutputOptions.Add(<span class='code_string'>"Multi"</span>, <span class='code_key'>typeof</span>(<span class='code_key'>string</span>), <span class='code_key'>false</span>);
<span class='span_code_line'></span> }
<span class='span_code_line'></span>}</pre>
</div>
<img width=208 src='./images/tu_mynode_single.png'/>
<p class='p_hightlight'>In <span class='span_mark'>multi-connection</span> mode, an option can be connected by multiple options of <span class='span_mark'>the same data type</span> (rectangle)</p>
<p class='p_hightlight'>In single-connection mode, an option can only be connected by one option of the same data type (circle)</p>
<hr/>
<div><h2 class='h_option anchor_point' anchor='a_f'>STNodeOption.Empty</h2></div>
<hr/>
<p><span class='span_mark'>STNodeOption.Empty</span> is a static property, added to <span class='span_mark'>STNode</span> is only used to occupy a place during auto layout.</p>
<div class='div_code'>
<pre class='pre_code'><span class='span_code_line'></span><span class='code_key'>public</span> <span class='code_key'>class</span> <span class='code_class'>MyNode</span> : <span class='code_class'>STNode</span> {
<span class='span_code_line'></span> <span class='code_key'>protected</span> <span class='code_key'>override</span> <span class='code_key'>void</span> OnCreate() {
<span class='span_code_line'></span> <span class='code_key'>base</span>.OnCreate();
<span class='span_code_line'></span> <span class='code_key'>this</span>.Title = <span class='code_string'>"MyNode"</span>;
<span class='span_code_line'></span> <span class='code_key'>this</span>.TitleColor = <span class='code_class'>Color</span>.FromArgb(200, <span class='code_class'>Color</span>.Goldenrod);
<span class='span_code_line'></span>
<span class='span_code_line'></span> <span class='code_key'>this</span>.InputOptions.Add(<span class='code_class'>STNodeOption</span>.Empty);
<span class='span_code_line'></span> <span class='code_key'>this</span>.InputOptions.Add(<span class='code_string'>"IN_1"</span>, <span class='code_key'>typeof</span>(<span class='code_key'>string</span>), <span class='code_key'>false</span>);
<span class='span_code_line'></span> <span class='code_key'>this</span>.InputOptions.Add(<span class='code_string'>"IN_2"</span>, <span class='code_key'>typeof</span>(<span class='code_key'>string</span>), <span class='code_key'>false</span>);
<span class='span_code_line'></span>
<span class='span_code_line'></span> <span class='code_key'>this</span>.OutputOptions.Add(<span class='code_string'>"OUT_1"</span>, <span class='code_key'>typeof</span>(<span class='code_key'>string</span>), <span class='code_key'>false</span>);
<span class='span_code_line'></span> <span class='code_key'>this</span>.OutputOptions.Add(<span class='code_class'>STNodeOption</span>.Empty);
<span class='span_code_line'></span> <span class='code_key'>this</span>.OutputOptions.Add(<span class='code_class'>STNodeOption</span>.Empty);
<span class='span_code_line'></span> <span class='code_key'>this</span>.OutputOptions.Add(<span class='code_string'>"OUT_2"</span>, <span class='code_key'>typeof</span>(<span class='code_key'>string</span>), <span class='code_key'>false</span>);
<span class='span_code_line'></span> }
<span class='span_code_line'></span>}</pre>
</div>
<img width=208 src='./images/tu_mynode_empty.png'/>
<p>a <span class='span_mark'>STNodeOption</span> height can set by <span class='span_mark'>STNode.ItemHeight(protected)</span></p>
<hr/>
<div><h2 class='h_option anchor_point' anchor='a_g'>STNode.AutoSize</h2></div>
<hr/>
<p><span class='span_mark'>AutoSize</span>default value is<span class='span_mark'>true</span>,when <span class='span_mark'>AitoSize</span> is set <span class='span_mark'>Width</span><span class='span_mark'>Height</span>can not be set</p>
<div class='div_code'>
<pre class='pre_code'><span class='span_code_line'></span><span class='code_key'>public</span> <span class='code_key'>class</span> <span class='code_class'>MyNode</span> : <span class='code_class'>STNode</span> {
<span class='span_code_line'></span> <span class='code_key'>protected</span> <span class='code_key'>override</span> <span class='code_key'>void</span> OnCreate() {
<span class='span_code_line'></span> <span class='code_key'>base</span>.OnCreate();
<span class='span_code_line'></span> <span class='code_key'>this</span>.Title = <span class='code_string'>"MyNode"</span>;
<span class='span_code_line'></span> <span class='code_key'>this</span>.TitleColor = <span class='code_class'>Color</span>.FromArgb(200, <span class='code_class'>Color</span>.Goldenrod);
<span class='span_code_line'></span>
<span class='span_code_line'></span> <span class='code_key'>this</span>.InputOptions.Add(<span class='code_string'>"IN"</span>, <span class='code_key'>typeof</span>(<span class='code_key'>string</span>), <span class='code_key'>false</span>);
<span class='span_code_line'></span> <span class='code_key'>this</span>.OutputOptions.Add(<span class='code_string'>"OUT"</span>, <span class='code_key'>typeof</span>(<span class='code_key'>string</span>), <span class='code_key'>false</span>);
<span class='span_code_line'></span> <span class='code_note'>//you should close AutoSize Model before you set the node size</span>
<span class='span_code_line'></span> <span class='code_key'>this</span>.AutoSize = <span class='code_key'>false</span>;
<span class='span_code_line'></span> <span class='code_key'>this</span>.Size = <span class='code_key'>new</span> <span class='code_class'>Size</span>(100, 100);
<span class='span_code_line'></span> }
<span class='span_code_line'></span>}</pre>
</div>
<img width=208 src='./images/tu_mynode_autosize.png'/>
<p>You can see that the size of <span class='span_mark'>MyNode</span> is no longer automatically calculated, but the position of <span class='span_mark'>STNodeOption</span> will still be automatically calculated. If you want to modify the position of <span class='span_mark'>STNodeOption</span>, you can override <span class='span_mark'>OnSetOptionXXX</span></p>
<div class='div_code'>
<pre class='pre_code'><span class='span_code_line'></span><span class='code_key'>public</span> <span class='code_key'>class</span> <span class='code_class'>MyNode</span> : <span class='code_class'>STNode</span>
<span class='span_code_line'></span>{
<span class='span_code_line'></span> <span class='code_key'>private</span> <span class='code_class'>STNodeOption</span> m_op_in;
<span class='span_code_line'></span> <span class='code_key'>private</span> <span class='code_class'>STNodeOption</span> m_op_out;
<span class='span_code_line'></span>
<span class='span_code_line'></span> <span class='code_key'>protected</span> <span class='code_key'>override</span> <span class='code_key'>void</span> OnCreate() {
<span class='span_code_line'></span> <span class='code_key'>base</span>.OnCreate();
<span class='span_code_line'></span> <span class='code_key'>this</span>.Title = <span class='code_string'>"MyNode"</span>;
<span class='span_code_line'></span> <span class='code_key'>this</span>.TitleColor = <span class='code_class'>Color</span>.FromArgb(200, <span class='code_class'>Color</span>.Goldenrod);
<span class='span_code_line'></span>
<span class='span_code_line'></span> m_op_in = <span class='code_key'>this</span>.InputOptions.Add(<span class='code_string'>"IN"</span>, <span class='code_key'>typeof</span>(<span class='code_key'>string</span>), <span class='code_key'>false</span>);
<span class='span_code_line'></span> m_op_out = <span class='code_key'>this</span>.OutputOptions.Add(<span class='code_string'>"OUT"</span>, <span class='code_key'>typeof</span>(<span class='code_key'>string</span>), <span class='code_key'>false</span>);
<span class='span_code_line'></span> <span class='code_key'>this</span>.AutoSize = <span class='code_key'>false</span>;
<span class='span_code_line'></span> <span class='code_key'>this</span>.Size = <span class='code_key'>new</span> <span class='code_class'>Size</span>(100, 100);
<span class='span_code_line'></span> }
<span class='span_code_line'></span> <span class='code_note'>//you can set the location of STNodeOption when the AutoSize is true</span>
<span class='span_code_line'></span> <span class='code_key'>protected</span> <span class='code_key'>override</span> <span class='code_class'>Point</span> OnSetOptionDotLocation(<span class='code_class'>STNodeOption</span> op, <span class='code_class'>Point</span> pt, <span class='code_key'>int</span> nIndex) {
<span class='span_code_line'></span> <span class='code_key'>if</span> (op == m_op_in) <span class='code_key'>return</span> <span class='code_key'>new</span> <span class='code_class'>Point</span>(pt.X, pt.Y + 20);
<span class='span_code_line'></span> <span class='code_key'>return</span> <span class='code_key'>base</span>.OnSetOptionDotLocation(op, pt, nIndex);
<span class='span_code_line'></span> }
<span class='span_code_line'></span> <span class='code_note'>//you can set the rectangle of STNodeOption when the AutoSize is true</span>
<span class='span_code_line'></span> <span class='code_key'>protected</span> <span class='code_key'>override</span> <span class='code_class'>Rectangle</span> OnSetOptionTextRectangle(<span class='code_class'>STNodeOption</span> op, <span class='code_class'>Rectangle</span> rect, <span class='code_key'>int</span> nIndex) {
<span class='span_code_line'></span> <span class='code_key'>if</span> (op == m_op_out) <span class='code_key'>return</span> <span class='code_key'>new</span> <span class='code_class'>Rectangle</span>(rect.X, rect.Y + 20, rect.Width, rect.Height);
<span class='span_code_line'></span> <span class='code_key'>return</span> <span class='code_key'>base</span>.OnSetOptionTextRectangle(op, rect, nIndex);
<span class='span_code_line'></span> }
<span class='span_code_line'></span>}</pre>
</div>
<img width=208 src='./images/tu_mynode_autosize_onset.png'/>
<p>You can see that in the code, the point and text area of the <span class='span_mark'>STNodeOption</span> connection line are modified by overloading the function. Why is it not designed to be <span class='span_mark'>STNodeOption.DotLeft=xxx</span> because the author thinks it will be more troublesome.</p>
<p>The <span class='span_mark'>pt</span> and <span class='span_mark'>rect</span> passed in the overloaded function are all automatically calculated data so that the developer will have a certain reference when modifying the position. If the method is <span class='span_mark'>STNodeOption.DotLeft=xxx</span>, the developer cannot Obtaining a reference position requires all calculations by yourself</p>
<p>It also needs to bind events such as <span class='span_mark'>STNode.Resize</span> to monitor the changes in the size of <span class='span_mark'>STNode</span> to recalculate the position, so the <span class='span_mark'>OnSetOptionXXX</span> method is more friendly in comparison.</p>
<p>All instances currently have no functions. In the next cases, functions will be added.</p>
<hr/>
<div><h2 class='h_option anchor_point' anchor='a_h'>e.g. - ClockNode</h2></div>
<hr/>
<p><span class='span_mark'>STNodeOption</span> can get all the data input of this option by binding to the <span class='span_mark'>DataTransfer</span> event</p>
<p><span class='span_mark'>STNodeOption.TransferData(object)</span> function can transfer data to all connections on this option</p>
<p>Next, implement a functional node. The best example for now is to create a clock node.</p>
<p>Because the content introduced so far is not enough to be able to freely provide arbitrary data to nodes, a node that can generate data by itself is needed.</p>
<p>The node outputs the current system time every second</p>
<div class='div_code'>
<pre class='pre_code'><span class='span_code_line'></span><span class='code_key'>public</span> <span class='code_key'>class</span> <span class='code_class'>ClockNode</span> : <span class='code_class'>STNode</span>
<span class='span_code_line'></span>{
<span class='span_code_line'></span> <span class='code_key'>private</span> <span class='code_class'>Thread</span> m_thread;
<span class='span_code_line'></span> <span class='code_key'>private</span> <span class='code_class'>STNodeOption</span> m_op_out_time;
<span class='span_code_line'></span>
<span class='span_code_line'></span> <span class='code_key'>protected</span> <span class='code_key'>override</span> <span class='code_key'>void</span> OnCreate() {
<span class='span_code_line'></span> <span class='code_key'>base</span>.OnCreate();
<span class='span_code_line'></span> <span class='code_key'>this</span>.Title = <span class='code_string'>"ClockNode"</span>;
<span class='span_code_line'></span> m_op_out_time = <span class='code_key'>this</span>.OutputOptions.Add(<span class='code_string'>"Time"</span>, <span class='code_key'>typeof</span>(<span class='code_class'>DateTime</span>), <span class='code_key'>false</span>);
<span class='span_code_line'></span> }
<span class='span_code_line'></span> <span class='code_note'>//when the owner changed</span>
<span class='span_code_line'></span> <span class='code_key'>protected</span> <span class='code_key'>override</span> <span class='code_key'>void</span> OnOwnerChanged() {
<span class='span_code_line'></span> <span class='code_key'>base</span>.OnOwnerChanged();
<span class='span_code_line'></span> <span class='code_key'>if</span> (<span class='code_key'>this</span>.Owner == <span class='code_key'>null</span>) { <span class='code_note'>//when the owner is null abort thread</span>
<span class='span_code_line'></span> <span class='code_key'>if</span> (m_thread != <span class='code_key'>null</span>) m_thread.Abort();
<span class='span_code_line'></span> <span class='code_key'>return</span>;
<span class='span_code_line'></span> }
<span class='span_code_line'></span> <span class='code_key'>this</span>.Owner.SetTypeColor(<span class='code_key'>typeof</span>(<span class='code_class'>DateTime</span>), <span class='code_class'>Color</span>.DarkCyan);
<span class='span_code_line'></span> m_thread = <span class='code_key'>new</span> <span class='code_class'>Thread</span>(() => {
<span class='span_code_line'></span> <span class='code_key'>while</span> (<span class='code_key'>true</span>) {
<span class='span_code_line'></span> <span class='code_class'>Thread</span>.Sleep(1000);
<span class='span_code_line'></span> <span class='code_note'>//STNodeOption.TransferData(object) will automatically post data to all connections on the option</span>
<span class='span_code_line'></span> <span class='code_note'>//STNodeOption.TransferData(object) will set STNodeOption.Data automatically</span>
<span class='span_code_line'></span> m_op_out_time.TransferData(<span class='code_class'>DateTime</span>.Now);
<span class='span_code_line'></span> <span class='code_note'>//if you need to operate across UI threads in a thread, the node provides Begin/Invoke() to complete the operation.</span>
<span class='span_code_line'></span> <span class='code_note'>//this.BeginInvoke(new MethodInvoker(() => m_op_out_time.TransferData(DateTime.Now)));</span>
<span class='span_code_line'></span> }
<span class='span_code_line'></span> }) { IsBackground = <span class='code_key'>true</span> };
<span class='span_code_line'></span> m_thread.Start();
<span class='span_code_line'></span> }
<span class='span_code_line'></span>}</pre>
</div>
<p>Of course, we can directly display the time of the above node, but in order to demonstrate the data transfer, we also need a node that accepts the data</p>
<div class='div_code'>
<pre class='pre_code'><span class='span_code_line'></span><span class='code_key'>public</span> <span class='code_key'>class</span> <span class='code_class'>ShowClockNode</span> : <span class='code_class'>STNode</span> {
<span class='span_code_line'></span> <span class='code_key'>private</span> <span class='code_class'>STNodeOption</span> m_op_time_in;
<span class='span_code_line'></span> <span class='code_key'>protected</span> <span class='code_key'>override</span> <span class='code_key'>void</span> OnCreate() {
<span class='span_code_line'></span> <span class='code_key'>base</span>.OnCreate();
<span class='span_code_line'></span> <span class='code_key'>this</span>.Title = <span class='code_string'>"ShowTime"</span>;
<span class='span_code_line'></span> <span class='code_note'>//use "single-connection" model</span>
<span class='span_code_line'></span> m_op_time_in = <span class='code_key'>this</span>.InputOptions.Add(<span class='code_string'>"--"</span>, <span class='code_key'>typeof</span>(<span class='code_class'>DateTime</span>), <span class='code_key'>true</span>);
<span class='span_code_line'></span> <span class='code_note'>//This event is triggered when data is transferred to m_op_time_in</span>
<span class='span_code_line'></span> m_op_time_in.DataTransfer += <span class='code_key'>new</span> STNodeOptionEventHandler(op_DataTransfer);
<span class='span_code_line'></span> }
<span class='span_code_line'></span>
<span class='span_code_line'></span> <span class='code_key'>void</span> op_DataTransfer(<span class='code_key'>object</span> sender, <span class='code_class'>STNodeOptionEventArgs</span> e) {
<span class='span_code_line'></span> <span class='code_note'>//This event is not only triggered when there is data coming in.</span>
<span class='span_code_line'></span> <span class='code_note'>//This event is also triggered when there is a connection or disconnection,</span>
<span class='span_code_line'></span> <span class='code_note'>//so you need to check the connection status.</span>
<span class='span_code_line'></span> <span class='code_key'>if</span> (e.Status != <span class='code_class'>ConnectionStatus</span>.Connected || e.TargetOption.Data == <span class='code_key'>null</span>) {
<span class='span_code_line'></span> <span class='code_note'>//When STNode.AutoSize=true, it is not recommended to use STNode.SetOptionText</span>
<span class='span_code_line'></span> <span class='code_note'>//the STNode will recalculate the layout every time the Text changes.</span>
<span class='span_code_line'></span> <span class='code_note'>//It should be displayed by adding controls.</span>
<span class='span_code_line'></span> <span class='code_note'>//Since STNodeControl has not yet been mentioned, the current design will be used for now.</span>
<span class='span_code_line'></span> <span class='code_key'>this</span>.SetOptionText(m_op_time_in, <span class='code_string'>"--"</span>);
<span class='span_code_line'></span> } <span class='code_key'>else</span> {
<span class='span_code_line'></span> <span class='code_key'>this</span>.SetOptionText(m_op_time_in, ((<span class='code_class'>DateTime</span>)e.TargetOption.Data).ToString());
<span class='span_code_line'></span> }
<span class='span_code_line'></span> }
<span class='span_code_line'></span>}</pre>
</div>
<p>add to STNodeEditor</p>
<img src='./images/tu_clockshowtime.gif'/>
<p>You can see that <span class='span_mark'>ShowClockNode</span> is refreshing every second</p>
<hr/>
<div><h2 class='h_option anchor_point' anchor='a_i'>STNode.SetOptionXXX</h2></div>
<hr/>
<p>In the above and previous examples, we can see that when some properties of <span class='span_mark'>STNodeOption</span> need to be modified, they are not modified in the way of <span class='span_mark'>STNodeOption.XXX=XXX</span>. This design is for safety.</p>
<p>The author thinks that <span class='span_mark'>STNodeOption</span> can only be modified by its owner, and the method of <span class='span_mark'>STNodeOption.XXX=XXX</span> cannot know who modified it and <span class='span_mark'>STNode.SetOptionXXX()</span> is marked by <span class='span_mark'>protected</span> only internally Is called and inside the function will check whether <span class='span_mark'>STNodeOption.Owner</span> is the current class</p>
<hr/>
<div><h2 class='h_option anchor_point' anchor='a_j'>About TransferData</h2></div>
<hr/>
<p class='p_hightlight'>It is not necessary to <span class='span_mark'>STNodeOption.TransferData(object)</span> to transfer data. <span class='span_mark'>TransferData(object)</span> only actively <span class='span_mark'>updates data</span></p>
<p>When a new connection is successful, the <span class='span_mark'>DataTransfer</span> event will also be triggered, the following will modify the code of <span class='span_mark'>ClockNode</span></p>
<div class='div_code'>
<pre class='pre_code'><span class='span_code_line'></span><span class='code_key'>public</span> <span class='code_key'>class</span> <span class='code_class'>ClockNode</span> : <span class='code_class'>STNode</span>
<span class='span_code_line'></span>{
<span class='span_code_line'></span> <span class='code_key'>private</span> <span class='code_class'>Thread</span> m_thread;
<span class='span_code_line'></span> <span class='code_key'>private</span> <span class='code_class'>STNodeOption</span> m_op_out_time;
<span class='span_code_line'></span>
<span class='span_code_line'></span> <span class='code_key'>protected</span> <span class='code_key'>override</span> <span class='code_key'>void</span> OnCreate() {
<span class='span_code_line'></span> <span class='code_key'>base</span>.OnCreate();
<span class='span_code_line'></span> <span class='code_key'>this</span>.Title = <span class='code_string'>"ClockNode"</span>;
<span class='span_code_line'></span> m_op_out_time = <span class='code_key'>this</span>.OutputOptions.Add(<span class='code_string'>"Time"</span>, <span class='code_key'>typeof</span>(<span class='code_class'>DateTime</span>), <span class='code_key'>false</span>);
<span class='span_code_line'></span> <span class='code_note'>//Assign value to option data</span>
<span class='span_code_line'></span> m_op_out_time.Data = <span class='code_class'>DateTime</span>.Now;
<span class='span_code_line'></span> }
<span class='span_code_line'></span>}</pre>
</div>
<img src='./images/tu_clocknode_data.gif'/>
<p>You can see that <span class='span_mark'>ShowClockNode</span> still shows the time but the data does not change because the <span class='span_mark'>DataTransfer</span> event will be triggered when the connection is successful. In the event, <span class='span_mark'>ShowClockNode</span> gets the data of the <span class='span_mark'>ClockNode</span> option through <span class='span_mark'>e.TargetOption.Data</span></p>
<p>When a connection is successful and disconnected, the event trigger sequence is as follows</p>
<p><span class='span_mark'>Connecting</span>-<span class='span_mark'>Connected</span>-<span class='span_mark'>DataTransfer</span> | <span class='span_mark'>DisConnecting</span>-<span class='span_mark'>DataTransfer</span>-<span class='span_mark'>DisConnected</span></p>
<hr/>
<div><h2 class='h_option anchor_point' anchor='a_k'>STNodeHub</h2></div>
<hr/>
<img src='./images/stnodehub.gif'/>
<p><span class='span_mark'>STNodeHub</span> is a built-in node that can disperse one output to multiple inputs or concentrate multiple outputs on one input point to prevent repeated connections. It can also be used for layout when the node connection is complicated.</p>
<h1 class='h_title anchor_point' anchor='a_l'>[basic]STNodeControl</h1>
<p>As the base class of <span class='span_mark'>STNode</span> control, <span class='span_mark'>STNodeControl</span> has many properties and events with the same name as <span class='span_mark'>System.Windows.Forms.Control</span>, allowing developers to develop a node like a <span class='span_mark'>WinForm</span> program.</p>
<p class='p_hightlight'>In this version (2.0), no available control is provided. Only the <span class='span_mark'>STNodeControl</span> base class needs to be extended by the developer. If available later, the author will improve it.</p>
<hr/>
<div><h2 class='h_option anchor_point' anchor='a_m'>add a control</h2></div>
<hr/>
<p>Same as <span class='span_mark'>System.Windows.Forms.Control</span> <span class='span_mark'>STNode</span> has the <span class='span_mark'>Controls</span> collection and its data type is <span class='span_mark'>STNodeControl</span></p>
<div class='div_code'>
<pre class='pre_code'><span class='span_code_line'></span><span class='code_key'>public</span> <span class='code_key'>class</span> <span class='code_class'>MyNode</span> : <span class='code_class'>STNode</span>
<span class='span_code_line'></span>{
<span class='span_code_line'></span> <span class='code_key'>protected</span> <span class='code_key'>override</span> <span class='code_key'>void</span> OnCreate() {
<span class='span_code_line'></span> <span class='code_key'>base</span>.OnCreate();
<span class='span_code_line'></span> <span class='code_key'>this</span>.Title = <span class='code_string'>"MyNode"</span>;
<span class='span_code_line'></span> <span class='code_key'>this</span>.TitleColor = <span class='code_class'>Color</span>.FromArgb(200, <span class='code_class'>Color</span>.Goldenrod);
<span class='span_code_line'></span> <span class='code_key'>this</span>.AutoSize = <span class='code_key'>false</span>;
<span class='span_code_line'></span> <span class='code_key'>this</span>.Size = <span class='code_key'>new</span> <span class='code_class'>Size</span>(100, 100);
<span class='span_code_line'></span>
<span class='span_code_line'></span> <span class='code_key'>var</span> ctrl = <span class='code_key'>new</span> <span class='code_class'>STNodeControl</span>();
<span class='span_code_line'></span> ctrl.Text = <span class='code_string'>"Button"</span>;
<span class='span_code_line'></span> ctrl.Location = <span class='code_key'>new</span> <span class='code_class'>Point</span>(10, 10);
<span class='span_code_line'></span> <span class='code_key'>this</span>.Controls.Add(ctrl);
<span class='span_code_line'></span> ctrl.MouseClick += <span class='code_key'>new</span> <span class='code_class'>MouseEventHandler</span>(ctrl_MouseClick);
<span class='span_code_line'></span> }
<span class='span_code_line'></span>
<span class='span_code_line'></span> <span class='code_key'>void</span> ctrl_MouseClick(<span class='code_key'>object</span> sender, <span class='code_class'>MouseEventArgs</span> e) {
<span class='span_code_line'></span> <span class='code_class'>MessageBox</span>.Show(<span class='code_string'>"MouseClick"</span>);
<span class='span_code_line'></span> }
<span class='span_code_line'></span>}</pre>
</div>
<img width=273 src='./images/tu_mynode_ctrl.png'/>
<p>You can see that there is almost no difference between developing a <span class='span_mark'>WinForm</span> program. The only difference is that <span class='span_mark'>STNode</span> does not yet provide a WYSIWYG UI designer.</p>
<hr/>
<div><h2 class='h_option anchor_point' anchor='a_n'>Customize a Button</h2></div>
<hr/>
<p>Although the above code looks like it adds a button control, in fact it is just the default drawing style of <span class='span_mark'>STNodeControl</span></p>
<p>The following is to customize a <span class='span_mark'>Button</span> control with mouse hovering and clicking effects to make it more like a button</p>
<div class='div_code'>
<pre class='pre_code'><span class='span_code_line'></span><span class='code_key'>public</span> <span class='code_key'>class</span> <span class='code_class'>STNodeButton</span> : <span class='code_class'>STNodeControl</span> {
<span class='span_code_line'></span>
<span class='span_code_line'></span> <span class='code_key'>private</span> <span class='code_key'>bool</span> m_b_enter;
<span class='span_code_line'></span> <span class='code_key'>private</span> <span class='code_key'>bool</span> m_b_down;
<span class='span_code_line'></span>
<span class='span_code_line'></span> <span class='code_key'>protected</span> <span class='code_key'>override</span> <span class='code_key'>void</span> OnMouseEnter(<span class='code_class'>EventArgs</span> e) {
<span class='span_code_line'></span> <span class='code_key'>base</span>.OnMouseEnter(e);
<span class='span_code_line'></span> m_b_enter = <span class='code_key'>true</span>;
<span class='span_code_line'></span> <span class='code_key'>this</span>.Invalidate();
<span class='span_code_line'></span> }
<span class='span_code_line'></span>
<span class='span_code_line'></span> <span class='code_key'>protected</span> <span class='code_key'>override</span> <span class='code_key'>void</span> OnMouseLeave(<span class='code_class'>EventArgs</span> e) {
<span class='span_code_line'></span> <span class='code_key'>base</span>.OnMouseLeave(e);
<span class='span_code_line'></span> m_b_enter = <span class='code_key'>false</span>;
<span class='span_code_line'></span> <span class='code_key'>this</span>.Invalidate();
<span class='span_code_line'></span> }
<span class='span_code_line'></span>
<span class='span_code_line'></span> <span class='code_key'>protected</span> <span class='code_key'>override</span> <span class='code_key'>void</span> OnMouseDown(<span class='code_class'>MouseEventArgs</span> e) {
<span class='span_code_line'></span> <span class='code_key'>base</span>.OnMouseDown(e);
<span class='span_code_line'></span> m_b_down = <span class='code_key'>true</span>;
<span class='span_code_line'></span> <span class='code_key'>this</span>.Invalidate();
<span class='span_code_line'></span> }
<span class='span_code_line'></span>
<span class='span_code_line'></span> <span class='code_key'>protected</span> <span class='code_key'>override</span> <span class='code_key'>void</span> OnMouseUp(<span class='code_class'>MouseEventArgs</span> e) {
<span class='span_code_line'></span> <span class='code_key'>base</span>.OnMouseUp(e);
<span class='span_code_line'></span> m_b_down = <span class='code_key'>false</span>;
<span class='span_code_line'></span> <span class='code_key'>this</span>.Invalidate();
<span class='span_code_line'></span> }
<span class='span_code_line'></span>
<span class='span_code_line'></span> <span class='code_key'>protected</span> <span class='code_key'>override</span> <span class='code_key'>void</span> OnPaint(<span class='code_class'>DrawingTools</span> dt) {
<span class='span_code_line'></span> <span class='code_note'>//base.OnPaint(dt);</span>
<span class='span_code_line'></span> <span class='code_class'>Graphics</span> g = dt.Graphics;
<span class='span_code_line'></span> <span class='code_class'>SolidBrush</span> brush = dt.<span class='code_class'>SolidBrush</span>;
<span class='span_code_line'></span> brush.<span class='code_class'>Color</span> = <span class='code_key'>base</span>.BackColor;
<span class='span_code_line'></span> <span class='code_key'>if</span> (m_b_down) brush.<span class='code_class'>Color</span> = <span class='code_class'>Color</span>.SkyBlue;
<span class='span_code_line'></span> <span class='code_key'>else</span> <span class='code_key'>if</span> (m_b_enter) brush.<span class='code_class'>Color</span> = <span class='code_class'>Color</span>.DodgerBlue;
<span class='span_code_line'></span> g.FillRectangle(brush, 0, 0, <span class='code_key'>this</span>.Width, <span class='code_key'>this</span>.Height);
<span class='span_code_line'></span> g.DrawString(<span class='code_key'>this</span>.Text, <span class='code_key'>this</span>.Font, <span class='code_class'>Brushes</span>.White, <span class='code_key'>this</span>.ClientRectangle, <span class='code_key'>base</span>.m_sf);
<span class='span_code_line'></span> }
<span class='span_code_line'></span>}</pre>
</div>
<img src='./images/tu_mynode_btn.gif'/>
<p>Of course, in order to make the code as simple as possible, the effect of the button is written in the code. The above code is just to demonstrate how to build a custom control. Of course, you need to have some <span class='span_mark'>GDI</span> related knowledge before this.</p>
<p><GDI+Programming> is a good book</p>
<img src='./images/gdip.png'/>
<hr/>
<div><h2 class='h_option anchor_point' anchor='a_o'>e.g. - Image info</h2></div>
<hr/>
<p>In the above <span class='span_mark'>ClockNode</span> case, the data for the data is written in the node through code. Next, in this case, the data is obtained through the <span class='span_mark'>STNodeButton</span> for output.</p>
<div class='div_code'>
<pre class='pre_code'><span class='span_code_line'></span><span class='code_key'>public</span> <span class='code_key'>class</span> <span class='code_class'>ImageShowNode</span> : <span class='code_class'>STNode</span>
<span class='span_code_line'></span>{
<span class='span_code_line'></span> <span class='code_key'>private</span> <span class='code_class'>STNodeOption</span> m_op_out;
<span class='span_code_line'></span>
<span class='span_code_line'></span> <span class='code_key'>protected</span> <span class='code_key'>override</span> <span class='code_key'>void</span> OnCreate() {
<span class='span_code_line'></span> <span class='code_key'>base</span>.OnCreate();
<span class='span_code_line'></span> <span class='code_key'>this</span>.Title = <span class='code_string'>"ImageShowNode"</span>;
<span class='span_code_line'></span> <span class='code_key'>this</span>.TitleColor = <span class='code_class'>Color</span>.FromArgb(200, <span class='code_class'>Color</span>.Goldenrod);
<span class='span_code_line'></span> <span class='code_key'>this</span>.AutoSize = <span class='code_key'>false</span>;
<span class='span_code_line'></span> <span class='code_key'>this</span>.Size = <span class='code_key'>new</span> <span class='code_class'>Size</span>(160, 150);
<span class='span_code_line'></span> m_op_out = <span class='code_key'>this</span>.OutputOptions.Add(<span class='code_string'>""</span>, <span class='code_key'>typeof</span>(<span class='code_class'>Image</span>), <span class='code_key'>false</span>);
<span class='span_code_line'></span>
<span class='span_code_line'></span> <span class='code_key'>var</span> ctrl = <span class='code_key'>new</span> <span class='code_class'>STNodeButton</span>();
<span class='span_code_line'></span> ctrl.Text = <span class='code_string'>"Open Image"</span>;
<span class='span_code_line'></span> ctrl.Location = <span class='code_key'>new</span> <span class='code_class'>Point</span>(5, 0);
<span class='span_code_line'></span> ctrl.Size = <span class='code_key'>new</span> <span class='code_class'>Size</span>(150, 20);
<span class='span_code_line'></span> <span class='code_key'>this</span>.Controls.Add(ctrl);
<span class='span_code_line'></span> ctrl.MouseClick += <span class='code_key'>new</span> <span class='code_class'>MouseEventHandler</span>(ctrl_MouseClick);
<span class='span_code_line'></span> }
<span class='span_code_line'></span>
<span class='span_code_line'></span> <span class='code_key'>void</span> ctrl_MouseClick(<span class='code_key'>object</span> sender, <span class='code_class'>MouseEventArgs</span> e) {
<span class='span_code_line'></span> OpenFileDialog ofd = <span class='code_key'>new</span> OpenFileDialog();
<span class='span_code_line'></span> ofd.Filter = <span class='code_string'>"*.png|*.png|*.jpg|*.jpg"</span>;
<span class='span_code_line'></span> <span class='code_key'>if</span> (ofd.ShowDialog() != DialogResult.OK) <span class='code_key'>return</span>;
<span class='span_code_line'></span> m_op_out.TransferData(<span class='code_class'>Image</span>.FromFile(ofd.FileName), <span class='code_key'>true</span>);
<span class='span_code_line'></span> <span class='code_key'>this</span>.Invalidate();
<span class='span_code_line'></span> }
<span class='span_code_line'></span>
<span class='span_code_line'></span> <span class='code_key'>protected</span> <span class='code_key'>override</span> <span class='code_key'>void</span> OnDrawBody(<span class='code_class'>DrawingTools</span> dt) {
<span class='span_code_line'></span> <span class='code_key'>base</span>.OnDrawBody(dt);
<span class='span_code_line'></span> <span class='code_note'>//of course you can extended STNodeControl to build a "STNodePictureBox" for display image</span>
<span class='span_code_line'></span> <span class='code_class'>Graphics</span> g = dt.<span class='code_class'>Graphics</span>;
<span class='span_code_line'></span> <span class='code_class'>Rectangle</span> rect = <span class='code_key'>new</span> <span class='code_class'>Rectangle</span>(<span class='code_key'>this</span>.Left + 5, <span class='code_key'>this</span>.Top + <span class='code_key'>this</span>.TitleHeight + 20, 150, 105);
<span class='span_code_line'></span> g.FillRectangle(<span class='code_class'>Brushes</span>.Gray, rect);
<span class='span_code_line'></span> <span class='code_key'>if</span> (m_op_out.Data != <span class='code_key'>null</span>)
<span class='span_code_line'></span> g.DrawImage((<span class='code_class'>Image</span>)m_op_out.Data, rect);
<span class='span_code_line'></span> }
<span class='span_code_line'></span>}</pre>
</div>
<p>Now we need a node to get image size.</p>
<div class='div_code'>
<pre class='pre_code'><span class='span_code_line'></span><span class='code_key'>public</span> <span class='code_key'>class</span> <span class='code_class'>ImageSizeNode</span> : <span class='code_class'>STNode</span> {
<span class='span_code_line'></span>
<span class='span_code_line'></span> <span class='code_key'>private</span> <span class='code_class'>STNodeOption</span> m_op_in;
<span class='span_code_line'></span>
<span class='span_code_line'></span> <span class='code_key'>protected</span> <span class='code_key'>override</span> <span class='code_key'>void</span> OnCreate() {
<span class='span_code_line'></span> <span class='code_key'>base</span>.OnCreate();
<span class='span_code_line'></span> <span class='code_key'>this</span>.Title = <span class='code_string'>"ImageSize"</span>;
<span class='span_code_line'></span> <span class='code_key'>this</span>.TitleColor = <span class='code_class'>Color</span>.FromArgb(200, <span class='code_class'>Color</span>.Goldenrod);
<span class='span_code_line'></span> m_op_in = <span class='code_key'>this</span>.InputOptions.Add(<span class='code_string'>"--"</span>, <span class='code_key'>typeof</span>(<span class='code_class'>Image</span>), <span class='code_key'>true</span>);
<span class='span_code_line'></span> m_op_in.DataTransfer += <span class='code_key'>new</span> STNodeOptionEventHandler(m_op_in_DataTransfer);
<span class='span_code_line'></span> }
<span class='span_code_line'></span>
<span class='span_code_line'></span> <span class='code_key'>void</span> m_op_in_DataTransfer(<span class='code_key'>object</span> sender, <span class='code_class'>STNodeOptionEventArgs</span> e) {
<span class='span_code_line'></span> <span class='code_key'>if</span> (e.Status != <span class='code_class'>ConnectionStatus</span>.Connected || e.TargetOption.Data == <span class='code_key'>null</span>) {
<span class='span_code_line'></span> <span class='code_key'>this</span>.SetOptionText(m_op_in, <span class='code_string'>"--"</span>);
<span class='span_code_line'></span> } <span class='code_key'>else</span> {
<span class='span_code_line'></span> <span class='code_class'>Image</span> img = (<span class='code_class'>Image</span>)e.TargetOption.Data;
<span class='span_code_line'></span> <span class='code_key'>this</span>.SetOptionText(m_op_in, <span class='code_string'>"W:"</span> + img.Width + <span class='code_string'>" H:"</span> + img.Height);
<span class='span_code_line'></span> }
<span class='span_code_line'></span> }
<span class='span_code_line'></span>}</pre>
</div>
<img width=418 src='./images/tu_imagenode.png'/>
<p>clickthe <span class='span_mark'>Open Image</span> button, you can select an image and display it in the node. After the <span class='span_mark'>ImageSizeNode</span> is connected, the size of the image will be display.</p>
<p>The code of the <span class='span_mark'>ImageChannel</span> node is not given here. The code is used in the <span class='span_mark'>WinNodeEditorDemo</span> project to extract the RGB channel of an image. For <span class='span_mark'>ImageShowNode</span>, it just provides the data source and displays it. For the <span class='span_mark'>ImageSizeNode</span> and <span class='span_mark'>ImageChannel</span> nodes, they don"t know what node will be connected. They just complete their functions and package the results to the output option, waiting to be connected by the next node</p>
<p>The execution logic is completely connected by the user to connect their functions together. During the development, there is no interaction between nodes and nodes. The only thing that ties them together is an <span class='span_mark'>Image</span> data type, so that nodes and nodes There is no coupling relationship between them. High class poly low coupling.</p>
<h1 class='h_title anchor_point' anchor='a_p'>[basic]STNodeEditor</h1>
<p><span class='span_mark'>STNodeEditor</span> as a container of <span class='span_mark'>STNode</span> also provides a large number of properties and events for developers to use. For more detailed API list of <span class='span_mark'>STNodeEditor</span>, please refer to <a target='_bank' href='./doc.html'>API document</a></p>
<hr/>
<div><h2 class='h_option anchor_point' anchor='a_q'>Save canvas</h2></div>
<hr/>
<p>The relationship between nodes and connections in <span class='span_mark'>STNodeEditor</span> can be saved to a file</p>
<p>The contents of the canvas can be saved to a file through the <span class='span_mark'>STNodeEditor.SaveCanvas(string strFileName)</span> function</p>
<p class='p_hightlight'>Note that the <span class='span_mark'>SaveCanvas()</span> function will call the <span class='span_mark'>internal byte[] STNode.GetSaveData()</span> function to get the binary data of each node</p>
<p>The <span class='span_mark'>GetSaveData()</span> function does not serialize the node itself. The <span class='span_mark'>GetSaveData()</span> function binarizes the basic data and original attributes of the node itself and then calls <span class='span_mark'>virtual OnSaveNode(Dictionary<string, byte[]> dic)</span> to The expansion node asks for the data that the node needs to save</p>
<p class='p_hightlight'>If there is a saving requirement, the node developer may need to override the <span class='span_mark'>OnSaveNode()</span> function to ensure that some required data can be saved</p>
<p>More content about saving nodes will be introduced in the following content</p>
<hr/>
<div><h2 class='h_option anchor_point' anchor='a_r'>Load canvas</h2></div>
<hr/>
<p>The saved data can be loaded from the file through the <span class='span_mark'>STNodeEditor.LoadCanvas(string strFileName)</span> function</p>
<p class='p_hightlight'>If <span class='span_mark'>STNodeEditor</span> has nodes in other assemblies, you need to load the assembly by calling <span class='span_mark'>STNodeEditor.LoadAssembly(string strFile)</span> to ensure that the nodes in the file can be restored correctly</p>
<p>Because the restored node is not serialized, a node is dynamically created by <span class='span_mark'>(STNode)Activator.CreateInstance(stNodeType)</span> and then called <span class='span_mark'>virtual OnSaveNode(Dictionary<string, byte[]> dic)</span> to restore the data, while <span class='span_mark'>dic</span> Is the data saved by <span class='span_mark'>OnSaveNode()</span></p>
<p class='p_hightlight'>Because the restore node is dynamically created through reflection, an empty parameter constructor must be provided in the extended <span class='span_mark'>STNode</span></p>
<p>More content about saving nodes will be introduced in the following content</p>
<hr/>
<div><h2 class='h_option anchor_point' anchor='a_s'>useful event</h2></div>
<hr/>
<p><span class='span_mark'>ActiveChanged</span>,<span class='span_mark'>SelectedChanged</span> can monitor the selected changes of the node in the control</p>
<div class='div_code'>
<pre class='pre_code'><span class='span_code_line'></span>stNodeEditor1.ActiveChanged += (s, e) => <span class='code_class'>Console</span>.WriteLine(stNodeEditor1.ActiveNode.Title);
<span class='span_code_line'></span>
<span class='span_code_line'></span>stNodeEditor1.SelectedChanged += (s, e) => {
<span class='span_code_line'></span> <span class='code_key'>foreach</span>(<span class='code_key'>var</span> n <span class='code_key'>in</span> stNodeEditor1.GetSelectedNode()){
<span class='span_code_line'></span> <span class='code_class'>Console</span>.WriteLine(n.Title);
<span class='span_code_line'></span> }
<span class='span_code_line'></span>};</pre>
</div>
<p>If you want to display the scale on the editor after each zoom of the canvas, you can get it through the <span class='span_mark'>CanvasScaled</span> event</p>
<div class='div_code'>
<pre class='pre_code'><span class='span_code_line'></span>stNodeEditor1.CanvasScaled += (s, e) => {
<span class='span_code_line'></span> stNodeEditor1.ShowAlert(stNodeEditor1.CanvasScale.ToString(<span class='code_string'>"F2"</span>),
<span class='span_code_line'></span> <span class='code_class'>Color</span>.White, <span class='code_class'>Color</span>.FromArgb(127, 255, 255, 0));
<span class='span_code_line'></span>};</pre>
</div>
<p>If you want to display the connection status when there are nodes connected in the canvas, you can get the status through the <span class='span_mark'>OptionConnected</span> event</p>
<div class='div_code'>
<pre class='pre_code'><span class='span_code_line'></span>stNodeEditor1.OptionConnected += (s, e) => {
<span class='span_code_line'></span> stNodeEditor1.ShowAlert(e.Status.ToString(), <span class='code_class'>Color</span>.White,
<span class='span_code_line'></span> <span class='code_class'>Color</span>.FromArgb(125, e.Status <span class='code_class'>ConnectionStatus</span>.Connected ? <span class='code_class'>Color</span>.Lime : <span class='code_class'>Color</span>.Red));
<span class='span_code_line'></span>};</pre>
</div>
<hr/>
<div><h2 class='h_option anchor_point' anchor='a_t'>useful function</h2></div>
<hr/>
<div class='div_code'>
<pre class='pre_code'><span class='span_code_line'></span><span class='code_note'>/// <summary></span>
<span class='span_code_line'></span><span class='code_note'>/// <span class='code_note_1'>Move the origin position of the canvas to the specified control position</span></span>
<span class='span_code_line'></span><span class='code_note'>/// <span class='code_note_1'>(cannot be moved when there is no Node)</span></span>
<span class='span_code_line'></span><span class='code_note'>/// </summary></span>
<span class='span_code_line'></span><span class='code_note'>/// <param name=<span class='code_string'>"x"</span>><span class='code_note_1'>X</span></param></span>
<span class='span_code_line'></span><span class='code_note'>/// <param name=<span class='code_string'>"y"</span>><span class='code_note_1'>Y</span></param></span>
<span class='span_code_line'></span><span class='code_note'>/// <param name=<span class='code_string'>"bAnimation"</span>><span class='code_note_1'>use animation</span></param></span>
<span class='span_code_line'></span><span class='code_note'>/// <param name=<span class='code_string'>"ma"</span>><span class='code_note_1'>Specify the position that needs to be modified</span></param></span>
<span class='span_code_line'></span><span class='code_key'>public</span> <span class='code_key'>void</span> MoveCanvas(<span class='code_key'>float</span> x, <span class='code_key'>float</span> y, <span class='code_key'>bool</span> bAnimation, <span class='code_class'>CanvasMoveArgs</span> ma);</pre>
</div>
<hr/>
<div class='div_code'>
<pre class='pre_code'><span class='span_code_line'></span><span class='code_note'>/// <summary></span>
<span class='span_code_line'></span><span class='code_note'>/// <span class='code_note_1'>Scale Canvas(cannot be moved when there is no Node)</span></span>
<span class='span_code_line'></span><span class='code_note'>/// </summary></span>
<span class='span_code_line'></span><span class='code_note'>/// <param name=<span class='code_string'>"f"</span>><span class='code_note_1'>scale</span></param></span>
<span class='span_code_line'></span><span class='code_note'>/// <param name=<span class='code_string'>"x"</span>><span class='code_note_1'>The position of the zoom center X on the control</span></param></span>
<span class='span_code_line'></span><span class='code_note'>/// <param name=<span class='code_string'>"y"</span>><span class='code_note_1'>The position of the zoom center Y on the control</span></param></span>
<span class='span_code_line'></span><span class='code_key'>public</span> <span class='code_key'>void</span> ScaleCanvas(<span class='code_key'>float</span> f, <span class='code_key'>float</span> x, <span class='code_key'>float</span> y);</pre>
</div>
<hr/>
<div class='div_code'>
<pre class='pre_code'><span class='span_code_line'></span><span class='code_note'>/// <summary></span>
<span class='span_code_line'></span><span class='code_note'>/// <span class='code_note_1'>Add the default data type color to the editor</span></span>
<span class='span_code_line'></span><span class='code_note'>/// </summary></span>
<span class='span_code_line'></span><span class='code_note'>/// <param name=<span class='code_string'>"t"</span>><span class='code_note_1'>data type</span></param></span>
<span class='span_code_line'></span><span class='code_note'>/// <param name=<span class='code_string'>"clr"</span>><span class='code_note_1'>color</span></param></span>
<span class='span_code_line'></span><span class='code_note'>/// <param name=<span class='code_string'>"bReplace"</span>><span class='code_note_1'>replace</span></param></span>
<span class='span_code_line'></span><span class='code_note'>/// <returns><span class='code_note_1'>new color</span></returns></span>
<span class='span_code_line'></span><span class='code_key'>public</span> <span class='code_class'>Color</span> SetTypeColor(<span class='code_class'>Type</span> t, <span class='code_class'>Color</span> clr, <span class='code_key'>bool</span> bReplace);</pre>
</div>
<hr/>
<div class='div_code'>
<pre class='pre_code'><span class='span_code_line'></span><span class='code_note'>/// <summary></span>
<span class='span_code_line'></span><span class='code_note'>/// <span class='code_note_1'>Display information in the canvas</span></span>
<span class='span_code_line'></span><span class='code_note'>/// </summary></span>
<span class='span_code_line'></span><span class='code_note'>/// <param name=<span class='code_string'>"strText"</span>><span class='code_note_1'>message text</span></param></span>
<span class='span_code_line'></span><span class='code_note'>/// <param name=<span class='code_string'>"foreColor"</span>><span class='code_note_1'>fore color</span></param></span>
<span class='span_code_line'></span><span class='code_note'>/// <param name=<span class='code_string'>"backColor"</span>><span class='code_note_1'>back color</span></param></span>
<span class='span_code_line'></span><span class='code_note'>/// <param name=<span class='code_string'>"nTime"</span>><span class='code_note_1'>time</span></param></span>
<span class='span_code_line'></span><span class='code_note'>/// <param name=<span class='code_string'>"al"</span>><span class='code_note_1'>message location</span></param></span>
<span class='span_code_line'></span><span class='code_note'>/// <param name=<span class='code_string'>"bRedraw"</span>><span class='code_note_1'>redraw</span></param></span>
<span class='span_code_line'></span><span class='code_key'>void</span> ShowAlert(<span class='code_key'>string</span> strText, <span class='code_class'>Color</span> foreColor, <span class='code_class'>Color</span> backColor, <span class='code_key'>int</span> nTime, <span class='code_class'>AlertLocation</span> al, <span class='code_key'>bool</span> bRedraw);
<span class='span_code_line'></span><span class='code_note'>//e.g.</span>
<span class='span_code_line'></span>stNodeEditor1.ShowAlert(<span class='code_string'>"this is test info"</span>, <span class='code_class'>Color</span>.White, <span class='code_class'>Color</span>.FromArgb(200, <span class='code_class'>Color</span>.Yellow));</pre>
</div>
<img width=208 src='./images/tu_editor_alert.png'/>
<p>For more <span class='span_mark'>property</span><span class='span_mark'>function</span><span class='span_mark'>events</span>of<span class='span_mark'>STNodeEditor</span>, please refer to <a target='_bank' href='./doc.html'>API document</a> This document pays more attention to the example of <span class='span_mark'>STNode</span></p>
<h1 class='h_title anchor_point' anchor='a_u'>STNodePropertyGrid</h1>
<p><span class='span_mark'>STNodePropertyGrid</span> is another control released with the class library and can be used with <span class='span_mark'>STNodeEditor</span></p>
<img width=462 src='./images/tu_stnodepropertygrid.png'/>
<p>There are two panels in <span class='span_mark'>STNodePropertyGrid</span>, which can be switched by the button on the top <span class='span_mark'>Property Panel</span> and <span class='span_mark'>Node Information Panel</span>.</p>
<p class='p_hightlight'>Only include <span class='span_mark'>attribute</span> or <span class='span_mark'>node information</span> will display their panel</p>
<hr/>
<div><h2 class='h_option anchor_point' anchor='a_v'>how to use</h2></div>
<hr/>
<p>The core method of <span class='span_mark'>STNodePropertyGrid</span> is <span class='span_mark'>SetNode(STNode)</span> usually used with <span class='span_mark'>STNodeEditor</span></p>
<div class='div_code'>
<pre class='pre_code'><span class='span_code_line'></span>stNodeEditor1.ActiveChanged += (s, e) => stNodePropertyGrid1.SetNode(stNodeEditor1.ActiveNode);</pre>
</div>
<p><span class='span_mark'>STNode</span> is a <span class='span_mark'>class</span> which of course can have properties, and <span class='span_mark'>STNodePropertyGrid</span> is to display and modify them, just like what you see in the UI designer during the development of <span class='span_mark'>WinForm</span></p>
<p>Let"s try it</p>
<div class='div_code'>
<pre class='pre_code'><span class='span_code_line'></span><span class='code_key'>public</span> <span class='code_key'>class</span> <span class='code_class'>PropertyTestNode</span> : <span class='code_class'>STNode</span> {
<span class='span_code_line'></span>
<span class='span_code_line'></span> <span class='code_key'>private</span> <span class='code_key'>int</span> _Number;
<span class='span_code_line'></span>
<span class='span_code_line'></span> <span class='code_key'>public</span> <span class='code_key'>int</span> Number {
<span class='span_code_line'></span> <span class='code_key'>get</span> { <span class='code_key'>return</span> _Number; }
<span class='span_code_line'></span> <span class='code_key'>set</span> { _Number = <span class='code_key'>value</span>; }
<span class='span_code_line'></span> }
<span class='span_code_line'></span>
<span class='span_code_line'></span> <span class='code_key'>protected</span> <span class='code_key'>override</span> <span class='code_key'>void</span> OnCreate() {
<span class='span_code_line'></span> <span class='code_key'>base</span>.OnCreate();
<span class='span_code_line'></span> <span class='code_key'>this</span>.Title = <span class='code_string'>"PropertyTest"</span>;
<span class='span_code_line'></span> }
<span class='span_code_line'></span>}</pre>
</div>
<img width=462 src='./images/tu_property_1.png'/>
<p>You will find that the <span class='span_mark'>Number</span> property is not displayed as you expected</p>
<p>The difference with <span class='span_mark'>System.Windows.Forms.PropertyGrid</span> is that <span class='span_mark'>PropertyGrid</span> will display all the properties in <span class='span_mark'>class</span> while <span class='span_mark'>STNodePropertyGrid</span> does not.</p>
<p>S T N O D E PropertyGrid This is <span class='span_mark'>STNodePropertyGrid</span> and does not display a property casually, because the author thinks that the developer may not want all the properties in <span class='span_mark'>STNode</span> to be displayed, even if it needs to be displayed, the developer may not want to display a property. What you see in the window is <span class='span_mark'>Number</span> but a different name. After all, <span class='span_mark'>Number</span> is used when writing code</p>
<p class='p_hightlight'>Only property with the <span class='span_mark'>STNodePropertyAttribute</span> feature added will be displayed in the <span class='span_mark'>STNodePropertyGrid</span></p>
<hr/>
<div><h2 class='h_option anchor_point' anchor='a_w'>STNodePropertyAttribute</h2></div>
<hr/>
<p><span class='span_mark'>STNodePropertyAttribute</span> has three properties <span class='span_mark'>Name</span>, <span class='span_mark'>Description</span> and <span class='span_mark'>DescriptorType</span></p>
<p><span class='span_mark'>Name</span>-the name of this property that you want to display on <span class='span_mark'>STNodePropertyGrid</span></p>
<p><span class='span_mark'>Description</span>-The description you want to display when the <span class='span_mark'>left mouse button</span> is <span class='span_mark'>long press</span> the property name on <span class='span_mark'>STNodePropertyGrid</span></p>
<p><span class='span_mark'>DescriptorType</span>-Data interaction interface with properoty editor, this property will be mentioned later</p>
<p>The constructor of <span class='span_mark'>STNodePropertyAttribute</span> is <span class='span_mark'>STNodePropertyAttribute(string strName,string strDescription)</span></p>
<div class='div_code'>
<pre class='pre_code'><span class='span_code_line'></span><span class='code_key'>public</span> <span class='code_key'>class</span> <span class='code_class'>PropertyTestNode</span> : <span class='code_class'>STNode</span>
<span class='span_code_line'></span>{
<span class='span_code_line'></span> <span class='code_key'>private</span> <span class='code_key'>int</span> _Number;
<span class='span_code_line'></span> [<span class='code_class'>STNodeProperty</span>(<span class='code_string'>"Name"</span>, <span class='code_string'>"Description for this property"</span>)]
<span class='span_code_line'></span> <span class='code_key'>public</span> <span class='code_key'>int</span> Number {
<span class='span_code_line'></span> <span class='code_key'>get</span> { <span class='code_key'>return</span> _Number; }
<span class='span_code_line'></span> <span class='code_key'>set</span> { _Number = <span class='code_key'>value</span>; }
<span class='span_code_line'></span> }
<span class='span_code_line'></span>
<span class='span_code_line'></span> <span class='code_key'>protected</span> <span class='code_key'>override</span> <span class='code_key'>void</span> OnCreate() {
<span class='span_code_line'></span> <span class='code_key'>base</span>.OnCreate();
<span class='span_code_line'></span> <span class='code_key'>this</span>.Title = <span class='code_string'>"PropertyTest"</span>;
<span class='span_code_line'></span> }
<span class='span_code_line'></span>}</pre>
</div>
<img width=462 src='./images/tu_property_2.png'/>
<p>Now you can see that the property is displayed correctly and can be set, and long press the property name will display the description information</p>
<hr/>
<div><h2 class='h_option anchor_point' anchor='a_x'>STNodeAttribute</h2></div>
<hr/>
<p>If you want to display node information, <span class='span_mark'>STNode</span> needs to be marked with the <span class='span_mark'>STNodeAttribute</span> feature</p>
<div class='div_code'>
<pre class='pre_code'><span class='span_code_line'></span>[<span class='code_class'>STNode</span>(<span class='code_string'>"AA/BB"</span>, <span class='code_string'>"Author"</span>, <span class='code_string'>"Mail"</span>, <span class='code_string'>"Link"</span>, <span class='code_string'>"Description"</span>)]
<span class='span_code_line'></span><span class='code_key'>public</span> <span class='code_key'>class</span> <span class='code_class'>PropertyTestNode</span> : <span class='code_class'>STNode</span>
<span class='span_code_line'></span>{
<span class='span_code_line'></span> <span class='code_key'>private</span> <span class='code_key'>int</span> _Number;
<span class='span_code_line'></span> [<span class='code_class'>STNodeProperty</span>(<span class='code_string'>"Name"</span>, <span class='code_string'>"Description for this property"</span>)]
<span class='span_code_line'></span> <span class='code_key'>public</span> <span class='code_key'>int</span> Number {
<span class='span_code_line'></span> <span class='code_key'>get</span> { <span class='code_key'>return</span> _Number; }
<span class='span_code_line'></span> <span class='code_key'>set</span> { _Number = <span class='code_key'>value</span>; }
<span class='span_code_line'></span> }
<span class='span_code_line'></span>
<span class='span_code_line'></span> <span class='code_key'>protected</span> <span class='code_key'>override</span> <span class='code_key'>void</span> OnCreate() {
<span class='span_code_line'></span> <span class='code_key'>base</span>.OnCreate();
<span class='span_code_line'></span> <span class='code_key'>this</span>.Title = <span class='code_string'>"PropertyTest"</span>;
<span class='span_code_line'></span> }
<span class='span_code_line'></span>}</pre>
</div>
<p>You can switch between panels through the button in the top</p>
<p class='p_hightlight'>The <span class='span_mark'>AA/BB</span> is used to construct the path in <span class='span_mark'>STNodeTreeView</span></p>
<img width=462 src='./images/tu_property_5.png'/>
<div class='div_code'>
<pre class='pre_code'><span class='span_code_line'></span>stNodePropertyGrid1.SetInfoKey(<span class='code_string'>"Author"</span>, <span class='code_string'>"Mail"</span>, <span class='code_string'>"Link"</span>, <span class='code_string'>"Show Help"</span>);</pre>
</div>
<p>The key of the content of the <span class='span_mark'>Information Panel</span> can be used to set the language through the <span class='span_mark'>SetInfoKey()</span> function, which is displayed in simplified Chinese by default.</p>
<hr/>
<div><h2 class='h_option anchor_point' anchor='a_y'>Show help</h2></div>
<hr/>
<p>In the example of <span class='span_mark'>Information Panel</span>, you can see that the <span class='span_mark'>Show Help</span> button is not available. If you want it to be available, you need to provide a <span class='span_mark'>magic method</span></p>
<div class='div_code'>
<pre class='pre_code'><span class='span_code_line'></span>[<span class='code_class'>STNode</span>(<span class='code_string'>"AA/BB"</span>, <span class='code_string'>"Author"</span>, <span class='code_string'>"Mail"</span>, <span class='code_string'>"Link"</span>, <span class='code_string'>"Description"</span>)]
<span class='span_code_line'></span><span class='code_key'>public</span> <span class='code_key'>class</span> <span class='code_class'>PropertyTestNode</span> : <span class='code_class'>STNode</span>
<span class='span_code_line'></span>{
<span class='span_code_line'></span> <span class='code_key'>private</span> <span class='code_key'>int</span> _Number;
<span class='span_code_line'></span> [<span class='code_class'>STNodeProperty</span>(<span class='code_string'>"Name"</span>, <span class='code_string'>"Description for this property"</span>)]
<span class='span_code_line'></span> <span class='code_key'>public</span> <span class='code_key'>int</span> Number {
<span class='span_code_line'></span> <span class='code_key'>get</span> { <span class='code_key'>return</span> _Number; }
<span class='span_code_line'></span> <span class='code_key'>set</span> { _Number = <span class='code_key'>value</span>; }
<span class='span_code_line'></span> }
<span class='span_code_line'></span>
<span class='span_code_line'></span> <span class='code_key'>protected</span> <span class='code_key'>override</span> <span class='code_key'>void</span> OnCreate() {
<span class='span_code_line'></span> <span class='code_key'>base</span>.OnCreate();
<span class='span_code_line'></span> <span class='code_key'>this</span>.Title = <span class='code_string'>"PropertyTest"</span>;
<span class='span_code_line'></span> }
<span class='span_code_line'></span> <span class='code_note'>/// <summary></span>
<span class='span_code_line'></span> <span class='code_note'>/// <span class='code_note_1'>This method is a magic method</span></span>
<span class='span_code_line'></span> <span class='code_note'>/// <span class='code_note_1'>If there is <span class='code_string'>"static void ShowHelpInfo(string)"</span> and this class is marked by <span class='code_string'>"STNodeAttribute"</span></span></span>
<span class='span_code_line'></span> <span class='code_note'>/// <span class='code_note_1'>Then this method will be used as the <span class='code_string'>"Show Help"</span> function on the property editor</span></span>
<span class='span_code_line'></span> <span class='code_note'>/// </summary></span>
<span class='span_code_line'></span> <span class='code_note'>/// <param name=<span class='code_string'>"strFileName"</span>><span class='code_note_1'>The file path of the module where this class is located.</span></param></span>
<span class='span_code_line'></span> <span class='code_key'>public</span> <span class='code_key'>static</span> <span class='code_key'>void</span> ShowHelpInfo(<span class='code_key'>string</span> strFileName) {
<span class='span_code_line'></span> <span class='code_class'>MessageBox</span>.Show(<span class='code_string'>"this is -> ShowHelpInfo(string);\r\n"</span> + strFileName);
<span class='span_code_line'></span> }
<span class='span_code_line'></span>}</pre>
</div>
<img width=462 src='./images/tu_property_6.png'/>
<p>Now find that the <span class='span_mark'>Show Help</span> button has become enabled. STNodeAttribute also provides two <span class='span_mark'>static</span> functions.</p>
<div class='div_code'>
<pre class='pre_code'><span class='span_code_line'></span><span class='code_note'>/// <summary></span>
<span class='span_code_line'></span><span class='code_note'>/// <span class='code_note_1'>Get help method for stNodeType</span></span>
<span class='span_code_line'></span><span class='code_note'>/// </summary></span>
<span class='span_code_line'></span><span class='code_note'>/// <param name=<span class='code_string'>"stNodeType"</span>><span class='code_note_1'>stNodeType</span></param></span>
<span class='span_code_line'></span><span class='code_note'>/// <returns><span class='code_note_1'>MethodInfo</span></returns></span>
<span class='span_code_line'></span><span class='code_key'>public</span> <span class='code_key'>static</span> <span class='code_class'>MethodInfo</span> GetHelpMethod(<span class='code_class'>Type</span> stNodeType);
<span class='span_code_line'></span><span class='code_note'>/// <summary></span>
<span class='span_code_line'></span><span class='code_note'>/// <span class='code_note_1'>Excute the <span class='code_string'>"ShowHelpInfo"</span> for stNodeType</span></span>
<span class='span_code_line'></span><span class='code_note'>/// </summary></span>
<span class='span_code_line'></span><span class='code_note'>/// <param name=<span class='code_string'>"stNodeType"</span>><span class='code_note_1'>节点类型</span></param></span>
<span class='span_code_line'></span><span class='code_key'>public</span> <span class='code_key'>static</span> <span class='code_key'>void</span> ShowHelp(<span class='code_class'>Type</span> stNodeType);</pre>
</div>
<hr/>
<div><h2 class='h_option anchor_point' anchor='a_z'>e.g. - Add number</h2></div>
<hr/>
<p><span class='span_mark'>STNodePropertyGrid</span> can display and modify properties. Then this case will provide a data input through the <span class='span_mark'>STNodePropertyGrid</span>.</p>
<div class='div_code'>
<pre class='pre_code'><span class='span_code_line'></span><span class='code_key'>public</span> <span class='code_key'>class</span> <span class='code_class'>NumberInputNode</span> : <span class='code_class'>STNode</span>
<span class='span_code_line'></span>{
<span class='span_code_line'></span> <span class='code_key'>private</span> <span class='code_key'>int</span> _Number;
<span class='span_code_line'></span> [<span class='code_class'>STNodeProperty</span>(<span class='code_string'>"Input"</span>, <span class='code_string'>"Input number"</span>)]
<span class='span_code_line'></span> <span class='code_key'>public</span> <span class='code_key'>int</span> Number {
<span class='span_code_line'></span> <span class='code_key'>get</span> { <span class='code_key'>return</span> _Number; }
<span class='span_code_line'></span> <span class='code_key'>set</span> {
<span class='span_code_line'></span> _Number = <span class='code_key'>value</span>;
<span class='span_code_line'></span> <span class='code_key'>this</span>.SetOptionText(m_op_out, <span class='code_key'>value</span>.ToString());
<span class='span_code_line'></span> m_op_out.TransferData(<span class='code_key'>value</span>);
<span class='span_code_line'></span> }
<span class='span_code_line'></span> }
<span class='span_code_line'></span>
<span class='span_code_line'></span> <span class='code_key'>private</span> <span class='code_class'>STNodeOption</span> m_op_out;
<span class='span_code_line'></span>
<span class='span_code_line'></span> <span class='code_key'>protected</span> <span class='code_key'>override</span> <span class='code_key'>void</span> OnCreate() {
<span class='span_code_line'></span> <span class='code_key'>base</span>.OnCreate();
<span class='span_code_line'></span> <span class='code_key'>this</span>.Title = <span class='code_string'>"NumberInput"</span>;
<span class='span_code_line'></span> m_op_out = <span class='code_key'>this</span>.OutputOptions.Add(<span class='code_string'>"0"</span>, <span class='code_key'>typeof</span>(<span class='code_key'>int</span>), <span class='code_key'>false</span>);
<span class='span_code_line'></span> }
<span class='span_code_line'></span>}</pre>
</div>
<div class='div_code'>
<pre class='pre_code'><span class='span_code_line'></span><span class='code_key'>public</span> <span class='code_key'>class</span> <span class='code_class'>NumberAddNode</span> : <span class='code_class'>STNode</span> {
<span class='span_code_line'></span>
<span class='span_code_line'></span> <span class='code_key'>private</span> <span class='code_class'>STNodeOption</span> m_op_in_1;
<span class='span_code_line'></span> <span class='code_key'>private</span> <span class='code_class'>STNodeOption</span> m_op_in_2;
<span class='span_code_line'></span> <span class='code_key'>private</span> <span class='code_class'>STNodeOption</span> m_op_out;
<span class='span_code_line'></span>
<span class='span_code_line'></span> <span class='code_key'>protected</span> <span class='code_key'>override</span> <span class='code_key'>void</span> OnCreate() {
<span class='span_code_line'></span> <span class='code_key'>base</span>.OnCreate();
<span class='span_code_line'></span> <span class='code_key'>this</span>.Title = <span class='code_string'>"NumberAdd"</span>;
<span class='span_code_line'></span> m_op_in_1 = <span class='code_key'>this</span>.InputOptions.Add(<span class='code_string'>"0"</span>, <span class='code_key'>typeof</span>(<span class='code_key'>int</span>), <span class='code_key'>true</span>);
<span class='span_code_line'></span> m_op_in_2 = <span class='code_key'>this</span>.InputOptions.Add(<span class='code_string'>"0"</span>, <span class='code_key'>typeof</span>(<span class='code_key'>int</span>), <span class='code_key'>true</span>);
<span class='span_code_line'></span> m_op_out = <span class='code_key'>this</span>.OutputOptions.Add(<span class='code_string'>"0"</span>, <span class='code_key'>typeof</span>(<span class='code_key'>int</span>), <span class='code_key'>false</span>);
<span class='span_code_line'></span>
<span class='span_code_line'></span> m_op_in_1.DataTransfer += <span class='code_key'>new</span> STNodeOptionEventHandler(m_op_in_DataTransfer);
<span class='span_code_line'></span> m_op_in_2.DataTransfer += <span class='code_key'>new</span> STNodeOptionEventHandler(m_op_in_DataTransfer);
<span class='span_code_line'></span> }
<span class='span_code_line'></span>
<span class='span_code_line'></span> <span class='code_key'>void</span> m_op_in_DataTransfer(<span class='code_key'>object</span> sender, <span class='code_class'>STNodeOptionEventArgs</span> e) {
<span class='span_code_line'></span> <span class='code_key'>if</span> (e.Status != <span class='code_class'>ConnectionStatus</span>.Connected || e.TargetOption == <span class='code_key'>null</span>) {
<span class='span_code_line'></span> <span class='code_key'>if</span> (sender == m_op_in_1) m_op_in_1.Data = 0;
<span class='span_code_line'></span> <span class='code_key'>if</span> (sender == m_op_in_2) m_op_in_2.Data = 0;
<span class='span_code_line'></span> } <span class='code_key'>else</span> {
<span class='span_code_line'></span> <span class='code_key'>if</span> (sender == m_op_in_1) m_op_in_1.Data = e.TargetOption.Data;
<span class='span_code_line'></span> <span class='code_key'>if</span> (sender == m_op_in_2) m_op_in_2.Data = e.TargetOption.Data;
<span class='span_code_line'></span> }
<span class='span_code_line'></span> <span class='code_key'>if</span> (m_op_in_1.Data == <span class='code_key'>null</span>) m_op_in_1.Data = 0;
<span class='span_code_line'></span> <span class='code_key'>if</span> (m_op_in_2.Data == <span class='code_key'>null</span>) m_op_in_2.Data = 0;
<span class='span_code_line'></span> <span class='code_key'>int</span> nResult = (<span class='code_key'>int</span>)m_op_in_1.Data + (<span class='code_key'>int</span>)m_op_in_2.Data;
<span class='span_code_line'></span> <span class='code_key'>this</span>.SetOptionText(m_op_in_1, m_op_in_1.Data.ToString());
<span class='span_code_line'></span> <span class='code_key'>this</span>.SetOptionText(m_op_in_2, m_op_in_2.Data.ToString());
<span class='span_code_line'></span> <span class='code_key'>this</span>.SetOptionText(m_op_out, nResult.ToString());
<span class='span_code_line'></span> m_op_out.TransferData(nResult);
<span class='span_code_line'></span> }
<span class='span_code_line'></span>}</pre>
</div>
<img width=462 src='./images/tu_property_3.png'/>
<p>Pass the entered number through the <span class='span_mark'>set</span> accessor of the <span class='span_mark'>Number</span> property.</p>
<hr/>
<div><h2 class='h_option anchor_point' anchor='b_a'>ReadOnlyModel</h2></div>
<hr/>
<p>In some cases, you don’t want <span class='span_mark'>STNodePropertyGrid</span> to set the properties, you just want to display the properties, you can enable <span class='span_mark'>ReadOnlyModel</span></p>
<div class='div_code'>
<pre class='pre_code'><span class='span_code_line'></span>stNodePropertyGrid1.ReadOnlyModel = <span class='code_key'>true</span>;</pre>
</div>
<img width=462 src='./images/tu_property_4.png'/>
<p>You can not set the properoty value from <span class='span_mark'>STNodePropertyGrid</span> when the <span class='span_mark'>ReadOnlyModel</span> is <span class='span_mark'>true</span></p>
<h1 class='h_title anchor_point' anchor='b_b'>STNodePropertyDescriptor</h1>
<p>The <span class='span_mark'>Name</span> and <span class='span_mark'>Description</span> properties of <span class='span_mark'>STNodePropertyAttribute</span> are introduced above. There is also a properoty <span class='span_mark'>DescriptorType</span> whose data type is <span class='span_mark'>Type</span> and the default value is <span class='span_mark'>typeof(STNodePropertyDescriptor)</span></p>
<p>Although from the current case, there is no problem with the above operation, but not all data type properties can be correctly supported by <span class='span_mark'>STNodePropertyGrid</span>. The default <span class='span_mark'>STNodePropertyDescriptor</span> only supports the following data types.</p>
<p><span class='span_mark'>int</span><span class='span_mark'>float</span><span class='span_mark'>double</span><span class='span_mark'>bool</span><span class='span_mark'>string</span><span class='span_mark'>Enum</span> and their <span class='span_mark'>Array</span></p>
<hr/>
<div><h2 class='h_option anchor_point' anchor='b_c'>e.g. - ColorTestNode</h2></div>
<hr/>
<p>Create a node below and add an properoty of type <span class='span_mark'>Color</span></p>
<div class='div_code'>
<pre class='pre_code'><span class='span_code_line'></span><span class='code_key'>public</span> <span class='code_key'>class</span> <span class='code_class'>ColorTestNode</span> : <span class='code_class'>STNode</span>
<span class='span_code_line'></span>{
<span class='span_code_line'></span> [<span class='code_class'>STNodeProperty</span>(<span class='code_string'>"TitleColor"</span>, <span class='code_string'>"Get or set the node TitleColor"</span>)]
<span class='span_code_line'></span> <span class='code_key'>public</span> <span class='code_class'>Color</span> ColorTest {
<span class='span_code_line'></span> <span class='code_key'>get</span> { <span class='code_key'>return</span> <span class='code_key'>this</span>.TitleColor; }
<span class='span_code_line'></span> <span class='code_key'>set</span> { <span class='code_key'>this</span>.TitleColor = <span class='code_key'>value</span>; }
<span class='span_code_line'></span> }
<span class='span_code_line'></span> <span class='code_key'>protected</span> <span class='code_key'>override</span> <span class='code_key'>void</span> OnCreate() {
<span class='span_code_line'></span> <span class='code_key'>base</span>.OnCreate();
<span class='span_code_line'></span> <span class='code_key'>this</span>.Title = <span class='code_string'>"ColorNode"</span>;
<span class='span_code_line'></span> }
<span class='span_code_line'></span>}</pre>
</div>
<img width=462 src='./images/tu_colornode_1.png'/>
<p>If you run the above code, you will find that there will be errors when setting properties through <span class='span_mark'>STNodePropertyGrid</span>, and the display of property values in <span class='span_mark'>STNodePropertyGrid</span> is also very strange.</p>
<p>Even though <span class='span_mark'>System.Windows.Forms.PropertyGrid</span> can support many data types, it does not support all types. For example, when the property type is a user-defined type, the property editor cannot know how to interact with the properties on the property grid.</p>
<p>The solution for <span class='span_mark'>System.Windows.Forms.PropertyGrid</span> is to provide <span class='span_mark'>TypeConverter</span>, mark the target type with <span class='span_mark'>TypeConverter</span> and implement overloading so that <span class='span_mark'>PropertyGrid</span> can know how to interact with the <span class='span_mark'>PropertyGrid</span> through <span class='span_mark'>TypeConverter</span> .</p>
<p>The solution provided by <span class='span_mark'>STNodePropertyGrid</span> is <span class='span_mark'>STNodePropertyDescriptor</span>.</p>
<hr/>
<div><h2 class='h_option anchor_point' anchor='b_d'>about properoty discriptor</h2></div>
<hr/>
<p class='p_hightlight'><span class='span_mark'>STNodePropertyGrid</span> can correctly obtain and modify the value of the <span class='span_mark'>STNode</span> property, because <span class='span_mark'>STNodePropertyDescriptor</span> is performing data conversion and responding to some event operations on the property window.</p>
<p class='p_hightlight'>The property marked by <span class='span_mark'>STNodePropertyAttribute</span> will be packaged as <span class='span_mark'>STNodePropertyDescriptor</span> and passed to <span class='span_mark'>STNodePropertyGrid</span>. A <span class='span_mark'>STNodePropertyDescriptor</span> contains the <span class='span_mark'>Name</span> and <span class='span_mark'>Description</span> in <span class='span_mark'>STNodePropertyAttribute</span> and the position information of the attribute will be displayed on <span class='span_mark'>STNodePropertyGrid</span>, and How to respond to mouse events or keyboard events.</p>
<p>It can be considered that <span class='span_mark'>STNodePropertyDescriptor</span> is a graphical interface for each properoty marked by <span class='span_mark'>STNodePropertyAttribute</span>, and the main function is to transfer data between the graphical interface and the real properoty.</p>
<hr/>
<div><h2 class='h_option anchor_point' anchor='b_e'>GetValueFromString()</h2></div>
<hr/>
<div class='div_code'>
<pre class='pre_code'><span class='span_code_line'></span><span class='code_note'>/// <summary></span>
<span class='span_code_line'></span><span class='code_note'>/// <span class='code_note_1'>Convert the value of the string to the value of the property's type.</span></span>
<span class='span_code_line'></span><span class='code_note'>/// <span class='code_note_1'>Only int float double string bool and the above types of Array are supported by default</span></span>
<span class='span_code_line'></span><span class='code_note'>/// <span class='code_note_1'>If the target type is not in the above, please override the function to convert it by yourself.</span></span>
<span class='span_code_line'></span><span class='code_note'>/// </summary></span>
<span class='span_code_line'></span><span class='code_note'>/// <param name=<span class='code_string'>"strText"</span>><span class='code_note_1'>text</span></param></span>
<span class='span_code_line'></span><span class='code_note'>/// <returns><span class='code_note_1'>the properoty value</span></returns></span>
<span class='span_code_line'></span><span class='code_key'>protected</span> <span class='code_key'>internal</span> <span class='code_key'>virtual</span> <span class='code_key'>object</span> GetValueFromString(<span class='code_key'>string</span> strText);</pre>
</div>
<p class='p_hightlight'>The <span class='span_mark'>GetValueFromString()</span> function converts the string entered by the user in the <span class='span_mark'>STNodePropertyGrid</span> into the real value required by the property.</p>
<p>For example: If the property is <span class='span_mark'>int</span> type value and the user can only enter the string <span class='span_mark'>123</span> in <span class='span_mark'>STNodePropertyGrid</span>, then the default <span class='span_mark'>GetValueFromString()</span> function will be <span class='span_mark'>int.Parse(strText)</span> such as <span class='span_mark'>string</span> Type <span class='span_mark'>123</span> becomes <span class='span_mark'>123</span> of type <span class='span_mark'>int</span></p>
<hr/>
<div><h2 class='h_option anchor_point' anchor='b_f'>GetStringFromValue()</h2></div>
<hr/>
<div class='div_code'>
<pre class='pre_code'><span class='span_code_line'></span><span class='code_note'>/// <summary></span>
<span class='span_code_line'></span><span class='code_note'>/// <span class='code_note_1'>Convert the value of the properoty to a string.</span></span>
<span class='span_code_line'></span><span class='code_note'>/// <span class='code_note_1'>Calling Tostring() of the property value is the default operation.</span></span>
<span class='span_code_line'></span><span class='code_note'>/// <span class='code_note_1'>If you need special processing, please override this function to convert by yourself</span></span>
<span class='span_code_line'></span><span class='code_note'>/// </summary></span>
<span class='span_code_line'></span><span class='code_note'>/// <returns><span class='code_note_1'>string</span></returns></span>
<span class='span_code_line'></span><span class='code_key'>protected</span> <span class='code_key'>internal</span> <span class='code_key'>virtual</span> <span class='code_key'>string</span> GetStringFromValue();</pre>
</div>
<p class='p_hightlight'>The <span class='span_mark'>GetStringFromValue()</span> function converts the property value into a string and displays it in the <span class='span_mark'>STNodePropertyGrid</span>. The default <span class='span_mark'>GetStringFromValue()</span> internally just calls the property value <span class='span_mark'>ToString()</span>.</p>
<hr/>
<div><h2 class='h_option anchor_point' anchor='b_g'>e.g. - ColorTestNode</h2></div>
<hr/>
<p>Extend the <span class='span_mark'>STNodePropertyDescriptor</span> of <span class='span_mark'>ColorTestNode.ColorTest</span> in the above failed example.</p>
<div class='div_code'>
<pre class='pre_code'><span class='span_code_line'></span><span class='code_key'>public</span> <span class='code_key'>class</span> <span class='code_class'>ColorTestNode</span> : <span class='code_class'>STNode</span>
<span class='span_code_line'></span>{
<span class='span_code_line'></span> <span class='code_note'>//Specify to use the extended STNodePropertyDescriptor in order to complete the support for the Color type.</span>
<span class='span_code_line'></span> [<span class='code_class'>STNodeProperty</span>(<span class='code_string'>"TitleColor"</span>, <span class='code_string'>"Get or set the node TitleColor"</span>, DescriptorType = <span class='code_key'>typeof</span>(<span class='code_class'>ColorDescriptor</span>))]
<span class='span_code_line'></span> <span class='code_key'>public</span> <span class='code_class'>Color</span> ColorTest {
<span class='span_code_line'></span> <span class='code_key'>get</span> { <span class='code_key'>return</span> <span class='code_key'>this</span>.TitleColor; }
<span class='span_code_line'></span> <span class='code_key'>set</span> { <span class='code_key'>this</span>.TitleColor = <span class='code_key'>value</span>; }
<span class='span_code_line'></span> }
<span class='span_code_line'></span> <span class='code_key'>protected</span> <span class='code_key'>override</span> <span class='code_key'>void</span> OnCreate() {
<span class='span_code_line'></span> <span class='code_key'>base</span>.OnCreate();
<span class='span_code_line'></span> <span class='code_key'>this</span>.Title = <span class='code_string'>"ColorNode"</span>;
<span class='span_code_line'></span> }
<span class='span_code_line'></span>}
<span class='span_code_line'></span>
<span class='span_code_line'></span><span class='code_key'>public</span> <span class='code_key'>class</span> <span class='code_class'>ColorDescriptor</span> : <span class='code_class'>STNodePropertyDescriptor</span>
<span class='span_code_line'></span>{
<span class='span_code_line'></span> <span class='code_key'>protected</span> <span class='code_key'>override</span> <span class='code_key'>object</span> GetValueFromString(<span class='code_key'>string</span> strText) {
<span class='span_code_line'></span> <span class='code_key'>string</span>[] strs = strText.Split(<span class='code_string'>','</span>);
<span class='span_code_line'></span> <span class='code_key'>return</span> <span class='code_class'>Color</span>.FromArgb(<span class='code_key'>int</span>.Parse(strs[0]), <span class='code_key'>int</span>.Parse(strs[1]), <span class='code_key'>int</span>.Parse(strs[2]), <span class='code_key'>int</span>.Parse(strs[3]));
<span class='span_code_line'></span> <span class='code_note'>//return base.GetValueFromString(strText);</span>
<span class='span_code_line'></span> }
<span class='span_code_line'></span>
<span class='span_code_line'></span> <span class='code_key'>protected</span> <span class='code_key'>override</span> <span class='code_key'>string</span> GetStringFromValue() {
<span class='span_code_line'></span> <span class='code_key'>var</span> v = (<span class='code_class'>Color</span>)<span class='code_key'>this</span>.GetValue(<span class='code_key'>null</span>);<span class='code_note'>//get the value</span>
<span class='span_code_line'></span> <span class='code_key'>return</span> v.A + <span class='code_string'>","</span> + v.R + <span class='code_string'>","</span> + v.G + <span class='code_string'>","</span> + v.B;
<span class='span_code_line'></span> <span class='code_note'>//return base.GetStringFromValue();</span>
<span class='span_code_line'></span> }
<span class='span_code_line'></span>}</pre>
</div>
<img width=462 src='./images/tu_colornode_2.png'/>
<p>At this time, the property value can be displayed and set correctly, because <span class='span_mark'>GetValueFromString()</span> and <span class='span_mark'>GetStringFromValue()</span> correctly complete the conversion between the property value and the string.</p>
<hr/>
<div><h2 class='h_option anchor_point' anchor='b_h'>e.g. - ColorTestNode</h2></div>
<hr/>
<p>Although the above example can be used to set the <span class='span_mark'>Color</span> property through ARGB,but this method is not friendly enough.</p>
<img width=462 src='./images/tu_colornode_3.png'/>
<p>Can it be the same as <span class='span_mark'>System.Windows.Forms.PropertyGrid</span> to allow users to make visual settings. <span class='span_mark'>STNodePropertyDescriptor</span> can of course do it.</p>
<p class='p_hightlight'><span class='span_mark'>STNodePropertyDescriptor</span> can be regarded as a custom control that displays properties. Since it is a custom control, it can respond to mouse events.</p>
<p>now add some code to <span class='span_mark'>ColorDescriptor</span></p>
<div class='div_code'>
<pre class='pre_code'><span class='span_code_line'></span><span class='code_key'>public</span> <span class='code_key'>class</span> <span class='code_class'>ColorDescriptor</span> : <span class='code_class'>STNodePropertyDescriptor</span>
<span class='span_code_line'></span>{
<span class='span_code_line'></span> <span class='code_note'>//This rect is used as a color preview for drawing on the STNodePropertyGrid.</span>
<span class='span_code_line'></span> <span class='code_key'>private</span> <span class='code_class'>Rectangle</span> m_rect;
<span class='span_code_line'></span>
<span class='span_code_line'></span> <span class='code_key'>protected</span> <span class='code_key'>override</span> <span class='code_key'>object</span> GetValueFromString(<span class='code_key'>string</span> strText) {
<span class='span_code_line'></span> <span class='code_key'>string</span>[] strs = strText.Split(<span class='code_string'>','</span>);
<span class='span_code_line'></span> <span class='code_key'>return</span> <span class='code_class'>Color</span>.FromArgb(<span class='code_key'>int</span>.Parse(strs[0]), <span class='code_key'>int</span>.Parse(strs[1]), <span class='code_key'>int</span>.Parse(strs[2]), <span class='code_key'>int</span>.Parse(strs[3]));
<span class='span_code_line'></span> <span class='code_note'>//return base.GetValueFromString(strText);</span>
<span class='span_code_line'></span> }
<span class='span_code_line'></span>
<span class='span_code_line'></span> <span class='code_key'>protected</span> <span class='code_key'>override</span> <span class='code_key'>string</span> GetStringFromValue() {
<span class='span_code_line'></span> <span class='code_key'>var</span> v = (<span class='code_class'>Color</span>)<span class='code_key'>this</span>.GetValue(<span class='code_key'>null</span>);
<span class='span_code_line'></span> <span class='code_key'>return</span> v.A + <span class='code_string'>","</span> + v.R + <span class='code_string'>","</span> + v.G + <span class='code_string'>","</span> + v.B;
<span class='span_code_line'></span> <span class='code_note'>//return base.GetStringFromValue();</span>
<span class='span_code_line'></span> }
<span class='span_code_line'></span>
<span class='span_code_line'></span> <span class='code_note'>//Called when the position of this property is confirmed in the STNodePropertyGrid.</span>
<span class='span_code_line'></span> <span class='code_key'>protected</span> <span class='code_key'>override</span> <span class='code_key'>void</span> OnSetItemLocation() {
<span class='span_code_line'></span> <span class='code_key'>base</span>.OnSetItemLocation();
<span class='span_code_line'></span> <span class='code_class'>Rectangle</span> rect = <span class='code_key'>base</span>.RectangleR;
<span class='span_code_line'></span> m_rect = <span class='code_key'>new</span> <span class='code_class'>Rectangle</span>(rect.Right - 25, rect.Top + 5, 19, 12);
<span class='span_code_line'></span> }
<span class='span_code_line'></span> <span class='code_note'>//Called when drawing this properoty value area in the STNodePropertyGrid</span>
<span class='span_code_line'></span> <span class='code_key'>protected</span> <span class='code_key'>override</span> <span class='code_key'>void</span> OnDrawValueRectangle(<span class='code_class'>DrawingTools</span> dt) {
<span class='span_code_line'></span> <span class='code_key'>base</span>.OnDrawValueRectangle(dt);<span class='code_note'>//call the base and then draw color preview</span>
<span class='span_code_line'></span> dt.SolidBrush.<span class='code_class'>Color</span> = (<span class='code_class'>Color</span>)<span class='code_key'>this</span>.GetValue(<span class='code_key'>null</span>);
<span class='span_code_line'></span> dt.<span class='code_class'>Graphics</span>.FillRectangle(dt.SolidBrush, m_rect);<span class='code_note'>//fill color</span>
<span class='span_code_line'></span> dt.<span class='code_class'>Graphics</span>.DrawRectangle(<span class='code_class'>Pens</span>.Black, m_rect); <span class='code_note'>//draw rectangle</span>
<span class='span_code_line'></span> }
<span class='span_code_line'></span>
<span class='span_code_line'></span> <span class='code_key'>protected</span> <span class='code_key'>override</span> <span class='code_key'>void</span> OnMouseClick(<span class='code_class'>MouseEventArgs</span> e) {
<span class='span_code_line'></span> <span class='code_note'>//If the user clicks in the color preview area, show system color dialog.</span>
<span class='span_code_line'></span> <span class='code_key'>if</span> (m_rect.Contains(e.Location)) {
<span class='span_code_line'></span> ColorDialog cd = <span class='code_key'>new</span> ColorDialog();
<span class='span_code_line'></span> <span class='code_key'>if</span> (cd.ShowDialog() != DialogResult.OK) <span class='code_key'>return</span>;
<span class='span_code_line'></span> <span class='code_key'>this</span>.SetValue(cd.<span class='code_class'>Color</span>, <span class='code_key'>null</span>);
<span class='span_code_line'></span> <span class='code_key'>this</span>.Invalidate();
<span class='span_code_line'></span> <span class='code_key'>return</span>;
<span class='span_code_line'></span> }
<span class='span_code_line'></span> <span class='code_note'>//Otherwise, show the text input box by default.</span>
<span class='span_code_line'></span> <span class='code_key'>base</span>.OnMouseClick(e);
<span class='span_code_line'></span> }
<span class='span_code_line'></span>}</pre>
</div>
<img width=462 src='./images/tu_colornode_4.png'/>
<p>At this point, you can see that compared with the previous example, there is one more color preview area, and clicking the preview area will pop up the system color dialog box to set the property value. If you click in the non-preview area, the default operation method will be show text input box to input ARGB.</p>
<p class='p_hightlight'>About <span class='span_mark'>STNodePropertyDescriptor</span> there are two important overloaded functions <span class='span_mark'>GetBytesFromValue()</span> and <span class='span_mark'>SetValue(byte[])</span> which will be introduced in <span class='span_mark'>Save Canvas</span> later.</p>
<h1 class='h_title anchor_point' anchor='b_i'>STNodeTreeView</h1>