首页 > 其他分享 >WPF实现树形下拉列表框(TreeComboBox)

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

时间:2024-04-05 17:36:29浏览次数:19  
标签:控件 列表框 ComboBox 树形 TreeComboBox WPF TreeView 模板

前言

  树形下拉菜单是许多WPF应用程序中常见的用户界面元素,它能够以分层的方式展示数据,提供更好的用户体验。本文将深入探讨如何基于WPF创建一个可定制的树形下拉菜单控件,涵盖从原理到实际实现的关键步骤。

一、需求分析

      树形下拉菜单控件的核心是将ComboBox与TreeView结合起来,以实现下拉时的树状数据展示。在WPF中,可以通过自定义控件模板、样式和数据绑定来实现这一目标。

      我们首先来分析一下ComboBox控件的模板。

<ControlTemplate x:Key="ComboBoxTemplate" TargetType="{x:Type ComboBox}">
    <Grid x:Name="templateRoot" SnapsToDevicePixels="true">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition MinWidth="{DynamicResource {x:Static SystemParameters.VerticalScrollBarWidthKey}}" Width="0"/>
        </Grid.ColumnDefinitions>
        <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}}">
            <theme:SystemDropShadowChrome x:Name="shadow" Color="Transparent" MinWidth="{Binding ActualWidth, ElementName=templateRoot}" MaxHeight="{TemplateBinding MaxDropDownHeight}">
                <Border x:Name="dropDownBorder" Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}" BorderBrush="{DynamicResource {x:Static SystemColors.WindowFrameBrushKey}}" BorderThickness="1">
                    <ScrollViewer x:Name="DropDownScrollViewer">
                        <Grid x:Name="grid" RenderOptions.ClearTypeHint="Enabled">
                            <Canvas x:Name="canvas" HorizontalAlignment="Left" Height="0" VerticalAlignment="Top" Width="0">
                                <Rectangle x:Name="opaqueRect" Fill="{Binding Background, ElementName=dropDownBorder}" Height="{Binding ActualHeight, ElementName=dropDownBorder}" Width="{Binding ActualWidth, ElementName=dropDownBorder}"/>
                            </Canvas>
                            <ItemsPresenter x:Name="ItemsPresenter" KeyboardNavigation.DirectionalNavigation="Contained" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
                        </Grid>
                    </ScrollViewer>
                </Border>
            </theme:SystemDropShadowChrome>
        </Popup>
        <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}"/>
        <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}"/>
    </Grid>
</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代码

<ControlTemplate TargetType="{x:Type local:TreeComboBox}">
    <Grid x:Name="templateRoot" SnapsToDevicePixels="true">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*" />
            <ColumnDefinition Width="0" MinWidth="{DynamicResource {x:Static SystemParameters.VerticalScrollBarWidthKey}}" />
        </Grid.ColumnDefinitions>
        <Popup
                            x:Name="PART_Popup"
                            Grid.ColumnSpan="2"
                            MaxHeight="{TemplateBinding MaxDropDownHeight}"
                            Margin="1"
                            AllowsTransparency="true"
                            IsOpen="{Binding IsDropDownOpen, Mode=TwoWay, RelativeSource={RelativeSource Mode=TemplatedParent}}"
                            Placement="Bottom"
                            PopupAnimation="{DynamicResource {x:Static SystemParameters.ComboBoxPopupAnimationKey}}">
            <Border
                                x:Name="PART_Border"
                                Width="{Binding RelativeSource={RelativeSource AncestorType=local:TreeComboBox}, Path=ActualWidth}"
                                Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"
                                BorderBrush="{DynamicResource {x:Static SystemColors.WindowFrameBrushKey}}"
                                BorderThickness="1"
                                SnapsToDevicePixels="true">
                <ScrollViewer
                                    x:Name="_tv_scrollviewer_"
                                    Padding="{TemplateBinding Padding}"
                                    Background="{TemplateBinding Background}"
                                    CanContentScroll="false"
                                    Focusable="false"
                                    HorizontalScrollBarVisibility="{TemplateBinding ScrollViewer.HorizontalScrollBarVisibility}"
                                    SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
                                    VerticalScrollBarVisibility="{TemplateBinding ScrollViewer.VerticalScrollBarVisibility}">
                    <ItemsPresenter />
                </ScrollViewer>
            </Border>
        </Popup>
        <ToggleButton
                            x:Name="toggleButton"
                            Grid.ColumnSpan="2"
                            Background="{TemplateBinding Background}"
                            BorderBrush="{TemplateBinding BorderBrush}"
                            BorderThickness="{TemplateBinding BorderThickness}"
                            IsChecked="{Binding IsDropDownOpen, Mode=TwoWay, RelativeSource={RelativeSource Mode=TemplatedParent}}"
                            Style="{StaticResource ComboBoxToggleButton}" />
        <ContentPresenter
                            x:Name="contentPresenter"
                            Margin="{TemplateBinding Padding}"
                            HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
                            VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
                            Content="{TemplateBinding SelectionBoxItem}"
                            ContentTemplate="{TemplateBinding SelectionBoxItemTemplate}"
                            IsHitTestVisible="False" />
    </Grid>
    <ControlTemplate.Triggers>
        <Trigger Property="IsEnabled" Value="false">
            <Setter TargetName="PART_Border" Property="Background" Value="{DynamicResource {x:Static SystemColors.ControlBrushKey}}" />
        </Trigger>
        <Trigger Property="VirtualizingPanel.IsVirtualizing" Value="true">
            <Setter TargetName="_tv_scrollviewer_" Property="CanContentScroll" Value="true" />
        </Trigger>
        <MultiTrigger>
            <MultiTrigger.Conditions>
                <Condition Property="IsGrouping" Value="true" />
                <Condition Property="VirtualizingPanel.IsVirtualizingWhenGrouping" Value="false" />
            </MultiTrigger.Conditions>
            <Setter Property="ScrollViewer.CanContentScroll" Value="false" />
        </MultiTrigger>
    </ControlTemplate.Triggers>
