22、wpf之Combobox使用小记

前言:作为wpf中常用的列表控件之一,Combox既具备了列表控件的下拉功能,又具备了Selector 类的选择功能,算是个复合性控件。现记录下MVVM模式下常用属性。

一、简介

ComboBox是一个ItemsControl,这意味着它可以包含任何类型的对象,例如字符串、图像或面板。

二、常用属性

用于MVVM模式下,常用的属性有这么几个,ItemsSource用于绑定数据集,剩下的几个基本上都是选择项相关操作,比如,选择了哪一项,这一项的索引是什么,这一项的值是什么等等。

ItemsSource:获取或设置用于生成ItemsControl的内容的集合,继承自ItemsControl。用于指定下拉列表绑定的集合类数据对象。

SelectedIndex:获取或设置当前选择中项的索引,如果选择为空,则返回负1,继承自Selector。下拉列表中选中行的索引。

DisplayMemberPath:获取或设置源对象上的值的路径,以用作对象的可视表示形式(继承自ItemsControl)。下拉列表中要显示的集合数据对象的列,因为集合数据对象可能会有多列。

SelectedItem:获取或设置当前选择中项,如果选择为空,则返回null,继承自Selector。

SelectedValue:获取或设置通过使用SelectedItem获取的SelectedValuePath的值(继承自Selector)

SelectedValuePath:获取或设置用于从SelectedValue获取SelectedItem的路径。继承自Selector。下拉列表中,对应与显示的集合数据对象的列,返回的集合数据对象的列。

SelectionBoxItem:获取选择框中显示的项。

Style:设置样式。 

2.1 ItemsSource

它是 ItemsControl的一个内容属性,类型是System.Collections.IEnumerable。可在XAML中进行绑定继承自IEnumerable类型的集合数据。

ItemsSource属性可绑定的类型很多,不管是框架带的list等,还是自定义的,只要这个类型继承了System.Collections.IEnumerable即可。

这里声明一个ObservableCollection<string>集合数据,其中ObservableCollection中泛型是stirng。

        private ObservableCollection<string> strings = new ObservableCollection<string>();
        //集合类型,注意这里集合中类型是string
        public ObservableCollection<string> Strings
        {
            get { return strings; }
            set
            {
                this.strings = value; 
                if (this.PropertyChanged != null)
                    PropertyChanged(this, new PropertyChangedEventArgs("Strings"));
            }
        }

        string s1="feng";
        string s1="yang";
        string s1="zhu";
        Strings.Add(s1);
        Strings.Add(s2);
        Strings.Add(s3);

XAML中通过ItemsSource属性进行绑定。 

<ComboBox Name="comboBox1"
          ItemsSource="{Binding Strings}"
          Text="xinnu"/>  

至于为什么ItemsControl类型会自动将集合属性中的泛型类型自动转换并显示,笔者5年前看深入浅出wpf中有过解释,说这个和ItemsControl模板中显示数据的控件有关系,具体的忘了怎么说的了,感兴趣的朋友可以去看看。笔者这里集合中泛型是string,ItemsControl中用来承载这个string的应该是个TextBox。

其实这里还有个问题,我们回到初衷,绑定数据集合无非就是为了显示集合中数据,这个集合中是string没啥问题,但是这种用法基本上没啥意义,实操中如果有这种需求,那肯定不是使用这种方法。

那么问题来了,如果不是这样用,那应该是怎么用?

自然是自定义类的形式作为这个集合中的泛型。这里引出第二个问题,集合绑定一个类,那么显示会怎么样呢?

        List<Grades> grades = new List<Grades>();
        public List<Grades> GradesProp
        {
            get { return grades; }
            set
            {
                grades = value;
                if (PropertyChanged != null)
                    PropertyChanged(this, new PropertyChangedEventArgs("Grades"));
            }
        }   

        public MainWindow()
        {
            InitializeComponent();
            this.DataContext = this;

            grades.Add(new Grades { GradeID = 1, GradeName = "高一" });
            grades.Add(new Grades { GradeID = 2, GradeName = "高二" });
            grades.Add(new Grades { GradeID = 3, GradeName = "高三" });
        } 
    
        public class Grades
        {
            public int GradeID { get; set; }

            public string GradeName { get; set; }
        }
