{"id":126,"date":"2017-01-10T00:03:20","date_gmt":"2017-01-09T15:03:20","guid":{"rendered":"http:\/\/reasty.net\/?p=126"},"modified":"2017-01-10T00:03:20","modified_gmt":"2017-01-09T15:03:20","slug":"expandablemenu-wpf-custom-control","status":"publish","type":"post","link":"https:\/\/reasty.net\/?p=126","title":{"rendered":"ExpandableMenu &#8211; WPF Custom Control"},"content":{"rendered":"<p><a href=\"http:\/\/192.168.10.240:8080\/wp-content\/uploads\/2017\/01\/init.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-medium wp-image-130\" src=\"http:\/\/192.168.10.240:8080\/wp-content\/uploads\/2017\/01\/init-211x300.png\" alt=\"\" width=\"211\" height=\"300\" srcset=\"https:\/\/reasty.net\/wp-content\/uploads\/2017\/01\/init-211x300.png 211w, https:\/\/reasty.net\/wp-content\/uploads\/2017\/01\/init.png 257w\" sizes=\"auto, (max-width: 211px) 100vw, 211px\" \/><\/a> <a href=\"http:\/\/192.168.10.240:8080\/wp-content\/uploads\/2017\/01\/expand_first_item.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-medium wp-image-127\" src=\"http:\/\/192.168.10.240:8080\/wp-content\/uploads\/2017\/01\/expand_first_item-211x300.png\" alt=\"\" width=\"211\" height=\"300\" srcset=\"https:\/\/reasty.net\/wp-content\/uploads\/2017\/01\/expand_first_item-211x300.png 211w, https:\/\/reasty.net\/wp-content\/uploads\/2017\/01\/expand_first_item.png 257w\" sizes=\"auto, (max-width: 211px) 100vw, 211px\" \/><\/a> <a href=\"http:\/\/192.168.10.240:8080\/wp-content\/uploads\/2017\/01\/expand_second_item.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-medium wp-image-129\" src=\"http:\/\/192.168.10.240:8080\/wp-content\/uploads\/2017\/01\/expand_second_item-211x300.png\" alt=\"\" width=\"211\" height=\"300\" srcset=\"https:\/\/reasty.net\/wp-content\/uploads\/2017\/01\/expand_second_item-211x300.png 211w, https:\/\/reasty.net\/wp-content\/uploads\/2017\/01\/expand_second_item.png 257w\" sizes=\"auto, (max-width: 211px) 100vw, 211px\" \/><\/a> <a href=\"http:\/\/192.168.10.240:8080\/wp-content\/uploads\/2017\/01\/expand_last_item.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-medium wp-image-128\" src=\"http:\/\/192.168.10.240:8080\/wp-content\/uploads\/2017\/01\/expand_last_item-211x300.png\" alt=\"\" width=\"211\" height=\"300\" srcset=\"https:\/\/reasty.net\/wp-content\/uploads\/2017\/01\/expand_last_item-211x300.png 211w, https:\/\/reasty.net\/wp-content\/uploads\/2017\/01\/expand_last_item.png 257w\" sizes=\"auto, (max-width: 211px) 100vw, 211px\" \/><\/a><\/p>\n<h1>1.\uac1c\uc694<\/h1>\n<p>\ud504\ub85c\uc81d\ud2b8 \uc911 \ub098\uc911\uc5d0 \uac1c\uc778\uc801\uc73c\ub85c\ub3c4 \uc0ac\uc6a9\ud560 \ub9cc\ud55c Custom Control \uc774 \uc788\uc5b4\uc11c \ub530\ub85c \uc815\ub9ac\ud558\uac8c \ub418\uc5c8\ub2e4.<br \/>\n\ubb3c\ub860 \uc2e4\uc81c \ud504\ub85c\uc81d\ud2b8\uc5d0 \uc801\uc6a9\ub41c \ucf54\ub4dc\uc640 50% \uc774\uc0c1 \ub2e4\ub974\ub2c8.. \uc548\uc2ec\ud558\uace0 ^^;<\/p>\n<h1>2. \uc694\uad6c\uc0ac\ud56d<\/h1>\n<p>UX \ud300\uc5d0\uc11c Microsoft Outlook 2008\uc758 \uc88c\uce21 \uba54\ub274\ucc98\ub7fc\u00a0\uc811\ud614\ub2e4 \ud3bc\uccd0\uc9c0\ub294 \uba54\ub274\ub97c \uc6d0\ud588\ub2e4. \uac70\uae30\ub2e4\uac00 \ud3bc\uccd0\uc9c4 \uba54\ub274 \uc774\uc678\uc758 \uba54\ub274\ub4e4\uc740 \ub9e8 \ud558\ub2e8\uc73c\ub85c \ub0b4\ub824\uac00\uac8c \ub418\uace0, \uc804\uccb4 \uba54\ub274\uc758 Height \ud06c\uae30\ub294 \ubcc0\ud558\uc9c0 \uc54a\uac8c \ud574\ub2ec\ub77c\uace0 \ud558\uc600\ub2e4. \uadf8\ub9ac\uace0 \ud3bc\uccd0\uc9c0\ub294 \uba54\ub274\ub294 \ubb34\uc870\uac74 \ud558\ub098.<\/p>\n<p>\uc77c\ubc18\uc801\uc73c\ub85c Expand \uba54\ub274\ub97c \uc0dd\uac01\ud558\uba74 StackPanel\uc5d0\u00a0Expander Control\uc744 \uc5ec\ub7ec \uac1c \uc313\uace0, Expander.Content\uc758 Height \ub9cc\ud07c \uae38\uc774 \uc81c\uc57d\uc774 \uc5c6\uc744\ud14c\uc9c0\ub9cc, \uc804\uccb4 \ucee8\ud2b8\ub864\uc758 Height \ud06c\uae30\ub9cc\ud07c \uc720\uc9c0 \ub418\uc5b4\uc57c \ud558\ub294\uac8c \uae4c\ub2e4\ub85c\uc6e0\ub2e4.<\/p>\n<h1>3. \uac1c\ubc1c<\/h1>\n<p>\ud3bc\uccd0\uc9c0\ub294 \uba54\ub274\uac00 \ubb34\uc870\uac74 \ud558\ub098\uc774\uae30\uc5d0 ListBox\uc758 Single Select\uac00 \uba3c\uc800 \ub5a0\uc62c\ub790\ub2e4. Selector.IsSelected \ud504\ub85c\ud37c\ud2f0\uc5d0 Expander Control\uc758 IsExpand \ud504\ub85c\ud37c\ud2f0\ub97c \ubc14\uc778\ub529 \uc2dc\ud0a4\uba74 \ud558\ub098\ub9cc \ud3bc\uccd0\uc9c0\uae30 \ub54c\ubb38\uc774\ub2e4.<\/p>\n<h2>3.1. ListBox Customizing<\/h2>\n<h3>3.1.1. ListBoxItemContainerStyle<\/h3>\n<p>ListBox\uc5d0 \ubc14\uc778\ub529 \ub420 \uba54\ub274\ub294 ListBoxItem \uc73c\ub85c \ucee8\ud14c\uc774\ub108\uac00 \uc0dd\uc131\ub418\ubbc0\ub85c Container\uc758 Template\ub97c \uc218\uc815\ud55c\ub2e4.<\/p>\n<pre title=\"ListBoxItemContainerStyle\" class=\"lang:xhtml decode:true \">&lt;ListBox.ItemContainerStyle&gt;\n  &lt;Style TargetType=\"{x:Type ListBoxItem}\"&gt;\n    &lt;Setter Property=\"Template\"&gt;\n      &lt;Setter.Value&gt;\n        &lt;ControlTemplate TargetType=\"{x:Type ListBoxItem}\"&gt;\n          &lt;Expander Header=\"{Binding Header}\" BorderBrush=\"{TemplateBinding BorderBrush}\" BorderThickness=\"{TemplateBinding BorderThickness}\" Background=\"{TemplateBinding Background}\" Padding=\"{TemplateBinding Padding}\" SnapsToDevicePixels=\"true\"\n                            IsExpanded=\"{Binding IsSelected, RelativeSource={RelativeSource AncestorType={x:Type ListBoxItem}}}\"&gt;\n            &lt;ContentPresenter HorizontalAlignment=\"{TemplateBinding HorizontalContentAlignment}\" SnapsToDevicePixels=\"{TemplateBinding SnapsToDevicePixels}\" VerticalAlignment=\"{TemplateBinding VerticalContentAlignment}\"\/&gt;\n          &lt;\/Expander&gt;\n        &lt;\/ControlTemplate&gt;\n      &lt;\/Setter.Value&gt;\n    &lt;\/Setter&gt;\n  &lt;\/Style&gt;\n&lt;\/ListBox.ItemContainerStyle&gt;<\/pre>\n<p>ControlTemplate\uc5d0 Expander \ub97c \ucd94\uac00\ud558\uace0 Expander.Content\uc5d0\ub294 ContentPresenter\ub97c \ub450\uc5b4 \ubcc4\ub3c4\uc758 Template \uc124\uc815\uc744 \uac00\ub2a5\ud558\ub3c4\ub85d \ud55c\ub2e4.<\/p>\n<p>\uadf8\ub9ac\uace0 Expander\uc758 IsExpanded \ud504\ub85c\ud37c\ud2f0\uc5d0 ListBoxItem\uc758 IsSelected \ud504\ub85c\ud37c\ud2f0\ub97c \ubc14\uc778\ub529\ud55c\ub2e4.<\/p>\n<p>\ub610\ud55c Expander \uc758 Header \ud504\ub85c\ud37c\ud2f0\uc5d0 \uc801\ub2f9\ud55c \uac12\uc774 \ub4e4\uc5b4\uac00\ub3c4\ub85d \ubc14\uc778\ub529\ud55c\ub2e4. \ub098\ub294 ExpandableMenu\ub97c \uc704\ud558\uc5ec \ubcc4\ub3c4\uc758 Class\ub97c \ubc14\uc778\ub529\ud560 \uacc4\ud68d\uc774\ubbc0\ub85c Header\ub77c\ub294 \ud504\ub85c\ud37c\ud2f0\uc5d0 \ubc14\uc778\ub529\ud558\uc600\ub2e4.<\/p>\n<h3>3.1.2. ItemTemplate<\/h3>\n<p>ListBox\uc758 ItemTemplate\ubd80\ubd84\uc5d0\ub294 \uc2e4\uc81c\ub85c SubMenu \uc601\uc5ed\uc774 \ub4e4\uc5b4\uac00\uac8c \ub420\ud14c\uc9c0\ub9cc, \uc5ec\uae30\uc11c\ub294 \uac04\ub2e8\ud558\uac8c \uc54c\uc544\ubcfc \uc218 \uc788\uc744 \uc815\ub3c4\ub85c\ub9cc TextBlock \ud558\ub098\ub9cc \ub193\ub3c4\ub85d \ud558\uaca0\ub2e4.<\/p>\n<pre title=\"ItemTemplate\" class=\"lang:xhtml decode:true \">&lt;ListBox.ItemTemplate&gt;\n  &lt;DataTemplate&gt;\n    &lt;TextBlock Margin=\"10\" Text=\"{Binding Content}\"\/&gt;\n  &lt;\/DataTemplate&gt;\n&lt;\/ListBox.ItemTemplate&gt;<\/pre>\n<h2>3.2. Resize Expander.Content.Height<\/h2>\n<p>ListBoxItem\uc774 \uc120\ud0dd\ub418\uc5c8\uc744\ub54c, Expander \uac00 Expand \ub418\ub294 \uac83\uae4c\uc9c0 \ud655\uc778\ud558\uc600\ub2e4. \uc774\uc81c Expander.Content \uc758 Height \ud06c\uae30\ub97c \uc870\uc808\ud574\uc57c \ud55c\ub2e4.<\/p>\n<p>Code behind \ub85c \uac00\uc11c ListBox\uc758 \uc774\uac83\uc800\uac83\uc744 \uc190\ubcf4\uc544\uc57c \ud55c\ub2e4.<\/p>\n<p>ItemsControl.ItemContainerGenerator \ub97c \uc774\uc6a9\ud558\uba74 ListBoxItemContainer\uc758 \uc5ec\ub7ec\uac00\uc9c0 \uc0c1\ud669\uc5d0 \ub530\ub77c \uc81c\uc5b4\ud560 \uc218 \uc788\ub2e4.<\/p>\n<p>\uc774 \uc911 StatusChanged Event\ub97c \uc774\uc6a9\ud558\uc5ec \ucee8\ud14c\uc774\ub108\uac00 \uc0dd\uc131\ub418\uc5b4\uc84c\uc744\ub54c Visual Parent\uc778 TreeView\uc758 ActualHeight\ub97c \uad6c\ud558\uace0, \ud3bc\uccd0\uc9c0\uc9c0 \uc54a\uc740 Expander\ub4e4\uc758 Height\ub97c \uad6c\ud558\uc5ec ListBox\uc5d0\uc11c \ube44\uc5b4 \uc788\ub294 \uc601\uc5ed\uc758 Height \ub97c Expander.Content\uc758 Height\ub85c \uc124\uc815\ud55c\ub2e4.<\/p>\n<p>\uc544\ub798\ub294 \uc8fc\uc694 \ucf54\ub4dc\uc774\ub2e4.<\/p>\n<pre title=\"ItemContainerGenerator.StatusChanged\" class=\"lang:c# mark:4,12,15,23,38 decode:true\">protected override void OnInitialized(EventArgs e)\n{\n    base.OnInitialized(e);\n    ItemContainerGenerator.StatusChanged += ItemContainerGenerator_StatusChanged;\n}\n\nprivate void ItemContainerGenerator_StatusChanged(object sender, EventArgs e)\n{\n    try\n    {\n        var actualHeight = ActualHeight;\n        if (ItemContainerGenerator.Status == System.Windows.Controls.Primitives.GeneratorStatus.ContainersGenerated)\n        {\n            var itemElements = Items.OfType&lt;object&gt;();\n            var firstOfvalidListBoxItems = (from item in itemElements\n                                            let l = ItemContainerGenerator.ContainerFromItem(item) as ListBoxItem\n                                            where l.IsSelected == false &amp;&amp; l.ActualHeight != 0 \/\/ \uc120\ud0dd\ub418\uba74 Fill \ub418\ubbc0\ub85c \uc120\ud0dd\ub418\uc9c0 \uc54a\uc740 ListBoxItem\n                                            select l).FirstOrDefault();\n\n            if (firstOfvalidListBoxItems == null)\n                return;\n\n            var collapsedHeight = firstOfvalidListBoxItems.ActualHeight * Items.Count;\n            foreach (var listBoxItem in itemElements)\n            {\n                var container = ItemContainerGenerator.ContainerFromItem(listBoxItem) as ListBoxItem;\n                Expander listBoxItemContainerExpander = VisualTreeHelper.GetChild(container, 0) as Expander;\n                if (listBoxItemContainerExpander == null)\n                    break;\n\n                var element = listBoxItemContainerExpander.Content as FrameworkElement;\n                if (element == null)\n                    break;\n\n                var elementHeight = (actualHeight - collapsedHeight);\n                if (elementHeight &gt;= 0)\n                {\n                    element.Height = elementHeight;\n                }\n            }\n        }\n    }\n    catch (Exception ex)\n    {\n        Debug.WriteLine(ex);\n    }\n}<\/pre>\n<h2>3.3. ExpandableMenuItem<\/h2>\n<p>\ubb50 \ud2b9\ubcc4\ud55c \uac70 \uc5c6\uc774 \uc6d0\ud558\ub294 Header (Menu Name), Content (Sub-Menu Name)\uc744 \uc124\uc815\ud558\uae30 \uc704\ud574 \ubcc4\ub3c4\uc758 \ud074\ub798\uc2a4\ub97c \ub9cc\ub4e4\uc5b4\uc11c ListBox.ItemsSource\uc5d0 \ubc14\uc778\ub529\ud558\uc600\ub2e4.<\/p>\n<pre title=\"ExpandableMenuItem\" class=\"lang:c# decode:true\">public class ExpandableMenuItem\n{\n    #region Properties\n\n    public object Header { get; set; }\n\n    public object Content { get; set; }\n\n    public bool IsExpanded { get; set; }\n\n    #endregion\n}<\/pre>\n<p>&nbsp;<\/p>\n<h1>4. \uc815\ub9ac<\/h1>\n<p>\uba87\uc904 \uc548\ub418\ub294 \ucf54\ub4dc\ub85c \uba4b\uc9c4 \ucee8\ud2b8\ub864\uc774 \uc644\uc131\ub418\uc5c8\ub2e4. \uc5ec\uae30\uc5d0 \ucd94\uac00\uc801\uc73c\ub85c \ube14\ub79c\ub354\uac00 \uc2a4\ud0c0\uc77c \uc880 \uc785\ud788\uba74 Telerik Control \ubd80\ub7fd\uc9c0 \uc54a\ub294 Custom Control\uc774 \ub41c\ub2e4. \ub514\uc790\uc778 \uac10\uac01\uc774 \ub6f0\uc5b4\ub09c \uac1c\ubc1c\uc790\ub77c\uba74 \uc2a4\uc2a4\ub85c \ud574\ubcf4\uae38 \ubc14\ub780\ub2e4. (<del>\ub09c \ub514\uc790\uc778 \uac10\uac01\uc774 \uc5c6\ub098\ubd10 \u3160<\/del>)<\/p>\n<p>\uc804\uccb4 \uc18c\uc2a4\ub294 Github\uc5d0 \uacf5\uac1c \ud558\uc600\ub2e4. \ucc38\uace0\ud558\uae38 \ubc14\ub780\ub2e4.<\/p>\n<p><a href=\"https:\/\/github.com\/reastykim\/ExpandableMenu\" target=\"_blank\" rel=\"noopener\">https:\/\/github.com\/reastykim\/ExpandableMenu<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>1.\uac1c\uc694 \ud504\ub85c\uc81d\ud2b8 \uc911 \ub098\uc911\uc5d0 \uac1c\uc778\uc801\uc73c\ub85c\ub3c4 \uc0ac\uc6a9\ud560 \ub9cc\ud55c Custom Control \uc774 \uc788\uc5b4\uc11c \ub530\ub85c \uc815\ub9ac\ud558\uac8c \ub418\uc5c8\ub2e4. \ubb3c\ub860 \uc2e4\uc81c \ud504\ub85c\uc81d\ud2b8\uc5d0 \uc801\uc6a9\ub41c \ucf54\ub4dc\uc640 50% \uc774\uc0c1 \ub2e4\ub974\ub2c8.. \uc548\uc2ec\ud558\uace0 ^^; 2. \uc694\uad6c\uc0ac\ud56d UX \ud300\uc5d0\uc11c Microsoft Outlook 2008\uc758 \uc88c\uce21 \uba54\ub274\ucc98\ub7fc\u00a0\uc811\ud614\ub2e4 \ud3bc\uccd0\uc9c0\ub294 \uba54\ub274\ub97c \uc6d0\ud588\ub2e4. \uac70\uae30\ub2e4\uac00 \ud3bc\uccd0\uc9c4 \uba54\ub274 \uc774\uc678\uc758 \uba54\ub274\ub4e4\uc740 \ub9e8 \ud558\ub2e8\uc73c\ub85c \ub0b4\ub824\uac00\uac8c \ub418\uace0, \uc804\uccb4 \uba54\ub274\uc758 Height \ud06c\uae30\ub294 \ubcc0\ud558\uc9c0 \uc54a\uac8c \ud574\ub2ec\ub77c\uace0 \ud558\uc600\ub2e4. \uadf8\ub9ac\uace0 \ud3bc\uccd0\uc9c0\ub294 \uba54\ub274\ub294 \ubb34\uc870\uac74 \ud558\ub098. \uc77c\ubc18\uc801\uc73c\ub85c Expand&#8230; <a href=\"https:\/\/reasty.net\/?p=126\">Read more &raquo;<\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[4,5],"tags":[24,30,51],"class_list":["post-126","post","type-post","status-publish","format-standard","hentry","category-net","category-wpf","tag-custom-control","tag-expandable-menu","tag-wpf"],"aioseo_notices":[],"jetpack_featured_media_url":"","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/reasty.net\/index.php?rest_route=\/wp\/v2\/posts\/126","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/reasty.net\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/reasty.net\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/reasty.net\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/reasty.net\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=126"}],"version-history":[{"count":0,"href":"https:\/\/reasty.net\/index.php?rest_route=\/wp\/v2\/posts\/126\/revisions"}],"wp:attachment":[{"href":"https:\/\/reasty.net\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=126"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/reasty.net\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=126"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/reasty.net\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=126"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}