如何在UserControl自定义事件中获取DataGrid对应列表项?
我有一个绑定了{Binding MyViewModel}的DataGrid,其中一列模板列包含删除列表项的Button,另一列模板列包含带有自定义ValueChanged事件的MyCustomUserControl。
在Button的Click事件中,我能正常获取DataGrid列表项并删除:
// MainWindow中的代码 private void DeleteItem_Click(object sender, RoutedEventArgs e){ ViewModelItem? obj = ((FrameworkElement)sender).DataContext as ViewModelItem; viewModel.ListItem.Remove(obj); }
但在UserControl的自定义事件中用同样方法时,获取到的obj为null:
// MainWindow中的代码 private void DoSomething_CustomEvent(object sender, ValueChangedEventArgs e){ // 此处obj为null ViewModelItem? obj = ((FrameworkElement)sender).DataContext as ViewModelItem; ... }
MyCustomUserControl的示例代码:
public partial class MyCustomUserControl : UserControl{ #region 自定义事件:列表添加新项时触发 public event EventHandler<ValueChangedEventArgs> ValueChanged; protected virtual void OnValueChanged(ValueChangedEventArgs e){ ValueChanged?.Invoke(this, e); // 触发事件 } public class ValueChangedEventArgs : EventArgs{ public object MyListObject { get; set; } public ValueChangedEventArgs(object MyListObject) { this.MyListObject = MyListObject; } } #endregion private readonly ObservableCollection<UserControlItem> UserControlListItem = new(); #region 构造函数 public MyCustomUserControl() { InitializeComponent(); // this.DataContext = this.viewModel; // 这种写法不好 // 改为从后台设置ItemsSource MyDataGrid.ItemsSource = UserControlListItem; } ... private void FireTheEvent(){ List<object> listObject = ... OnValueChanged(new ValueChangedEventArgs(listObject)); // 触发事件通知MainWindow } }
请问如何像Button点击事件那样获取对应的DataGrid列表项?
方法1:将UserControl的DataContext传入事件参数
因为MyCustomUserControl位于DataGrid的模板列中,它的DataContext默认继承自所在的DataGridRow(即对应的ViewModelItem)。可以修改事件参数,把这个上下文传递出去:
- 修改
ValueChangedEventArgs,新增存储父级DataContext的属性:
public class ValueChangedEventArgs : EventArgs{ public object MyListObject { get; set; } public object ParentDataContext { get; set; } // 新增属性 public ValueChangedEventArgs(object myListObject, object parentDataContext) { MyListObject = myListObject; ParentDataContext = parentDataContext; } }
- 在UserControl触发事件时,传入自身的
DataContext:
private void FireTheEvent(){ List<object> listObject = ... OnValueChanged(new ValueChangedEventArgs(listObject, this.DataContext)); }
- 在MainWindow的事件处理方法中直接获取:
private void DoSomething_CustomEvent(object sender, ValueChangedEventArgs e){ ViewModelItem? obj = e.ParentDataContext as ViewModelItem; // 此时obj可正确获取到对应列表项 ... }
方法2:通过VisualTreeHelper查找父级DataGridRow
如果不想修改事件参数,可在MainWindow中通过VisualTreeHelper向上遍历可视化树,找到UserControl所在的DataGridRow,再获取其DataContext:
private void DoSomething_CustomEvent(object sender, ValueChangedEventArgs e){ var userControl = sender as MyCustomUserControl; var dataGridRow = FindParent<DataGridRow>(userControl); if(dataGridRow != null){ ViewModelItem? obj = dataGridRow.DataContext as ViewModelItem; // 后续业务逻辑 } } // 通用父级控件查找方法 private T FindParent<T>(DependencyObject child) where T : DependencyObject { DependencyObject parent = VisualTreeHelper.GetParent(child); if(parent == null) return null; if(parent is T target) return target; return FindParent<T>(parent); }
方法3:确保UserControl未覆盖DataContext
检查MyCustomUserControl的XAML或后台代码,确认没有手动设置DataContext(比如你注释掉的this.DataContext = this.viewModel;)。只要UserControl未主动覆盖DataContext,它会自动继承父级DataGridRow的上下文,此时在MainWindow中直接使用((FrameworkElement)sender).DataContext即可获取到正确的ViewModelItem。
内容的提问来源于stack exchange,提问作者Polar

