翼度科技»论坛 编程开发 .net 查看内容

WPF实现树形下拉列表框(TreeComboBox)

8

主题

8

帖子

24

积分

新手上路

Rank: 1

积分
24
前言
  树形下拉菜单是许多WPF应用程序中常见的用户界面元素,它能够以分层的方式展示数据,提供更好的用户体验。本文将深入探讨如何基于WPF创建一个可定制的树形下拉菜单控件,涵盖从原理到实际实现的关键步骤。
一、需求分析
      树形下拉菜单控件的核心是将ComboBox与TreeView结合起来,以实现下拉时的树状数据展示。在WPF中,可以通过自定义控件模板、样式和数据绑定来实现这一目标。
      我们首先来分析一下ComboBox控件的模板。
  1. <ControlTemplate x:Key="ComboBoxTemplate" TargetType="{x:Type ComboBox}">
  2.     <Grid x:Name="templateRoot" SnapsToDevicePixels="true">
  3.         <Grid.ColumnDefinitions>
  4.             <ColumnDefinition Width="*"/>
  5.             <ColumnDefinition MinWidth="{DynamicResource {x:Static SystemParameters.VerticalScrollBarWidthKey}}" Width="0"/>
  6.         </Grid.ColumnDefinitions>
  7.         <Popup x:Name="PART_Popup" AllowsTransparency="true" Grid.ColumnSpan="2" IsOpen="{Binding IsDropDownOpen, Mode=TwoWay, RelativeSource={RelativeSource Mode=TemplatedParent}}" Margin="1" Placement="Bottom" PopupAnimation="{DynamicResource {x:Static SystemParameters.ComboBoxPopupAnimationKey}}">
  8.             <theme:SystemDropShadowChrome x:Name="shadow" Color="Transparent" MinWidth="{Binding ActualWidth, ElementName=templateRoot}" MaxHeight="{TemplateBinding MaxDropDownHeight}">
  9.                 <Border x:Name="dropDownBorder" Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}" BorderBrush="{DynamicResource {x:Static SystemColors.WindowFrameBrushKey}}" BorderThickness="1">
  10.                     <ScrollViewer x:Name="DropDownScrollViewer">
  11.                         <Grid x:Name="grid" RenderOptions.ClearTypeHint="Enabled">
  12.                             <Canvas x:Name="canvas" HorizontalAlignment="Left" Height="0" VerticalAlignment="Top" Width="0">
  13.                                 <Rectangle x:Name="opaqueRect" Fill="{Binding Background, ElementName=dropDownBorder}" Height="{Binding ActualHeight, ElementName=dropDownBorder}" Width="{Binding ActualWidth, ElementName=dropDownBorder}"/>
  14.                             </Canvas>
  15.                             <ItemsPresenter x:Name="ItemsPresenter" KeyboardNavigation.DirectionalNavigation="Contained" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
  16.                         </Grid>
  17.                     </ScrollViewer>
  18.                 </Border>
  19.             </theme:SystemDropShadowChrome>
  20.         </Popup>
  21.         <ToggleButton x:Name="toggleButton" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Grid.ColumnSpan="2" IsChecked="{Binding IsDropDownOpen, Mode=TwoWay, RelativeSource={RelativeSource Mode=TemplatedParent}}" Style="{StaticResource ComboBoxToggleButton}"/>
  22.         <ContentPresenter x:Name="contentPresenter" ContentStringFormat="{TemplateBinding SelectionBoxItemStringFormat}" ContentTemplate="{TemplateBinding SelectionBoxItemTemplate}" Content="{TemplateBinding SelectionBoxItem}" ContentTemplateSelector="{TemplateBinding ItemTemplateSelector}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" IsHitTestVisible="false" Margin="{TemplateBinding Padding}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
  23.     </Grid>
  24. </ControlTemplate>
复制代码
  从以上代码可以看出,其中的Popup控件就是下拉部分,那么按照常理,我们在Popup控件中放入一个TreeView控件即可实现该需求,但是现实情况远没有这么简单。我们开发一个控件,不仅要从外观上实现功能,还需要考虑数据绑定、事件触发、自定义模板等方面的问题,显然,直接放置一个TreeView控件虽然也能实现功能,但是从封装的角度看,它并不优雅,使用也不方便。那么有没有更好的方法满足以上需求呢?下面提供另一种思路,其核心思想就是融合ComboBox控件与TreeView控件模板,让控件既保留TreeView的特性,又拥有ComboBox的外观。
 二、代码实现
