许可优化
许可优化
产品
产品
解决方案
解决方案
服务支持
服务支持
关于
关于
软件库
当前位置:服务支持 >  软件文章 >  WPF仿Element UI级联框组件开发

WPF仿Element UI级联框组件开发

阅读数 24
点赞 0
article_banner

在Element UI中看到了级联框,恰好项目中需要使用,于是就仿照 element   ui的样式做了一个
在这里插入图片描述

   话不多说,先上效果

单选模式

在这里插入图片描述

多选模式

在这里插入图片描述

MainWindow. xaml

        <ui:Cascader Name="cas" Width="200"  IsMultipleChoose="True"/>

MainWindow.xaml.cs

 public MainWindow()
        {
            InitializeComponent();
            cas.ItemsSource = TreeModel.ItemsSource();

        }

TreeModel .cs

    public class TreeModel : SelectedPropertyChanged
    {
        public string Name { get; set; }

        public string Code { get; set; }
        public List<TreeModel> Children { get; set; }

        public TreeModel() { }

        public static List<TreeModel> ItemsSource()
        {
            return new List<TreeModel> {
                new TreeModel {
                    Code = "1", Name = "指南", Children = 
                    new List<TreeModel> {
                        new TreeModel { Code = "2", Name = "设计原则", Children =
                        new List<TreeModel> {
                            new TreeModel { Code = "3", Name = "一致" },
                            new TreeModel { Code = "3", Name = "反馈" },
                            new TreeModel { Code = "3", Name = "效率" },
                            new TreeModel { Code = "3", Name = "可控" },
                        }

                        },
                        new TreeModel { Code = "2", Name = "导航", Children =
                        new List<TreeModel> {
                            new TreeModel { Code = "3", Name = "侧向导航" },
                            new TreeModel { Code = "3", Name = "顶部导航" },
                        }
                        }
                    }
                },
                          new TreeModel {
                    Code = "1", Name = "组件", Children =
                    new List<TreeModel> {
                        new TreeModel { Code = "2", Name = "Basic", Children =
                        new List<TreeModel> {
                            new TreeModel { Code = "3", Name = "Layout布局" },
                            new TreeModel { Code = "3", Name = "Color色彩" },
                            new TreeModel { Code = "3", Name = "字体" },
                            new TreeModel { Code = "3", Name = "图标" },
                        }

                        },
                        new TreeModel { Code = "2", Name = "Form", Children =
                        new List<TreeModel> {
                            new TreeModel { Code = "3", Name = "编辑表单" },
                            new TreeModel { Code = "3", Name = "提交表单" },
                        }
                        },
                        new TreeModel { Code = "2", Name = "Data", Children =
                        new List<TreeModel> {
                            new TreeModel { Code = "3", Name = "数据1" },
                            new TreeModel { Code = "3", Name = "数据2" },
                        }
                        }, new TreeModel { Code = "2", Name = "Notice", Children =
                        new List<TreeModel> {
                            new TreeModel { Code = "3", Name = "普通" },
                            new TreeModel { Code = "3", Name = "特殊" },
                                                        new TreeModel { Code = "3", Name = "顶级" },
                            new TreeModel { Code = "3", Name = "提示" },

                        }
                        },
                    }
                },
            };
        }
    }
public class SelectedPropertyChanged:BindableBase
    {
		private bool? isSelected = false;

		public bool? IsSelected
		{
			get { return isSelected; }
			set { isSelected = value;RaisePropertyChanged(); }
		}

		//private bool? isChecked;

		//public bool? IsChecked
		//{
		//	get { return isChecked; }
		//	set { isChecked = value; RaisePropertyChanged(); }
		//}


	}

