使用 MVVM 模式在 WPF 工具包 DataGrid 中检索行标题和单元格
本文档展示了基于数据分组的网格中的行标题实现,以及从基于单元格的网格中检索单元格信息的方法。
1. 引言
本文档展示了基于数据分组的网格中的行标题实现,以及从基于单元格的网格中检索单元格信息的方法。
上述网格中的项目包含两个分组。第一个分组包含值“行标题 1”和“行标题 2”。第二个分组包含值“分组 11”、“分组 12”、“分组 21”和“分组 22”。网格显示基于分组的两个行标题。该网格是基于单元格的。单击单元格时,将弹出一个与单元格信息相关的表单。这需要在单元格上单击鼠标时访问单元格信息。本文档展示了使用 WPF Toolkit DataGrid (http://wpf.codeplex.com/releases/view/40535) 和 MVVM 模式 (http://wpf.codeplex.com/wikipage?title=WPF%20Model-View-ViewModel%20Toolkit) 实现这两个需求。
2. 网格中的行标题
WPF Toolkit 中的 DataGrid
提供了网格中的数据分组。GroupStyle
为行标题创建一个组样式。GroupItem
的模板将行标题描述为 TextBlock
,并相应地旋转它。
<GroupStyle>
<GroupStyle.ContainerStyle>
<Style TargetType="{x:Type GroupItem}">
<Setter Property="Margin" Value="0,0,0,0"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type GroupItem}">
<HeaderedContentControl BorderBrush="#FFA4B97F" BorderThickness="0,0,0,1">
<HeaderedContentControl.Header>
<TextBlock FontWeight="Bold" FontSize="12"
Text="{Binding Path=Name}" Margin="5,0,0,0">
<TextBlock.RenderTransform>
<RotateTransform Angle="270" />
</TextBlock.RenderTransform>
</TextBlock>
</HeaderedContentControl.Header>
<HeaderedContentControl.Content>
<ItemsPresenter/>
</HeaderedContentControl.Content>
</HeaderedContentControl>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<DataTrigger Binding="{Binding Name}" Value="Group 11">
<Setter Property="Template" Value="{StaticResource defaultGroup}" />
</DataTrigger>
<DataTrigger Binding="{Binding Name}" Value="Group 12">
<Setter Property="Template" Value="{StaticResource defaultGroup}" />
</DataTrigger>
<DataTrigger Binding="{Binding Name}" Value="Group 21">
<Setter Property="Template" Value="{StaticResource defaultGroup}" />
</DataTrigger>
<DataTrigger Binding="{Binding Name}" Value="Group 22">
<Setter Property="Template" Value="{StaticResource defaultGroup}" />
</DataTrigger>
</Style.Triggers>
</Style>
</GroupStyle.ContainerStyle>
</GroupStyle>
触发器根据行标题名称将标题样式切换为“defaultGroup
”。
行标题的多个层级使用 DataGrid
中 ItemsSource
中描述的多个数据分组,这是一个 CollectionViewSource
,具有 GroupDescriptions
来定义组。
<WpfToolkit:DataGrid x:Name="listView"
ItemsSource="{Binding Source={StaticResource src}}" … />
<CollectionViewSource x:Key='src' Source="{Binding Items}">
<CollectionViewSource.GroupDescriptions>
<PropertyGroupDescription PropertyName="Category1"/>
<PropertyGroupDescription PropertyName="Category2"/>
</CollectionViewSource.GroupDescriptions>
</CollectionViewSource>
3. 网格中的单元格检索
使用 DataGrid
将网格设置为基于单元格:SelectionUnit="Cell"
。通过处理事件 SelectedCellsChanged: ((Microsoft.Windows.Controls.DataGrid)sender).CurrentCell
,在代码后置中检索单元格信息。但 WPF MVVM 只将与视图相关的代码放在代码后置中。业务逻辑应该在 ViewModel 中。将事件处理程序移动到 ViewModel 的一种方法是使用附加依赖属性,它将 ViewModel 中的 Command 绑定到 View 中的依赖属性。
DataGridItemsHelper
类将 ICommand
注册为 DataGrid
的 MouseUp
事件的命令依赖属性,并将选定的 DataGridRow
和 DataGridCell
传递给 Command 处理程序。
public class DataGridItemsHelper
{
public static readonly DependencyProperty CommandProperty =
DependencyProperty.RegisterAttached("Command", typeof(ICommand),
typeof(DataGridItemsHelper),
new UIPropertyMetadata(null, new PropertyChangedCallback(OnCommandChanged)));
public static ICommand GetCommand(DependencyObject obj)
{
return (ICommand)obj.GetValue(CommandProperty);
}
public static void SetCommand(DependencyObject obj, ICommand value)
{
obj.SetValue(CommandProperty, value);
}
private static void OnCommandChanged(DependencyObject sender,
DependencyPropertyChangedEventArgs e)
{
if (e.OldValue != null)
((ItemsControl)sender).MouseUp -=
new MouseButtonEventHandler(OnMouseUpClick);
if (e.NewValue != null)
((ItemsControl)sender).MouseUp +=
new MouseButtonEventHandler(OnMouseUpClick);
}
private static void OnMouseUpClick(object sender, MouseButtonEventArgs e)
{
DependencyObject source = (DependencyObject)e.OriginalSource;
var row = TryFindParent<DataGridRow>(source);
var cell = TryFindParent<DataGridCell>(source);
if (cell == null || row == null ) return;
var command = GetCommand((DependencyObject)sender);
if (command != null)
{
if (command.CanExecute(new DataGridItem(row, cell)))
command.Execute(new DataGridItem(row, cell));
}
}
public static T TryFindParent<T>(DependencyObject child)
where T : DependencyObject
{
DependencyObject parentObject = GetParentObject(child);
if (parentObject == null) return null;
T parent = parentObject as T;
if (parent != null)
{
return parent;
}
else
{
return TryFindParent<T>(parentObject);
}
}
public static DependencyObject GetParentObject(DependencyObject child)
{
if (child == null) return null;
ContentElement contentElement = child as ContentElement;
if (contentElement != null)
{
DependencyObject parent = ContentOperations.GetParent(contentElement);
if (parent != null) return parent;
FrameworkContentElement fce = contentElement as FrameworkContentElement;
return fce != null ? fce.Parent : null;
}
return VisualTreeHelper.GetParent(child);
}
}
DataGrid
中的依赖属性“Command”将 ViewModel 中的命令属性“CellClickCommand
”绑定到 DataGrid
单元格中的 OnMouseUp
事件。
utils:DataGridItemsHelper.Command = "{Binding CellClickCommand}"
“CellClickCommand
”是 ViewModel 中的一个委托命令,它将所选单元格的 DataGridRow
和 DataGridCell
传递给命令处理程序。
public DelegateCommand<DataGridItem> CellClickCommand { get; private set; }
CellClickCommand = new DelegateCommand<DataGridItem>(
CellClickSelection, CanCellClickSelection);
private void CellClickSelection(DataGridItem item)
{
MessageBox.Show("Cell's column is " +
item.Gridcell.Column.Header.ToString() +
" it's ID is " +
((RowHeaderGrid.Models.Item)(item.Gridrow.Item)).ID);
}
4. 结论
此实现为基于数据分组的行标题以及使用 MVVM 模式在 WPF Toolkit DataGrid 中检索单元格提供了一种解决方案。