<p>It was a winter. The author who was studying radio security used <atarget='_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 <atarget='_bank'href='https://www.blender.org/'>Blender</a>. That was the second time the author used the node editor.</p>
<p>It was a summer, I don’t know why the author started to learn <atarget='_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><spanclass='span_mark'>STNode</span> is the core of the whole framework. If <spanclass='span_mark'>STNodeEditor</span> is regarded as <spanclass='span_mark'>Desktop</span>, then an <spanclass='span_mark'>STNode</span> can be regarded as an <spanclass='span_mark'>application</span> on the desktop. It is very necessary to develop a strong <spanclass='span_mark'>STNode</span>.</p>
<p>The base class <spanclass='span_mark'>STNode</span> of the node is modified by <spanclass='span_mark'>abstract</span>, so the node must be extended to <spanclass='span_mark'>STNode</span></p>
<p><spanclass='span_mark'>STNode</span> contains a large number of <spanclass='span_mark'>virtual</span> functions for developers to overload. For more information, please refer to <atarget='_bank'href='./doc.html'>API Documentation</a></p>
<spanclass='span_code_line'></span><spanclass='code_key'>public</span><spanclass='code_class'>MyNode</span>() { <spanclass='code_note'>//Equivalent to OnCreate()</span>
<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 <spanclass='span_mark'>string</span> and <spanclass='span_mark'>int</span>. Color should be added to the data type to distinguish different data types.</p>
<p>Such a node is created, but this node does not have any functions. In the next case, functions will be added.</p>
<pclass='p_hightlight'>In any case, developers should try to keep an empty parameter constructor for the extended <spanclass='span_mark'>STNode</span>, otherwise there will be unnecessary troubles in many functions.</p>
<p>From the above case, we can see that <spanclass='span_mark'>STNodeOption</span> is the connection option of <spanclass='span_mark'>STNode</span>. The connection option can be <spanclass='span_mark'>multi-connection</span> and <spanclass='span_mark'>single-connection</span> mode.</p>
<pclass='p_hightlight'>In <spanclass='span_mark'>multi-connection</span> mode, an option can be connected by multiple options of the same data type (rectangle)</p>
<pclass='p_hightlight'>In <spanclass='span_mark'>single-connection</span> mode, an option can only be connected by one option of the same data type (circle)</p>
<p><spanclass='span_mark'>STNodeOption.Empty</span> is a static property, added to <spanclass='span_mark'>STNode</span> is only used to occupy a place during auto layout.</p>
<p><spanclass='span_mark'>AutoSize</span>default value is<spanclass='span_mark'>true</span>,when <spanclass='span_mark'>AitoSize</span> is set <spanclass='span_mark'>Width</span><spanclass='span_mark'>Height</span>can not be set</p>
<p>You can see that the size of <spanclass='span_mark'>MyNode</span> is no longer automatically calculated, but the position of <spanclass='span_mark'>STNodeOption</span> will still be automatically calculated. If you want to modify the position of <spanclass='span_mark'>STNodeOption</span>, you can override <spanclass='span_mark'>OnSetOptionXXX</span></p>
<p>You can see that in the code, the point and text area of the <spanclass='span_mark'>STNodeOption</span> connection line are modified by overloading the function. Why is it not designed to be <spanclass='span_mark'>STNodeOption.DotLeft=xxx</span> because the author thinks it will be more troublesome.</p>
<p>The <spanclass='span_mark'>pt</span> and <spanclass='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 <spanclass='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 <spanclass='span_mark'>STNode.Resize</span> to monitor the changes in the size of <spanclass='span_mark'>STNode</span> to recalculate the position, so the <spanclass='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>
<p><spanclass='span_mark'>STNodeOption</span> can get all the data input of this option by binding to the <spanclass='span_mark'>DataTransfer</span> event</p>
<p><spanclass='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>
<spanclass='span_code_line'></span><spanclass='code_note'>//STNodeOption.TransferData(object) will automatically post data to all connections on the option</span>
<spanclass='span_code_line'></span><spanclass='code_note'>//STNodeOption.TransferData(object) will set STNodeOption.Data automatically</span>
<spanclass='span_code_line'></span><spanclass='code_note'>//if you need to operate across UI threads in a thread, the node provides Begin/Invoke() to complete the operation.</span>
<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>
<spanclass='span_code_line'></span><spanclass='code_note'>//When STNode.AutoSize=true, it is not recommended to use STNode.SetOptionText</span>
<spanclass='span_code_line'></span><spanclass='code_note'>//the STNode will recalculate the layout every time the Text changes.</span>
<spanclass='span_code_line'></span><spanclass='code_note'>//It should be displayed by adding controls.</span>
<spanclass='span_code_line'></span><spanclass='code_note'>//Since STNodeControl has not yet been mentioned, the current design will be used for now.</span>
<p>In the above and previous examples, we can see that when some properties of <spanclass='span_mark'>STNodeOption</span> need to be modified, they are not modified in the way of <spanclass='span_mark'>STNodeOption.XXX=XXX</span>. This design is for safety.</p>
<p>The author thinks that <spanclass='span_mark'>STNodeOption</span> can only be modified by its owner, and the method of <spanclass='span_mark'>STNodeOption.XXX=XXX</span> cannot know who modified it and <spanclass='span_mark'>STNode.SetOptionXXX()</span> is marked by <spanclass='span_mark'>protected</span> only internally Is called and inside the function will check whether <spanclass='span_mark'>STNodeOption.Owner</span> is the current class</p>
<pclass='p_hightlight'>It is not necessary to <spanclass='span_mark'>STNodeOption.TransferData(object)</span> to transfer data. <spanclass='span_mark'>TransferData(object)</span> only actively <spanclass='span_mark'>updates data</span></p>
<p>When a new connection is successful, the <spanclass='span_mark'>DataTransfer</span> event will also be triggered, the following will modify the code of <spanclass='span_mark'>ClockNode</span></p>
<p>You can see that <spanclass='span_mark'>ShowClockNode</span> still shows the time but the data does not change because the <spanclass='span_mark'>DataTransfer</span> event will be triggered when the connection is successful. In the event, <spanclass='span_mark'>ShowClockNode</span> gets the data of the <spanclass='span_mark'>ClockNode</span> option through <spanclass='span_mark'>e.TargetOption.Data</span></p>
<p>When a connection is successful and disconnected, the event trigger sequence is as follows</p>
<p><spanclass='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>
<p>As the base class of <spanclass='span_mark'>STNode</span> control, <spanclass='span_mark'>STNodeControl</span> has many properties and events with the same name as <spanclass='span_mark'>System.Windows.Forms.Control</span>, allowing developers to develop a node like a <spanclass='span_mark'>WinForm</span> program.</p>
<pclass='p_hightlight'>In this version (2.0), no available control is provided. Only the <spanclass='span_mark'>STNodeControl</span> base class needs to be extended by the developer. If available later, the author will improve it.</p>
<hr/>
<div><h2class='h_option anchor_point'anchor='a_m'>add a control</h2></div>
<hr/>
<p>Same as <spanclass='span_mark'>System.Windows.Forms.Control</span><spanclass='span_mark'>STNode</span> has the <spanclass='span_mark'>Controls</span> collection and its data type is <spanclass='span_mark'>STNodeControl</span></p>
<p>You can see that there is almost no difference between developing a <spanclass='span_mark'>WinForm</span> program. The only difference is that <spanclass='span_mark'>STNode</span> does not yet provide a WYSIWYG UI designer.</p>
<hr/>
<div><h2class='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 <spanclass='span_mark'>STNodeControl</span></p>
<p>The following is to customize a <spanclass='span_mark'>Button</span> control with mouse hovering and clicking effects to make it more like a button</p>
<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 <spanclass='span_mark'>GDI</span> related knowledge before this.</p>
<p>In the above <spanclass='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 <spanclass='span_mark'>STNodeButton</span> for output.</p>
<spanclass='span_code_line'></span><spanclass='code_note'>//of course you can extended STNodeControl to build a "STNodePictureBox" for display image</span>
<spanclass='span_code_line'></span><spanclass='code_class'>Graphics</span> g = dt.<spanclass='code_class'>Graphics</span>;
<p>click the <spanclass='span_mark'>Open Image</span> button, you can select an image and display it in the node. After the <spanclass='span_mark'>ImageSizeNode</span> is connected, the size of the image will be display.</p>
<p>The code of the <spanclass='span_mark'>ImageChannel</span> node is not given here. The code is used in the <spanclass='span_mark'>WinNodeEditorDemo</span> project to extract the RGB channel of an image. For <spanclass='span_mark'>ImageShowNode</span>, it just provides the data source and displays it. For the <spanclass='span_mark'>ImageSizeNode</span> and <spanclass='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 <spanclass='span_mark'>Image</span> data type, so that nodes and nodes There is no coupling relationship between them. High class poly low coupling.</p>
<p><spanclass='span_mark'>STNodeEditor</span> as a container of <spanclass='span_mark'>STNode</span> also provides a large number of properties and events for developers to use. For more detailed API list of <spanclass='span_mark'>STNodeEditor</span>, please refer to <atarget='_bank'href='./doc.html'>API document</a></p>
<p>The relationship between nodes and connections in <spanclass='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 <spanclass='span_mark'>STNodeEditor.SaveCanvas(string strFileName)</span> function</p>
<pclass='p_hightlight'>Note that the <spanclass='span_mark'>SaveCanvas()</span> function will call the <spanclass='span_mark'>internal byte[] STNode.GetSaveData()</span> function to get the binary data of each node</p>
<p>The <spanclass='span_mark'>GetSaveData()</span> function does not serialize the node itself. The <spanclass='span_mark'>GetSaveData()</span> function binarizes the basic data and original attributes of the node itself and then calls <spanclass='span_mark'>virtual OnSaveNode(Dictionary<string, byte[]> dic)</span> to The expansion node asks for the data that the node needs to save</p>
<pclass='p_hightlight'>If there is a saving requirement, the node developer may need to override the <spanclass='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>
<p>The saved data can be loaded from the file through the <spanclass='span_mark'>STNodeEditor.LoadCanvas(string strFileName)</span> function</p>
<pclass='p_hightlight'>If <spanclass='span_mark'>STNodeEditor</span> has nodes in other assemblies, you need to load the assembly by calling <spanclass='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 <spanclass='span_mark'>(STNode)Activator.CreateInstance(stNodeType)</span> and then called <spanclass='span_mark'>virtual OnSaveNode(Dictionary<string, byte[]> dic)</span> to restore the data, while <spanclass='span_mark'>dic</span> Is the data saved by <spanclass='span_mark'>OnSaveNode()</span></p>
<pclass='p_hightlight'>Because the restore node is dynamically created through reflection, an empty parameter constructor must be provided in the extended <spanclass='span_mark'>STNode</span></p>
<p>More content about saving nodes will be introduced in the following content</p>
<p><spanclass='span_mark'>ActiveChanged</span>,<spanclass='span_mark'>SelectedChanged</span> can monitor the selected changes of the node in the control</p>
<spanclass='span_code_line'></span><spanclass='code_key'>foreach</span>(<spanclass='code_key'>var</span> n <spanclass='code_key'>in</span> stNodeEditor1.GetSelectedNode()){
<p>If you want to display the scale on the editor after each zoom of the canvas, you can get it through the <spanclass='span_mark'>CanvasScaled</span> event</p>
<p>If you want to display the connection status when there are nodes connected in the canvas, you can get the status through the <spanclass='span_mark'>OptionConnected</span> event</p>
<spanclass='span_code_line'></span><spanclass='code_note'>/// <spanclass='code_note_1'>Move the origin position of the canvas to the specified control position</span></span>
<spanclass='span_code_line'></span><spanclass='code_note'>/// <spanclass='code_note_1'>(cannot be moved when there is no Node)</span></span>
<spanclass='span_code_line'></span><spanclass='code_note'>/// <param name=<spanclass='code_string'>"ma"</span>><spanclass='code_note_1'>Specify the position that needs to be modified</span></param></span>
<spanclass='span_code_line'></span><spanclass='code_key'>public</span><spanclass='code_key'>void</span> MoveCanvas(<spanclass='code_key'>float</span> x, <spanclass='code_key'>float</span> y, <spanclass='code_key'>bool</span> bAnimation, <spanclass='code_class'>CanvasMoveArgs</span> ma);</pre>
<spanclass='span_code_line'></span><spanclass='code_note'>/// <spanclass='code_note_1'>Scale Canvas(cannot be moved when there is no Node)</span></span>
<spanclass='span_code_line'></span><spanclass='code_note'>/// <param name=<spanclass='code_string'>"x"</span>><spanclass='code_note_1'>The position of the zoom center X on the control</span></param></span>
<spanclass='span_code_line'></span><spanclass='code_note'>/// <param name=<spanclass='code_string'>"y"</span>><spanclass='code_note_1'>The position of the zoom center Y on the control</span></param></span>
<spanclass='span_code_line'></span>stNodeEditor1.ShowAlert(<spanclass='code_string'>"this is test info"</span>, <spanclass='code_class'>Color</span>.White, <spanclass='code_class'>Color</span>.FromArgb(200, <spanclass='code_class'>Color</span>.Yellow));</pre>
</div>
<imgwidth=208src='./images/tu_editor_alert.png'/>
<p>For more <spanclass='span_mark'>property</span><spanclass='span_mark'>function</span><spanclass='span_mark'>events</span>of<spanclass='span_mark'>STNodeEditor</span>, please refer to <atarget='_bank'href='./doc.html'>API document</a> This document pays more attention to the example of <spanclass='span_mark'>STNode</span></p>
<p><spanclass='span_mark'>STNodePropertyGrid</span> is another control released with the class library and can be used with <spanclass='span_mark'>STNodeEditor</span></p>
<p>There are two panels in <spanclass='span_mark'>STNodePropertyGrid</span>, which can be switched by the button on the top <spanclass='span_mark'>Property Panel</span> and <spanclass='span_mark'>Node Information Panel</span>.</p>
<pclass='p_hightlight'>Only include <spanclass='span_mark'>attribute</span> or <spanclass='span_mark'>node information</span> will display their panel</p>
<hr/>
<div><h2class='h_option anchor_point'anchor='a_v'>how to use</h2></div>
<hr/>
<p>The core method of <spanclass='span_mark'>STNodePropertyGrid</span> is <spanclass='span_mark'>SetNode(STNode)</span> usually used with <spanclass='span_mark'>STNodeEditor</span></p>
<p><spanclass='span_mark'>STNode</span> is a <spanclass='span_mark'>class</span> which of course can have properties, and <spanclass='span_mark'>STNodePropertyGrid</span> is to display and modify them, just like what you see in the UI designer during the development of <spanclass='span_mark'>WinForm</span></p>
<p>You will find that the <spanclass='span_mark'>Number</span> property is not displayed as you expected</p>
<p>The difference with <spanclass='span_mark'>System.Windows.Forms.PropertyGrid</span> is that <spanclass='span_mark'>PropertyGrid</span> will display all the properties in <spanclass='span_mark'>class</span> while <spanclass='span_mark'>STNodePropertyGrid</span> does not.</p>
<p>S T N O D E PropertyGrid This is <spanclass='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 <spanclass='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 <spanclass='span_mark'>Number</span> but a different name. After all, <spanclass='span_mark'>Number</span> is used when writing code</p>
<pclass='p_hightlight'>Only property with the <spanclass='span_mark'>STNodePropertyAttribute</span> feature added will be displayed in the <spanclass='span_mark'>STNodePropertyGrid</span></p>
<p><spanclass='span_mark'>STNodePropertyAttribute</span> has three properties <spanclass='span_mark'>Name</span>, <spanclass='span_mark'>Description</span> and <spanclass='span_mark'>DescriptorType</span></p>
<p><spanclass='span_mark'>Name</span>-the name of this property that you want to display on <spanclass='span_mark'>STNodePropertyGrid</span></p>
<p><spanclass='span_mark'>Description</span>-The description you want to display when the <spanclass='span_mark'>left mouse button</span> is <spanclass='span_mark'>long press</span> the property name on <spanclass='span_mark'>STNodePropertyGrid</span></p>
<p><spanclass='span_mark'>DescriptorType</span>-Data interaction interface with properoty editor, this property will be mentioned later</p>
<p>The constructor of <spanclass='span_mark'>STNodePropertyAttribute</span> is <spanclass='span_mark'>STNodePropertyAttribute(string strName,string strDescription)</span></p>
<spanclass='span_code_line'></span> [<spanclass='code_class'>STNodeProperty</span>(<spanclass='code_string'>"Name"</span>, <spanclass='code_string'>"Description for this property"</span>)]
<spanclass='span_code_line'></span><spanclass='code_key'>public</span><spanclass='code_key'>int</span> Number {
<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>
<p>If you want to display node information, <spanclass='span_mark'>STNode</span> needs to be marked with the <spanclass='span_mark'>STNodeAttribute</span> feature</p>
<spanclass='span_code_line'></span> [<spanclass='code_class'>STNodeProperty</span>(<spanclass='code_string'>"Name"</span>, <spanclass='code_string'>"Description for this property"</span>)]
<spanclass='span_code_line'></span><spanclass='code_key'>public</span><spanclass='code_key'>int</span> Number {
<p>The key of the content of the <spanclass='span_mark'>Information Panel</span> can be used to set the language through the <spanclass='span_mark'>SetInfoKey()</span> function, which is displayed in simplified Chinese by default.</p>
<p>In the example of <spanclass='span_mark'>Information Panel</span>, you can see that the <spanclass='span_mark'>Show Help</span> button is not available. If you want it to be available, you need to provide a <spanclass='span_mark'>magic method</span></p>
<spanclass='span_code_line'></span> [<spanclass='code_class'>STNodeProperty</span>(<spanclass='code_string'>"Name"</span>, <spanclass='code_string'>"Description for this property"</span>)]
<spanclass='span_code_line'></span><spanclass='code_key'>public</span><spanclass='code_key'>int</span> Number {
<spanclass='span_code_line'></span><spanclass='code_note'>/// <spanclass='code_note_1'>This method is a magic method</span></span>
<spanclass='span_code_line'></span><spanclass='code_note'>/// <spanclass='code_note_1'>If there is <spanclass='code_string'>"static void ShowHelpInfo(string)"</span> and this class is marked by <spanclass='code_string'>"STNodeAttribute"</span></span></span>
<spanclass='span_code_line'></span><spanclass='code_note'>/// <spanclass='code_note_1'>Then this method will be used as the <spanclass='code_string'>"Show Help"</span> function on the property editor</span></span>
<spanclass='span_code_line'></span><spanclass='code_note'>/// <param name=<spanclass='code_string'>"strFileName"</span>><spanclass='code_note_1'>The file path of the module where this class is located.</span></param></span>
<spanclass='span_code_line'></span><spanclass='code_class'>MessageBox</span>.Show(<spanclass='code_string'>"this is -> ShowHelpInfo(string);\r\n"</span> + strFileName);
<spanclass='span_code_line'></span> }
<spanclass='span_code_line'></span>}</pre>
</div>
<imgwidth=462src='./images/tu_property_6.png'/>
<p>Now find that the <spanclass='span_mark'>Show Help</span> button has become enabled. STNodeAttribute also provides two <spanclass='span_mark'>static</span> functions.</p>
<spanclass='span_code_line'></span><spanclass='code_note'>/// <spanclass='code_note_1'>Excute the <spanclass='code_string'>"ShowHelpInfo"</span> for stNodeType</span></span>
<p><spanclass='span_mark'>STNodePropertyGrid</span> can display and modify properties. Then this case will provide a data input through the <spanclass='span_mark'>STNodePropertyGrid</span>.</p>
<p>In some cases, you don’t want <spanclass='span_mark'>STNodePropertyGrid</span> to set the properties, you just want to display the properties, you can enable <spanclass='span_mark'>ReadOnlyModel</span></p>
<p>You can not set the properoty value from <spanclass='span_mark'>STNodePropertyGrid</span> when the <spanclass='span_mark'>ReadOnlyModel</span> is <spanclass='span_mark'>true</span></p>
<p>The <spanclass='span_mark'>Name</span> and <spanclass='span_mark'>Description</span> properties of <spanclass='span_mark'>STNodePropertyAttribute</span> are introduced above. There is also a properoty <spanclass='span_mark'>DescriptorType</span> whose data type is <spanclass='span_mark'>Type</span> and the default value is <spanclass='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 <spanclass='span_mark'>STNodePropertyGrid</span>. The default <spanclass='span_mark'>STNodePropertyDescriptor</span> only supports the following data types.</p>
<p><spanclass='span_mark'>int</span><spanclass='span_mark'>float</span><spanclass='span_mark'>double</span><spanclass='span_mark'>bool</span><spanclass='span_mark'>string</span><spanclass='span_mark'>Enum</span> and their <spanclass='span_mark'>Array</span></p>
<spanclass='span_code_line'></span> [<spanclass='code_class'>STNodeProperty</span>(<spanclass='code_string'>"TitleColor"</span>, <spanclass='code_string'>"Get or set the node TitleColor"</span>)]
<p>If you run the above code, you will find that there will be errors when setting properties through <spanclass='span_mark'>STNodePropertyGrid</span>, and the display of property values in <spanclass='span_mark'>STNodePropertyGrid</span> is also very strange.</p>
<p>Even though <spanclass='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 <spanclass='span_mark'>System.Windows.Forms.PropertyGrid</span> is to provide <spanclass='span_mark'>TypeConverter</span>, mark the target type with <spanclass='span_mark'>TypeConverter</span> and implement overloading so that <spanclass='span_mark'>PropertyGrid</span> can know how to interact with the <spanclass='span_mark'>PropertyGrid</span> through <spanclass='span_mark'>TypeConverter</span> .</p>
<p>The solution provided by <spanclass='span_mark'>STNodePropertyGrid</span> is <spanclass='span_mark'>STNodePropertyDescriptor</span>.</p>
<pclass='p_hightlight'><spanclass='span_mark'>STNodePropertyGrid</span> can correctly obtain and modify the value of the <spanclass='span_mark'>STNode</span> property, because <spanclass='span_mark'>STNodePropertyDescriptor</span> is performing data conversion and responding to some event operations on the property window.</p>
<pclass='p_hightlight'>The property marked by <spanclass='span_mark'>STNodePropertyAttribute</span> will be packaged as <spanclass='span_mark'>STNodePropertyDescriptor</span> and passed to <spanclass='span_mark'>STNodePropertyGrid</span>. A <spanclass='span_mark'>STNodePropertyDescriptor</span> contains the <spanclass='span_mark'>Name</span> and <spanclass='span_mark'>Description</span> in <spanclass='span_mark'>STNodePropertyAttribute</span> and the position information of the attribute will be displayed on <spanclass='span_mark'>STNodePropertyGrid</span>, and How to respond to mouse events or keyboard events.</p>
<p>It can be considered that <spanclass='span_mark'>STNodePropertyDescriptor</span> is a graphical interface for each properoty marked by <spanclass='span_mark'>STNodePropertyAttribute</span>, and the main function is to transfer data between the graphical interface and the real properoty.</p>
<spanclass='span_code_line'></span><spanclass='code_note'>/// <spanclass='code_note_1'>Convert the value of the string to the value of the property's type.</span></span>
<spanclass='span_code_line'></span><spanclass='code_note'>/// <spanclass='code_note_1'>Only int float double string bool and the above types of Array are supported by default</span></span>
<spanclass='span_code_line'></span><spanclass='code_note'>/// <spanclass='code_note_1'>If the target type is not in the above, please override the function to convert it by yourself.</span></span>
<pclass='p_hightlight'>The <spanclass='span_mark'>GetValueFromString()</span> function converts the string entered by the user in the <spanclass='span_mark'>STNodePropertyGrid</span> into the real value required by the property.</p>
<p>For example: If the property is <spanclass='span_mark'>int</span> type value and the user can only enter the string <spanclass='span_mark'>123</span> in <spanclass='span_mark'>STNodePropertyGrid</span>, then the default <spanclass='span_mark'>GetValueFromString()</span> function will be <spanclass='span_mark'>int.Parse(strText)</span> such as <spanclass='span_mark'>string</span> Type <spanclass='span_mark'>123</span> becomes <spanclass='span_mark'>123</span> of type <spanclass='span_mark'>int</span></p>
<spanclass='span_code_line'></span><spanclass='code_note'>/// <spanclass='code_note_1'>Convert the value of the properoty to a string.</span></span>
<spanclass='span_code_line'></span><spanclass='code_note'>/// <spanclass='code_note_1'>Calling Tostring() of the property value is the default operation.</span></span>
<spanclass='span_code_line'></span><spanclass='code_note'>/// <spanclass='code_note_1'>If you need special processing, please override this function to convert by yourself</span></span>
<pclass='p_hightlight'>The <spanclass='span_mark'>GetStringFromValue()</span> function converts the property value into a string and displays it in the <spanclass='span_mark'>STNodePropertyGrid</span>. The default <spanclass='span_mark'>GetStringFromValue()</span> internally just calls the property value <spanclass='span_mark'>ToString()</span>.</p>
<p>Extend the <spanclass='span_mark'>STNodePropertyDescriptor</span> of <spanclass='span_mark'>ColorTestNode.ColorTest</span> in the above failed example.</p>
<spanclass='span_code_line'></span><spanclass='code_note'>//Specify to use the extended STNodePropertyDescriptor in order to complete the support for the Color type.</span>
<spanclass='span_code_line'></span> [<spanclass='code_class'>STNodeProperty</span>(<spanclass='code_string'>"TitleColor"</span>, <spanclass='code_string'>"Get or set the node TitleColor"</span>, DescriptorType = <spanclass='code_key'>typeof</span>(<spanclass='code_class'>ColorDescriptor</span>))]
<spanclass='span_code_line'></span><spanclass='code_key'>var</span> v = (<spanclass='code_class'>Color</span>)<spanclass='code_key'>this</span>.GetValue(<spanclass='code_key'>null</span>);<spanclass='code_note'>//get the value</span>
<p>At this time, the property value can be displayed and set correctly, because <spanclass='span_mark'>GetValueFromString()</span> and <spanclass='span_mark'>GetStringFromValue()</span> correctly complete the conversion between the property value and the string.</p>
<p>Although the above example can be used to set the <spanclass='span_mark'>Color</span> property through ARGB,but this method is not friendly enough.</p>
<imgwidth=462src='./images/tu_colornode_3.png'/>
<p>Can it be the same as <spanclass='span_mark'>System.Windows.Forms.PropertyGrid</span> to allow users to make visual settings. <spanclass='span_mark'>STNodePropertyDescriptor</span> can of course do it.</p>
<pclass='p_hightlight'><spanclass='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 <spanclass='span_mark'>ColorDescriptor</span></p>
<spanclass='span_code_line'></span><spanclass='code_key'>var</span> v = (<spanclass='code_class'>Color</span>)<spanclass='code_key'>this</span>.GetValue(<spanclass='code_key'>null</span>);
<spanclass='span_code_line'></span><spanclass='code_key'>base</span>.OnDrawValueRectangle(dt);<spanclass='code_note'>//call the base and then draw color preview</span>
<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>
<pclass='p_hightlight'>About <spanclass='span_mark'>STNodePropertyDescriptor</span> there are two important overloaded functions <spanclass='span_mark'>GetBytesFromValue()</span> and <spanclass='span_mark'>SetValue(byte[])</span> which will be introduced in <spanclass='span_mark'>Save Canvas</span> later.</p>
<p><spanclass='span_mark'>STNodeTreeView</span>, like <spanclass='span_mark'>STNodePropertyGrid</span>, is another control released with the class library and can be used with <spanclass='span_mark'>STNodeEditor</span>.</p>
<imgsrc='./images/stnodetreeview.gif'/>
<p>The nodes in <spanclass='span_mark'>STNodeTreeView</span> can be dragged and dropped into <spanclass='span_mark'>STNodeEditor</span>, and preview and search functions are provided.</p>
<p><spanclass='span_mark'>STNodeTreeView</span> is easy to use, and there is no need to create a tree directory by yourself like <spanclass='span_mark'>System.Windows.Forms.TreeView</span>.</p>
<p>Mark the <spanclass='span_mark'>STNode</span> subclass by <spanclass='span_mark'>STNodeAttribute</span> to set the path you want to display in <spanclass='span_mark'>STNodeTreeView</span> and the information you want to display in <spanclass='span_mark'>STNodePropertyGrid</span>.</p>
<pclass='p_hightlight'>Note: If you want nodes to be displayed in <spanclass='span_mark'>STNodeTreeView</span>, you must use <spanclass='span_mark'>STNodeAttribute</span> to mark the <spanclass='span_mark'>STNode</span> subclass.</p>
<div><h2class='h_option anchor_point'anchor='b_j'>how to use</h2></div>
<p>The nodes in the above example are all added through the <spanclass='span_mark'>STNodeEditor.Nodes.Add(STNode)</span> method, and then added through the <spanclass='span_mark'>STNodeTreeView</span> drag-and-drop method. But before that, you need to add the nodes to the <spanclass='span_mark'>STNodeTreeView</span> first.</p>
<p>You can see that the added nodes are displayed in <spanclass='span_mark'>STNodeTreeView</span> and the path is automatically created. The nodes can be previewed and dragged to <spanclass='span_mark'>STNodeEditor</span>.</p>
<pclass='p_hightlight'>There cannot be a node with the same name in the same path, otherwise it will be overwritten.</p>
<p>In addition to <spanclass='span_mark'>STNodeTreeView.AddNode(Type)</span> to add nodes to the tree, you can also load nodes from an assembly through the <spanclass='span_mark'>LoadAssembly(string)</span> method. <spanclass='span_mark'>LoadAssembly(string)</span> will automatically check the nodes marked by <spanclass='span_mark'>STNodeAttribute</span> in the assembly and add them, and will create a root node display with the assembly name.</p>
<spanclass='span_code_line'></span><spanclass='code_note'>//If the node is in another assembly, STNodeEditor should do the same to confirm that the node is recognized by STNodeEditor.</span>
<p>You can see that there are two root nodes <spanclass='span_mark'>AA</span> and <spanclass='span_mark'>WinNodeEditorDemo</span>. In fact, <spanclass='span_mark'>MyNode</span> belongs to the <spanclass='span_mark'>WinNodeEditorDemo</span> assembly, but it cannot be found in the child nodes of <spanclass='span_mark'>WinNodeEditorDemo</span>. Because <spanclass='span_mark'>MyNode</span> is added before loading the assembly, when the loading assembly recognizes that <spanclass='span_mark'>MyNode</span> has been added repeatedly, it is skipped.</p>
<pclass='p_hightlight'><spanclass='span_mark'>STNodeTreeView</span> cannot add a node of the same type and the same name with the same path.</p>
<p>In the introduction of <spanclass='span_mark'>STNodeEditor</span> above, we mentioned the saving and loading of canvas. Here we will introduce it in detail.</p>
<p>When <spanclass='span_mark'>STNodeEditor.SaveCanvas()</span>, <spanclass='span_mark'>STNodeEditor</span> will call <spanclass='span_mark'>internal byte[] STNode.GetSaveData()</span> of each <spanclass='span_mark'>Nodes</span> to get the binary data of each node.</p>
<p><spanclass='span_mark'>GetSaveData()</span> is an internal method modified by <spanclass='span_mark'>internal</span>. The method body is as follows.</p>
<p>You can see that <spanclass='span_mark'>GetSaveData()</span> gets the binary of its own basic data, and then calls <spanclass='span_mark'>OnSaveNode()</span> to get a dictionary.</p>
<spanclass='span_code_line'></span><spanclass='code_key'>var</span> attr = a <spanclass='code_key'>as</span><spanclass='code_class'>STNodePropertyAttribute</span>;<spanclass='code_note'>//Get the property marked by STNodePropertyAttribute and save them automatically.</span>
<spanclass='span_code_line'></span><spanclass='code_key'>byte</span>[] byData = desc.GetBytesFromValue();<spanclass='code_note'>//Get the binary data of the property through STNodePropertyDescriptor.</span>
<spanclass='span_code_line'></span><spanclass='code_note'>/// <spanclass='code_note_1'>When saving the node, get the data that needs to be saved from the expansion node.</span></span>
<spanclass='span_code_line'></span><spanclass='code_note'>/// <spanclass='code_note_1'>Note: Saving is not serialization. When restoring, only re-create this Node through the empty parameter constructor,</span></span>
<spanclass='span_code_line'></span><spanclass='code_note'>/// <spanclass='code_note_1'>and then call OnLoadNode() to restore the saved data.</span></span>
<spanclass='span_code_line'></span><spanclass='code_note'>/// <param name=<spanclass='code_string'>"dic"</span>><spanclass='code_note_1'>the data need to save</span></param></span>
<p>From the above code, you can see that <spanclass='span_mark'>STNode</span> will save binary data of its own basic property, and will recognize the property marked by <spanclass='span_mark'>STNodePropertyAttribute</span> and get the property binary data through <spanclass='span_mark'>GetBytesFromValue()</span>, and then call <spanclass='span_mark'>OnSaveNode(dic)</span> Ask the expansion node for the data that needs to be saved.</p>
<p>If there is an properoty that needs to be saved, it should be marked with <spanclass='span_mark'>STNodePropertyAttribute</span> and make sure that <spanclass='span_mark'>GetBytesFromValue()</span> can correctly get the binary data of the property, or save it through <spanclass='span_mark'>OnSaveNode(dic)</span></p>
<p>If there are private fields that need to be saved, they should be saved through <spanclass='span_mark'>OnSaveNode(dic)</span>.</p>
<p>The above code snippet is the overload of <spanclass='span_mark'>OnSaveNode(dic)</span> and <spanclass='span_mark'>OnLoadNode(dic)</span> in <spanclass='span_mark'>STNodeHub</span>. You can see that an additional <spanclass='span_mark'>count</span> data is saved because the option of <spanclass='span_mark'>STNodeHub</span> is dynamically created, and a newly created <spanclass='span_mark'>STNodeHub</span> has only one line of connection. Therefore, you need to record the status when saving to ensure that the connection status can be restored correctly, and then restore the status in <spanclass='span_mark'>OnLoadNode(dic)</span>.</p>
<pclass='p_hightlight'>In addition to saving node data, <spanclass='span_mark'>STNodeEditor</span> also saves the connection relationship of node options. It will number each <spanclass='span_mark'>STNodeOption</span> of all nodes in the current canvas and save the numbering information. Therefore, when saving and restoring <spanclass='span_mark'>STNodeHub</span>, make sure that the number of <spanclass='span_mark'>STNodeOption</span> is unchanged.</p>
<p>For properties marked by <spanclass='span_mark'>STNodePropertyAttribute</span>, <spanclass='span_mark'>STNodePropertyDescriptor.GetBytesFromValue()</span> will be automatically called when saving to get the binary data of the attribute.</p>
<p><spanclass='span_mark'>GetBytesFromValue()</span> and <spanclass='span_mark'>GetValueFromBytes()</span> correspond to each other</p>
<spanclass='span_code_line'></span><spanclass='code_note'>/// <spanclass='code_note_1'>Convert properoty value to binary It is called when saving node properoty.</span></span>
<spanclass='span_code_line'></span><spanclass='code_note'>/// <spanclass='code_note_1'>By default, GetStringFromValue() is called and then the string is converted to binary data.</span></span>
<spanclass='span_code_line'></span><spanclass='code_note'>/// <spanclass='code_note_1'>If you need special processing, please override this method to convert by yourself and override GetValueFromBytes()</span></span>
<spanclass='span_code_line'></span><spanclass='code_note'>/// <spanclass='code_note_1'>When restoring the properoty value, convert the binary to the properoty value.</span></span>
<spanclass='span_code_line'></span><spanclass='code_note'>/// <spanclass='code_note_1'>Convert it to a string by default and then call GetValueFromString(string).</span></span>
<spanclass='span_code_line'></span><spanclass='code_note'>/// <spanclass='code_note_1'>If override this method,you should override GetBytesFromValue() together.</span></span>
<p><spanclass='span_mark'>STNodePropertyDescriptor.GetBytesFromValue()</span> calls <spanclass='span_mark'>STNodePropertyDescriptor.GetStringFromValue()</span> by default to get the string value of the property and then converts the string to <spanclass='span_mark'>byte[]</span>.</p>
<pclass='p_hightlight'>If <spanclass='span_mark'>GetStringFromValue()</span> and <spanclass='span_mark'>GetValueFromString(strText)</span> can run correctly, then the property value can be saved correctly using the default processing method.</p>
<p>When restoring a node, <spanclass='span_mark'>STNodeEditor</span> creates a node through <spanclass='span_mark'>(STNode)Activator.CreateInstance(stNodeType)</span> and then calls <spanclass='span_mark'>OnLoadNode(dic)</span>.</p>
<spanclass='span_code_line'></span><spanclass='code_key'>var</span> attr = a <spanclass='code_key'>as</span><spanclass='code_class'>STNodePropertyAttribute</span>;
<p>The default <spanclass='span_mark'>OnLoadNode(dic)</span> only restores its own basic properties and properties marked by <spanclass='span_mark'>STNodePropertyAttribute</span>.</p>
<p>For <spanclass='span_mark'>OnLoadNode(dic)</span> of <spanclass='span_mark'>STNodeHub</span></p>
<p>If some nodes need some initialization operations after the canvas is restored, you can override <spanclass='span_mark'>OnEditorLoadCompleted</span> to complete the operation.</p>
<spanclass='span_code_line'></span><spanclass='code_note'>/// <spanclass='code_note_1'>Occurs when the editor has finished loading all nodes.</span></span>