Cascader.cs

    public class Cascader:Control
    {
        private List<string> nameList = new List<string>();
        private List<object> objList = new List<object>();

        /// <summary>
        /// 是否下拉展开
        /// </summary>
        public bool IsDropDown
        {
            get { return (bool)GetValue(IsDropDownProperty); }
            set { SetValue(IsDropDownProperty, value); }
        }

        // Using a DependencyProperty as the backing store for IsDropDown.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty IsDropDownProperty =
            DependencyProperty.Register("IsDropDown", typeof(bool), typeof(Cascader), new PropertyMetadata(default(bool)));

        public string SelectedNamePath
        {
            get { return (string)GetValue(SelectedNamePathProperty); }
            set { SetValue(SelectedNamePathProperty, value); }
        }

        // Using a DependencyProperty as the backing store for SelectedNamePath.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty SelectedNamePathProperty =
            DependencyProperty.Register("SelectedNamePath", typeof(string), typeof(Cascader), new PropertyMetadata(default(string)));

        public List<object> SelectedValues
        {
            get { return (List<object>)GetValue(SelectedValuesProperty); }
            set { SetValue(SelectedValuesProperty, value); }
        }

        // Using a DependencyProperty as the backing store for SelectedValues.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty SelectedValuesProperty =
            DependencyProperty.Register("SelectedValues", typeof(List<object>), typeof(Cascader), new PropertyMetadata(null));

        public CornerRadius CornerRadius
        {
            get { return (CornerRadius)GetValue(CornerRadiusProperty); }
            set { SetValue(CornerRadiusProperty, value); }
        }

        // Using a DependencyProperty as the backing store for CornerRadius.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty CornerRadiusProperty =
            DependencyProperty.Register("CornerRadius", typeof(CornerRadius), typeof(Cascader), new PropertyMetadata(default(CornerRadius)));

        /// <summary>
        /// 内容资源
        /// </summary>
        public IEnumerable<object> ItemsSource
        {
            get { return (IEnumerable<object>)GetValue(ItemsSourceProperty); }
            set { SetValue(ItemsSourceProperty, value); }
        }

        // Using a DependencyProperty as the backing store for ItemsSource.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty ItemsSourceProperty =
            DependencyProperty.Register("ItemsSource", typeof(IEnumerable<object>), typeof(Cascader), new PropertyMetadata(null));

        public object SelectedItem
        {
            get { return (object)GetValue(SelectedItemProperty); }
            set { SetValue(SelectedItemProperty, value); }
        }

        // Using a DependencyProperty as the backing store for SelectedItem.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty SelectedItemProperty =
            DependencyProperty.Register("SelectedItem", typeof(object), typeof(Cascader), new PropertyMetadata(default(object)));

        public bool IsMultipleChoose
        {
            get { return (bool)GetValue(IsMultipleChooseProperty); }
            set { SetValue(IsMultipleChooseProperty, value); }
        }

        // Using a DependencyProperty as the backing store for IsMultipleChoose.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty IsMultipleChooseProperty =
            DependencyProperty.Register("IsMultipleChoose", typeof(bool), typeof(Cascader), new PropertyMetadata(default(bool), OnIsMultipleChooseChanged));

        public bool IsLastDisplay
        {
            get { return (bool)GetValue(IsLastDisplayProperty); }
            set { SetValue(IsLastDisplayProperty, value); }
        }

        // Using a DependencyProperty as the backing store for IsLastDisplay.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty IsLastDisplayProperty =
            DependencyProperty.Register("IsLastDisplay", typeof(bool), typeof(Calendar), new PropertyMetadata(default(bool)));

        private static void OnIsMultipleChooseChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            if (d is Cascader cascader) {
                if (cascader.cascaderItem != null) { 
                    cascader.cascaderItem.IsMultipleChoose = (bool)e.NewValue;
                }
            }
        }

        private CascaderItem cascaderItem;
        private TextBlock textBlock;
        private Button showCleanButton;
        private Border bd1;

        public event EventHandler SelectedChanged;

        public Cascader()
        {
           
        }

        private void CascaderItem_SelectedChanged(object sender, EventArgs e)
        {
            nameList.Clear();
            objList.Clear();
            if (!IsMultipleChoose)
            {
                SelectedItem = cascaderItem.SelectedItem;
                
                if (SelectedItem != null)
                {
                    if (IsLastDisplay)
                    {
                        var nameProperty = SelectedItem.GetType().GetProperty("Name");
                        if (nameProperty != null)
                        {
                            object nameValue = nameProperty.GetValue(SelectedItem, null);
                            if (nameValue != null)
                            {
                                SelectedNamePath = nameValue.ToString();
                            }
                        }
                    }
                    else {
                        Recursion(cascaderItem.SelectedView);
                        nameList.Reverse();
                        objList.Reverse();
                        SelectedValues = objList;
                        SelectedNamePath = string.Join("/", nameList);
                        IsDropDown = false;
                        SelectedChanged?.Invoke(this, e);
                    }
                }
            }
            else {
                if (ItemsSource != null && ItemsSource is IEnumerable<object> list) {
                    //递归获取所有最后一级的选中状态
                    string str = "";
                    GetChildrenStates(list,str);
                    if (nameList.Count > 0)
                    {
                        SelectedNamePath = string.Join(",", nameList);
                    }
                    else {
                        SelectedNamePath = null;
                    }

                }
            }
            textBlock.Text = SelectedNamePath?.ToString();

        }
        void GetChildrenStates(IEnumerable<object> array,string str)
        {
            foreach (var item in array)
            {
                if (!IsLastDisplay) {
                    var mianSelectProperty = item.GetType().GetProperty("IsSelected");
                    if (mianSelectProperty != null) { 
                        var mainSelectValue = mianSelectProperty.GetValue(item, null);
                        if (mainSelectValue == null||(bool?)mainSelectValue == true) {
                            var mainNameProperty = item.GetType().GetProperty("Name");
                            if (mainNameProperty != null)
                            {
                                var mainNameValue = mainNameProperty.GetValue(item, null);
                                if (mainNameValue != null)
                                {
                                    str += mainNameValue.ToString() + "/";
                                }
                            }
                        }
                    }
                    
                }

                var childrenProperty = item.GetType().GetProperty("Children");
                if (childrenProperty != null) {
                    object childrenValue = childrenProperty.GetValue(item, null);
                    if (childrenValue != null && childrenValue is IEnumerable<object> innerArray)
                    {
                        GetChildrenStates(innerArray, str);
                    }
                    else {
                        //最后一层
                        //判断最后一层是否已经勾选
                        var selectProperty = item.GetType().GetProperty("IsSelected");
                        if (selectProperty != null) { 
                            var selectValue = selectProperty.GetValue(item, null);
                            if ((bool?)selectValue == true) {
                                //名字
                                var nameProperty = item.GetType().GetProperty("Name");
                                if (nameProperty != null) { 
                                    var nameVaule = nameProperty.GetValue(item, null);
                                    if (!IsLastDisplay)
                                    {
                                        str += nameVaule?.ToString();
                                        nameList.Add(str);
                                    }
                                    else {
                                        nameList.Add(nameVaule?.ToString());
                                    }

                                }
                                //对象
                                objList.Add(item);
                            }
                        }
                    }
                }
            }
        }
        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();
            cascaderItem = GetTemplateChild("cascaderItem") as CascaderItem;
            textBlock = GetTemplateChild("textBlock") as TextBlock;
            showCleanButton = GetTemplateChild("showCleanButton") as Button;
            bd1 = GetTemplateChild("bd1") as Border;
            if (cascaderItem != null)
            {
                cascaderItem.SelectedChanged += CascaderItem_SelectedChanged;
            }
            if (showCleanButton != null) {
                showCleanButton.Click += ShowCleanButton_Click;
            }
            if (bd1 != null) {
                bd1.MouseLeftButtonDown += OnMouseLeftButtonDown;
            }
        }

        private void OnMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            IsDropDown = !IsDropDown;
        }

        private void ShowCleanButton_Click(object sender, RoutedEventArgs e)
        {
            SelectedValues = null; SelectedNamePath = null;SelectedItem = null; textBlock.Text = null;IsDropDown = false;
        }

        /// <summary>
        /// 递归计算所有的节点
        /// </summary>
        private void Recursion(CascaderInnerList innerObj) {
            if (innerObj != null&&innerObj.SelectedItem!=null) {


                var nameProperty = innerObj.SelectedItem.GetType().GetProperty("Name");
                if (nameProperty != null)
                {
                    object nameValue = nameProperty.GetValue(innerObj.SelectedItem, null);
                    if (nameValue != null)
                    {
                        nameList.Add(nameValue.ToString());
                        objList.Add(innerObj.SelectedItem);
                    }
                    //查看父级对象
                    if (innerObj.ParentSource != null) {
                        Recursion(innerObj.ParentSource);
                    }

                }
            }
        }

    }

