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

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

控件名:CascadePicker

作   者:WPFDevelopersOrg - 驚鏵

原文链接[1]:https://github.com/yanjinhuagood/WPFCascade

码云链接[2]:https://gitee.com/yanjinhua/WPFCascade

  • 框架使用.NET5

  • Visual Studio 2022;

dddea40c786501001cf1b799a57d03fd.png


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}" />
0e0fa7b2d7b29d07b4f169965712967c.gif

更多源码请下载源码

参考资料

[1]

原文链接: https://github.com/yanjinhuagood/WPFCascade

[2]

码云链接: https://gitee.com/yanjinhua/WPFCascade

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: communitytoolkit.mvvm datagrid 是一种基于MVVM模式的数据表格控件,该控件提供了多种数据绑定的方式,可以快速且简便地展示数据。同时,communitytoolkit.mvvm datagrid 还提供了多种功能,如单元格编辑、分页、排序、筛选等等。这让用户能够方便地处理大量数据。 在使用 communitytoolkit.mvvm datagrid 控件时,需要绑定数据源并提供列的定义,控件会自动将数据填充到表格中。在提供列定义时,可以对不同列的排序方式、列宽、列标题等进行自定义的设置。此外,还可以对每个单元格进行自定义的数据格式化。 与传统的数据表格控件不同的是,communitytoolkit.mvvm datagrid 控件遵循 MVVM 模式的设计,使得数据与 UI 分离,易于维护和扩展。通过将数据、业务逻辑与界面之间的关系明确分离,可以大大提高应用程序的可读性、可扩展性和可维护性。 总的来说,communitytoolkit.mvvm datagrid 是一款功能强大且易于使用的数据表格控件,通过采用 MVVM 模式,可以使数据与 UI 分离,极大地提高应用程序的可读性、可扩展性和可维护性。无论是处理大量数据、展示分页数据、自定义数据格式化等,都是 communitytoolkit.mvvm datagrid 优秀的解决方案。 ### 回答2: CommunityToolkit.Mvvm 是一个帮助开发人员实现 MVVM 模式在 UWP 和 WPF 应用程序中的工具包。这个工具包提供了丰富的控件和类库,使得我们能够更加方便地使用 MVVM 模式来构建应用程序。 其中,DataGrid 控件是 CommunityToolkit.Mvvm 中非常重要的一个控件,它允许我们以表格的形式展示数据,并且支持排序、筛选、列隐藏等基本功能。除此之外,CommunityToolkit.Mvvm 的 DataGrid 还支持分页和自定义单元格样式等高级功能,这些功能都使得我们更加容易构建强大可靠的应用程序。 在使用 CommunityToolkit.Mvvm 的 DataGrid 控件时,我们需要定义一些属性和命令,在 ViewModel 中实现数据的加载、数据的操作等逻辑处理。具体而言,我们需要定义 DataGrid 的 ItemsSource 属性,这个属性绑定到 ViewModel 中的数据集合,然后定义排序、筛选等命令,使得用户点击控件时能够实现相应的逻辑操作。 总之,CommunityToolkit.Mvvm 提供的 DataGrid 控件是一个非常实用的控件,它能够大大简化我们构建 MVVM 应用程序的过程。通过这个控件,我们能够快速轻松地展现数据,同时也能够支持高级功能,使得我们能够更加专注于应用程序的逻辑设计和开发。 ### 回答3: communitytoolkit.mvvm datagrid 是一个开源的工具包,旨在为使用 MVVM 模式的开发人员提供一个易于使用和高度可定制的数据网格。该工具包是由 Microsoft 社区开发的,可以使用 NuGet 包管理器轻松安装和集成到您的应用程序中。 该工具包使用 C# 和 XAML 构建,支持 WPF 和 UWP 平台。它提供了许多有用的功能,如排序、筛选和分页,使用户能够更轻松地浏览和操作数据。此外,它还提供了高度可定制的外观和行为选项,以便开发人员可以使其适应他们的应用程序的视觉设计和用户体验。 与传统的数据网格不同,使用 communitytoolkit.mvvm datagrid 可以使用 MVVM 模式进行数据绑定和操作,将视图逻辑与数据逻辑分离。这使得应用程序更易于维护和扩展,并提高了代码的可重用性和可测试性。此外,它还提供了一个非常灵活的 API,使用户可以轻松地自定义和扩展其功能和行为。 总的来说,communitytoolkit.mvvm datagrid 是一个非常有用的工具包,为开发人员提供了一个高度可定制的数据网格,支持 MVVM 模式,使其更易于使用和维护。如果你正在开发一个需要展示和操作数据的应用程序,那么这个工具包是值得一试的。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值