MvvmLight : 如何取消订阅 "RaisePropertychanged" 事件
MvvmLight : How to unsubscribe "RaisePropertychanged" Event
我在使用 MvvmLight 的 WPF 应用程序中遇到问题。
我的申请:
- 我有一个带有菜单的 MainView,每个 MenuItem 都会打开一个新视图(每个 menuitem 不同)。
- 每个 MenuItem 都绑定到我的 MainViewModel 的 RelayCommand
- 在我的 MainViewModel 中,RelayCommand 只是对 MainView 执行
Messenger.Default.Send(this, "ShowMyView")
。
- 在我的 MainView 中,我注册了消息 "ShowMyView" 并且操作是:
var v = new MyView();
v.Owner = this;
v.ShowDialog();
Messenger.Default.Unregister(v);
此新视图 (MyView) 绑定到包含属性的视图模型 (MyViewModel)。
此视图的元素(文本框、单选按钮等)绑定到这些属性。
当我关闭 "MyView" 时,它已被处理,我回到主视图,但 "MyViewModel" 仍然存在。
问题:
当我第一次打开"MyView"时,绑定正常,如果"MyViewModel"设置了一个属性,调用了RaisePropertyChanged,那么这个属性的"get" =] 调用一次。
但是如果我打开和关闭我的视图 20 次,在第 20 次,当调用 RaisePropertyChanged 时,这个 属性 的 get 是调用 20 次!
问题:
那么当我关闭视图时如何取消订阅这些"RaisePropertyChanged"?
希望你清楚,抱歉我的英语不好。
编辑:代码
编辑二
请在我的 github 上找到出现问题的完整项目,而不是放一些代码。
https://github.com/damgot/MvvmLightProblemExample
当我运行应用程序时,MainWindow启动:
您可以在调试输出中看到:
Starting
Creating NewViewModel
然后,当我点击 NewView 菜单时,新视图打开 :
您可以在调试输出中看到:
Initialize NewViewModel and set MyBool to true
MyBool Set call + RaiseProperty
MyBool Get call
MyBool Get call
似乎没问题,因为我在 "MyBool"
上绑定了 2 个单选按钮
现在如果我 select "MyBool is false" 单选按钮 :
并在输出中:
MyBool Set call + RaiseProperty
MyBool Get call
MyBool Get call
还是不错的。
现在,如果我关闭 NewView,然后再次打开它,然后 select "MyBool is false" 单选按钮,输出为:
MyBool Set call + RaiseProperty
MyBool Get call
MyBool Get call
MyBool Get call
MyBool Get call
然后再一次,输出是:
MyBool Set call + RaiseProperty
MyBool Get call
MyBool Get call
MyBool Get call
MyBool Get call
MyBool Get call
MyBool Get call
再一次:
MyBool Set call + RaiseProperty
MyBool Get call
MyBool Get call
MyBool Get call
MyBool Get call
MyBool Get call
MyBool Get call
MyBool Get call
MyBool Get call
等等...
如你所见,"Get"调用我的属性,增加每组我重新打开视图
我找到了一个解决方案,但它很丑陋。
正如我在评论中所说,我的项目并不孤单,解决方案涉及我无法编辑的其他项目。所以我不能简单地压制 "ViewModelLocator".
所以为了解决我的问题,在我的 ViewModelLocator 中,在我的 ViewModels 属性中,而不是这样做:
public NewViewModel NewView
{
get
{
var vm = ServiceLocator.Current.GetInstance<NewViewModel>();
vm.Initialize();
return vm;
}
}
我这样做 :
public NewViewModel NewView
{
get
{
SimpleIoc.Default.Unregister<NewViewModel>();
SimpleIoc.Default.Register<NewViewModel>(true);
var vm = ServiceLocator.Current.GetInstance<NewViewModel>();
vm.Initialize();
return vm;
}
}
每次我调用 ViewModel(因此当我打开一个视图时),该模型先未注册,然后再注册。它强制重新创建一个 ViewModel,因此所有对 PropertyChanged 的订阅都被重置。
我不会将此解决方案设置为好的解决方案,因为我认为此解决方案不是很漂亮。但它有效并且使用的内存似乎没有增加。
我找到了另一个解决方案,比我发布的第一个解决方案更漂亮。
在我的 ViewModelLocator 中,我删除了所有视图模型属性。
而我创建了这个静态函数:
public static T GetViewModelInstance<T>(Window w)
{
var uniqueKey = System.Guid.NewGuid().ToString();
T VMInstance = ServiceLocator.Current.GetInstance<T>(uniqueKey);
w.Closed += (sender, args) => SimpleIoc.Default.Unregister(uniqueKey);
return VMInstance;
}
因此,如您所见,我没有 return ViewModel,而是它的一个新实例(带有 uniqueKey 令牌)。
我在调用视图 Closed 事件上注册以注销创建的 VM 实例。
我不再在 xaml 中定义 DataContext,而是 xaml.cs,在构造函数中,我将 DataContext 设置如下:
DataContext = ViewModelLocator.GetViewModelInstance<NewViewModel>(this);
而且效果很好。
我在使用 MvvmLight 的 WPF 应用程序中遇到问题。
我的申请:
- 我有一个带有菜单的 MainView,每个 MenuItem 都会打开一个新视图(每个 menuitem 不同)。
- 每个 MenuItem 都绑定到我的 MainViewModel 的 RelayCommand
- 在我的 MainViewModel 中,RelayCommand 只是对 MainView 执行
Messenger.Default.Send(this, "ShowMyView")
。 - 在我的 MainView 中,我注册了消息 "ShowMyView" 并且操作是:
var v = new MyView();
v.Owner = this;
v.ShowDialog();
Messenger.Default.Unregister(v);
此新视图 (MyView) 绑定到包含属性的视图模型 (MyViewModel)。 此视图的元素(文本框、单选按钮等)绑定到这些属性。
当我关闭 "MyView" 时,它已被处理,我回到主视图,但 "MyViewModel" 仍然存在。
问题:
当我第一次打开"MyView"时,绑定正常,如果"MyViewModel"设置了一个属性,调用了RaisePropertyChanged,那么这个属性的"get" =] 调用一次。
但是如果我打开和关闭我的视图 20 次,在第 20 次,当调用 RaisePropertyChanged 时,这个 属性 的 get 是调用 20 次!
问题:
那么当我关闭视图时如何取消订阅这些"RaisePropertyChanged"?
希望你清楚,抱歉我的英语不好。
编辑:代码
编辑二
请在我的 github 上找到出现问题的完整项目,而不是放一些代码。
https://github.com/damgot/MvvmLightProblemExample
当我运行应用程序时,MainWindow启动:
您可以在调试输出中看到:
Starting
Creating NewViewModel
然后,当我点击 NewView 菜单时,新视图打开 :
您可以在调试输出中看到:
Initialize NewViewModel and set MyBool to true
MyBool Set call + RaiseProperty
MyBool Get call
MyBool Get call
似乎没问题,因为我在 "MyBool"
上绑定了 2 个单选按钮现在如果我 select "MyBool is false" 单选按钮 :
并在输出中:
MyBool Set call + RaiseProperty
MyBool Get call
MyBool Get call
还是不错的。
现在,如果我关闭 NewView,然后再次打开它,然后 select "MyBool is false" 单选按钮,输出为:
MyBool Set call + RaiseProperty
MyBool Get call
MyBool Get call
MyBool Get call
MyBool Get call
然后再一次,输出是:
MyBool Set call + RaiseProperty
MyBool Get call
MyBool Get call
MyBool Get call
MyBool Get call
MyBool Get call
MyBool Get call
再一次:
MyBool Set call + RaiseProperty
MyBool Get call
MyBool Get call
MyBool Get call
MyBool Get call
MyBool Get call
MyBool Get call
MyBool Get call
MyBool Get call
等等...
如你所见,"Get"调用我的属性,增加每组我重新打开视图
我找到了一个解决方案,但它很丑陋。
正如我在评论中所说,我的项目并不孤单,解决方案涉及我无法编辑的其他项目。所以我不能简单地压制 "ViewModelLocator".
所以为了解决我的问题,在我的 ViewModelLocator 中,在我的 ViewModels 属性中,而不是这样做:
public NewViewModel NewView
{
get
{
var vm = ServiceLocator.Current.GetInstance<NewViewModel>();
vm.Initialize();
return vm;
}
}
我这样做 :
public NewViewModel NewView
{
get
{
SimpleIoc.Default.Unregister<NewViewModel>();
SimpleIoc.Default.Register<NewViewModel>(true);
var vm = ServiceLocator.Current.GetInstance<NewViewModel>();
vm.Initialize();
return vm;
}
}
每次我调用 ViewModel(因此当我打开一个视图时),该模型先未注册,然后再注册。它强制重新创建一个 ViewModel,因此所有对 PropertyChanged 的订阅都被重置。
我不会将此解决方案设置为好的解决方案,因为我认为此解决方案不是很漂亮。但它有效并且使用的内存似乎没有增加。
我找到了另一个解决方案,比我发布的第一个解决方案更漂亮。
在我的 ViewModelLocator 中,我删除了所有视图模型属性。 而我创建了这个静态函数:
public static T GetViewModelInstance<T>(Window w)
{
var uniqueKey = System.Guid.NewGuid().ToString();
T VMInstance = ServiceLocator.Current.GetInstance<T>(uniqueKey);
w.Closed += (sender, args) => SimpleIoc.Default.Unregister(uniqueKey);
return VMInstance;
}
因此,如您所见,我没有 return ViewModel,而是它的一个新实例(带有 uniqueKey 令牌)。 我在调用视图 Closed 事件上注册以注销创建的 VM 实例。
我不再在 xaml 中定义 DataContext,而是 xaml.cs,在构造函数中,我将 DataContext 设置如下:
DataContext = ViewModelLocator.GetViewModelInstance<NewViewModel>(this);
而且效果很好。