Cascader.xaml

    <Style TargetType="local:Cascader">
        <Setter Property="Height" Value="30"/>
        <Setter Property="Width" Value="120"/>
        <Setter Property="BorderBrush" Value="#dcdfe6"/>
        <Setter Property="BorderThickness" Value="1"/>
        <Setter Property="Background" Value="Transparent"/>
        <Setter Property="FontSize" Value="13"/>
        <Setter Property="CornerRadius" Value="4"/>
        <Setter Property="Foreground" Value="#606266"/>
        <Setter Property="FocusVisualStyle" Value="{x:Null}"/>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="local:Cascader">
                    <ControlTemplate.Resources>
                        <converter:String2VisibilityConverter x:Key="String2VisibilityConverter"/>
                        <converter:String2VisibilityReConverter x:Key="String2VisibilityReConverter"/>
                        <converter:String2BooleanConverter x:Key="String2BooleanConverter"/>
                        <converter:CascaderPopupWidthConverter x:Key="CascaderPopupWidthConverter"/>
                    </ControlTemplate.Resources>
                    <Grid>
                        <Border x:Name="bd1" Width="{TemplateBinding Width}" Height="{TemplateBinding Height}" CornerRadius="{TemplateBinding CornerRadius}" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}">
                            <Grid Margin="3 0">
                                <Grid.ColumnDefinitions>
                                    <ColumnDefinition Width="*"/>
                                    <ColumnDefinition Width="auto"/>
                                    <ColumnDefinition Width="auto"/>
                                </Grid.ColumnDefinitions>
                                <Path Grid.Column="2" x:Name="path1" Width="14" RenderTransformOrigin="0.5,0.5" Fill="{TemplateBinding BorderBrush}" Stretch="Uniform" Data="M512 745.8c-34.1 0-66.9-12.3-92.6-34.7L16.6 362.5c-20-17.3-22.2-47.7-4.9-67.7 17.3-20 47.7-22.2 67.7-4.9l403.1 348.8c16.8 14.7 42.2 14.7 59 0l0.2-0.2 402.9-348.7c20-17.3 50.4-15.2 67.7 4.9 17.3 20 15.2 50.4-4.9 67.7L604.6 711.1c-25.7 22.4-58.5 34.7-92.6 34.7z">
                                    <Path.RenderTransform>
                                        <TransformGroup>
                                            <ScaleTransform />
                                            <SkewTransform />
                                            <RotateTransform x:Name="RotateTransform" Angle="0" />
                                            <TranslateTransform />
                                        </TransformGroup>
                                    </Path.RenderTransform>
                                </Path>
                                <Button Grid.Column="1"  Visibility="{Binding ElementName=textBlock,Path=Text,Converter={StaticResource String2VisibilityConverter}}" Name="showCleanButton" Width="18" Height="18">
                                    <Button.Template>
                                        <ControlTemplate TargetType="Button">
                                            <Border CornerRadius="7" Height="14" Name="bd_close" Width="14" Background="#dcdfe6">
                                                <wpf:PackIcon Kind="Close" VerticalAlignment="Center" Width="12" HorizontalAlignment="Center"/>
                                            </Border>
                                            <ControlTemplate.Triggers>
                                                <Trigger Property="IsPressed" Value="True">
                                                    <Setter Property="Background" Value="#606266" TargetName="bd_close"/>
                                                </Trigger>
                                                <Trigger Property="IsMouseOver" Value="True">
                                                    <Setter Property="Background" Value="#a9a9a9" TargetName="bd_close"/>
                                                </Trigger>
                                            </ControlTemplate.Triggers>
                                        </ControlTemplate>
                                    </Button.Template>
                                </Button>
                                <TextBlock  FontSize="{TemplateBinding FontSize}" Text="请选择" Foreground="#c0c4d1" Margin="6 0 0 0" Visibility="{Binding ElementName=textBlock,Path=Text,Converter={StaticResource String2VisibilityReConverter}}" VerticalAlignment="Center"/>

                                <TextBlock TextTrimming="CharacterEllipsis" Foreground="{TemplateBinding Foreground}" ToolTipService.IsEnabled="{Binding ElementName=textBlock,Path=Text,Converter={StaticResource String2BooleanConverter}}" ToolTip="{Binding ElementName=textBlock,Path=Text}" x:Name="textBlock" FontSize="{TemplateBinding FontSize}" VerticalAlignment="Center"/>
                                <Popup x:Name="pop" MinWidth="{TemplateBinding Width,Converter={StaticResource CascaderPopupWidthConverter}}" StaysOpen="True" PopupAnimation="Slide" Placement="Bottom" PlacementTarget="{Binding ElementName=bd1}" IsOpen="{TemplateBinding  IsDropDown}" VerticalOffset="-6" HorizontalOffset="-6" AllowsTransparency="True">
                                    <local:CascaderItem ItemsSource="{TemplateBinding ItemsSource}" IsMultipleChoose="{TemplateBinding IsMultipleChoose}" x:Name="cascaderItem"/>
                                </Popup>
                            </Grid>
                        </Border>


                    </Grid>
                    <ControlTemplate.Triggers>
                        <Trigger Property="IsMouseOver" Value="True">
                            <Setter Property="BorderBrush" Value="#c0c4cc"/>
                        </Trigger>
                        
                        <Trigger Property="IsDropDown" Value="True">
                            <Setter Property="BorderBrush" Value="#409eff"/>
                            <Trigger.EnterActions>
                                <BeginStoryboard>
                                    <Storyboard>
                                        <DoubleAnimation
                                                            Storyboard.TargetName="RotateTransform"
                                                            Storyboard.TargetProperty="Angle"
                                                            To="180"
                                                            Duration="0:0:0.2" />
                                    </Storyboard>
                                </BeginStoryboard>
                            </Trigger.EnterActions>
                            <Trigger.ExitActions>
                                <BeginStoryboard>
                                    <Storyboard>
                                        <DoubleAnimation
                                                            Storyboard.TargetName="RotateTransform"
                                                            Storyboard.TargetProperty="Angle"
                                                            To="0"
                                                            Duration="0:0:0.2" />
                                    </Storyboard>
                                </BeginStoryboard>
                            </Trigger.ExitActions>
                        </Trigger>
                        

                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