</ControlTemplate>

       以下为使用控件的代码。

<TreeComboBox
                Width="315"
                MinHeight="30"
                Padding="5"
                HorizontalAlignment="Center"
                VerticalAlignment="Top"
                VerticalContentAlignment="Stretch"
                IsAutoCollapse="True"
                ItemsSource="{Binding Collection}">
    <TreeComboBox.SelectionBoxItemTemplate>
        <ItemContainerTemplate>
            <Border>
                <TextBlock VerticalAlignment="Center" Text="{Binding Property1}" />
            </Border>
        </ItemContainerTemplate>
    </TreeComboBox.SelectionBoxItemTemplate>
    <TreeComboBox.ItemTemplate>
        <HierarchicalDataTemplate ItemsSource="{Binding Collection}">
            <TextBlock
                            Margin="5,0,0,0"
                            VerticalAlignment="Center"
                            Text="{Binding Property1}" />
        </HierarchicalDataTemplate>
    </TreeComboBox.ItemTemplate>
</TreeComboBox>

三、运行效果

3.1 单选效果

 

 3.2 多选效果

 

四、个性化外观

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

4.1 单选效果

 

4.2 多选效果

 

 

 

标签:控件,列表框,ComboBox,树形,TreeComboBox,WPF,TreeView,模板
From: https://www.cnblogs.com/qushi2020/p/18115948

相关文章

  • WPF中Ribbon控件的使用
    WPF中Ribbon控件的使用这篇博客将分享如何在WPF程序中使用Ribbon控件。Ribbon可以很大的提高软件的便捷性。上面截图使Outlook2010的界面,在Home标签页中,将所属的Menu都平铺的布局,非常容易的可以找到想要的Menu。在Outlook2003时代,将Home下面的Menu都垂直的排列下来,操作的便捷程......
  • 【WPF应用34】WPF基本控件-Menu的详解与示例
    WPF(WindowsPresentationFoundation)是.NET框架的一个部分,用于构建桌面应用程序的用户界面。在WPF中,菜单(Menu)是一种常用的控件,用于提供一组选项或命令,使用户可以根据自己的需要执行特定的操作。本文将详细介绍WPF中的Menu控件,包括其基本用法、属性和事件。同时,我们将通过一......
  • 【WPF应用35】深度解析WPF中的TreeView控件:功能、用法、特性与最佳实践
    WPF(WindowsPresentationFoundation)是微软推出的一个用于构建桌面应用程序的图形子系统。在WPF中,TreeView是一种常用的树形控件,用于显示层次结构的数据显示。本文将详细介绍WPF中的TreeView控件,并提供一个简单的示例。一、TreeView控件的基本概念TreeView控件用于显示一......
  • WPF FlowDocument结构详解
    FlowDocument一、介绍FlowDocument是WPF中的流文档,用来承载大段文章。可以用来显示文章。或者在RichTextBox中编辑文章。FlowDocument没有多少方法,但有大量的属性,用来设置文章的字体,字体大小,前景色,背景色之类的。但这些都没什么特别的,最重要的,是FlowDocument的结构。二、FlowDocum......
  • 可能是迄今为止最好用的WPF加载动画功能(没有之一)
    可能是迄今为止最好用的WPF加载动画功能(没有之一) 前言当我们在开发应用程序时,用户体验往往是至关重要的一环。在应用程序加载大量数据或执行复杂操作时,为用户提供一个良好的加载体验变得至关重要。加载动画是其中一个有效的方式,它不仅能够告知用户应用程序正在进行工......
  • Wpf Combobox display multiple fields columns properties
    <ComboBoxGrid.Row="0"x:Name="cbx"VirtualizingPanel.VirtualizationMode="Recycling"HorizontalAlignment="Stretch"VerticalContentAlignment="Center"FontSize="30"Selec......
  • WPF-基础及进阶扩展合集(持续更新)
    目录一、基础1、GridSplitter分割线2、x:static访问资源文件3、wpf触发器4、添加xaml资源文件5、Convert转换器6、多路绑定与多路转换器二、进阶扩展1、HierarchicalDataTemplate2、XmlDataProvider从外部文件获取源3、TextBox在CellTemplate中的焦点问题4、让窗体......
  • WPF开发分页控件:实现可定制化分页功能及实现原理解析
    概要本文将详细介绍如何使用WPF(WindowsPresentationFoundation)开发一个分页控件,并深入解析其实现原理。我们将通过使用XAML和C#代码相结合的方式构建分页控件,并确保它具有高度的可定制性,以便在不同的应用场景中满足各种需求。一.简介分页控件是在许多应用程序中常见......
  • Wpf Beginstoryboard Storyboard DoubleAnimation Storyboart.TargetName,Storyboary.
    <Windowx:Class="WpfApp32.MainWindow"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:d="http://schemas.microsoft.......
  • WPF Storyboary DoubleAnimationUsingPath PathGeometry
    <Windowx:Class="WpfApp30.MainWindow"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:d="http://schemas.microsoft.......