WPF利用CommunityToolkit.Mvvm实现级联选择器

 更新时间:2023年12月25日 10:16:59   作者:WPF开发者  
这篇文章主要介绍了WPF如何利用CommunityToolkit.Mvvm实现级联选择器,文中的示例代码讲解详细,对我们的学习或工作有一定帮助,需要的小伙伴可以参考一下

WPF 使用 CommunityToolkit.Mvvm 实现级联选择器

框架使用.NET5

Visual Studio 2022;

示例代码

1)CascadePicker.cs 代码如下:

  • Text 获取或设置级联选择器的文本值。
  • IsDropDownOpen 级联选择器的下拉菜单是否打开。
  • MaxDropDownHeight 级联选择器下拉菜单的最大高度。
  • OnApplyTemplate 重写了基类的模板应用方法。
  • MenuItem_Click 菜单项点击事件,根据用户选择的菜单项更新级联选择器的文本值,并关闭下拉菜单。
  • GetHierarchicalPath 菜单项的层次结构获取完整的路径字符串。
  • GetParentHierarchy 菜单项的父级层次结构。
using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Media;

namespace WpfCascadePicker.Controls
{
    public class CascadePicker : MenuBase
    {
        private static readonly Type _typeofSelf = typeof(CascadePicker);
        static CascadePicker()
        {
            DefaultStyleKeyProperty.OverrideMetadata(_typeofSelf, new FrameworkPropertyMetadata(_typeofSelf));
        }

        public string Text
        {
            get { return (string)GetValue(TextProperty); }
            set { SetValue(TextProperty, value); }
        }

        public static readonly DependencyProperty TextProperty =
            DependencyProperty.Register("Text", typeof(string), typeof(CascadePicker), new PropertyMetadata(string.Empty));



        public static readonly DependencyProperty IsDropDownOpenProperty = DependencyProperty.Register(
            "IsDropDownOpen", typeof(bool), typeof(CascadePicker), new PropertyMetadata(false));

        public bool IsDropDownOpen
        {
            get { return (bool)GetValue(IsDropDownOpenProperty); }
            set { SetValue(IsDropDownOpenProperty, value); }
        }

        public static readonly DependencyProperty MaxDropDownHeightProperty = DependencyProperty.Register(
            "MaxDropDownHeight", typeof(double), typeof(CascadePicker), new PropertyMetadata(SystemParameters.PrimaryScreenHeight / 3.0));
        public double MaxDropDownHeight
        {
            get
            {
                return (double)GetValue(MaxDropDownHeightProperty);
            }
            set
            {
                SetValue(MaxDropDownHeightProperty, value);
            }
        }
        
        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();
        }
        public CascadePicker()
        {
            AddHandler(MenuItem.ClickEvent, new RoutedEventHandler(MenuItem_Click));
        }
        private void MenuItem_Click(object sender, RoutedEventArgs e)
        {
            var clickedMenuItem = e.OriginalSource as MenuItem;
            if (clickedMenuItem != null)
            {
                List<string> hierarchy = GetParentHierarchy(clickedMenuItem);
                Text = GetHierarchicalPath(hierarchy);
            }
            IsDropDownOpen = false;
        }
        private string GetHierarchicalPath(List<string> hierarchy)
        {
            hierarchy.Reverse();
            return string.Join(" / ", hierarchy);
        }
        private List<string> GetParentHierarchy(MenuItem item)
        {
            var hierarchy = new List<string>();

            var headerObject = item.Header;
            var propertyInfo = headerObject.GetType().GetProperty(DisplayMemberPath);
            if (propertyInfo != null)
            {
                var displayValue = propertyInfo.GetValue(headerObject, null) as string;
                if (!string.IsNullOrEmpty(displayValue))
                    hierarchy.Add(displayValue);
            }
            var parent = VisualTreeHelper.GetParent(item);
            if (parent != null)
            {
                var stackPanel = parent as StackPanel;
                if (stackPanel != null)
                {
                    var menuItemParent = stackPanel.TemplatedParent as MenuItem;
                    if (menuItemParent != null)
                    {
                        var parentHierarchy = GetParentHierarchy(menuItemParent);
                        hierarchy.AddRange(parentHierarchy);
                    }
                }
            }
            return hierarchy;
        }
    }
}