<ComboBox Name="comboBox1"
          ItemsSource="{Binding Grades}"
          Text="xingnu"/> 

这里显示的是:命名空间.泛型类名。

从官网文档可以看到,用来承载集合数据的控件会根据ItemsControl实际继承类类型来确定。

所以上面集合中泛型是类,他就会默认将类作为一个字符串进行显示。但是这样肯定不是我们想要的啊,这就需要另一个属性来显示我们想要展示的内容了。

2.2 DisplayMemberPath

<ComboBox Name="comboBox1"
          ItemsSource="{Binding GradesProp}"
          DisplayMemberPath="GradeName"
          Text="xingnu"/>

通过DisplayMemberPath属性来显示集合中类的指定属性。其实这里笔者有个疑问,为什么不适用binding来绑定属性值的。

说明:条目控件,顾名思义,这个控件有很多Item,应该是有很多item,然后再写一个类,把这些item重新包装起来,就好比作者写文章,写的多了,就出个合订本,并且按篇幅长短分为短篇、中篇、长篇等。集合,同样有很多item,整好对上了,集合中每个item整好给条目控件中每一个item去承载。

简单的显示实现了,下面就是基于MVVM模式下实现前端选中后,在后台对选中的item进行处理了。

这里一定要转变一个思想,不管是编程,还是日常中处理问题,我们要解决的是事情是由一个个具体的问题组成的,我们既要关注事情本身,还要从细处着手解决这一个个问题个体。能从事情本身把握的一般都是架构师级别的,大多数人更多是去解决一个个具体问题。  

我们知道这个条目控件(工具人)就是把我们这一个具体问题集中起来了,我们下面要做的就要操作具体item。

2.3 SelectedItem

        public class LocationRoad
        {
            public int ID { set; get; }
            public string Code { set; get; }
            public string Info { set; get; }
        }

        private LocationRoad _selectLocation;
        /// <summary>
        /// 当ComboBox选中项更改时发生
        /// </summary>
        public LocationRoad SelectLocation
        {
            get
            {
                return this._selectLocation;
            }
            set
            {
                this._selectLocation = value;
                //这里操作更改变化的值
                MessageBox.Show(_selectLocation.Info);
                if (this.PropertyChanged != null)
                    PropertyChanged(this, new           PropertyChangedEventArgs("SelectLocation"));
            }
        }
        
        private ObservableCollection<LocationRoad> _locationRoad = null;
        /// <summary>
        /// 220720 komla 集合数据
        /// </summary>
        public ObservableCollection<LocationRoad> LocationSource
        {
            get
            {
                if (this._locationRoad == null)
                {
                    this._locationRoad = new ObservableCollection<LocationRoad>() {
                        new LocationRoad() { ID = 1, Code = "NGQ", Info = "南岗区" },
                        new LocationRoad() { ID = 2, Code = "DLQ", Info = "道里区" },
                        new LocationRoad() { ID = 3, Code = "DWQ", Info = "道外区" },
                        new LocationRoad() { ID = 4, Code = "PFQ", Info = "平房区" },
                        new LocationRoad() { ID = 5, Code = "XFQ", Info = "香坊区" },
                    };

                }
                return this._locationRoad;
            }
            set
            {
                this._locationRoad = value;
                if (this.PropertyChanged != null)
                    PropertyChanged(this, new PropertyChangedEventArgs("LocationSource"));
            }
        }
<ComboBox Name="comboBox1"
          ItemsSource="{Binding LocationSource}"
          DisplayMemberPath="Info"
          SelectedItem="{Binding SelectLocation}"
          Text="xingnu"/>

