如何为WPF DataGrid每行添加带参数的MouseDoubleClick命令触发?
问题
我在XAML中有一个DataGrid,想要给每个DataGridRow添加MouseDoubleClick事件,调用带参数的命令。但遇到两个限制:
- 无法用EventSetter绑定命令(因为需要传递参数);
- 不能用代码后置:一是用了ViewModel定位器,强转DataContext不够优雅;二是目标方法是异步方法,而事件Handler要求返回
void而非Task。
原XAML代码
<DataGrid AutoGenerateColumns="False" Grid.Row="2" IsReadOnly="True" ItemsSource="{Binding Entities, Converter={x:Static convertors:FileEntityEnumerableToFileDataGridEntityEnumerableConvertor.Instance}}" Name="ViewerDataGrid" SelectedItem="{Binding SelectedItem}" Visibility="{Binding IsDataGridVisible, Converter={x:Static materialDesign:BooleanToVisibilityConverter.HiddenInstance}}"> <DataGrid.Resources> <Style BasedOn="{StaticResource MaterialDesignDataGridColumnHeader}" TargetType="DataGridColumnHeader"> <Setter Property="Background" Value="{Binding ElementName=CloseButton, Path=Background}" /> </Style> <Style BasedOn="{StaticResource MaterialDesignDataGridCell}" TargetType="DataGridCell"> <Style.Triggers> <Trigger Property="IsSelected" Value="True"> <Setter Property="BorderBrush" Value="Transparent" /> </Trigger> </Style.Triggers> </Style> </DataGrid.Resources> <DataGrid.Columns> <DataGridTextColumn Binding="{Binding Name}" Header="Name" /> <DataGridTextColumn Binding="{Binding Size}" Header="Size" /> <DataGridTextColumn Binding="{Binding LastChanging}" Header="Last changing" /> <DataGridTextColumn Binding="{Binding CreatedAt}" Header="Created at" /> </DataGrid.Columns> <DataGrid.RowStyle> <Style BasedOn="{StaticResource {x:Type DataGridRow}}" TargetType="{x:Type DataGridRow}"> <Setter Property="i:Interaction.Triggers"> <Setter.Value> <i:Interaction.Triggers> <i:EventTrigger EventName="MouseDoubleClick"> <i:InvokeCommandAction Command="{Binding DataContext.ResetDirectoryContentCommand, RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}}" CommandParameter="{Binding}" /> </i:EventTrigger> </i:Interaction.Triggers> </Setter.Value> </Setter> </Style> </DataGrid.RowStyle> </DataGrid>
尝试过的无效方式
<DataGrid.RowStyle> <Style BasedOn="{StaticResource {x:Type DataGridRow}}" TargetType="{x:Type DataGridRow}"> <EventSetter Event="MouseDoubleClick" Handler="{Binding DataContext.ResetDirectoryContentCommand, RelativeSource={RelativeSource AncestorType=DataGrid}}" /> </Style> </DataGrid.RowStyle>
解决方案
方案1:使用Microsoft.Xaml.Behaviors(Blend交互行为)
你原XAML中的写法方向是对的,只需补充异步命令的实现即可:
- 引入NuGet包:安装
Microsoft.Xaml.Behaviors.Wpf(WPF项目),确保XAML头部已声明命名空间:xmlns:i="http://schemas.microsoft.com/xaml/behaviors" - 实现异步命令:封装支持异步逻辑的
ICommand,避免UI阻塞并处理重复点击:public class AsyncRelayCommand<T> : ICommand { private readonly Func<T, Task> _execute; private readonly Predicate<T> _canExecute; private bool _isExecuting; public event EventHandler CanExecuteChanged; public AsyncRelayCommand(Func<T, Task> execute, Predicate<T> canExecute = null) { _execute = execute ?? throw new ArgumentNullException(nameof(execute)); _canExecute = canExecute; } public bool CanExecute(object parameter) { return !_isExecuting && (_canExecute?.Invoke((T)parameter) ?? true); } public async void Execute(object parameter) { if (!CanExecute(parameter)) return; _isExecuting = true; try { await _execute((T)parameter); } finally { _isExecuting = false; RaiseCanExecuteChanged(); } } public void RaiseCanExecuteChanged() { CanExecuteChanged?.Invoke(this, EventArgs.Empty); } } - ViewModel中定义命令:
public AsyncRelayCommand<YourEntityType> ResetDirectoryContentCommand { get; } public YourViewModel() { ResetDirectoryContentCommand = new AsyncRelayCommand<YourEntityType>(ExecuteResetDirectoryContent); } private async Task ExecuteResetDirectoryContent(YourEntityType entity) { // 你的异步业务逻辑 } - 保留原XAML的RowStyle:你的写法已经正确绑定了命令和当前行的实体参数,只要命令是异步实现即可正常工作。
方案2:自定义附加属性(无额外依赖)
如果不想引入第三方包,可以自己写附加属性处理事件绑定:
- 实现附加属性类:
public static class DataGridRowExtensions { public static readonly DependencyProperty DoubleClickCommandProperty = DependencyProperty.RegisterAttached( "DoubleClickCommand", typeof(ICommand), typeof(DataGridRowExtensions), new PropertyMetadata(null, OnDoubleClickCommandChanged)); public static ICommand GetDoubleClickCommand(DependencyObject obj) { return (ICommand)obj.GetValue(DoubleClickCommandProperty); } public static void SetDoubleClickCommand(DependencyObject obj, ICommand value) { obj.SetValue(DoubleClickCommandProperty, value); } private static void OnDoubleClickCommandChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { if (d is DataGridRow row) { row.MouseDoubleClick -= Row_MouseDoubleClick; if (e.NewValue is ICommand command) { row.MouseDoubleClick += Row_MouseDoubleClick; } } } private static async void Row_MouseDoubleClick(object sender, MouseButtonEventArgs e) { if (sender is DataGridRow row && GetDoubleClickCommand(row) is AsyncRelayCommand<object> asyncCommand) { await ((Func<object, Task>)asyncCommand.Execute).Invoke(row.DataContext); } else if (sender is DataGridRow row && GetDoubleClickCommand(row) is ICommand syncCommand) { syncCommand.Execute(row.DataContext); } } } - XAML中使用附加属性:
先引入自定义类的命名空间(假设命名空间为YourNamespace):
再修改RowStyle:xmlns:local="clr-namespace:YourNamespace"<DataGrid.RowStyle> <Style BasedOn="{StaticResource {x:Type DataGridRow}}" TargetType="{x:Type DataGridRow}"> <Setter Property="local:DataGridRowExtensions.DoubleClickCommand" Value="{Binding DataContext.ResetDirectoryContentCommand, RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}}" /> </Style> </DataGrid.RowStyle>
内容的提问来源于stack exchange,提问作者TheMakarik
相关产品推荐
相关产品推荐