CascaderItem.cs

    public class CascaderItem:Control
    {
        private StackPanel panel;

        /// <summary>
        /// 内容资源
        /// </summary>
        public IEnumerable<object> ItemsSource
        {
            get { return (IEnumerable<object>)GetValue(ItemsSourceProperty); }
            set { SetValue(ItemsSourceProperty, value); }
        }

        // Using a DependencyProperty as the backing store for ItemsSource.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty ItemsSourceProperty =
            DependencyProperty.Register("ItemsSource", typeof(IEnumerable<object>), typeof(CascaderItem), new PropertyMetadata(null));



        public object SelectedItem
        {
            get { return (object)GetValue(SelectedItemProperty); }
            set { SetValue(SelectedItemProperty, value); }
        }

        // Using a DependencyProperty as the backing store for SelectedItem.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty SelectedItemProperty =
            DependencyProperty.Register("SelectedItem", typeof(object), typeof(CascaderItem), new PropertyMetadata(default(object)));

        public CascaderInnerList SelectedView
        {
            get { return (CascaderInnerList)GetValue(SelectedViewProperty); }
            set { SetValue(SelectedViewProperty, value); }
        }

        // Using a DependencyProperty as the backing store for SelectedView.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty SelectedViewProperty =
            DependencyProperty.Register("SelectedView", typeof(CascaderInnerList), typeof(CascaderItem), new PropertyMetadata(null));


        public bool IsMultipleChoose
        {
            get { return (bool)GetValue(IsMultipleChooseProperty); }
            set { SetValue(IsMultipleChooseProperty, value); }
        }

        // Using a DependencyProperty as the backing store for IsMultipleChoose.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty IsMultipleChooseProperty =
            DependencyProperty.Register("IsMultipleChoose", typeof(bool), typeof(CascaderItem), new PropertyMetadata(default(bool), OnIsMultipleChooseChanged));

        private static void OnIsMultipleChooseChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            if (d is CascaderItem  cascaderItem)
            {
                if (cascaderItem.panel != null && cascaderItem.Count > 0) {
                    foreach (var item in cascaderItem.panel.Children)
                    {
                        if (item is CascaderInnerList cascaderInner)
                        {
                            cascaderInner.IsMultipleChoose = (bool)e.NewValue;
                        }
                        else if (item is StackPanel stack) { 
                            CascaderInnerList innerList = stack.Children[1] as CascaderInnerList;
                            innerList.IsMultipleChoose = (bool)e.NewValue;
                        }
                    }
                }
            }
        }
        private int Count { get { return panel.Children.Count; } }



        public CascaderItem()
        {
            Loaded += CascaderItem_Loaded;
            Unloaded += CascaderItem_Unloaded;
        }

        private void CascaderItem_Unloaded(object sender, RoutedEventArgs e)
        {
           
        }

        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();
            panel = GetTemplateChild("sp_path") as StackPanel;

        }

        public event EventHandler SelectedChanged;

        private void CascaderItem_Loaded(object sender, RoutedEventArgs e)
        {
            if (ItemsSource != null && panel != null&&panel.Children.Count==0)
            {
                CascaderInnerList cascaderInner = new CascaderInnerList();
                cascaderInner.ItemsSource = ItemsSource;
                cascaderInner.DeepIndex = 0;
                cascaderInner.IsMultipleChoose = IsMultipleChoose;
                cascaderInner.SelectionChanged += ListBox_SelectionChanged;
                panel.Children.Add(cascaderInner);
            }
        }

        private void ListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            if (sender is CascaderInnerList inner) {
                ListBox listBox = inner.innerListBox;
                SelectedItem = listBox.SelectedItem;

                //还要再次反射 看是不是最后一层
                //通过反射获取数据
                if (!IsMultipleChoose)
                {
                    var innerGetType = listBox.SelectedItem.GetType().GetProperty("Children");
                    if (innerGetType != null)
                    {
                        object innerContent = innerGetType.GetValue(listBox.SelectedItem, null);
                        if (innerContent == null)
                        {
                            SelectedView = inner;
                            //是最后一层了 通过事件通知出去
                            SelectedChanged?.Invoke(this, EventArgs.Empty);
                        }
                    }
                }
                else {
                    SelectedView = inner;
                    //勾选便通知
                    SelectedChanged?.Invoke(this, EventArgs.Empty);
                }

                //通过层级关系 判断是否清空之前的数据
                if (Count > inner.DeepIndex + 1)
                {
                    panel.Children.RemoveRange(inner.DeepIndex + 1, panel.Children.Count - 1);
                }
                
                if (listBox.SelectedItem != null) {
                    var obj = listBox.SelectedItem;
                    //通过反射获取数据
                    var getType = obj.GetType().GetProperty("Children");
                    if (getType != null)
                    {
                        object content = getType.GetValue(obj, null);
                        
                        if (content != null && content is IEnumerable<object> itemSource) { //判断是否存在子对象 也就是下级菜单
                            //存在开始拼接下级菜单
                            //添加分割线

                            //添加一个容器 好用于计算层数
                            StackPanel st = new StackPanel();
                            st.Orientation = Orientation.Horizontal;
                            //st.VerticalAlignment = VerticalAlignment.Center;
                            Border border = new Border();
                            border.Width = 1;
                            border.Background = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#e4e7ed"));
                            st.Children.Add(border);
                            //添加子集菜单
                            CascaderInnerList cascaderInner = new CascaderInnerList();
                            cascaderInner.ItemsSource = itemSource;
                            cascaderInner.DeepIndex = inner.DeepIndex + 1;
                            cascaderInner.ParentSource = inner;
                            cascaderInner.IsMultipleChoose = IsMultipleChoose;
                            //cascaderInner.SetValue(Extensions.CascaderExtensions.MultipleChoiceProperty, false);

                            cascaderInner.SelectionChanged += ListBox_SelectionChanged;
                            st.Children.Add(cascaderInner);
                            
                            panel.Children.Add(st);

                        }
                    }
                }
            }
        }
    }