SelectItem:获取或设置当前选择中项,返回类型是object,示例中集合中泛型类型是 LocationRoad,对应的条目控件中的item也是LocationRoad,那么通过binding设置或返回的数据类型自然是LocationRoad,这个LocationRoad也是我们想要进行操作的对象,比如,用户选中了香坊区,我们就要对这个香坊区进行操作,香坊区面积、人口、交通、医疗等等信息。

2.4 SelectedValuePath、SelectedValue

这两个是一起用的,起到的效果和SelectedItem差不多,不知道是不是针对的需求不同,笔者这里先不讨论了,有知道的 小伙伴留言下。

SelectedValue:当前被选中的item的值。默认情况下这个值是item本身,这时SelectedValue与SelectedItem是一样的。通过设置SelectedValuePath去选择任意的属性或者表达式,用来表示每个Item的值(SelectedValuePath 与DisplayMemberPath 工作原理一样)。

    public partial class MainWindow : Window, INotifyPropertyChanged
    {
        public List<ComboBoxItemModel<Person>> list { get; set; }

        private Person selectedModel;
        public Person SelectedModel
        {
            get { return selectedModel; }
            set
            {
                selectedModel = value;
                OnPropertyChanged();
                MessageBox.Show($"{value.Name} {value.Salary}");
            }
        } 

        public MainWindow()
        {
            InitializeComponent();
            this.DataContext = this;

            list = new List<ComboBoxItemModel<Person>>
            {
                new ComboBoxItemModel<Person>{Description="Item0",SelectedModel=new Person{ Name="张三",Salary=10000m },IsEnable=true},
                new ComboBoxItemModel<Person>{Description="Item1",SelectedModel=new Person{ Name="李四",Salary=10000m },IsEnable=false},
                new ComboBoxItemModel<Person>{Description="Item2",SelectedModel=new Person{ Name="赵五",Salary=10000m },IsEnable=true},
                new ComboBoxItemModel<Person>{Description="Item3",SelectedModel=new Person{ Name="孙六",Salary=10000m },IsEnable=false},
                new ComboBoxItemModel<Person>{Description="Item4",SelectedModel=new Person{ Name="王七",Salary=10000m },IsEnable=true},
            };
            SelectedModel = list[0].SelectedModel; 
        }

        public event PropertyChangedEventHandler? PropertyChanged;
        private void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        } 
    }

    public class ComboBoxItemModel<T>
    {
        public string Description { get; set; }
        public T SelectedModel { get; set; }
        public bool IsEnable { get; set; }
    }

    public class Person
    {
        public string Name { get; set; }
        public Decimal Salary { get; set; }
    } 
        <ComboBox
            HorizontalAlignment="Center"
            VerticalAlignment="Center"
            DisplayMemberPath="Description"
            ItemsSource="{Binding list}"
            SelectedValue="{Binding SelectedModel}"
            SelectedValuePath="SelectedModel">
            <ComboBox.ItemContainerStyle>
                <Style TargetType="ComboBoxItem">
                    <Setter Property="IsEnabled" Value="{Binding IsEnable}" />
                </Style>
            </ComboBox.ItemContainerStyle>
        </ComboBox>

这里SelectedValue是绑定的值,也就是我们需要对用户选中后的item进行处理的值,但是需要告诉程序,这个值的路径,这个通讯员就是SelectedValuePath,这个的类型是string,为什么不用绑定可能是因为这个原因吧,这个string不是依赖属性(存疑)。

2.5 总结

这里面常用到的就这几个,ItemsSource数据源,泛型集合。这里一定要明确,这个条目控件对应的是集合,除了这个,其他的都不要想,剩下的东西都是和集合中泛型相关的,比如DisplayMemberPath、SelectedValuePath的值都是集合中泛型的属性,而SelectedValue和SelectedItem也是针对泛型的,因为我们最终是要对这个泛型进行操作的,就好比一本书,我们是要去读书中的文章的,书名、书类型、书材质、大小都不重要(这里不重要),说白了,我们可以用张白纸把里面每篇文章重新包装下,所以书的装订形式不重要,可能是16开、也可能是32开,无非就是里面的排版不同而已,文章的内容才是重点。

