You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在UserControl自定义事件中获取DataGrid对应列表项?

问题:自定义UserControl事件中无法获取DataGrid列表项的DataContext

我有一个绑定了{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)。可以修改事件参数,把这个上下文传递出去:

  1. 修改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;
    }
}
  1. 在UserControl触发事件时,传入自身的DataContext:
private void FireTheEvent(){
    List<object> listObject = ...
    OnValueChanged(new ValueChangedEventArgs(listObject, this.DataContext));
}
  1. 在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.25 13:48:22