CascaderItem.xaml

<Style TargetType="local:CascaderItem">
        
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="local:CascaderItem">
                    <Border  Height="120" CornerRadius="4" Background="White" Margin="8" BorderThickness="1" BorderBrush="#dcdfe6" Effect="{StaticResource MaterialDesignElevationShadow4}">
                        <StackPanel Orientation="Horizontal" Name="sp_path">

                        </StackPanel>
                    </Border>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

CascaderItem.cs

    public class CascaderItem:Control
    {
        private StackPanel panel;

        /// <summary>
        /// 内容资源
        /// </summary>
        public IEnumerable<object> ItemsSource
        {
            get { return (IEnumerable<object>)GetValue(ItemsSourceProperty); }
            set { SetValue(ItemsSourceProperty, value); }
        }

        // Using a DependencyProperty as the backing store for ItemsSource.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty ItemsSourceProperty =
            DependencyProperty.Register("ItemsSource", typeof(IEnumerable<object>), typeof(CascaderItem), new PropertyMetadata(null));



        public object SelectedItem
        {
            get { return (object)GetValue(SelectedItemProperty); }
            set { SetValue(SelectedItemProperty, value); }
        }

        // Using a DependencyProperty as the backing store for SelectedItem.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty SelectedItemProperty =
            DependencyProperty.Register("SelectedItem", typeof(object), typeof(CascaderItem), new PropertyMetadata(default(object)));

        public CascaderInnerList SelectedView
        {
            get { return (CascaderInnerList)GetValue(SelectedViewProperty); }
            set { SetValue(SelectedViewProperty, value); }
        }

        // Using a DependencyProperty as the backing store for SelectedView.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty SelectedViewProperty =
            DependencyProperty.Register("SelectedView", typeof(CascaderInnerList), typeof(CascaderItem), new PropertyMetadata(null));


        public bool IsMultipleChoose
        {
            get { return (bool)GetValue(IsMultipleChooseProperty); }
            set { SetValue(IsMultipleChooseProperty, value); }
        }

        // Using a DependencyProperty as the backing store for IsMultipleChoose.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty IsMultipleChooseProperty =
            DependencyProperty.Register("IsMultipleChoose", typeof(bool), typeof(CascaderItem), new PropertyMetadata(default(bool), OnIsMultipleChooseChanged));

        private static void OnIsMultipleChooseChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            if (d is CascaderItem  cascaderItem)
            {
                if (cascaderItem.panel != null && cascaderItem.Count > 0) {
                    foreach (var item in cascaderItem.panel.Children)
                    {
                        if (item is CascaderInnerList cascaderInner)
                        {
                            cascaderInner.IsMultipleChoose = (bool)e.NewValue;
                        }
                        else if (item is StackPanel stack) { 
                            CascaderInnerList innerList = stack.Children[1] as CascaderInnerList;
                            innerList.IsMultipleChoose = (bool)e.NewValue;
                        }
                    }
                }
            }
        }
        private int Count { get { return panel.Children.Count; } }



        public CascaderItem()
        {
            Loaded += CascaderItem_Loaded;
            Unloaded += CascaderItem_Unloaded;
        }

        private void CascaderItem_Unloaded(object sender, RoutedEventArgs e)
        {
           
        }

        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();
            panel = GetTemplateChild("sp_path") as StackPanel;

        }

        public event EventHandler SelectedChanged;

        private void CascaderItem_Loaded(object sender, RoutedEventArgs e)
        {
            if (ItemsSource != null && panel != null&&panel.Children.Count==0)
            {
                CascaderInnerList cascaderInner = new CascaderInnerList();
                cascaderInner.ItemsSource = ItemsSource;
                cascaderInner.DeepIndex = 0;
                cascaderInner.IsMultipleChoose = IsMultipleChoose;
                cascaderInner.SelectionChanged += ListBox_SelectionChanged;
                panel.Children.Add(cascaderInner);
            }
        }

        private void ListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            if (sender is CascaderInnerList inner) {
                ListBox listBox = inner.innerListBox;
                SelectedItem = listBox.SelectedItem;

                //还要再次反射 看是不是最后一层
                //通过反射获取数据
                if (!IsMultipleChoose)
                {
                    var innerGetType = listBox.SelectedItem.GetType().GetProperty("Children");
                    if (innerGetType != null)
                    {
                        object innerContent = innerGetType.GetValue(listBox.SelectedItem, null);
                        if (innerContent == null)
                        {
                            SelectedView = inner;
                            //是最后一层了 通过事件通知出去
                            SelectedChanged?.Invoke(this, EventArgs.Empty);
                        }
                    }
                }
                else {
                    SelectedView = inner;
                    //勾选便通知
                    SelectedChanged?.Invoke(this, EventArgs.Empty);
                }

                //通过层级关系 判断是否清空之前的数据
                if (Count > inner.DeepIndex + 1)
                {
                    panel.Children.RemoveRange(inner.DeepIndex + 1, panel.Children.Count - 1);
                }
                
                if (listBox.SelectedItem != null) {
                    var obj = listBox.SelectedItem;
                    //通过反射获取数据
                    var getType = obj.GetType().GetProperty("Children");
                    if (getType != null)
                    {
                        object content = getType.GetValue(obj, null);
                        
                        if (content != null && content is IEnumerable<object> itemSource) { //判断是否存在子对象 也就是下级菜单
                            //存在开始拼接下级菜单
                            //添加分割线

                            //添加一个容器 好用于计算层数
                            StackPanel st = new StackPanel();
                            st.Orientation = Orientation.Horizontal;
                            //st.VerticalAlignment = VerticalAlignment.Center;
                            Border border = new Border();
                            border.Width = 1;
                            border.Background = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#e4e7ed"));
                            st.Children.Add(border);
                            //添加子集菜单
                            CascaderInnerList cascaderInner = new CascaderInnerList();
                            cascaderInner.ItemsSource = itemSource;
                            cascaderInner.DeepIndex = inner.DeepIndex + 1;
                            cascaderInner.ParentSource = inner;
                            cascaderInner.IsMultipleChoose = IsMultipleChoose;
                            //cascaderInner.SetValue(Extensions.CascaderExtensions.MultipleChoiceProperty, false);

                            cascaderInner.SelectionChanged += ListBox_SelectionChanged;
                            st.Children.Add(cascaderInner);
                            
                            panel.Children.Add(st);

                        }
                    }
                }
            }
        }
    }

