【c#】wpf 自定义可伸缩瓦片状多选控件

一、效果图

在这里插入图片描述
在这里插入图片描述

二、代码

1、ListBox做架构主界面

1.1 xaml

<UserControl x:Class="YZ.HIS.UserControls.TelescopicMultiSelector.TelescopicMultiSelector"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:YZ.HIS.UserControls.TelescopicMultiSelector"
             mc:Ignorable="d" >
    <StackPanel Margin="4" x:Name="outPanel">
        <DockPanel>
            <Border Margin="4" Padding="4" x:Name="tip">
                <TextBlock  x:Name="textBlock">(多选)</TextBlock>
            </Border>
            <ListBox ItemsSource="{Binding DataList1}" x:Name="ListBoxFirst">
                <ListBox.Template>
                    <ControlTemplate>
                        <StackPanel IsItemsHost="True" Orientation="Horizontal"/>
                    </ControlTemplate>
                </ListBox.Template>
            </ListBox>
            <Border Margin="4" Padding="4"  HorizontalAlignment="Right">
                <Button x:Name="button" Style="{DynamicResource btnText}" Click="Button_Click" Cursor="Hand">
                    <Button.Template>
                        <ControlTemplate>
                            <StackPanel Orientation="Horizontal">
                                <TextBlock x:Name="btnTB" Text="{Binding BtnText}"></TextBlock>
                                <Image Source="{Binding BtnImg}" Width="16" Height="16"></Image>
                            </StackPanel>
                        </ControlTemplate>
                    </Button.Template>
                </Button>
            </Border>
        </DockPanel>
        <ListBox ItemsSource="{Binding DataList2}" Visibility="Collapsed" x:Name="ListBoxSec">
            <ListBox.Template>
                <ControlTemplate>
                    <WrapPanel IsItemsHost="True" Orientation="Horizontal"></WrapPanel>
                </ControlTemplate>
            </ListBox.Template>
        </ListBox>
    </StackPanel>
</UserControl>

2.2 cs

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using YZ.HIS.Common;

namespace YZ.HIS.UserControls.TelescopicMultiSelector
{
    /// <summary>
    /// TelescopicMultiSelector.xaml 的交互逻辑
    /// </summary>
    public partial class TelescopicMultiSelector : UserControl,INotifyPropertyChanged
    {
        public EventHandler SelectComplete;

        //传入id,name的source和selectedList
        private List<KeyValuePair<long, string>> IdNameSourceList { get; set; }
        private List<KeyValuePair<long, string>> IdNameSelectedList { get; set; }
        //只传入name的source和selectedList
        private List<String> NameSourceList { get; set; }
        private List<String> NameSelectedList { get; set; }
        private bool WithId { get; set; } = false;

        private List<TMSelectorItem> _DataList1 = new List<TMSelectorItem>();
        public List<TMSelectorItem> DataList1
        {
            get
            {
                return _DataList1;
            }
            set
            {
                _DataList1 = value;
                PropertyChange("DataList1");
            }
        }
        private List<TMSelectorItem> _DataList2 = new List<TMSelectorItem>();
        public List<TMSelectorItem> DataList2
        {
            get
            {
                return _DataList2;
            }
            set
            {
                _DataList2 = value;
                PropertyChange("DataList2");
            }
        }
        private List<string> SelectedList = null;
        private Dictionary<string, KeyValuePair<long, string>> SelectedDic = null;
        private Dictionary<string, KeyValuePair<long, string>> SourceDic = null;

        private double Size { get; set; } = 4D;
        private double FirstRowSize { get; set; } = 0D;
        private bool OpenStatus { get; set; } = false;
        public string BtnText
        {
            get
            {
                if (OpenStatus)
                    return "收起";
                return "展开";
            }
        }
        public string BtnImg
        {
            get
            {
                if (OpenStatus)
                    return "/YZ.His;component/Images/OnlineHospital/arrow_up.png";
                return "/YZ.His;component/Images/OnlineHospital/arrow_down.png";
            }
        }


        public TelescopicMultiSelector(double Width, List<KeyValuePair<long, string>> IdNameSourceList, List<KeyValuePair<long, string>> IdNameSelectList = null,double FontSize = 12)
        {
            this.IdNameSourceList = IdNameSourceList;
            this.IdNameSelectedList = IdNameSelectedList;
            this.Width = Width;
            this.FontSize = FontSize;
            this.WithId = true;
            this.DataContext = this;
            InitializeComponent();
            InitStruct();
            InitListBox();
            SelectComplete += sc;
        }

        private void sc(object sender,EventArgs e)
        {
           
        }
        public TelescopicMultiSelector(double Width, List<string> NameSourceList, List<string> NameSelectList = null,  double FontSize = 12)
        {
            this.NameSourceList = NameSourceList;
            this.NameSelectedList = NameSelectedList;
            this.Width = Width;
            this.FontSize = FontSize;
            this.WithId = false;
            this.DataContext = this;
            InitializeComponent();
            InitStruct();
            InitListBox();
            SelectComplete += sc;
        }


