有一些时候我们会有对DataGrid进行绑定的需求,我们知道在WPF中只有DependencyProperty才能进行绑定,而如下图
SelectedItems并不是依赖属性,所以按道理来说我们是不能对其进行绑定的,那么我们只能自己定义依赖属性或者附加属性来实现,这边我是用的附加属性来实现的。
首先上界面:
上面的DataGrid多选选中的项能实时体现到下面的DataGrid,并且点击按钮能选中上面成绩>85的学生,下面的DataGrid也能修改,在不用后置代码也就是不获取DataGrid这个对象的方式完成,那么我就想到用SelectedItems双向绑定了,那么我们现在就来完成它。
首先建一个附加属性的类:
public class BindingSelectItemsAttach
{
public static IList GetSelectItemsAttach(DependencyObject obj)
{
return (IList)obj.GetValue(SelectItemsAttachProperty);
}
public static void SetSelectItemsAttach(DependencyObject obj, IList value)
{
obj.SetValue(SelectItemsAttachProperty, value);
}
// Using a DependencyProperty as the backing store for SelectItemsAttach. This enables animation, styling, binding, etc...
public static readonly DependencyProperty SelectItemsAttachProperty =
DependencyProperty.RegisterAttached("SelectItemsAttach", typeof(IList), typeof(BindingSelectItemsAttach), new PropertyMetadata(null));
public static bool GetSelectedChange(DependencyObject obj)
{
return (bool)obj.GetValue(SelectedChangeProperty);
}
public static void SetSelectedChange(DependencyObject obj, bool value)
{
obj.SetValue(SelectedChangeProperty, value);
}
// Using a DependencyProperty as the backing store for SelectedChange. This enables animation, styling, binding, etc...
public static readonly DependencyProperty SelectedChangeProperty =
DependencyProperty.RegisterAttached("SelectedChange", typeof(bool), typeof(BindingSelectItemsAttach), new PropertyMetadata(false, SelectedChangedCallBack));
private static void SelectedChangedCallBack(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is MultiSelector)
{
MultiSelector multiSelector = (MultiSelector)d;
if ((bool)e.NewValue)
{
multiSelector.SelectionChanged += MultiSelector_SelectionChanged;
}
else
{
multiSelector.SelectionChanged -= MultiSelector_SelectionChanged;
}
}
else
{
throw new InvalidOperationException();
}
}
private static void MultiSelector_SelectionChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e)
{
MultiSelector multiSelector = (MultiSelector)sender;
SetSelectItemsAttach(multiSelector, multiSelector.SelectedItems);
System.Windows.Data.BindingExpression bind = multiSelector.GetBindingExpression(SelectItemsAttachProperty);
bind?.UpdateSource();
}
}
这里主要是定了了两个附加属性。SelectItemsAttach:用来绑定多选项,定义为IList类型,因为实际SelectItems就是IList类型的
第二个附加属性SelectedChange是一个开关,控制当前DataGrid是否要开启SelectItems绑定。为Bool类型,其回调函数SelectedChangedCallBack中判断若是值为True则添加SelectedChanged事件,在SelectedChanged事件中修改源数据,那么由界面目标到源数据的单向通道完成了。
当然如果我们最终目的是要双向的,虽然这个需求有点奇怪,我们要有一点额外操作。
我们需要在Model中加一个是否选中的属性。
public class StudentModel : INotifyPropertyChanged
{
private string _name;
public string Name
{
get { return _name; }
set { _name = value; }
}
private double _grade;
public double Grade
{
get { return _grade; }
set { _grade = value; }
}
private bool _isSelected;
public bool IsSelected
{
get { return _isSelected; }
set { _isSelected = value; RaisePropertyChanged("IsSelected"); }
}
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged(string porpertyName)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(porpertyName));
}
}
}
这边定义了一个Student的Model类。其中IsSelected项就是用来绑定的。
ViewModel:
public class StudentsViewModel : INotifyPropertyChanged
{
public StudentsViewModel()
{
SelectedCommand = new CommandBase();
SelectedCommand.ExecuteCommand = new Action<object>(SelectedClickCommand);
}
private IList _studentList;
public IList StudentList
{
get { return _studentList; }
set { _studentList = value; RaisePropertyChanged("StudentList"); }
}
private IList _selectedItems;
public IList SelectedItems
{
get
{
return _selectedItems;
}
set { _selectedItems = value; RaisePropertyChanged("SelectedItems"); }
}
public CommandBase SelectedCommand { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged(string porpertyName)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(porpertyName));
}
}
private void SelectedClickCommand(object obj)
{
foreach (var item in StudentList)
{
StudentModel student = (item as StudentModel);
if (student.Grade >= 85)
{
student.IsSelected = true;
}
}
}
}
其中包含了SelectedItems,也就是等下要在DataGrid上绑定那个附加属性SelectItemsAttach的项,还有一个StudentList总项,以及一个判断成绩的Command。
UI:
<Window x:Class="SelectedItems.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:SelectedItems"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="800" WindowStartupLocation="CenterScreen">
<Grid>
<DataGrid ItemsSource="{Binding StudentList}" local:BindingSelectItemsAttach.SelectItemsAttach="{Binding SelectedItems,Mode=OneWayToSource}" local:BindingSelectItemsAttach.SelectedChange="True" AutoGenerateColumns="False" CanUserAddRows="False">
<DataGrid.Columns>
<DataGridTextColumn Header="名字" Width="150" Binding="{Binding Name}"></DataGridTextColumn>
<DataGridTextColumn Header="成绩" Width="150" Binding="{Binding Grade}"></DataGridTextColumn>
</DataGrid.Columns>
<DataGrid.RowStyle>
<Style TargetType="{x:Type DataGridRow}">
<Setter Property="IsSelected" Value="{Binding IsSelected}"/>
</Style>
</DataGrid.RowStyle>
</DataGrid>
<StackPanel Orientation="Horizontal" VerticalAlignment="Bottom" Height="150" Margin="20,0,20,20">
<TextBlock Text="已选中的" VerticalAlignment="Center" Margin="10,0,0,0"/>
<DataGrid ItemsSource="{Binding SelectedItems}" CanUserAddRows="False" AutoGenerateColumns="False" DisplayMemberPath="Name" Width="100" Height="150" Margin="10,0,0,0">
<DataGrid.Columns>
<DataGridTextColumn Header="名字" Width="100" Binding="{Binding Name}"></DataGridTextColumn>
</DataGrid.Columns>
</DataGrid>
<Button Content="选中成绩大于85的" Command="{Binding SelectedCommand}" Width="120" Height="25" Margin="10,0,0,0"/>
</StackPanel>
</Grid>
</Window>
注意
这里是单向绑定 ,目标到源
源到目标在这
那么 一个SelectedItems的双向绑定就完成了