【问题标题】:WPF DataGrid : how to bind an object to reflect the item whose row is checkedWPF DataGrid:如何绑定对象以反映检查其行的项目
【发布时间】:2017-06-18 02:33:38
【问题描述】:

我有一个填充了元素的数据网格和每个元素的复选框。 我正在寻找一种方法,让 ViewModel 中的对象成为当前选中复选框的任何元素。
到目前为止,这是我的 XAML:

<Window x:Class="fun_with_DataGridCheckBoxColumns.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:local="clr-namespace:fun_with_DataGridCheckBoxColumns"
    mc:Ignorable="d"
    Title="MainWindow" Height="350" Width="525">
<DockPanel>
    <StackPanel Orientation="Horizontal" DockPanel.Dock="Top">
        <Label Content="Chosen One : " />
        <Label Content="{Binding ChosenOne.Name, Mode=OneWay}" />
    </StackPanel>
    <DataGrid ItemsSource="{Binding People}" AutoGenerateColumns="False" CanUserAddRows="False">
        <DataGrid.Columns>
            <DataGridTextColumn Header="ID" Binding="{Binding ID, Mode=OneWay}" IsReadOnly="True"/>
            <DataGridTextColumn Header="Name" Binding="{Binding Name, Mode=OneWay}" IsReadOnly="True"/>
            <DataGridCheckBoxColumn Header="Is Chosen"/>
        </DataGrid.Columns>
    </DataGrid>
</DockPanel>

还有我的 CS:

using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Windows;

namespace fun_with_DataGridCheckBoxColumns
{
    public partial class MainWindow : Window
    {
        public Person ChosenOne { get; set; }

        public MainWindow()
        {
        InitializeComponent();
        DataContext = new Viewmodel();
        }
    }

    public class Viewmodel : INotifyPropertyChanged
    {
        public ObservableCollection<Person> People { get; private set; }
        private Person _chosenOne = null;
        public Person ChosenOne
        {
            get
            {
                if (_chosenOne == null) { return new Person { Name = "Does Not Exist" }; }
                else return _chosenOne;
            }
            set
            {
                _chosenOne = value;
                NotifyPropertyChanged("ChosenOne");
            }
        }

        public Viewmodel()
        {
            People = new ObservableCollection<Person>
            {
                new Person { Name = "John" },
                new Person { Name = "Marie" },
                new Person { Name = "Bob" },
                new Person { Name = "Sarah" }
            };
        }

        public event PropertyChangedEventHandler PropertyChanged;
        public void NotifyPropertyChanged(string propertyName)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }

    public class Person
    {
        private static int person_quantity = 0;
        private int _id = ++person_quantity;
        public int ID { get { return _id; } }
        public string Name { get; set; }
    }
}

这是我正在寻找的行为:

  • ViewModel 中的 ChosenOne 成为选中复选框的任何 Person
  • 当一个复选框被选中时,所有其他的都被取消选中
  • 如果未选中任何复选框,则将 ChosenOne 设置为 null

基本上,这与我将其放入 DataGrid (XAML) 中的行为相同:

SelectedItem="{Binding ChosenOne, Mode=TwoWay}"

但在我的情况下,ChosenOne 不能成为数据网格的 SelectedItem,因为我需要 SelectedItem 来做其他事情,而且出于公司原因,我必须使用复选框。

我还没有找到如何用复选框模拟这个“SelectedItem”逻辑。

我知道我可以在我的 Person 类中放置一个“bool IsChosen”属性并将复选框绑定到它,但我真的宁愿避免这种情况。如果一切都失败了,这将是我的解决方案。

谢谢。