        public event PropertyChangedEventHandler PropertyChanged;
        private void PropertyChange(string val)
        {
            if (string.IsNullOrEmpty(val))
                return;
            if (PropertyChanged != null)
                PropertyChanged.Invoke(this, new PropertyChangedEventArgs(val));
        }

        
        private void InitStruct()
        {
            // 设置listbox的SelectionMode.Multiple,ListBox可以多选***
            this.ListBoxFirst.SelectionMode = SelectionMode.Multiple;
            this.ListBoxSec.SelectionMode = SelectionMode.Multiple;
            if (this.FontSize != 12)
            {
                Size = this.FontSize / 3;
                // 初始化多选和展开按钮的位置
                // 设置margin和padding
                this.outPanel.Margin = new Thickness(Size);
                this.tip.Margin = new Thickness(Size);
                this.tip.Padding = new Thickness(Size);
                this.button.Margin = new Thickness(Size);
                this.button.Padding = new Thickness(Size);
            }
            FirstRowSize = MeasureString(this.textBlock).Width + MeasureString(this.textBlock, BtnText).Width+Size*10 +16;//16是image的宽度数据


        }
        // 初始化加载ListBox及其中的items
        private void InitListBox()
        {
            //带id的传入处理
            if (WithId)
            {
                if (IdNameSourceList == null || IdNameSourceList.Count == 0)
                    return;
                if (IdNameSelectedList != null && IdNameSelectedList.Count > 0)
                {
                    if (SelectedDic == null)
                        SelectedDic = new Dictionary<string, KeyValuePair<long, string>>();
                    foreach (KeyValuePair<long, string> pair in IdNameSelectedList)
                    {
                        if (pair.Key==0L)
                            continue;
                        SelectedDic.Add(pair.Key + "", pair);
                    }
                        
                }
                if (SourceDic == null)
                    SourceDic = new Dictionary<string, KeyValuePair<long, string>>();
                foreach (KeyValuePair<long, string> pair in IdNameSourceList)
                {
                    long id = pair.Key;
                    string name = pair.Value;
                    if (id == 0L || string.IsNullOrEmpty(name))
                        continue;
                    string key = id + "";
                    SourceDic.Add(key,pair);
                    TMSelectorItem item = new TMSelectorItem()
                    {
                        FontSize = this.FontSize,
                        SpaceSize = Size,
                        TextContent = name,
                        Uid = key

                    };
                    item.SelectEvent += SelectAndRemove;
                    if (SelectedDic != null && SelectedDic.ContainsKey(key))
                        item.IsSelected = true;
                    DataListDevide(item);
                }
            }
            //不带id的传入处理
            else
            {
                if (NameSourceList == null || NameSourceList.Count == 0)
                    return;
                if (NameSelectedList != null && NameSelectedList.Count > 0)
                {
                    if (SelectedList == null)
                        SelectedList = new List<string>();
                    foreach (string str in NameSelectedList)
                        SelectedList.Add(str);
                }
                foreach(string str in NameSourceList)
                {
                    if (string.IsNullOrEmpty(str))
                        continue;
                    TMSelectorItem item = new TMSelectorItem()
                    {
                        FontSize = this.FontSize,
                        SpaceSize = Size,
                        TextContent = str,
                        Uid = str

                    };
                    item.SelectEvent += SelectAndRemove;
                    if (SelectedList != null && SelectedList.Contains(str))
                        item.IsSelected = true;
                    DataListDevide(item);
                }
            }
        }

        //选择或取消每一项
        private void SelectAndRemove(object sender,EventArgs e)
        {
            var sItem = sender as TMSelectorItem;
            if(WithId)
            {
                SelectedDicHandle(sItem.IsSelected, sItem.Uid);
                SelectComplete.Invoke(SelectedDic.Values, e);
            }
            else
            {
                SelectedListHandle(sItem.IsSelected, sItem.Uid);
                SelectComplete.Invoke(SelectedList, e);
            }
            
        }

        private void SelectedDicHandle(bool flag,string key)
        {
            //true为新增操作,false为移除操作
            if (flag)
            {
                if (SelectedDic == null)
                    SelectedDic = new Dictionary<string, KeyValuePair<long, string>>();
                if (SelectedDic.ContainsKey(key))
                    return;
                SelectedDic.Add(key, SourceDic[key]);
            }
            else
            {
                SelectedDic.Remove(key);
            }
        }

        private void SelectedListHandle(bool flag,string content)
        {
            if (flag)
            {
                if (SelectedList == null)
                    SelectedList = new List<string>();
                if (SelectedList.Contains(content))
                    return;
                SelectedList.Add(content);
            }
            else
            {
                SelectedList.Remove(content);
            }
        }