2.6 SelectedIndex

SelectedIndex:非负的整型数,它表示哪个item被选择,如果没有东西被选择,则用1表示。item是根据被添加到集合中的顺序来计数的。

这个先不介绍用法了,暂时没想到有什么好用的。

三、弯路

其实最开始时候并没有使用itemsSource属性绑定,而是通过XAML中添加ComboBox控件,如上图。然后还要实现MVVM模式,就使用SelectedItem,但是用的很别扭,不过能实现功能。因为SelectedItem属性类型是object,所以可以绑定任意类型,先一步步来,首先SelectedItem绑定常用属性看下,这里用string,SelectedItem返回的值自然也是string类型。

private string _fake = "";
public string Fake
{
    get { return _fake; }
    set
    {
         _fake = value;
         MessageBox.Show(_fake);
         if (PropertyChanged != null)
             PropertyChanged(this, new PropertyChangedEventArgs("Fake"));
    }
}
<ComboBox Height="25" Width="85" Margin="2" 
          VerticalAlignment="Center" 
          FontSize="16" SelectedIndex="0"
          SelectedItem="{Binding Fake}">
    <ComboBoxItem Content="fengbi"/>
    <ComboBoxItem Content="yang"/>
    <ComboBoxItem Content="xyao"/>  
    <ComboBoxItem Content="chunm"/>
    <ComboBoxItem Content="rb"/>
</ComboBox>

这样使用结果如下图,就是说SelectedItem选中的值返回的是命名空间.类名.item的content值。

但是我们肯定不想要这种值,而是ComboBox的Content值,应该怎么办呢?好办,把这个SelectedItem绑定到ComboBoxItem 类型属性上面,这样通过SelectedItem绑定返回的类型自然就是ComboBoxItem类型了,然后对这个返回的ComboBoxItem操作就可以了。

<ComboBox Height="25" Width="85" Margin="2" 
        VerticalAlignment="Center" 
        FontSize="16" SelectedIndex="0"
        SelectedItem="{Binding CbxPautItem, Mode=OneWayToSource}">
    <ComboBoxItem Content="9600"/>
    <ComboBoxItem Content="4800"/>
    <ComboBoxItem Content="2400"/>  
    <ComboBoxItem Content="1200"/>
    <ComboBoxItem Content="600"/>
</ComboBox>
private string _cbxPaut; 
/// <summary>
/// 220628 komla 波特率
/// </summary>
public ComboBoxItem CbxPautItem
{ 
    set 
    {
        if (value != null)
        {
            _cbxPaut = value.Content.ToString();
            CostomSerialPort.BaudRate=Convert.ToInt32(_cbxPaut);
        } 
    }
}

四、引用文献

4.1 WPF:选择器(selector)_sheila_1988的博客-CSDN博客_wpf 选择器

4.2 WPF ComboBox_Stupid·XL的博客-CSDN博客_wpf中combobox

4.3 WPF_ComboBox的MVVM绑定(三)_heater404的博客-CSDN博客

4.4【WPF】UI元素--《深入浅出WPF》by刘铁锰_阿月浑子2021的博客-CSDN博客

4.5 WPF (六) 常用控件 之 条目控件 (ItemsControls) - 知乎

4.6 WPF的MVVM模式给ComboBox绑定数据和读取 - jack_Meng - 博客园

4.7 WPF中ComboBox几种数据绑定的方法_哈喽,猿先生的博客-CSDN博客_combobox数据绑定 wpf4.8 WPF中ComboBox控件的SelectedItem和SelectedValue的MVVM绑定 - 南风小斯 - 博客园 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值