2.1 编辑TreeView模板;
2.2 提取ComboBox的模板代码;
2.3 将ComboBox的模板代码移植到TreeView模板中;
2.4 将TreeView模板包含ItemsPresenter部分的关键代码放入ComboBox模板中的Popup控件内;
      以下为融合后的xaml代码
  1. <ControlTemplate TargetType="{x:Type local:TreeComboBox}">
  2.     <Grid x:Name="templateRoot" SnapsToDevicePixels="true">
  3.         <Grid.ColumnDefinitions>
  4.             <ColumnDefinition Width="*" />
  5.             <ColumnDefinition Width="0" MinWidth="{DynamicResource {x:Static SystemParameters.VerticalScrollBarWidthKey}}" />
  6.         </Grid.ColumnDefinitions>
  7.         <Popup
  8.                             x:Name="PART_Popup"
  9.                             Grid.ColumnSpan="2"
  10.                             MaxHeight="{TemplateBinding MaxDropDownHeight}"
  11.                             Margin="1"
  12.                             AllowsTransparency="true"
  13.                             IsOpen="{Binding IsDropDownOpen, Mode=TwoWay, RelativeSource={RelativeSource Mode=TemplatedParent}}"
  14.                             Placement="Bottom"
  15.                             PopupAnimation="{DynamicResource {x:Static SystemParameters.ComboBoxPopupAnimationKey}}">
  16.             <Border
  17.                                 x:Name="PART_Border"
  18.                                 Width="{Binding RelativeSource={RelativeSource AncestorType=local:TreeComboBox}, Path=ActualWidth}"
  19.                                 Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"
  20.                                 BorderBrush="{DynamicResource {x:Static SystemColors.WindowFrameBrushKey}}"
  21.                                 BorderThickness="1"
  22.                                 SnapsToDevicePixels="true">
  23.                 <ScrollViewer
  24.                                     x:Name="_tv_scrollviewer_"
  25.                                     Padding="{TemplateBinding Padding}"
  26.                                     Background="{TemplateBinding Background}"
  27.                                     CanContentScroll="false"
  28.                                     Focusable="false"
  29.                                     HorizontalScrollBarVisibility="{TemplateBinding ScrollViewer.HorizontalScrollBarVisibility}"
  30.                                     SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
  31.                                     VerticalScrollBarVisibility="{TemplateBinding ScrollViewer.VerticalScrollBarVisibility}">
  32.                     <ItemsPresenter />
  33.                 </ScrollViewer>
  34.             </Border>
  35.         </Popup>
  36.         <ToggleButton
  37.                             x:Name="toggleButton"
  38.                             Grid.ColumnSpan="2"
  39.                             Background="{TemplateBinding Background}"
  40.                             BorderBrush="{TemplateBinding BorderBrush}"
  41.                             BorderThickness="{TemplateBinding BorderThickness}"
  42.                             IsChecked="{Binding IsDropDownOpen, Mode=TwoWay, RelativeSource={RelativeSource Mode=TemplatedParent}}"
  43.                             Style="{StaticResource ComboBoxToggleButton}" />
  44.         <ContentPresenter
  45.                             x:Name="contentPresenter"
  46.                             Margin="{TemplateBinding Padding}"
  47.                             HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
  48.                             VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
  49.                             Content="{TemplateBinding SelectionBoxItem}"
  50.                             ContentTemplate="{TemplateBinding SelectionBoxItemTemplate}"
  51.                             IsHitTestVisible="False" />
  52.     </Grid>
  53.     <ControlTemplate.Triggers>
  54.         <Trigger Property="IsEnabled" Value="false">
  55.             <Setter TargetName="PART_Border" Property="Background" Value="{DynamicResource {x:Static SystemColors.ControlBrushKey}}" />
  56.         </Trigger>
  57.         <Trigger Property="VirtualizingPanel.IsVirtualizing" Value="true">
  58.             <Setter TargetName="_tv_scrollviewer_" Property="CanContentScroll" Value="true" />
  59.         </Trigger>
  60.         <MultiTrigger>
  61.             <MultiTrigger.Conditions>
  62.                 <Condition Property="IsGrouping" Value="true" />
  63.                 <Condition Property="VirtualizingPanel.IsVirtualizingWhenGrouping" Value="false" />
  64.             </MultiTrigger.Conditions>
  65.             <Setter Property="ScrollViewer.CanContentScroll" Value="false" />
  66.         </MultiTrigger>
  67.     </ControlTemplate.Triggers>
  68. </ControlTemplate>
复制代码
       以下为使用控件的代码。
  1. <TreeComboBox
  2.                 Width="315"
  3.                 MinHeight="30"
  4.                 Padding="5"
  5.                 HorizontalAlignment="Center"
  6.                 VerticalAlignment="Top"
  7.                 VerticalContentAlignment="Stretch"
  8.                 IsAutoCollapse="True"
  9.                 ItemsSource="{Binding Collection}">
  10.     <TreeComboBox.SelectionBoxItemTemplate>
  11.         <ItemContainerTemplate>
  12.             <Border>
  13.                 <TextBlock VerticalAlignment="Center" Text="{Binding Property1}" />
  14.             </Border>
  15.         </ItemContainerTemplate>
  16.     </TreeComboBox.SelectionBoxItemTemplate>
  17.     <TreeComboBox.ItemTemplate>
  18.         <HierarchicalDataTemplate ItemsSource="{Binding Collection}">
  19.             <TextBlock
  20.                             Margin="5,0,0,0"
  21.                             VerticalAlignment="Center"
  22.                             Text="{Binding Property1}" />
  23.         </HierarchicalDataTemplate>
  24.     </TreeComboBox.ItemTemplate>
  25. </TreeComboBox>
复制代码
三、运行效果
3.1 单选效果

 
 3.2 多选效果

 
四、个性化外观
   当控件默认外观无法满足需求时,我们可以通过编辑样式的方式来实现个性化外观,也可以引用第三方UI库样式,以下为使用MaterialDesign的效果。
4.1 单选效果

 
4.2 多选效果
 
 

 
 
来源:https://www.cnblogs.com/qushi2020/p/18115948
免责声明:由于采集信息均来自互联网,如果侵犯了您的权益,请联系我们【E-Mail:cb@itdo.tech】 我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x

举报 回复 使用道具