CascaderInnerList.xaml

    <Style  TargetType="local:CascaderInnerList">
        <Setter Property="IsMultipleChoose" Value="False"/>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="local:CascaderInnerList">
                    <ListBox ItemsSource="{TemplateBinding ItemsSource}" Name="listBox">
                        <ListBox.ItemsPanel>
                            <ItemsPanelTemplate>
                                <StackPanel Orientation="Vertical"/>
                            </ItemsPanelTemplate>
                        </ListBox.ItemsPanel>

                        <ListBox.ItemContainerStyle>
                            <Style TargetType="ListBoxItem">
                                <Setter Property="Margin" Value="3 1"/>
                                <Setter Property="Padding" Value="6 8"/>
                                <Setter Property="MinWidth" Value="60"/>
                                <Setter Property="Focusable" Value="True"/>

                                <Setter Property="Template">
                                    <Setter.Value>
                                        <ControlTemplate TargetType="ListBoxItem">
                                            <ControlTemplate.Resources>
                                                <converter:Object2VisibilityConverter x:Key="Object2VisibilityConverter"/>
                                                <converter:Boolean2VisibilityConverter x:Key="Boolean2VisibilityConverter"/>
                                            </ControlTemplate.Resources>
                                            <Border x:Name="bd1" Padding="{TemplateBinding Padding}">
                                                <Grid x:Name="gd1" Background="Transparent" VerticalAlignment="Center" >
                                                    <Grid.ColumnDefinitions>
                                                        <ColumnDefinition Width="auto"/>
                                                        <ColumnDefinition Width="auto"/>
                                                        <ColumnDefinition Width="*" MinWidth="20"/>
                                                        <ColumnDefinition Width="auto"/>
                                                    </Grid.ColumnDefinitions>
                                                    <CheckBox Margin="0 0 4 0" Focusable="False" Name="checkbox" IsChecked="{Binding IsSelected,UpdateSourceTrigger=PropertyChanged,Mode=TwoWay}" CommandParameter="{Binding RelativeSource={RelativeSource Mode=Self}}" Command="{x:Static local:CascaderInnerList.ClickCommand}" Visibility="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type local:CascaderInnerList}},Path=IsMultipleChoose,Converter={StaticResource Boolean2VisibilityConverter}}"/>
                                                    <TextBlock Text="{Binding Name}" Tag="1" Name="tb1" TextAlignment="Center" VerticalAlignment="Center" FontSize="14" Grid.Column="1"/>
                                                    <Path Width="11" Height="11" Visibility="{Binding Children,Converter={StaticResource Object2VisibilityConverter}}" Grid.Column="3" Name="path" Stretch="Uniform" Fill="Black" Data="M767.055482 512.122299a51.11001 51.11001 0 0 1-12.412431 36.50715L329.699826 1010.51791a41.618151 41.618151 0 0 1-61.332012 0 51.11001 51.11001 0 0 1 0-66.077942l398.658077-432.536712L268.367814 79.29353a51.11001 51.11001 0 0 1 0-65.71287 41.618151 41.618151 0 0 1 61.332012 0l424.943225 461.88846a51.11001 51.11001 0 0 1 12.412431 36.50715v0.146029z"/>
                                                </Grid>
                                            </Border>
                                            <ControlTemplate.Triggers>
                                                <Trigger Property="IsMouseOver" Value="True">
                                                    <Setter Property="Background" Value="#f5f7fa" TargetName="bd1"/>
                                                </Trigger>
                                                <Trigger Property="IsSelected" Value="True">
                                                    <Setter Property="Foreground" Value="#40a9fe" TargetName="tb1"/>
                                                    <Setter Property="Fill" Value="#40a9fe" TargetName="path"/>
                                                </Trigger>
                                            </ControlTemplate.Triggers>
                                        </ControlTemplate>
                                    </Setter.Value>
                                </Setter>
                               
                            </Style>
                        </ListBox.ItemContainerStyle>
                    </ListBox>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