        //将source区分处理,分为两部分分别进行第一行和下面的展开项中显示
        private void DataListDevide(TMSelectorItem item)
        {
            if(FirstRowSize < this.Width)
                FirstRowSize += MeasureString(this.textBlock,item.TextContent).Width+Size*4;
            if (FirstRowSize > this.Width)
            {
                DataList2.Add(item);
                return;
            }
            DataList1.Add(item);
        }

		// 点击进行展开或收起的处理
        private void Button_Click(object sender, RoutedEventArgs e)
        {
            
            OpenStatus = !OpenStatus;
            //按钮变化显示
            ButtonDisPlay();
            if (OpenStatus)
            {
                this.ListBoxSec.Visibility = Visibility.Visible;
            }
            else
            {
                this.ListBoxSec.Visibility = Visibility.Collapsed;
            }
        }

        private void ButtonDisPlay()
        {
            PropertyChange("BtnText");
            PropertyChange("BtnImg");
        }

        //计算TextBlock的大小
        private Size MeasureString(TextBlock tb,string content=null)
        {
            if (content == null)
                content = tb.Text.ToString();
            var formattedText = new FormattedText(
                content,
                CultureInfo.CurrentCulture,
                FlowDirection.LeftToRight,
                new Typeface(tb.FontFamily, tb.FontStyle, tb.FontWeight, tb.FontStretch),
                tb.FontSize,
                Brushes.Black,
                new NumberSubstitution(),
                TextFormattingMode.Display);

            return new Size(formattedText.Width, formattedText.Height);
        }
    }
}

2、ListBoxItem每个可选择项

2.1 xaml
注:listBoxItem中不能通过触发器对其中的元素的foreground或background等进行设置。可以将需要设置的元素Property项的value与listBoxItem自身的相关联 如下写法:
BorderBrush="{Binding Foreground,RelativeSource={RelativeSource AncestorType=ListBoxItem}}"
再使用触发器设置listBoxItem就好,从12行<Style.Triggers>开始。

<ListBoxItem x:Class="YZ.HIS.UserControls.TelescopicMultiSelector.TMSelectorItem"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:YZ.HIS.UserControls.TelescopicMultiSelector"
             mc:Ignorable="d" Style="{DynamicResource ThisItemStyle}">
    <ListBoxItem.Resources>
        <Style TargetType="ListBoxItem" x:Key="ThisItemStyle">
            <Setter Property="Foreground" Value="#b2b2b2"></Setter>
            <Setter Property="Cursor" Value="Hand"></Setter>
            <Style.Triggers>
                <MultiDataTrigger>
                    <MultiDataTrigger.Conditions>
                        <Condition Binding="{Binding IsSelected}" Value="True"></Condition>
                    </MultiDataTrigger.Conditions>
                    <Setter Property="Foreground" Value="#076ff5"></Setter>
                </MultiDataTrigger>
                <MultiDataTrigger>
                    <MultiDataTrigger.Conditions>
                        <Condition Binding="{Binding IsSelected}" Value="False"></Condition>
                    </MultiDataTrigger.Conditions>
                    <Setter Property="Foreground" Value="#b2b2b2"></Setter>
                </MultiDataTrigger>
            </Style.Triggers>
        </Style>
    </ListBoxItem.Resources>
    <ListBoxItem.Template>
        <ControlTemplate>
            <RadioButton Click="RadioButton_Click">
                <RadioButton.Template>
                    <ControlTemplate>
                        <Border CornerRadius="5" Background="#fff" BorderThickness="1" Padding="{Binding SpaceSize}" Margin="{Binding SpaceSize}" BorderBrush="{Binding Foreground,RelativeSource={RelativeSource AncestorType=ListBoxItem}}">
                            <TextBlock Text="{Binding TextContent}" HorizontalAlignment="Center" VerticalAlignment="Center" Foreground="{Binding Foreground,RelativeSource={RelativeSource AncestorType=ListBoxItem}}"></TextBlock>
                        </Border>
                    </ControlTemplate>
                </RadioButton.Template>
            </RadioButton>
        </ControlTemplate>
    </ListBoxItem.Template>
</ListBoxItem>

2.2 cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace YZ.HIS.UserControls.TelescopicMultiSelector
{
    /// <summary>
    /// TMSelectorItem.xaml 的交互逻辑
    /// </summary>
    public partial class TMSelectorItem : ListBoxItem
    {
        public double SpaceSize { get; set; } = 4;
        public string TextContent { get; set; } = "";
        public EventHandler SelectEvent;
        public TMSelectorItem()
        {
            InitializeComponent();
            this.DataContext = this;
        }

        private void RadioButton_Click(object sender, RoutedEventArgs e)
        {
        	//按钮的选择与listBoxItem 的选择关联
            var btn = sender as RadioButton;
            if (this.IsSelected)
                btn.IsChecked = false;
            if (btn.IsChecked == true)
            {
                this.IsSelected = true;
            }
            else
            {
                this.IsSelected = false;
            }
            // 选择后触发事件
            SelectEvent.Invoke(this, new EventArgs());
        }
    }
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值