当您使用 MVVM 时,您必须了解什么是数据,什么是严格的 UI。
您的SelectedItems 会成为您数据的一部分,还是只是您的用户界面?
如果它是您数据的一部分,那么您的数据模型上确实应该有一个 IsSelected 属性,即使这意味着扩展数据类以包含一个 IsSelected 属性,或者创建一个仅包含 @987654327 的包装类@ 和 object MyDataItem。第一个选项可能是首选,因为您可以保留AutoGenerateColumns="True",它使列绑定更简单。
然后您只需将您的DataGridRow.SelectedItem 绑定到数据项的IsSelected 属性:
<Style TargetType="{x:Type DataGridRow}">
<Setter Property="IsSelected" Value="{Binding IsSelected}" />
</Style>
但是如果您的SelectedItems 仅用于 UI,或者您在这种情况下由于某种原因破坏了 MVVM 模式,那么您可以创建未绑定的 CheckBox 并使用一些代码来确保 CheckBox已正确同步到SelectedItem。
我做了一个快速示例应用程序,我的代码如下所示:
首先,我刚刚使用DataGridTemplateColumn 将未绑定的CheckBox 列添加到列列表中。这将被添加到 AutoGenerateColumns 列列表之前。
<DataGrid x:Name="TestDataGrid" ItemsSource="{Binding Test}"
SelectionMode="Extended" CanUserAddRows="False"
PreviewMouseLeftButtonDown="TestDataGrid_PreviewMouseLeftButtonDown_1">
<DataGrid.Columns>
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<CheckBox x:Name="TestCheckBox"
PreviewMouseLeftButtonDown="CheckBox_PreviewMouseLeftButtonDown" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
其次,我在CheckBox 中添加了一个PreviewMouseDown 事件,以使其设置行的IsSelected 属性。
private void CheckBox_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
var chk = (CheckBox)sender;
var row = VisualTreeHelpers.FindAncestor<DataGridRow>(chk);
var newValue = !chk.IsChecked.GetValueOrDefault();
row.IsSelected = newValue;
chk.IsChecked = newValue;
// Mark event as handled so that the default
// DataGridPreviewMouseDown doesn't handle the event
e.Handled = true;
}
它需要导航VisualTree 以找到与单击的CheckBox 关联的DataGridRow 以选择它,并且为了让生活更轻松,我正在使用一些自定义VisualTreeHelpers that I have on my blog 来查找DataGridRow。您可以使用相同的代码,也可以创建自己的方法来搜索VisualTree。
最后,如果用户点击CheckBox 以外的任何地方,我们希望禁用默认的DataGrid 选择事件。这确保了IsSelected 的值只会在您单击CheckBox 时发生变化。
有多种方法可以禁用不同级别的选择,但为了简单起见,如果用户没有点击CheckBox,我只是禁用了DataGrid.PreviewMouseLeftButtonDown 事件。
private void TestDataGrid_PreviewMouseLeftButtonDown_1(object sender, MouseButtonEventArgs e)
{
var chk = VisualTreeHelpers.FindAncestor<CheckBox>((DependencyObject)e.OriginalSource, "TestCheckBox");
if (chk == null)
e.Handled = true;
}
我再次使用我的自定义 VisualTreeHelpers 来导航可视化树并确定是否单击了 CheckBox,如果用户单击了 CheckBox 以外的任何位置,则取消事件。
至于您将CheckBox 添加到SelectAll 或UnselectAll 项目的第二个请求,这将再次取决于您的选择是UI 还是数据的一部分。
如果它是 UI 的一部分,只需将 CheckBox 添加到 DataGridTemplateColumn.HeaderTemplate,然后单击它,循环遍历 DataGrid.Rows,在第一列中找到 CheckBox,然后选中或取消选中它。
如果它是数据的一部分,你仍然可以做同样的事情(只在DataGrid.Items 中设置绑定值,而不是DataGrid.Rows 中的CheckBox.IsChecked),或者你可以像Adolfo Perez suggested 那样做,并且将其绑定到ViewModel 上的属性。