{"id":2185,"date":"2019-10-17T18:10:48","date_gmt":"2019-10-17T18:10:48","guid":{"rendered":"https:\/\/mindfusion.eu\/blog\/?p=2185"},"modified":"2021-01-23T16:17:26","modified_gmt":"2021-01-23T16:17:26","slug":"custom-nodes-in-wpf-diagram","status":"publish","type":"post","link":"https:\/\/mindfusion.dev\/blog\/custom-nodes-in-wpf-diagram\/","title":{"rendered":"Custom Nodes in WPF Diagram"},"content":{"rendered":"<p>Here we will look how to define custom diagram nodes in the WPF diagram control, how to style them, how to make their properties appear in the property grid and how to save and load them with the diagram&#8217;s <a title=\"WPF Diagram API Reference\" href=\"https:\/\/www.mindfusion.dev\/onlinehelp\/wpfdiagram\/index.htm?O_T_MindFusion_Diagramming_Wpf_Diagram_SaveToXml.htm\">saveToXml<\/a> and <a title=\"WPF Diagram API Reference\" href=\"https:\/\/www.mindfusion.dev\/onlinehelp\/wpfdiagram\/index.htm?O_T_MindFusion_Diagramming_Wpf_Diagram_LoadFromXml.htm\">loadFromXml<\/a> methods.<\/p>\n<p>Here is a screenshot of our SubjectNode custom node class that is used in an application for a school curriculum:<\/p>\n<p><a title=\"Custom Diagram Nodes in WPF\" href=\"http:\/\/mindfusion.dev\/samples\/wpf\/diagram\/wpf_custom_diagram_nodes.png\"><img decoding=\"async\" title=\"WPF Diagram: Custom Nodes\" src=\"http:\/\/mindfusion.dev\/samples\/wpf\/diagram\/wpf_custom_diagram_nodes.png\" \/><\/a><\/p>\n<p><strong>I. XAML Template<\/strong><\/p>\n<p>You will need to add a XAML template for the node us you are creating a custom node because you want to have special-looking nodes. Let&#8217;s create a node that has 3 text fields and a background. We will declare the template for this node that we call SubjectNode in XAML this way:<\/p>\n<pre id=\"line1\">&lt;<span class=\"start-tag\">style<\/span> <span class=\"attribute-name\">targettype<\/span>=\"<a class=\"attribute-value\">local:SubjectNode<\/a>\"&gt;\n<span id=\"line247\"><\/span>    &lt;Setter Property=\"Template\"&gt;\n<span id=\"line248\"><\/span>      &lt;Setter.Value&gt;\n<span id=\"line249\"><\/span>        &lt;DataTemplate DataType=\"local:SubjectNode\"&gt;\n<span id=\"line250\"><\/span>          &lt;Grid&gt;\n<span id=\"line251\"><\/span>\n<span id=\"line252\"><\/span>            &lt;Rectangle\n<span id=\"line253\"><\/span>\t\tStroke=\"{Binding Stroke}\"\n<span id=\"line254\"><\/span>\t\tFill=\"{Binding Background}\" \/&gt;\n<span id=\"line255\"><\/span>\n<span id=\"line256\"><\/span>            &lt;Grid&gt;              \n<span id=\"line257\"><\/span>\n<span id=\"line258\"><\/span>               &lt;StackPanel Margin=\"4,8,0,0\"  Orientation=\"Vertical\" Grid.Column=\"1\"&gt;\n<span id=\"line259\"><\/span>                &lt;TextBlock Text=\"{Binding Subject}\" FontWeight=\"800\" Foreground=\"Black\" \/&gt;\n<span id=\"line260\"><\/span>                &lt;TextBlock Text=\"{Binding Teacher}\" Foreground=\"Blue\" \/&gt;\n<span id=\"line261\"><\/span>                &lt;TextBlock Text=\"{Binding Remarks}\" FontSize=\"9\" Foreground=\"Black\" \/&gt;\n<span id=\"line262\"><\/span>              &lt;\/StackPanel&gt;\n<span id=\"line263\"><\/span>            &lt;\/Grid&gt;\n<span id=\"line264\"><\/span>\n<span id=\"line265\"><\/span>          &lt;\/Grid&gt;\n<span id=\"line266\"><\/span>        &lt;\/DataTemplate&gt;\n<span id=\"line267\"><\/span>      &lt;\/Setter.Value&gt;\n<span id=\"line268\"><\/span>    &lt;\/Setter&gt;\n<span id=\"line269\"><\/span>  &lt;\/<span class=\"end-tag\">style<\/span>&gt;<\/pre>\n<p>That goes in the contents of &lt;ResourceDictionary&gt;&#8230;..&lt;\/ResourceDictionary&gt; in the xaml file where you store this resourrce dictionary.<\/p>\n<p>You see here that we use a gird as the principal layout container. There we add a rectangle, whose Fill property is bound to a property called Background in the SubjectNode. Next we have another grid that holds a StackPanel. The stack panel is with vertical orientation and it arranges the three TextBlock-s for the three custom fields of the node.<\/p>\n<p><strong>II. Declaring the Custom Node Class<\/strong><\/p>\n<p>When you create a custom node you need to inherit the <a title=\"WPF Diagram API Reference\" href=\"https:\/\/www.mindfusion.dev\/onlinehelp\/wpfdiagram\/index.htm?T_MindFusion_Diagramming_Wpf_TemplatedNode.htm\">TemplatedNode<\/a> class. In the static construcotr you should call OverrideMetadata on the DefaultStyleKeyProperty to make it use the template that we&#8217;ve declared in XAML:<\/p>\n<pre>public class SubjectNode : TemplatedNode\n{\n\tstatic SubjectNode()\n\t{\n\t\tDefaultStyleKeyProperty.OverrideMetadata(\n\t\t\ttypeof(SubjectNode), new FrameworkPropertyMetadata(typeof(SubjectNode)));\n}\n\npublic SubjectNode()\n{\t\t\t\n}\n<\/pre>\n<p>Then we declare a constructor without any parameters that is required for the node to be created in XAML. If you want users to be able to create instance of the SubjectNode through drag and drop, you need to declare one more constructor:<\/p>\n<pre>\t\/\/ Required for creating nodes by dragging them from the NodeListView\npublic SubjectNode(SubjectNode prototype) : base(prototype)\n{\n\tSubject = prototype.Subject;\n\tTeacher = prototype.Teacher;\n\tRemarks = prototype.Remarks;\n}\n<\/pre>\n<p><strong>III. Properties<\/strong><\/p>\n<p>We declare the properties that we want: Subject, TeacherName, Remarks and Background as dependency properties the standard way:<\/p>\n<pre>public Brush Background\n{\n\tget { return (Brush)GetValue(BackgroundProperty); }\n\tset { SetValue(BackgroundProperty, value); }\n}\n\npublic static readonly DependencyProperty BackgroundProperty = DependencyProperty.Register(\n\t\"Background\",\n\ttypeof(Brush),\n\ttypeof(SubjectNode),\n\tnew PropertyMetadata(new SolidColorBrush(Color.FromRgb(223, 235, 250))));\n\n<\/pre>\n<p>and for the text properties:<\/p>\n<pre>public string Remarks\n{\n\tget { return (string)GetValue(RemarksProperty); }\n\tset { SetValue(RemarksProperty, value); }\n}\n\npublic static readonly DependencyProperty RemarksProperty = DependencyProperty.Register(\n\t\"Remarks\",\n\ttypeof(string),\n\ttypeof(SubjectNode),\n\tnew PropertyMetadata(\"\"));\n\n<\/pre>\n<p>If we want the properties to be listed in a property grid we need to add a new class that inherits from <a title=\"WPF Diagram API Reference\" href=\"https:\/\/www.mindfusion.dev\/onlinehelp\/wpfdiagram\/index.htm?T_MindFusion_Diagramming_Wpf_DiagramNodeProperties.htm\">DiagramNodeProperties<\/a>. In it we do nothing but list the custom properties together with their type:<\/p>\n<pre>public class SubjectNodeProperties : DiagramNodeProperties\n{\n        internal string Subject;\n\tinternal string Teacher;\n\tinternal string Remarks;\n\tinternal Brush Background;\n} \n<\/pre>\n<p><strong>IV. More Options<\/strong><\/p>\n<p>Standard diagram nodes support undo and redo as well serialization out of the box. If you want your custom class to support those features as well you need to implement a few more methods. The methods to support undo\/redo are SaveProperties and RestoreProperties. They take an instance of the <a title=\"WPF Diagram API Reference\" href=\"https:\/\/www.mindfusion.dev\/onlinehelp\/wpfdiagram\/index.htm?T_MindFusion_Diagramming_Wpf_DiagramItemProperties.htm\">DiagramItemProperties<\/a> class that allows you to transfer data between the instance of the current node and its DiagramItemProperties instance that store the values of the node&#8217;s properties:<\/p>\n<pre>protected override void RestoreProperties(DiagramItemProperties props)\n{\n\tbase.RestoreProperties(props);\n\tvar state = (SubjectNodeProperties)props;\n\tSubject = state.Subject;\n\tTeacher = state.Teacher;\n\tRemarks = state.Remarks;\n\tBackground = state.Background;\n}\n\nprotected override void SaveToXml(XmlElement xmlElement, XmlPersistContext context)\n{\n\tbase.SaveToXml(xmlElement, context);\n\tcontext.WriteString(Subject, \"Subject\", xmlElement);\n\tcontext.WriteString(Teacher, \"Teacher\", xmlElement);\n\tcontext.WriteString(Remarks, \"Remarks\", xmlElement);\n\tcontext.WriteBrush(Background, \"Background\", xmlElement);\n}\n<\/pre>\n<p>The <a title=\"WPF Diagram API Reference\" href=\"https:\/\/www.mindfusion.dev\/onlinehelp\/wpfdiagram\/index.htm?T_MindFusion_Diagramming_Wpf_Diagram.htm\">Diagram<\/a> uses XML for serialization, so if you want your node to be saved and loaded correctly through the <a title=\"WPF Diagram API Reference\">Diagram<\/a>&#8216;s <a title=\"WPF Diagram API Reference\" href=\"https:\/\/www.mindfusion.dev\/onlinehelp\/wpfdiagram\/index.htm?O_T_MindFusion_Diagramming_Wpf_Diagram_SaveToXml.htm\">saveToXml<\/a> and <a title=\"WPF Diagram API Reference\" href=\"https:\/\/www.mindfusion.dev\/onlinehelp\/wpfdiagram\/index.htm?O_T_MindFusion_Diagramming_Wpf_Diagram_LoadFromXml.htm\">loadFromXml<\/a> methods you should implement SaveToXml and LoadFromXml. There you write the values o the custom properties of SubjectNode to XML elements and read them from XML elements as well:<\/p>\n<pre>protected override void SaveToXml(XmlElement xmlElement, XmlPersistContext context)\n{\n\tbase.SaveToXml(xmlElement, context);\n\tcontext.WriteString(Subject, \"Subject\", xmlElement);\n\tcontext.WriteString(Teacher, \"Teacher\", xmlElement);\n\tcontext.WriteString(Remarks, \"Remarks\", xmlElement);\n\tcontext.WriteBrush(Background, \"Background\", xmlElement);\n}\n\nprotected override void LoadFromXml(XmlElement xmlElement, XmlPersistContext context)\n{\n\tbase.LoadFromXml(xmlElement, context);\n\tSubject = context.ReadString(\"Subject\", xmlElement);\n\tTeacher = context.ReadString(\"Teacher\", xmlElement);\n\tRemarks = context.ReadString(\"Remarks\", xmlElement);\n\tBackground = context.ReadBrush(\"Background\", xmlElement);\n}\n<\/pre>\n<p>You can download the sample that uses custom SubjectNode from <a title=\"Wpf Diagram Sample with Custom Node\" href=\"https:\/\/mindfusion.dev\/samples\/wpf\/diagram\/Curriculum.zip\">https:\/\/mindfusion.dev\/samples\/wpf\/diagram\/Curriculum.zip<\/a><\/p>\n<p><em>About Diagramming for WPF:<\/em> This is the right tool to create flowcharts in WPF that always meet your requirements. The library offers more than 100 predefined node shapes, extensive event set and more than 15 exporters and importers. Each diagram that you build has a completely customizable look through styles, themes and appearance properties for each part of the flowchart. The numerous samples and detailed documentation help you learn quickly how to integrate the component into your own application. You can download the trial version, which has no feature restrictions and does not expire from the <a title=\"MindFusion WPF Diagram Control\" href=\"https:\/\/mindfusion.dev\/wpf-diagram.html\">WPF Diagram Page on MindFusion website.<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Here we will look how to define custom diagram nodes in the WPF diagram control, how to style them, how to make their properties appear in the property grid and how to save and load them with the diagram&#8217;s saveToXml &hellip; <a href=\"https:\/\/mindfusion.dev\/blog\/custom-nodes-in-wpf-diagram\/\">Continue reading <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"jetpack_post_was_ever_published":false,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":false,"jetpack_social_options":{"image_generator_settings":{"template":"highway","enabled":false},"version":2}},"categories":[95],"tags":[590,587,586,589,588,591],"class_list":["post-2185","post","type-post","status-publish","format-standard","hentry","category-diagramming-2","tag-wpf-control","tag-wpf-custom-nodes","tag-wpf-diagram","tag-wpf-flowchart-tutorial","tag-wpf-nodes","tag-wpf-sample-code"],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"","jetpack_shortlink":"https:\/\/wp.me\/p3RlKs-zf","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/mindfusion.dev\/blog\/wp-json\/wp\/v2\/posts\/2185","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/mindfusion.dev\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/mindfusion.dev\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/mindfusion.dev\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/mindfusion.dev\/blog\/wp-json\/wp\/v2\/comments?post=2185"}],"version-history":[{"count":9,"href":"https:\/\/mindfusion.dev\/blog\/wp-json\/wp\/v2\/posts\/2185\/revisions"}],"predecessor-version":[{"id":2635,"href":"https:\/\/mindfusion.dev\/blog\/wp-json\/wp\/v2\/posts\/2185\/revisions\/2635"}],"wp:attachment":[{"href":"https:\/\/mindfusion.dev\/blog\/wp-json\/wp\/v2\/media?parent=2185"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/mindfusion.dev\/blog\/wp-json\/wp\/v2\/categories?post=2185"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/mindfusion.dev\/blog\/wp-json\/wp\/v2\/tags?post=2185"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}