使用 MVVM 通过拖放合并 WPF 工具包 DataGrid 中的两行
如何使用 MVVM 在 DataGrid 中通过拖放合并两行。
概述
此实现使用 MVVM 在 DataGrid
中通过拖放合并两行。合并逻辑在 ViewModel 中实现。
1. 引言
本文档展示了如何在 DataGrid
中实现行的拖放。详细说明在下文中有提供。与 DataGrid
的拖放功能相关的所有代码都在代码隐藏文件中。为了通过拖放合并 DataGrid
中的两行,本文尝试使用附加依赖属性将行合并逻辑放入 ViewModel 中,从而将 ViewModel 中的 DelegateCommand
绑定到视图中的依赖属性。实现使用了 WPF Toolkit DataGrid
[3] 和 WPF Model-View-ViewModel Toolkit [4]。
2. 通过拖放合并 DataGrid 中的两行
为了处理 DataGrid
中的拖放,DataGrid
的 XAML 中有一些属性绑定。
<WpfToolkit:DataGrid x:Name="listView" RowHeight="30"
ItemsSource="{Binding Path=Items}"
AutoGenerateColumns="False"
SelectionMode="Single"
AllowDrop="True"
MouseMove="OnMainGridMouseMove"
localControls:MouseMoveInGridSupport.MouseMoveCommand="{Binding MouseMoveCommand}"
localControls:MouseDropInGridSupport.MouseDropCommand="{Binding MouseDropCommand}"
将 AllowDrop
属性设置为 True
,从而启用 DataGrid
中的放置操作。代码隐藏处理程序 OnMainGridMouseMove
处理 MouseMove
事件,该事件检测网格中的鼠标移动,执行 DragDrop
,并将控制权传递给其他处理程序。这里没有与 ViewModel 和 Model 相关的业务逻辑或数据。
private void OnMainGridMouseMove(object sender, MouseEventArgs e)
{
if (e.LeftButton != MouseButtonState.Pressed)
{
e.Handled = true;
return;
}
var row = FindVisualParent<DataGridRow>(e.OriginalSource as FrameworkElement);
if ((row != null) && row.IsSelected)
{
var selectedItem = row.Item;
var finalDropEffect = DragDrop.DoDragDrop(row, selectedItem, DragDropEffects.Move);
if (finalDropEffect == DragDropEffects.Move)
{
e.Handled = false;
}
}
}
在 DataGrid
的 XAML 中有两个附加依赖属性。一个是 MouseMoveInGridSupport
public class MouseMoveInGridSupport : ItemSupportBase
{
public static ICommand GetMouseMoveCommand(DependencyObject obj)
{
return (ICommand)obj.GetValue(MouseMoveCommandProperty);
}
public static void SetMouseMoveCommand(DependencyObject obj, ICommand value)
{
obj.SetValue(MouseMoveCommandProperty, value);
}
public static readonly DependencyProperty MouseMoveCommandProperty =
DependencyProperty.RegisterAttached("MouseMoveCommand",
typeof(ICommand), typeof(MouseMoveInGridSupport),
new UIPropertyMetadata(null, new PropertyChangedCallback(OnCommandChanged)));
private static void OnCommandChanged(DependencyObject sender,
DependencyPropertyChangedEventArgs e)
{
if (e.OldValue != null)
((ItemsControl)sender).MouseMove -= new MouseEventHandler(OnMouseMoveClick);
if (e.NewValue != null)
((ItemsControl)sender).MouseMove += new MouseEventHandler(OnMouseMoveClick);
}
private static void OnMouseMoveClick(object sender, MouseEventArgs e)
{
DependencyObject source = (DependencyObject)e.OriginalSource;
var row = TryFindParent<DataGridRow>(source);
var item = row.Item;
if (item == null) return;
var command = GetMouseMoveCommand((DependencyObject)sender);
if (command != null)
{
if (command.CanExecute(item))
command.Execute(item);
}
}
}
MouseMoveInGridSupport
检测 DataGrid
中发生的 MouseMove
事件,找到相关的 DataGridRow
,并将它的 DataItem
传递给 ViewModel 中的 DelegateCommand
“MouseMoveCommand
”。
第二个是 MouseDropInGridSupport
public class MouseDropInGridSupport : ItemSupportBase
{
public static ICommand GetMouseDropCommand(DependencyObject obj)
{
return (ICommand)obj.GetValue(MouseDropCommandProperty);
}
public static void SetMouseDropCommand(DependencyObject obj, ICommand value)
{
obj.SetValue(MouseDropCommandProperty, value);
}
public static readonly DependencyProperty MouseDropCommandProperty =
DependencyProperty.RegisterAttached("MouseDropCommand",
typeof(ICommand), typeof(MouseDropInGridSupport),
new UIPropertyMetadata(null, new PropertyChangedCallback(OnMouseDropCommandChanged)));
private static void OnMouseDropCommandChanged(DependencyObject sender,
DependencyPropertyChangedEventArgs e)
{
if (e.OldValue != null)
((ItemsControl)sender).Drop -= new DragEventHandler(OnDropClick);
if (e.NewValue != null)
((ItemsControl)sender).Drop += new DragEventHandler(OnDropClick);
}
private static void OnDropClick(object sender, DragEventArgs e)
{
DependencyObject source = (DependencyObject)e.OriginalSource;
var row = TryFindParent<DataGridRow>(source);
var item = row.Item;
if (row == null) return;
var command = GetMouseDropCommand((DependencyObject)sender);
if (command != null)
{
if (command.CanExecute(item))
command.Execute(item);
}
}
}
MouseDropInGridSupport
检测 DataGrid
中发生的 Drop 事件,找到相关的 DataGridRow
,并将它的 DataItem
传递给 ViewModel 中的 DelegateCommand
“MouseDropCommand
”。
合并两行的逻辑在 ViewModel 中
public class MainViewModel : ViewModelBase
{
#region Fields
private Item dragItem;
private Item dropItem;
private DelegateCommand exitCommand;
public DelegateCommand<Item> MouseMoveCommand { get; private set; }
public DelegateCommand<Item> MouseDropCommand { get; private set; }
#endregion
#region Constructor
public MainViewModel()
{
_items = DataAccess.DataAccess.LoadItems();
MouseMoveCommand = new DelegateCommand<Item>(MouseMove, CanMouseMove);
MouseDropCommand = new DelegateCommand<Item>(MouseDrop, CanMouseDrop);
}
#endregion
#region Properties
IList<Item> _items;
public IList<Item> Items
{
get { return _items; }
set
{
_items = value;
base.OnPropertyChanged("Items");
}
}
#endregion
#region Commands
public ICommand ExitCommand
{
get
{
if (exitCommand == null)
{
exitCommand = new DelegateCommand(Exit);
}
return exitCommand;
}
}
#endregion
#region Methods
private void Exit()
{
Application.Current.Shutdown();
}
private void MouseMove(Item item)
{
IList<Item> items = new List<Item>(_items);
dragItem = item;
if (dropItem != null && dragItem != null)
{
Item newItem = new Item();
newItem.Name = "Merge row: " + dragItem.Name + " " + dropItem.Name;
newItem.Data = (dropItem.Data + dragItem.Data) + 100;
newItem.Id = (dropItem.Id + dragItem.Id) + 100;
items.Remove(dragItem);
items.Remove(dropItem);
items.Add(newItem);
Items = items;
}
}
private bool CanMouseMove(Item item)
{
return true;
}
private void MouseDrop(Item item)
{
dropItem = item;
}
private bool CanMouseDrop(Item item)
{
return true;
}
#endregion
}
DelegateCommand
“MouseDropCommand
” 获取放置的项目。DelegateCommand
“MouseDropCommand
” 获取移动的项目,然后合并放置和移动的项目。
3. 结论
通过拖放合并网格中的两行是在 MVVM 中实现的。它使用附加依赖属性和 DelegateCommand
将所有合并逻辑放入 ViewModel 中。
4. 参考资料
- 常用的 DataGrid 插件:http://code.msdn.microsoft.com/Common-DataGrid-Add-Ons-4f64fcee
- MVVM 和 WPF DataGrid:MVVM_DataGrid.aspx
- WPF Toolkit DataGrid:http://wpf.codeplex.com/releases/view/40535
- WPF Model-View-ViewModel Toolkit:http://wpf.codeplex.com/wikipage?title=WPF%20Model-View-ViewModel%20Toolkit