【问题讨论】:

    标签: wpf checkbox data-binding datagrid wpfdatagrid


    【解决方案1】:

    另一种方法是用支持检查的东西包装你的对象。 Source

    using System.ComponentModel;
    
    namespace Jarloo
    {
        public class CheckedListItem<T> : INotifyPropertyChanged
        {
            public event PropertyChangedEventHandler PropertyChanged;
    
            private bool isChecked;
            private T item;
    
            public CheckedListItem()
            {}
    
            public CheckedListItem(T item, bool isChecked=false)
            {
                this.item = item;
                this.isChecked = isChecked;
            }
    
            public T Item
            {
                get { return item; }
                set
                {
                    item = value;
                    if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs("Item"));
                }
            }
    
    
            public bool IsChecked
            {
                get { return isChecked; }
                set
                {
                    isChecked = value;
                    if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs("IsChecked"));
                }
            }
        }
    }
    

    【讨论】:

    • 谢谢。我正在考虑制作一个从 Person 继承的“CheckablePerson”类。诉诸这一点会让我感到难过,我更喜欢你的包装建议,因为它实际上是可重复使用的。干杯!
    • 很高兴你喜欢它。对我来说效果很好,希望它也对你有用。
    【解决方案2】:

    Person类中添加IsChecked属性并实现INotifyPropertyChanged接口:

    public class Person : INotifyPropertyChanged
    {
        private static int person_quantity = 0;
        private int _id = ++person_quantity;
        public int ID { get { return _id; } }
        public string Name { get; set; }
    
        private bool _isChecked;
        public bool IsChecked
        {
            get { return _isChecked; }
            set { _isChecked = value; NotifyPropertyChanged("IsChecked"); }
        }
    
        public event PropertyChangedEventHandler PropertyChanged;
        public void NotifyPropertyChanged(string propertyName)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }
    

    将 DataGridCheckBoxColumn 绑定到该属性:

    <DataGridCheckBoxColumn Header="Is Chosen" Binding="{Binding IsChecked, UpdateSourceTrigger=PropertyChanged}"/>
    

    然后您可以在视图模型类中处理逻辑。这应该确保一次只选择一个Person

    public class Viewmodel : INotifyPropertyChanged
    {
        public ObservableCollection<Person> People { get; private set; }
        private Person _chosenOne = null;
        public Person ChosenOne
        {
            get
            {
                if (_chosenOne == null) { return new Person { Name = "Does Not Exist" }; }
                else return _chosenOne;
            }
            set
            {
                _chosenOne = value;
                NotifyPropertyChanged("ChosenOne");
            }
        }
    
        public Viewmodel()
        {
            People = new ObservableCollection<Person>
            {
                new Person { Name = "John" },
                new Person { Name = "Marie" },
                new Person { Name = "Bob" },
                new Person { Name = "Sarah" }
            };
    
            foreach(Person p in People)
                p.PropertyChanged += P_PropertyChanged;
        }
    
        private bool handle = true;
        private void P_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if(handle && e.PropertyName == "IsChecked")
            {
                handle = false;
                //uncheck all other persons
                foreach (Person p in People)
                    if(p != sender)
                        p.IsChecked = false;
    
                ChosenOne = sender as Person;
    
                handle = true;
            }
        }
    
        public event PropertyChangedEventHandler PropertyChanged;
        public void NotifyPropertyChanged(string propertyName)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }
    

    如果您计划在运行时动态地将新的 Person 对象添加到 People 集合中,您还应该确保您也为这些对象处理 PropertyChanged 事件:

    public Viewmodel()
    {
        People = new ObservableCollection<Person>
                {
                    new Person { Name = "John" },
                    new Person { Name = "Marie" },
                    new Person { Name = "Bob" },
                    new Person { Name = "Sarah" }
                };
    
        foreach (Person p in People)
            p.PropertyChanged += P_PropertyChanged;
    
    
        People.CollectionChanged += (s, e) =>
        {
            if (e.NewItems != null)
            {
                foreach (object person in e.NewItems)
                {
                    (person as INotifyPropertyChanged).PropertyChanged
                        += new PropertyChangedEventHandler(P_PropertyChanged);
                }
            }
    
            if (e.OldItems != null)
            {
                foreach (object person in e.OldItems)
                {
                    (person as INotifyPropertyChanged).PropertyChanged
                        -= new PropertyChangedEventHandler(P_PropertyChanged);
                }
            }
        };
    
        People.Add(new Person() { Name = "New..." });
    }
    

    【讨论】:

    • 是的,我知道这个解决方案,但希望不必在 Person 类中放置 IsChecked 属性。我感觉我所追求的解决方案不存在,并且可能会诉诸于此。谢谢。
    • 如果您的问题已经解决,请记住接受答案:meta.stackexchange.com/questions/23138/…。在选中复选框的情况下,您需要提出一个事件,并且使用MVVM模式的方式是绑定到人类类的源属性,并根据我建议的填充属性。 span>
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-01-29
    • 2023-03-16
    • 2012-11-16
    • 2017-09-18
    • 2011-03-01
    • 1970-01-01
    • 2018-04-06
    相关资源
    最近更新 更多