2)CascadePicker.xaml 代码如下:

  • 子菜单项的样式使用了WD.DefaultMenuItem样式,并绑定了ItemsSource属性。
  • 定义了控件的外观和行为,包括边框、文本框、下拉箭头、下拉弹出窗口等元素。
  • 触发器定义了控件的交互行为,例如鼠标悬停、下拉弹出窗口的打开和关闭等。
<Application
    x:Class="WpfCascade.App"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:controls="clr-namespace:WpfCascadePicker.Controls"
    xmlns:local="clr-namespace:WpfCascade"
    xmlns:wd="https://github.com/WPFDevelopersOrg/WPFDevelopers"
    StartupUri="MainWindow.xaml">
    <Application.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="pack://application:,,,/WPFDevelopers;component/Themes/Light.Blue.xaml" />
                <!--  需要注意 wd:Resources 必须在配色主题后,Theme="Dark" 为黑色皮肤  -->
                <wd:Resources Theme="Light" />
                <ResourceDictionary Source="pack://application:,,,/WPFDevelopers;component/Themes/Theme.xaml" />
            </ResourceDictionary.MergedDictionaries>
            <Style BasedOn="{StaticResource WD.ControlBasicStyle}" TargetType="{x:Type controls:CascadePicker}">
                <Setter Property="HorizontalContentAlignment" Value="Left" />
                <Setter Property="VerticalContentAlignment" Value="Center" />
                <Setter Property="BorderBrush" Value="{DynamicResource WD.BaseSolidColorBrush}" />
                <Setter Property="BorderThickness" Value="1" />
                <Setter Property="Background" Value="{DynamicResource WD.BackgroundSolidColorBrush}" />
                <Setter Property="Padding" Value="{StaticResource WD.DefaultPadding}" />
                <Setter Property="ItemContainerStyle">
                    <Setter.Value>
                        <Style BasedOn="{StaticResource WD.DefaultMenuItem}" TargetType="MenuItem">
                            <Setter Property="ItemsSource" Value="{Binding ItemsSource}" />
                            <Setter Property="ItemContainerStyle">
                                <Setter.Value>
                                    <Style BasedOn="{StaticResource WD.DefaultMenuItem}" TargetType="MenuItem">
                                        <Setter Property="ItemsSource" Value="{Binding ItemsSource}" />
                                    </Style>
                                </Setter.Value>
                            </Setter>
                        </Style>
                    </Setter.Value>
                </Setter>
                <Setter Property="Template">
                    <Setter.Value>
                        <ControlTemplate TargetType="{x:Type controls:CascadePicker}">
                            <ControlTemplate.Resources>
                                <Storyboard x:Key="OpenStoryboard">
                                    <DoubleAnimation
                                        EasingFunction="{StaticResource WD.ExponentialEaseOut}"
                                        Storyboard.TargetName="PART_DropDown"
                                        Storyboard.TargetProperty="(Grid.RenderTransform).(ScaleTransform.ScaleY)"
                                        To="1"
                                        Duration="00:00:.2" />
                                </Storyboard>
                                <Storyboard x:Key="CloseStoryboard">
                                    <DoubleAnimation
                                        EasingFunction="{StaticResource WD.ExponentialEaseOut}"
                                        Storyboard.TargetName="PART_DropDown"
                                        Storyboard.TargetProperty="(Grid.RenderTransform).(ScaleTransform.ScaleY)"
                                        To="0"
                                        Duration="00:00:.2" />
                                </Storyboard>
                            </ControlTemplate.Resources>
                            <wd:SmallPanel SnapsToDevicePixels="True">
                                <Border
                                    Name="PART_Border"
                                    Background="{TemplateBinding Background}"
                                    BorderBrush="{TemplateBinding BorderBrush}"
                                    BorderThickness="{TemplateBinding BorderThickness}"
                                    CornerRadius="{Binding Path=(wd:ElementHelper.CornerRadius), RelativeSource={RelativeSource TemplatedParent}}"
                                    SnapsToDevicePixels="True" />
                                <TextBox
                                    Name="PART_EditableTextBox"
                                    Margin="{TemplateBinding Padding}"
                                    HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
                                    VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
                                    Background="{TemplateBinding Background}"
                                    Focusable="True"
                                    Foreground="{DynamicResource WD.PrimaryTextSolidColorBrush}"
                                    IsReadOnly="True"
                                    SelectionBrush="{DynamicResource WD.WindowBorderBrushSolidColorBrush}"
                                    Style="{x:Null}"
                                    Template="{StaticResource WD.ComboBoxTextBox}"
                                    Text="{Binding Text, RelativeSource={RelativeSource TemplatedParent}, Mode=TwoWay}" />
                                <TextBlock
                                    Name="PART_Watermark"
                                    Margin="{TemplateBinding Padding}"
                                    Padding="1,0"
                                    HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
                                    VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
                                    Background="Transparent"
                                    Foreground="{DynamicResource WD.RegularTextSolidColorBrush}"
                                    IsHitTestVisible="False"
                                    Text="{Binding Path=(wd:ElementHelper.Watermark), RelativeSource={RelativeSource TemplatedParent}}"
                                    TextBlock.FontSize="{StaticResource WD.NormalFontSize}"
                                    TextTrimming="CharacterEllipsis"
                                    Visibility="Collapsed" />
                                <ToggleButton
                                    Name="PART_ToggleButton"
                                    Background="{TemplateBinding Background}"
                                    ClickMode="Release"
                                    Focusable="False"
                                    IsChecked="{Binding IsDropDownOpen, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}"
                                    Style="{x:Null}"
                                    Template="{StaticResource WD.ComboBoxToggleButton}" />
                                <Popup
                                    Name="PART_Popup"
                                    AllowsTransparency="True"
                                    IsOpen="{Binding IsDropDownOpen, RelativeSource={RelativeSource TemplatedParent}, Mode=TwoWay}"
                                    Placement="Bottom"
                                    PlacementTarget="{Binding ElementName=PART_ToggleButton}"
                                    StaysOpen="False">
                                    <wd:SmallPanel
                                        Name="PART_DropDown"
                                        MinWidth="{TemplateBinding FrameworkElement.ActualWidth}"
                                        MaxHeight="{TemplateBinding MaxDropDownHeight}"
                                        Margin="24,2,24,24"
                                        RenderTransformOrigin=".5,0"
                                        SnapsToDevicePixels="True">
                                        <wd:SmallPanel.RenderTransform>
                                            <ScaleTransform ScaleY="0" />
                                        </wd:SmallPanel.RenderTransform>
                                        <Border
                                            Name="PART_DropDownBorder"
                                            Background="{TemplateBinding Background}"
                                            BorderBrush="{TemplateBinding BorderBrush}"
                                            BorderThickness="{TemplateBinding BorderThickness}"
                                            CornerRadius="{Binding Path=(wd:ElementHelper.CornerRadius), RelativeSource={RelativeSource TemplatedParent}}"
                                            Effect="{StaticResource WD.PopupShadowDepth}"
                                            SnapsToDevicePixels="True"
                                            UseLayoutRounding="True">
                                            <ItemsPresenter />
                                        </Border>
                                    </wd:SmallPanel>
                                </Popup>
                            </wd:SmallPanel>
                            <ControlTemplate.Triggers>
                                <Trigger SourceName="PART_ToggleButton" Property="IsChecked" Value="True">
                                    <Trigger.EnterActions>
                                        <BeginStoryboard x:Name="BeginStoryboardOpenStoryboard" Storyboard="{StaticResource OpenStoryboard}" />
                                    </Trigger.EnterActions>
                                    <Trigger.ExitActions>
                                        <StopStoryboard BeginStoryboardName="BeginStoryboardOpenStoryboard" />
                                    </Trigger.ExitActions>
                                </Trigger>
                                <Trigger SourceName="PART_ToggleButton" Property="IsChecked" Value="False">
                                    <Trigger.EnterActions>
                                        <BeginStoryboard x:Name="BeginStoryboardCloseStoryboard" Storyboard="{StaticResource CloseStoryboard}" />
                                    </Trigger.EnterActions>
                                    <Trigger.ExitActions>
                                        <StopStoryboard BeginStoryboardName="BeginStoryboardCloseStoryboard" />
                                    </Trigger.ExitActions>
                                </Trigger>
                                <Trigger Property="UIElement.IsMouseOver" Value="True">
                                    <Setter TargetName="PART_Border" Property="BorderBrush" Value="{DynamicResource WD.PrimaryNormalSolidColorBrush}" />
                                </Trigger>
                                <Trigger SourceName="PART_EditableTextBox" Property="Text" Value="">
                                    <Setter TargetName="PART_Watermark" Property="Visibility" Value="Visible" />
                                </Trigger>
                                <Trigger SourceName="PART_EditableTextBox" Property="Text" Value="{x:Null}">
                                    <Setter TargetName="PART_Watermark" Property="Visibility" Value="Visible" />
                                </Trigger>
                            </ControlTemplate.Triggers>
                        </ControlTemplate>
                    </Setter.Value>
                </Setter>
            </Style>
        </ResourceDictionary>
    </Application.Resources>
