带有 DataGrid WPF 的复选框
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/17114603/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Checkbox with DataGrid WPF
提问by Rajat Suneja
I am trying to create a DataGrid in WPF 4.0 using MVVM...
我正在尝试使用 MVVM 在 WPF 4.0 中创建一个 DataGrid ...
Features required -
所需功能 -
- Muti - Select rows using a checkbox (single click)
- A select all checkbox to check all the checkboxes in the datagrid
- Muti - 使用复选框选择行(单击)
- 全选复选框以选中数据网格中的所有复选框
Something like this -
像这样的东西——
It has been 2 days and i am not able to figure out how to solve the problem effectively..
已经 2 天了,我无法弄清楚如何有效地解决问题..
A working example is what I need now ASAP..
一个工作示例是我现在需要的尽快..
I'll highly appreciate if someone has a working solution to share with me...
如果有人有可行的解决方案与我分享,我将不胜感激...
N please don't tell me to google this thing because none of the things worked out for me...
N 请不要告诉我用谷歌搜索这件事,因为所有事情都不适合我......
UPDATE -
更新 -
- I am using AutoGeneration of Columns
- I don't want to add "IsSelected" or any of such property in my MODEL..
- I am just facing 2 problems -
- 我正在使用自动生成列
- 我不想在我的模型中添加“IsSelected”或任何此类属性。
- 我只是面临两个问题 -
Firstly, "Select all" Feature i.e. checking all checkboxes on the checkbox click of the one present in the column header...(I am able to select and unselect the datagrid but not able to tick/untick the checkboxes)
首先,“全选”功能,即检查列标题中存在的复选框上的所有复选框...(我可以选择和取消选择数据网格,但不能勾选/取消勾选复选框)
Secondly, Multiple selection on mouse click without holding Ctrl Key..
其次,在不按住 Ctrl 键的情况下单击鼠标进行多项选择。
回答by Rachel
When you're working with MVVM, you have to be aware of what is considered data and what is strictly UI.
当您使用 MVVM 时,您必须了解什么是数据,什么是严格的 UI。
Is your SelectedItems
going to be part of your data, or only your UI?
您SelectedItems
是要成为数据的一部分,还是只是您的用户界面?
If it's part of your data, you really should have an IsSelected
property on your data model, even if that means extending the data class to include an IsSelected
property, or creating a wrapper class that only contains bool IsSelected
and object MyDataItem
. The first option is probably preferred, since you could keep AutoGenerateColumns="True"
, and it makes the column bindings simpler.
如果它是您数据的一部分,那么您确实应该IsSelected
在数据模型上拥有一个属性,即使这意味着扩展数据类以包含一个IsSelected
属性,或者创建一个仅包含bool IsSelected
和的包装类object MyDataItem
。第一个选项可能是首选,因为您可以保留AutoGenerateColumns="True"
,并且它使列绑定更简单。
Then you would just bind your DataGridRow.SelectedItem
to the IsSelected
property of the data item:
然后你只需将你DataGridRow.SelectedItem
的绑定到IsSelected
数据项的属性:
<Style TargetType="{x:Type DataGridRow}">
<Setter Property="IsSelected" Value="{Binding IsSelected}" />
</Style>
But if your SelectedItems
is only for the UI, or if you are breaking the MVVM pattern for some reason in this instance, than you can create the unbound CheckBox
and use some code behind to ensure the CheckBox
is correctly synchronized to the SelectedItem
.
但是,如果您SelectedItems
仅用于 UI,或者在这种情况下由于某种原因破坏了 MVVM 模式,那么您可以创建未绑定CheckBox
并使用一些代码来确保CheckBox
正确同步到SelectedItem
.
I did a quick sample app, and here is what my code looked like:
我做了一个快速示例应用程序,这是我的代码的样子:
First off, I just added the unbound CheckBox
column to the column list using a DataGridTemplateColumn
. This will get added before the AutoGenerateColumns
list of columns.
首先,我只是CheckBox
使用DataGridTemplateColumn
. 这将在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>
Second, I added a PreviewMouseDown
event to the CheckBox
to make it set the IsSelected
property of the row.
其次,我向 中添加了一个PreviewMouseDown
事件以CheckBox
使其设置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;
}
It needs to navigate the VisualTree
to find the DataGridRow
associated with the clicked CheckBox
to select it, and to make life easier I am using some custom VisualTreeHelpers that I have on my blogto find the DataGridRow
. You can use the same code, or you can create your own method for searching the VisualTree
.
它需要导航VisualTree
以找到DataGridRow
与单击的相关联的CheckBox
以选择它,并且为了让生活更轻松,我使用我博客上的一些自定义VisualTreeHelpers来查找DataGridRow
. 您可以使用相同的代码,也可以创建自己的方法来搜索VisualTree
.
And last of all, if the user clicks on anywhere other than the CheckBox
, we want to disable the default DataGrid
selection event. This ensures that the IsSelected
value will only change when you click on the CheckBox
.
最后,如果用户单击 以外的任何地方CheckBox
,我们希望禁用默认DataGrid
选择事件。这可确保该IsSelected
值仅在您单击 时才会更改CheckBox
。
There are multiple ways of doing this that will disable the selection at different levels, but to make life simple I just disabled the DataGrid.PreviewMouseLeftButtonDown
event if the user didn't click on the CheckBox
.
有多种方法可以禁用不同级别的选择,但为了简单起见,DataGrid.PreviewMouseLeftButtonDown
如果用户没有单击CheckBox
.
private void TestDataGrid_PreviewMouseLeftButtonDown_1(object sender, MouseButtonEventArgs e)
{
var chk = VisualTreeHelpers.FindAncestor<CheckBox>((DependencyObject)e.OriginalSource, "TestCheckBox");
if (chk == null)
e.Handled = true;
}
I using my custom VisualTreeHelpersagain to navigate the visual tree and find out if the CheckBox was clicked on, and cancelling the event if the user clicked on anywhere other than the CheckBox
.
我再次使用我的自定义VisualTreeHelpers来导航可视化树并找出是否单击了 CheckBox,如果用户单击了CheckBox
.
As for your 2nd request of adding a CheckBox
to SelectAll
or UnselectAll
items, this would once again be dependent on if your selection is part of the UI or the data.
至于您添加CheckBox
toSelectAll
或UnselectAll
items 的第二个请求,这将再次取决于您的选择是 UI 还是数据的一部分。
If it's part of the UI, simply add a CheckBox
to the DataGridTemplateColumn.HeaderTemplate
, and when it's clicked, loop through the DataGrid.Rows
, find the CheckBox
in the first column, and check or uncheck it.
如果它是 UI 的一部分,只需将 a 添加CheckBox
到DataGridTemplateColumn.HeaderTemplate
,当它被单击时,循环遍历DataGrid.Rows
,CheckBox
在第一列中找到,然后选中或取消选中它。
If it's part of the data you could still do the same thing (only set the bound value in the DataGrid.Items
instead of the CheckBox.IsChecked
from the DataGrid.Rows
), or you could do as Adolfo Perez suggested, and bind it to a property on the ViewModel
.
如果它是数据的一部分,您仍然可以做同样的事情(只在 中设置绑定值DataGrid.Items
而不是CheckBox.IsChecked
从 中设置DataGrid.Rows
),或者您可以按照Adolfo Perez 建议的那样做,并将其绑定到ViewModel
.
回答by Adolfo Perez
For an MVVM Solution you could try this:
对于 MVVM 解决方案,您可以尝试以下操作:
<StackPanel>
<DataGrid ItemsSource="{Binding Path=TestItems}" AutoGenerateColumns="False" Name="MyDataGrid"
CanUserAddRows="False">
<DataGrid.Columns>
<DataGridCheckBoxColumn Binding="{Binding IsSelected}" Width="50" >
<DataGridCheckBoxColumn.HeaderTemplate>
<DataTemplate x:Name="dtAllChkBx">
<CheckBox Name="cbxAll" Content="All" IsChecked="{Binding Path=DataContext.AllSelected,RelativeSource={RelativeSource AncestorType=DataGrid}}"/>
</DataTemplate>
</DataGridCheckBoxColumn.HeaderTemplate>
</DataGridCheckBoxColumn>
<DataGridTemplateColumn Header="Name" Width="SizeToCells" IsReadOnly="True">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</StackPanel>
In your ViewModel
:
在您的ViewModel
:
private void PopulateTestItems()
{
TestItems = new ObservableCollection<TestItem>();
for (int i = 0; i < 5; i++)
{
TestItem ti = new TestItem();
ti.Name = "TestItem" + i;
ti.IsSelected = true;
TestItems.Add(ti);
}
}
private bool _AllSelected;
public bool AllSelected
{
get { return _AllSelected; }
set
{
_AllSelected = value;
TestItems.ToList().ForEach(x => x.IsSelected = value);
NotifyPropertyChanged(m => m.AllSelected);
}
}
private ObservableCollection<TestItem> _TestItems;
public ObservableCollection<TestItem> TestItems
{
get { return _TestItems; }
set
{
_TestItems = value;
NotifyPropertyChanged(m => m.TestItems);
}
}
And finally the sample Model class:
最后是示例模型类:
public class TestItem : ModelBase<TestItem>
{
private string _Name;
public string Name
{
get { return _Name; }
set
{
_Name = value;
NotifyPropertyChanged(m => m.Name);
}
}
private bool _IsSelected;
public bool IsSelected
{
get { return _IsSelected; }
set
{
_IsSelected = value;
NotifyPropertyChanged(m => m.IsSelected);
}
}
}
Most of the code above should be self-explanatory but if you have any question let me know
上面的大部分代码应该是不言自明的,但如果您有任何问题,请告诉我
回答by srsyogesh
Your view can be something like
你的观点可能是这样的
<DataGrid Name="SomeDataGrid" Grid.Row="0" ItemsSource="{Binding Path=SomeCollection}">
<DataGrid.Columns>
<DataGridTemplateColumn>
<DataGridTemplateColumn.HeaderTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding
RelativeSource={RelativeSource AncestorType={x:Type DataGrid}},
Path=DataContext.AllItemsAreChecked}" />
</DataTemplate>
</DataGridTemplateColumn.HeaderTemplate>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate DataType="{x:Type local:SomeType}">
<CheckBox Focusable="False" IsChecked="{Binding Path=IsSelected, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
HorizontalAlignment="Left" VerticalAlignment="Center"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="RandomNumber" Width="160">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate DataType="{x:Type local:SomeType}">
<TextBlock Text="{Binding Path=RandomNumber}" TextWrapping="Wrap" HorizontalAlignment="Left" VerticalAlignment="Center"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="Date" Width="160">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate DataType="{x:Type local:SomeType}">
<TextBlock Text="{Binding Path=Date}" TextWrapping="Wrap" HorizontalAlignment="Left" VerticalAlignment="Center"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="Time" Width="50">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate DataType="{x:Type local:SomeType}">
<TextBlock Text="{Binding Time}" HorizontalAlignment="Left" VerticalAlignment="Center"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
And in viewmodel SomeCollection binding property is an observablecollection sometype contains properties like IsSelected , RandomNumber ,Date , Time
在视图模型中 SomeCollection 绑定属性是一个 observablecollection sometype 包含诸如 IsSelected 、 RandomNumber 、Date 、 Time 之类的属性
for eg:
例如:
class ViewModel
{
public ObservableCollection<SomeType> SomeCollection{get;set;}
}
class SomeType
{
public string Date {get;set;}
public string Time {get;set;}
public string RandomNumber {get;set;}
public bool IsSelected {get;set;}
}