UWP FrameworkElement的DataContextChanged事件中如何获取旧DataContext值
解决UWP中DataContextChanged事件获取旧值的问题
这个坑我之前踩过!UWP里直接订阅DataContextChanged事件的话,确实只有NewValue能用,不像WPF那样事件参数里直接给旧值。不过要拿到旧值处理(比如移除INotifyPropertyChanged的事件订阅),还是有几种靠谱的办法:
方案1:自定义控件中重写OnDataContextChanged(最推荐)
如果你是在写自定义控件,直接重写控件基类的OnDataContextChanged方法就完事了!这个方法的参数是DependencyPropertyChangedEventArgs,里面同时包含OldValue和NewValue,完全满足需求:
public class MyCustomControl : Control { private INotifyPropertyChanged _lastViewModel; protected override void OnDataContextChanged(DependencyPropertyChangedEventArgs e) { // 先移除旧ViewModel的事件订阅 if (_lastViewModel != null) { _lastViewModel.PropertyChanged -= ViewModel_PropertyChanged; } // 别忘了调用基类的实现 base.OnDataContextChanged(e); // 订阅新ViewModel的事件 _lastViewModel = e.NewValue as INotifyPropertyChanged; if (_lastViewModel != null) { _lastViewModel.PropertyChanged += ViewModel_PropertyChanged; } } private void ViewModel_PropertyChanged(object sender, PropertyChangedEventArgs e) { // 这里写你的属性变更处理逻辑 if (e.PropertyName == nameof(MyViewModel.SomeProperty)) { // 更新UI之类的操作 } } }
方案2:手动存储旧值(适用于现有控件)
如果是用的系统自带控件(比如Grid、TextBlock)没法重写方法,那就在代码里自己维护一个变量存旧的DataContext:
private object _oldDataContext; public MainPage() { this.InitializeComponent(); // 订阅DataContextChanged事件 MyGrid.DataContextChanged += MyGrid_DataContextChanged; } private void MyGrid_DataContextChanged(FrameworkElement sender, DataContextChangedEventArgs args) { // 处理旧值:移除事件订阅 if (_oldDataContext is INotifyPropertyChanged oldVm) { oldVm.PropertyChanged -= Vm_PropertyChanged; } // 处理新值:添加事件订阅 if (args.NewValue is INotifyPropertyChanged newVm) { newVm.PropertyChanged += Vm_PropertyChanged; } // 更新旧值存储 _oldDataContext = args.NewValue; } private void Vm_PropertyChanged(object sender, PropertyChangedEventArgs e) { // 你的业务逻辑 }
方案3:用附加属性封装跟踪逻辑(复用性高)
如果多个控件都需要这个功能,写个附加属性封装起来会更优雅,不用每个控件都重复写存储逻辑:
public static class DataContextTracker { // 附加属性:存储旧的DataContext public static readonly DependencyProperty OldDataContextProperty = DependencyProperty.RegisterAttached( "OldDataContext", typeof(object), typeof(DataContextTracker), new PropertyMetadata(null)); public static object GetOldDataContext(DependencyObject obj) { return obj.GetValue(OldDataContextProperty); } public static void SetOldDataContext(DependencyObject obj, object value) { obj.SetValue(OldDataContextProperty, value); } // 对外暴露的跟踪方法 public static void EnableTracking(UIElement element) { element.DataContextChanged += Element_DataContextChanged; } private static void Element_DataContextChanged(FrameworkElement sender, DataContextChangedEventArgs args) { var oldContext = GetOldDataContext(sender); // 移除旧上下文的事件 if (oldContext is INotifyPropertyChanged oldVm) { oldVm.PropertyChanged -= Vm_PropertyChanged; } // 订阅新上下文的事件 if (args.NewValue is INotifyPropertyChanged newVm) { newVm.PropertyChanged += Vm_PropertyChanged; } // 更新附加属性里的旧值 SetOldDataContext(sender, args.NewValue); } private static void Vm_PropertyChanged(object sender, PropertyChangedEventArgs e) { // 这里可以根据需求扩展,比如通过附加属性传递事件处理逻辑 // 或者留空让调用者自己处理 } }
使用的时候只需要在页面初始化时调用:
DataContextTracker.EnableTracking(MyTextBlock);
内容的提问来源于stack exchange,提问作者Alex Marshall
相关产品推荐
相关产品推荐

