我正在尝试使用MVVM在WPF 4.0中创建一个DataGrid ...
所需功能 -
像这样 -
已经有2天了,我无法弄清楚如何有效地解决问题。
我现在需要一个工作范例。
如果有人有一个可行的解决方案与我分享,我将非常感激......
N请不要告诉我google这件事,因为没有任何事情能帮助我......
更新 -
首先,“选择所有”功能,即选中复选框上的所有复选框,单击列标题中存在的复选框...(我可以选择和取消选择数据网格但不能勾选/取消勾选复选框)< / p>
其次,鼠标点击多次选择而不按住Ctrl键..
答案 0 :(得分:19)
当您使用MVVM时,您必须了解什么是数据以及什么是严格的UI。
您的SelectedItems
是您的数据的一部分,还是只是您的用户界面?
如果它是数据的一部分,那么您的数据模型上应该有一个IsSelected
属性,即使这意味着扩展数据类以包含IsSelected
属性,或创建一个包装类仅包含bool IsSelected
和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
。
我做了一个快速的示例应用程序,这就是我的代码:
首先,我刚使用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>
其次,我向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;
}
需要导航VisualTree
以找到与点击的DataGridRow
相关联的CheckBox
以选择它,并且为了让生活更轻松我使用一些自定义VisualTreeHelpers that I have on my blog来找到DataGridRow
。您可以使用相同的代码,也可以创建自己的方法来搜索VisualTree
。
最后,如果用户点击CheckBox
以外的任何地方,我们希望停用默认的DataGrid
选择事件。这样可确保IsSelected
值仅在您点击CheckBox
时更改。
有多种方法可以在不同级别禁用选择,但为了简化生活我只是禁用DataGrid.PreviewMouseLeftButtonDown
事件,如果用户没有点击CheckBox
。< / p>
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还是数据的一部分。
如果它是用户界面的一部分,只需向CheckBox
添加DataGridTemplateColumn.HeaderTemplate
,点击它后,循环浏览DataGrid.Rows
,找到第一个CheckBox
列,并选中或取消选中它。
如果它是数据的一部分,您仍然可以执行相同的操作(仅在DataGrid.Items
中设置绑定值而不是CheckBox.IsChecked
中的DataGrid.Rows
,或者您可以执行此操作作为Adolfo Perez suggested,并将其绑定到ViewModel
上的属性。
答案 1 :(得分:3)
对于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>
在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);
}
}
最后是样本Model类:
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);
}
}
}
上面的大多数代码都应该是不言自明的,但如果您有任何疑问,请告诉我
答案 2 :(得分:2)
您的观点可能类似
<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>
在viewmodel中 SomeCollection绑定属性是一个observablecollection sometype包含IsSelected,RandomNumber,Date,Time
等属性例如:
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;}
}