{"id":3917,"date":"2020-04-25T15:47:36","date_gmt":"2020-04-25T13:47:36","guid":{"rendered":"https:\/\/eskerahn.dk\/?p=3917"},"modified":"2024-03-05T01:50:34","modified_gmt":"2024-03-05T00:50:34","slug":"windows-treeview-with-more-multi-line-tree-content","status":"publish","type":"post","link":"https:\/\/eskerahn.dk\/?p=3917","title":{"rendered":"Windows TreeView with e.g. multi-line tree content"},"content":{"rendered":"<p>Some claim that you need a third party tool to have a TreeView with multi-line text (outside WPS).<\/p>\n<p>But it is actually rather easy to do with a few additional steps with the standard TreeView.<\/p>\n<p><a href=\"https:\/\/eskerahn.dk\/?p=3917\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-3918\" src=\"https:\/\/eskerahn.dk\/wp-content\/uploads\/2020\/04\/TreeSample.png\" alt=\"\" width=\"648\" height=\"482\" srcset=\"https:\/\/eskerahn.dk\/wp-content\/uploads\/2020\/04\/TreeSample.png 648w, https:\/\/eskerahn.dk\/wp-content\/uploads\/2020\/04\/TreeSample-300x223.png 300w, https:\/\/eskerahn.dk\/wp-content\/uploads\/2020\/04\/TreeSample-150x112.png 150w\" sizes=\"auto, (max-width: 648px) 100vw, 648px\" \/><\/a><\/p>\n<p><!--more--><\/p>\n<p>Below is the code sample.<\/p>\n<p>The whole point is using the .<em>OwnerDrawText<\/em> and calculate the size to display, and centring the text vertically. No rocket science here&#8230;.<\/p>\n<p>Obviously the logic can be used to display more complex stuff also, say if you have saved some interesting stuff in the Tag.<br \/>\nBut here I have kept it simple, and use text only, and rely on simple rendering of the text with <em>MeasureString<\/em> for later print with <em>DrawString<\/em>. <span style=\"color: #999999;\">(No fancy RTF-like stuff, Should not be <em>too<\/em> complex to implement if needed though, see <span style=\"text-decoration: underline;\"><a style=\"color: #999999; text-decoration: underline;\" href=\"https:\/\/stackoverflow.com\/questions\/2364970\/measure-string-inside-richtextbox-control\">this<\/a><\/span>)<\/span><\/p>\n<p>It is reduced and substantially modified code based on the MS sample <a href=\"https:\/\/docs.microsoft.com\/en-us\/dotnet\/api\/system.windows.forms.treeview.drawnode?view=netframework-4.8\" target=\"_blank\" rel=\"noopener noreferrer\">here<\/a>. That code do the &#8216;magic&#8217; on text saved in the .Tag object not the .Text itself. <span style=\"color: #999999;\">In this I ignore the .Tag e.g. for when used to hold some not displayed linked object.<\/span><\/p>\n<p>This code has a know issue that the build in mouse-over ToolTip hoovering over a node too long to be fully displayed in the window is not handled, so the bubble is displayed in the wrong font and ignoring the LF \ud83d\ude44<\/p>\n<p style=\"padding-left: 40px;\"><a href=\"https:\/\/eskerahn.dk\/wp-content\/uploads\/2020\/04\/TreeSampleKnownIssue.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-3922\" src=\"https:\/\/eskerahn.dk\/wp-content\/uploads\/2020\/04\/TreeSampleKnownIssue.png\" alt=\"\" width=\"294\" height=\"74\" srcset=\"https:\/\/eskerahn.dk\/wp-content\/uploads\/2020\/04\/TreeSampleKnownIssue.png 294w, https:\/\/eskerahn.dk\/wp-content\/uploads\/2020\/04\/TreeSampleKnownIssue-150x38.png 150w\" sizes=\"auto, (max-width: 294px) 100vw, 294px\" \/><\/a><\/p>\n<p>Not a catastrophe, but not completely satisfying either&#8230;<\/p>\n<p>Unfortunately you will manually have to assure that the tree&#8217;s ItemHeight is large enough to contain any multi-line text, as well as any images in the tree.<br \/>\nIf you try to adjust the ItemHeight inside the DrawNode sub it does not immediately throw an exception, but the app crashes severely (<em>AccessViolationException<\/em>) continuing after Form_Load<\/p>\n<p>To my knowledge different branches of a TreeView tree can not have different (collapsed) heights BTW.<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\r\n\r\nusing System;\r\nusing System.Drawing;\r\nusing System.Windows.Forms;\r\n\r\nnamespace TreeTest {\r\n    public partial class Form1 : Form {\r\n        public Form1() {\r\n            InitializeComponent();\r\n        }\r\n\r\n        void treeView_DrawNode(object sender, DrawTreeNodeEventArgs e) {\r\n            \/\/ Draw the background and node text for a selected node.\r\n            e.DrawDefault = false;\r\n            Rectangle eNodeBounds = NodeBounds(e.Node);\r\n            if (eNodeBounds.X==0 &amp;&amp; eNodeBounds.Y==0) return;\r\n\r\n            e.Graphics.FillRectangle(SystemBrushes.Window, e.Node.Bounds); \/\/ clear any remains from system\r\n\r\n            \/\/ Draw the background of the selected node. The NodeBounds method makes the highlight rectangle large enough for the text.\r\n            e.Graphics.FillRectangle(((e.State &amp; TreeNodeStates.Selected) != 0) ? SystemBrushes.Highlight : SystemBrushes.HighlightText\r\n                                      , eNodeBounds);\r\n\r\n            \/\/ Draw the node text using  the node font. If the node font has not been set, use the TreeView font.\r\n            e.Graphics.DrawString(e.Node.Text, e.Node.NodeFont??e.Node.TreeView?.Font??((TreeView)sender).Font,\r\n                                  ((e.State &amp; TreeNodeStates.Selected) != 0) ? SystemBrushes.Window : SystemBrushes.WindowText, eNodeBounds);\r\n\r\n            \/\/ If the node has focus, draw the focus rectangle.\r\n            if ((e.State &amp; TreeNodeStates.Focused) != 0) {\r\n                using (Pen focusPen = new Pen(Color.Black)) {\r\n                    focusPen.DashStyle = System.Drawing.Drawing2D.DashStyle.Dot;\r\n                    e.Graphics.DrawRectangle(focusPen, new Rectangle(eNodeBounds.Location, Rectangle.Inflate(eNodeBounds, -1, -1).Size));\r\n                }\r\n            }\r\n        }\r\n\r\n        \/\/ Selects a node that is clicked on its label text.\r\n        private void treeView_MouseDown(object sender, MouseEventArgs e) {\r\n            TreeNode clickedNode = ((TreeView)sender).GetNodeAt(e.X, e.Y);\r\n            if (NodeBounds(clickedNode).Contains(e.X, e.Y)) ((TreeView)sender).SelectedNode = clickedNode;\r\n        }\r\n\r\n        \/\/ Returns the bounds of the specified node label, Possibly multi line.\r\n        private Rectangle NodeBounds(TreeNode node) {\r\n            if (node?.TreeView!=null  &amp;&amp; node?.Text!=null &amp;&amp; (0&lt;node.Bounds.Location.X || 0&lt;node.Bounds.Location.Y)) {\r\n                \/\/ Retrieve a Graphics object from the TreeView handle and use it to calculate the display size of the text.\r\n                using (Graphics g = node.TreeView.CreateGraphics()) {\r\n                    SizeF textSize = g.MeasureString(node.Text, node.NodeFont ?? node.TreeView.Font);\r\n                    return Rectangle.Ceiling(new RectangleF(PointF.Add(node.Bounds.Location\r\n                                                           , new SizeF(0, (node.TreeView.ItemHeight-textSize.Height)\/2))\r\n                                           , textSize)); \/\/Centre Y\r\n                }\r\n            } else return node?.Bounds??new Rectangle(); \/\/FallBack to the normal node bounds, and to empty 0,0.\r\n        }\r\n\r\n        private void Form1_Load(object sender, EventArgs e) {\r\n            treeView1.DrawMode = TreeViewDrawMode.OwnerDrawText;\r\n            treeView1.DrawNode += treeView_DrawNode; ;\r\n            treeView1.MouseDown += treeView_MouseDown; ;\r\n            treeView1.ItemHeight = 40;\r\n            \/\/treeView1.ImageList = new ImageList();\r\n            \/\/treeView1.ImageList.ImageSize = new System.Drawing.Size(32, 32);\r\n            TreeNode root = new TreeNode(&quot;Root&quot;);\r\n            treeView1.Nodes.Add(root);\r\n            root.Nodes.Add(new TreeNode(&quot;Simple text&quot;));\r\n            root.Nodes.Add(new TreeNode(&quot;X&quot;));\r\n            root.Nodes.Add(new TreeNode(&quot;A text over\\ntwo lines&quot;));\r\n            root.Nodes.Add(new TreeNode(&quot;Z&quot;));\r\n            root.Nodes.Add(new TreeNode(&quot;\u250c 1  0 \u2510 Identity matrix example\\n&quot;\r\n                                       +&quot;\u2514 0  1 \u2518 Fixed pitch&quot;){ NodeFont = new Font(&quot;Consolas&quot;, 10)});\r\n            root.Nodes.Add(new TreeNode(&quot;        \u250c X \u2510    \u250c 1 \u2510    \u250c  723 \u2510\\n&quot;\r\n                                      + &quot;3DBox : | Y | in | 7 | to | 1431 |\\n&quot;\r\n                                      + &quot;        \u2514 Z \u2518    \u2514 5 \u2518    \u2514  361 \u2518&quot;,new TreeNode&#x5B;] { new TreeNode(&quot;blah blah&quot;) }\r\n                                       ){ NodeFont = new Font(&quot;Consolas&quot;, 8) }\r\n                           );\r\n            root.Nodes.Add(new TreeNode(&quot;More text&quot;));\r\n\r\n            root.NodeFont=new Font(treeView1.Font.FontFamily,22f);\r\n        }\r\n    }\r\n}\r\n\r\n<\/pre>\n<p>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Some claim that you need a third party tool to have a TreeView with multi-line text (outside WPS). But it is actually rather easy to do with a few additional steps with the standard TreeView.<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[15],"tags":[],"class_list":["post-3917","post","type-post","status-publish","format-standard","hentry","category-pc-and-code-samples"],"_links":{"self":[{"href":"https:\/\/eskerahn.dk\/index.php?rest_route=\/wp\/v2\/posts\/3917","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/eskerahn.dk\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/eskerahn.dk\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/eskerahn.dk\/index.php?rest_route=\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/eskerahn.dk\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=3917"}],"version-history":[{"count":5,"href":"https:\/\/eskerahn.dk\/index.php?rest_route=\/wp\/v2\/posts\/3917\/revisions"}],"predecessor-version":[{"id":5790,"href":"https:\/\/eskerahn.dk\/index.php?rest_route=\/wp\/v2\/posts\/3917\/revisions\/5790"}],"wp:attachment":[{"href":"https:\/\/eskerahn.dk\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=3917"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/eskerahn.dk\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=3917"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/eskerahn.dk\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=3917"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}