c# - 在切换视图时禁用 CanExecute
问题描述
我有一个静态属性,可以跟踪我的视图模型中的属性更改:
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;
}
当我的视图发生变化并且用户想要切换视图时,我想向他们显示一个警告消息框。因此,在我的主窗口 viewModel 中,我有一个用于更改视图的命令:
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 仍然调用旧 viewmodel 的 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}">
解决方案
在我看来,这听起来像是一个约束性问题。具体来说:View(用户控件)不知道 ViewModel 发生变化的地方。
如果父 ViewModel 中的 ViewModel 可以更改,请确保添加 PropertyChanged 事件以通知 View 它绑定的 ViewModel 已更改。
过去在代码隐藏中设置数据上下文让我很头疼,所以我建议不要这样做:(this.DataContext = null)也不要这样做(this.DataContext = ViewModel)。
因此,请确保 Open_view 看起来像这样:
public SomeViewModel Open_view
{
get { return _open_view; }
set { _open_view = value; OnPropertyChanged(nameof(Open_view)); }
}
在父视图(包含用户控件的视图)中绑定数据上下文,如下所示:
<SomeUserControl ... DataContext={Binding Path=Open_view, Mode=OneWay, UpdateSourceTrigger=PropertyChanged} ... />
当然,您需要将路径更改为实际路径。
推荐阅读
- android - 在 Kotlin 中,我可以将 Elvis 表达式添加到 simpledateformat 格式吗?
- php - DevOps:将 AWS 和 GC 结合到一个网站中
- javascript - 画架 JS 形状欠细
- javascript - Google Apps 脚本中的数组“包含”方法失败
- sql - information_schema.columns 命令在 DBeaver 中不起作用
- python-2.7 - 我们可以使用 lambda python 或任何替代方法将两个相同的 route53 记录集从主要切换到次要吗?
- tensorflow - 为什么我不能在一维卷积中设置内核大小?
- javascript - AudioContext 不允许在 ToneJS (Chrome) 中启动
- android - 不改变已进入的方向
- python - 气流 DAG 阻止其他 DAGS 启动