CascaderInnerList.cs

    public class CascaderInnerList:Control
    {
        /// <summary>
        /// 内容资源
        /// </summary>
        public IEnumerable<object> ItemsSource
        {
            get { return (IEnumerable<object>)GetValue(ItemsSourceProperty); }
            set { SetValue(ItemsSourceProperty, value); }
        }

        // Using a DependencyProperty as the backing store for ItemsSource.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty ItemsSourceProperty =
            DependencyProperty.Register("ItemsSource", typeof(IEnumerable<object>), typeof(CascaderInnerList), new PropertyMetadata(null));



        public object SelectedItem
        {
            get { return (object)GetValue(SelectedItemProperty); }
            set { SetValue(SelectedItemProperty, value); }
        }

        // Using a DependencyProperty as the backing store for SelectedItem.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty SelectedItemProperty =
            DependencyProperty.Register("SelectedItem", typeof(object), typeof(CascaderInnerList), new PropertyMetadata(default(object)));

        public bool IsMultipleChoose
        {
            get { return (bool)GetValue(IsMultipleChooseProperty); }
            set { SetValue(IsMultipleChooseProperty, value); }
        }

        // Using a DependencyProperty as the backing store for IsMultipleChoose.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty IsMultipleChooseProperty =
            DependencyProperty.Register("IsMultipleChoose", typeof(bool), typeof(CascaderInnerList), new PropertyMetadata(default(bool)));




        public static ICommand ClickCommand { get; private set; }

        public ListBox innerListBox;

        public event SelectionChangedEventHandler SelectionChanged;
        /// <summary>
        /// 深度
        /// </summary>
        public int DeepIndex { get; set; }

        public CascaderInnerList ParentSource { get; set; }

        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();
            innerListBox = GetTemplateChild("listBox") as ListBox;
            innerListBox.SelectionChanged += (e, s) => {
                SelectedItem = innerListBox.SelectedItem;
                SelectionChanged?.Invoke(this, s);
            };
        }

        public CascaderInnerList()
        {
            Loaded += CascaderInnerList_Loaded;
            ClickCommand = new DelegateCommand<object>(ClickCommandExecute);
           
        }
        private void CascaderInnerList_Loaded(object sender, RoutedEventArgs e)
        {

        }
        private void ClickCommandExecute(object obj)
        {
            if (obj != null && obj is CheckBox box) {
                var checkState = box.IsChecked;
               
                var data = box.DataContext;
                innerListBox.SelectedItem = data;

                //设置子对象状态
                SetChildrenState(checkState,data);
                
                //设置父对象状态
                SetParentState(checkState,this);

                SelectionChanged?.Invoke(this, null);
            }
        }

        private void SetParentState(bool? checkState, CascaderInnerList innerObj)
        {
            //获取父对象状态
            if (innerObj.ItemsSource != null && innerObj.ItemsSource is IEnumerable<object> array)
            {
                List<bool?> selectValues = new List<bool?>();
                foreach (object item in array)
                {
                    var selectedProperty = item.GetType().GetProperty("IsSelected");
                    if (selectedProperty != null)
                    {
                        var selectValue = selectedProperty.GetValue(item, null) as bool?;
                        selectValues.Add(selectValue);
                    }
                }
                if (selectValues.Count(x => x == true) == array.Count())
                {
                    checkState = true;
                }
                else if (selectValues.Count(x => x == false) == array.Count())
                {
                    checkState = false;
                }
                else {
                    checkState = null;
                }
            }

            if (innerObj.ParentSource != null && innerObj.ParentSource.SelectedItem != null)
            {
               
                var selectedProperty = innerObj.ParentSource.SelectedItem.GetType().GetProperty("IsSelected");
                if (selectedProperty != null)
                {
                    selectedProperty.SetValue(innerObj.ParentSource.SelectedItem, checkState);
                    //查看父级对象
                    if (innerObj.ParentSource != null)
                    {
                        SetParentState(checkState, innerObj.ParentSource);
                    }
                }
            }
        }

        void SetChildrenState(bool? state,object data) {
            
            var childrenProperty = data.GetType().GetProperty("Children");
            if (childrenProperty != null)
            {
                object childrenValue = childrenProperty.GetValue(data, null);
                if (childrenValue != null&&childrenValue is IEnumerable<object> array)
                {
                    foreach ( object item in array)
                    {
                        var selectedProperty = item.GetType().GetProperty("IsSelected");
                        if (selectedProperty != null) {
                            selectedProperty.SetValue(item,state);
                            SetChildrenState(state, item);
                        }
                    }             
                }           
            }
        }     
    }




免责声明:本文系网络转载或改编,未找到原创作者,版权归原作者所有。如涉及版权,请联系删


相关文章
技术文档
QR Code
微信扫一扫,欢迎咨询~
customer

online

联系我们
武汉格发信息技术有限公司
湖北省武汉市经开区科技园西路6号103孵化器
电话:155-2731-8020 座机:027-59821821
邮件:tanzw@gofarlic.com
Copyright © 2023 Gofarsoft Co.,Ltd. 保留所有权利
遇到许可问题?该如何解决!?
评估许可证实际采购量? 
不清楚软件许可证使用数据? 
收到软件厂商律师函!?  
想要少购买点许可证,节省费用? 
收到软件厂商侵权通告!?  
有正版license,但许可证不够用,需要新购? 
联系方式 board-phone 155-2731-8020
close1
预留信息,一起解决您的问题
* 姓名:
* 手机:

* 公司名称:

姓名不为空

姓名不为空

姓名不为空
手机不正确

手机不正确

手机不正确
公司不为空

公司不为空

公司不为空