</Application>

3)CascadePickerExample.xaml 示例代码如下:

 <controls:CascadePicker
            Width="240"
            Height="40"
            wd:ElementHelper.Watermark="请选择内容"
            DisplayMemberPath="Name"
            Text="{Binding City, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
            ItemsSource="{Binding CityInfos}" />

效果图

以上就是WPF利用CommunityToolkit.Mvvm实现级联选择器的详细内容,更多关于WPF级联选择器的资料请关注脚本之家其它相关文章!

相关文章

  • C#利用FluentFTP实现FTP上传下载功能详解

    C#利用FluentFTP实现FTP上传下载功能详解

    FTP作为日常工作学习中,非常重要的一个文件传输存储空间,想必大家都非常的熟悉了,那么如何快速的实现文件的上传下载功能呢,本文以一个简单的小例子,简述如何通过FluentFTP实现文件的上传和下载功能
    2023-02-02
  • C# Dynamic之:ExpandoObject,DynamicObject,DynamicMetaOb的应用(下)

    C# Dynamic之:ExpandoObject,DynamicObject,DynamicMetaOb的应用(下)

    本篇文章是对C#中ExpandoObject,DynamicObject,DynamicMetaOb的应用进行了详细的分析介绍,需要的朋友参考下
    2013-05-05
  • C#使用带like的sql语句时防sql注入的方法

    C#使用带like的sql语句时防sql注入的方法

    这篇文章主要介绍了C#使用带like的sql语句时防sql注入的方法,采用了一个比较简单的字符串过滤方法就可以有效提高sql语句的安全性,防止sql注入,需要的朋友可以参考下
    2014-09-09
  • C#创建Windows服务与服务的安装、卸载

    C#创建Windows服务与服务的安装、卸载

    这篇文章介绍了C#创建Windows服务与服务的安装、卸载,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-02-02
  • Unity3D基于陀螺仪实现VR相机功能

    Unity3D基于陀螺仪实现VR相机功能

    这篇文章主要为大家详细介绍了Unity3D基于陀螺仪实现VR相机功能,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2020-04-04
  • 利用C#编写扫雷游戏(附源码)

    利用C#编写扫雷游戏(附源码)

    扫雷游戏相信不用给大家过多介绍,大家基本都玩过,下面这篇文章主要给大家介绍了关于如何利用C#编写扫雷游戏的相关资料,文中通过示例代码介绍的非常详细,需要的朋友可以参考下
    2019-01-01
  • C#获取文件名和文件路径的两种实现方式

    C#获取文件名和文件路径的两种实现方式

    这篇文章主要介绍了C#获取文件名和文件路径的两种实现方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-07-07
  • C#判断某程序是否运行的方法

    C#判断某程序是否运行的方法

    这篇文章主要介绍了C#判断某程序是否运行的方法,代码结构简单功能实用,需要的朋友可以参考下
    2014-09-09
  • c# 实现RSA非对称加密算法

    c# 实现RSA非对称加密算法

    RSA解决了对称加密的一个不足,比如AES算法加密和解密时使用的是同一个秘钥,因此这个秘钥不能公开,因此对于需要公开秘钥的场合,我们需要在加密和解密过程中使用不同的秘钥,加密使用的公钥可以公开,解密使用的私钥要保密,这就是非对称加密的好处。 
    2021-06-06
  • C#实现如何使用短信平台自动通知用户实例

    C#实现如何使用短信平台自动通知用户实例

    这篇文章主要介绍了C#实现如何使用短信平台自动通知用户实例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-02-02

最新评论