【发布时间】:2021-07-22 20:25:09
【问题描述】:
我有一个静态属性来跟踪我的视图模型中的属性变化:
public static class Global
{
public static int Warning
{
get { return _warning; }
set { _warning = value; OnStaticPropertyChanged(); }
}
private static int _warning;
}
然后,在视图模型中我有 CanExecute 命令(用于取消更改的按钮):
private bool Cancel_CanExecute(object parameter)
{
bool changes=false;
if (!string.IsNullOrEmpty(Name) && !string.IsNullOrEmpty(SurName))
{
changes= true;
}
if (changes)
{
Global.Warning = 1; //Changes are true, store this state
}
return changes;
}
当我的视图发生变化,并且用户想要切换视图时,我想向他们显示一个警告消息框。所以在我的主窗口视图模型中,我有一个更改视图的命令:
private void Open_view(object parameter)
{
if (Global.Warning != 0)
{
var msg = _windowservice.ShowMessage("Want to leave? Changes won't be saved.");
if (msg == true) //User clicked OK in MessageBox
{
Global.Warning = 0; // reset static property to default, and proceed with code
}
else
{
return; //Prevent opening new view
}
}
//Switch to new view - Calling this line before 'Global.Warning = 0;' doesn't help either
Open_view = new SomeViewModel();
//...
}
但是,当我确认 MessageBox 离开视图而不保存更改并将静态属性 Warning 重置为 0 时,CommandManager 仍然调用旧视图模型的 CanExecute 命令,所以我的 Warning 属性再次获得 1 的值。
我的视图都是在资源字典中定义了 DataTemplates 的 UserControls,我能设法解决这种行为的唯一方法是通过 UserControl 后面的代码,如下所示:
private void UserControl_Unloaded(object sender, System.Windows.RoutedEventArgs e)
{
this.DataContext = null; //
}
问题:如何在 MVVM 中正确处理这种情况? 底线是我想在我的视图中跟踪属性更改,同时仍然能够通知用户未保存的更改如果他想离开这个相同的观点。
编辑:不确定是否有帮助,但这也是我的命令实现:
public class MyCommand : ICommand
{
private readonly Action<object> _execute;
private readonly Predicate<object> _canExecute;
public MyCommand(Action<object> execute, Predicate<object> canExecute)
{
_execute = execute;
_canExecute = canExecute;
}
public bool CanExecute(object parameter)
{
if (_canExecute == null)
{
return true;
}
return _canExecute(parameter);
}
public void Execute(object parameter)
{
_execute?.Invoke(parameter);
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public void RaiseCanExecuteChanged()
{
CommandManager.InvalidateRequerySuggested();
}
}
定义的DataTemplates(资源字典,在app.xaml中注册):
<!--Each view has It's own reference of ViewModel-->
<DataTemplate DataType="{x:Type ViewModels:HomeViewModel}">
<Views:HomeView />
</DataTemplate>
//...etc..
改变视图的属性:
///<summary>Inherited Base property</summary>
public object Open_view
{
get { return _open_view; }
set { _open_view = value; OnPropertyChanged(); }
}
private object _open_view;
所有视图都通过 ContentControl 在 MainWindow 中打开(在这里也尝试了 UpdateSourceTrigger ..):
<ContentControl Grid.Column="1" Grid.Row="2" Content="{Binding Open_view,UpdateSourceTrigger=PropertyChanged}">
【问题讨论】:
-
能否添加部分父类的Xaml和用户控件本身的Xaml?此外,父视图模型对于帮助找到问题也很有帮助。
-
将带有检查的代码从
Cancel_CanExecute移动到Cancel_Command本身,然后它只会被调用一次。 -
@CédricMoers,我认为在这里发布代码会有点压倒性甚至违反 SO 规则。太多了 - 一个 lof ox xaml 和相互继承的类。
-
@Rekshino,你到底是什么意思?我的命令在 viewmodel 中是这样设置的:
CANCEL = new MyCommand(Cancel_Execute, Cancel_CanExecute);。 CANCEL 是MyCommand类型的属性。 -
您可以发布您将视图模型分配给视图的代码部分吗?通过绑定还是在代码中?还有你如何定义你的 Open_view 属性。请参阅我的回答以澄清我的意思。