WPF DataGrid:如何绑定对象以反映选中行的项目
WPF DataGrid : how to bind an object to reflect the item whose row is checked
我有一个填充了元素的数据网格,每个元素都有一个复选框。
我正在寻找一种方法,让我的 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>
和我的客服:
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 成为选中复选框的那个人
- 选中一个复选框后,所有其他复选框都未选中
- 如果未选中任何复选框,则将 ChosenOne 设置为空
基本上这与我将其放入 DataGrid (XAML) 时的行为相同:
SelectedItem="{Binding ChosenOne, Mode=TwoWay}"
但在我的例子中,ChosenOne 不能是数据网格的 SelectedItem,因为我需要 SelectedItem 用于其他用途,并且出于公司原因我必须使用复选框。
我还没有找到如何用复选框模拟这个 "SelectedItem" 逻辑。
我知道我可以在我的 Person class 中放置一个 "bool IsChosen" 属性 并将复选框绑定到它,但我真的宁愿避免这种情况。如果一切都失败了,这将是我的解决方案。
谢谢。
在Person
class中添加一个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}"/>
然后您可以处理视图模型中的逻辑 class。这应该确保一次只选择一个 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..." });
}
另一种方法是用支持检查的东西包装您的对象。
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"));
}
}
}
}
我有一个填充了元素的数据网格,每个元素都有一个复选框。
我正在寻找一种方法,让我的 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>
和我的客服:
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 成为选中复选框的那个人
- 选中一个复选框后,所有其他复选框都未选中
- 如果未选中任何复选框,则将 ChosenOne 设置为空
基本上这与我将其放入 DataGrid (XAML) 时的行为相同:
SelectedItem="{Binding ChosenOne, Mode=TwoWay}"
但在我的例子中,ChosenOne 不能是数据网格的 SelectedItem,因为我需要 SelectedItem 用于其他用途,并且出于公司原因我必须使用复选框。
我还没有找到如何用复选框模拟这个 "SelectedItem" 逻辑。
我知道我可以在我的 Person class 中放置一个 "bool IsChosen" 属性 并将复选框绑定到它,但我真的宁愿避免这种情况。如果一切都失败了,这将是我的解决方案。
谢谢。
在Person
class中添加一个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}"/>
然后您可以处理视图模型中的逻辑 class。这应该确保一次只选择一个 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..." });
}
另一种方法是用支持检查的东西包装您的对象。 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"));
}
}
}
}