3

我搜索了一下,很惊讶我没有找到更简单的答案。我遵循一个简单的 MVVM 模型,并有一个视图模型(“parentVM”),它包含另一个视图模型(“childVM”)作为由公共属性(get/set)公开的字段。我遇到的问题是我试图在父的初始化程序中订阅子视图模型的 propertychanged 事件。但是当设置一个新的 childVM 时,事件处理程序就会丢失。

public parentVM : INotitifyPropertyChange{
    private _childVM = new childVM();

    public parentVM(){
        _childVM.PropertyChanged += someHandler;
    }


    public childVM {
         get{
             return _childVM;
         }
         set{
             _childVM = value;
             //EVENTHANDLERS ARE NOW GONE
         }
    }
    ....
}

有没有办法将现有的事件处理程序转移到新对象?

这是一个简化的例子。我知道我可以简单地移动这条线:

_childVM.PropertyChange += someHandler;

但这不适用于订阅该事件的外部对象。在这些情况下,您不太可能知道对象是什么以及处理程序是什么。

也许有一种解决方法,方法是在内部引用一个 List,从 += eventHandler 更改为更手动的 AddHander 方法,然后填充 List 以供将来参考。不知道在这一点上只是在考虑外卖。

PS如果示例中有语法错误,请原谅,我没有智能感知,这不是真正的代码......只是试图传达这一点。

4

3 回答 3

2

我会考虑根本不创建一个新实例childVM,而只是更新它的内部模型。

但是,假设您不希望这样做,下面的代码应该按照您的描述进行。请注意,它还从原始文件中删除了事件处理程序ChildVM(我在代码中使用了更标准的大写字母)。未能删除事件处理程序通常会导致内存泄漏。

class ParentVM
{
    private ChildVM _childVM = new ChildVM();

    public ParentVM()
    {
        _childVM.PropertyChanged += SomeHandler;
    }

    public ChildVM ChildVM
    {
        get
        {
            return _childVM;
        }
        set
        {
            foreach (var handler in _childVM.GetPropertyChangedHandlers())
            {
                _childVM.PropertyChanged -= handler;
                value.PropertyChanged += handler;
            }

            _childVM = value;
        }
    }

    private void SomeHandler(object sender, PropertyChangedEventArgs e)
    {
    }
}

不幸的是,您将在子视图模型类中需要这个额外的方法GetPropertyChangedHandlers,因为您不能GetInvocationList从定义事件的类型之外调用。这是代码ChildVM

class ChildVM : INotifyPropertyChanged
{
    public PropertyChangedEventHandler[] GetPropertyChangedHandlers()
    {
        return PropertyChanged.GetInvocationList().
               OfType<PropertyChangedEventHandler>().ToArray();
    }

    public event PropertyChangedEventHandler PropertyChanged;
}

回到替代解决方案:您确定有必要考虑订阅这些事件的外部代码吗?如果您正在执行简单的数据绑定,则应该在设置属性时引发该属性的PropertyChanged事件ChildVM,这应该可以解决您的问题,而无需所有这些额外的代码。

于 2013-04-02T17:46:50.267 回答
2

您是否考虑过迁移到 EventAggregator?

它们周围有很多,例如在 Prism 和 caliburn.micro 中。

基本思想是:有实体发布事件和实体订阅事件(即处理它们)。因此,您从直接订阅 (+=) 转移到更松散耦合的架构,因为您仅将事件发布到 EventAggregator 并且它处理应该调用这些事件的处理程序的位置(多播委托与 += 之前一样)

这真的很容易,您的代码中不再有这种依赖关系。

请查看以下链接

MSDN Prism 对 EventAggregator 的描述(非常好)

EventAggregator 的 caliburn.micro 描述

于 2013-04-02T17:55:24.660 回答
1

更好的答案:

在 ParentVM 上实施 INotifyPropertyChanged。更改 ChildVM 时引发事件。订阅 ChildVM 事件的外部对象可以订阅 ParentVMs 属性更改事件,并且可以在收到更改通知时更新 ChildVM 上的事件处理程序。

原始答案: 我会创建两种方法SubscribeChildVMEvents(ChildVM)UnsubscribeChildVMEvent(ChildVM)并在您的设置器中执行此操作:

set{
  UnsubscribeChildVMEvents(_childVM);
             _childVM = value;
  SubscribeChildVMEvents(_childVM);
             //EVENTHANDLERS ARE NOW NOT GONE
         }

从对象中删除事件处理程序是一种很好的做法。

于 2013-04-02T17:39:31.980 回答