当 CanExecute 为 false 时,uwp MVVM ContentDialog 启用主按钮
uwp MVVM ContentDialog with enabled primary button when CanExecute is false
我尝试使用 mvvm 构建类似于 https://docs.microsoft.com/en-us/uwp/api/windows.ui.xaml.controls.contentdialog 中示例的 ContentDialog。
对于 CanExecute 验证,我创建了一个派生的 ContentDialog class,如 https://social.msdn.microsoft.com/Forums/en-US/6d5d6fd9-5f03-4cb6-b6c0-19ca01ddaab8/uwpcontentdialog-buttons-do-not-respect-canexecute?forum=wpdevelop
中所述
这有效,但我如何启用该按钮,以便它可以单击以进行 CanExecute 验证。
绑定到视图时,ICommand 接口的 CanExecuteChanged 事件中缺少 link。它只有在完美的情况下才有效,而根据我的经验,它永远不会完美。
诀窍是在适当的值发生变化时调用 CanExecuteChanged 以将 CanExecute 切换为 true 或 false。
如果您正在使用中继命令,我所做的就是向中继命令添加一个 public 方法。
public UpdateCanExecute() => CanExecuteChanged?.Invoke(this, EventArgs.Empty);
然后在 属性 或属性或值中更改是否应该 return true 或 false 只需调用该方法。
public bool IsWorking
{
get { return isWorking; }
set
{
isWorking = value;
Notify(nameof(IsWorking));
MyRelayCommand.UpdateCanExecute();
}
}
这可能会让您了解我在说什么。如果没有或者如果您需要更多帮助,我可以 post 为这个答案添加更多代码以帮助澄清。
这是我的解决方案。我创建了一个内容对话框扩展。扩展包含
- 一个 CancelableCommand 命令
- 一个CancelableCommandParameter参数
- 和可绑定的 DialogCancel 属性。
这些是我的扩展的附加属性。
namespace BSE.UI.Xaml.Controls.Extensions
{
public static class ContentDialog
{
public static readonly DependencyProperty DialogCancelProperty =
DependencyProperty.RegisterAttached("DialogCancel",
typeof(bool),
typeof(ContentDialog), new PropertyMetadata(false));
public static readonly DependencyProperty CancelableCommandParameterProperty =
DependencyProperty.Register("CancelableCommandParameter",
typeof(object),
typeof(ContentDialog), null);
public static readonly DependencyProperty CancelableCommandProperty =
DependencyProperty.RegisterAttached("CancelableCommand",
typeof(ICommand),
typeof(ContentDialog),
new PropertyMetadata(null, OnCancelableCommandChanged));
public static void SetDialogCancel(DependencyObject obj, bool value)
{
obj.SetValue(DialogCancelProperty, value);
}
public static bool GetDialogCancel(DependencyObject obj)
{
return (bool)obj.GetValue(DialogCancelProperty);
}
public static ICommand GetCancelableCommand(DependencyObject obj)
{
return (ICommand)obj.GetValue(CancelableCommandProperty);
}
public static void SetCancelableCommand(DependencyObject obj, ICommand value)
{
obj.SetValue(CancelableCommandProperty, value);
}
public static object GetCancelableCommandParameter(DependencyObject obj)
{
return obj.GetValue(CancelableCommandParameterProperty);
}
public static void SetCancelableCommandParameter(DependencyObject obj, object value)
{
obj.SetValue(CancelableCommandParameterProperty, value);
}
private static void OnCancelableCommandChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
var contentDialog = obj as Windows.UI.Xaml.Controls.ContentDialog;
if (contentDialog != null)
{
contentDialog.Loaded += (sender, routedEventArgs) =>
{
((Windows.UI.Xaml.Controls.ContentDialog)sender).PrimaryButtonClick += OnPrimaryButtonClick;
};
}
}
private static void OnPrimaryButtonClick(Windows.UI.Xaml.Controls.ContentDialog sender, ContentDialogButtonClickEventArgs args)
{
var contentDialog = sender as Windows.UI.Xaml.Controls.ContentDialog;
if (contentDialog != null)
{
var command = GetCancelableCommand(contentDialog);
command?.Execute(GetCancelableCommandParameter(contentDialog));
args.Cancel = GetDialogCancel(contentDialog);
}
}
}
}
内容对话框的 xaml 看起来像这样
<ContentDialog
x:Class="MyClass.Views.MyContentDialog"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:MyClass.Views"
xmlns:controlextensions="using:BSE.UI.Xaml.Controls.Extensions"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
PrimaryButtonText="Button1"
SecondaryButtonText="Button2"
controlextensions:ContentDialog.DialogCancel="{Binding Cancel}"
controlextensions:ContentDialog.CancelableCommandParameter="{Binding}"
controlextensions:ContentDialog.CancelableCommand="{Binding MyCancelableCommand}">
</ContentDialog>
这是视图模型
namespace MyClass.ViewModels
{
public class MyContentDialogViewModel : ViewModelBase
{
private ICommand m_myCancelableCommand;
private bool m_cancel;
public ICommand MyCancelableCommand=> m_myCancelableCommand ?? (m_myCancelableCommand = new RelayCommand<object>(CancelableSave));
public bool Cancel
{
get
{
return m_cancel;
}
set
{
m_cancel = value;
RaisePropertyChanged("Cancel");
}
}
private void CancelableSave(object obj)
{
Cancel = !ValidateDialog();
}
private bool ValidateDialog()
{
return true// if saving successfull otherwise false
}
}
}
我尝试使用 mvvm 构建类似于 https://docs.microsoft.com/en-us/uwp/api/windows.ui.xaml.controls.contentdialog 中示例的 ContentDialog。
对于 CanExecute 验证,我创建了一个派生的 ContentDialog class,如 https://social.msdn.microsoft.com/Forums/en-US/6d5d6fd9-5f03-4cb6-b6c0-19ca01ddaab8/uwpcontentdialog-buttons-do-not-respect-canexecute?forum=wpdevelop
中所述这有效,但我如何启用该按钮,以便它可以单击以进行 CanExecute 验证。
绑定到视图时,ICommand 接口的 CanExecuteChanged 事件中缺少 link。它只有在完美的情况下才有效,而根据我的经验,它永远不会完美。
诀窍是在适当的值发生变化时调用 CanExecuteChanged 以将 CanExecute 切换为 true 或 false。
如果您正在使用中继命令,我所做的就是向中继命令添加一个 public 方法。
public UpdateCanExecute() => CanExecuteChanged?.Invoke(this, EventArgs.Empty);
然后在 属性 或属性或值中更改是否应该 return true 或 false 只需调用该方法。
public bool IsWorking
{
get { return isWorking; }
set
{
isWorking = value;
Notify(nameof(IsWorking));
MyRelayCommand.UpdateCanExecute();
}
}
这可能会让您了解我在说什么。如果没有或者如果您需要更多帮助,我可以 post 为这个答案添加更多代码以帮助澄清。
这是我的解决方案。我创建了一个内容对话框扩展。扩展包含
- 一个 CancelableCommand 命令
- 一个CancelableCommandParameter参数
- 和可绑定的 DialogCancel 属性。
这些是我的扩展的附加属性。
namespace BSE.UI.Xaml.Controls.Extensions
{
public static class ContentDialog
{
public static readonly DependencyProperty DialogCancelProperty =
DependencyProperty.RegisterAttached("DialogCancel",
typeof(bool),
typeof(ContentDialog), new PropertyMetadata(false));
public static readonly DependencyProperty CancelableCommandParameterProperty =
DependencyProperty.Register("CancelableCommandParameter",
typeof(object),
typeof(ContentDialog), null);
public static readonly DependencyProperty CancelableCommandProperty =
DependencyProperty.RegisterAttached("CancelableCommand",
typeof(ICommand),
typeof(ContentDialog),
new PropertyMetadata(null, OnCancelableCommandChanged));
public static void SetDialogCancel(DependencyObject obj, bool value)
{
obj.SetValue(DialogCancelProperty, value);
}
public static bool GetDialogCancel(DependencyObject obj)
{
return (bool)obj.GetValue(DialogCancelProperty);
}
public static ICommand GetCancelableCommand(DependencyObject obj)
{
return (ICommand)obj.GetValue(CancelableCommandProperty);
}
public static void SetCancelableCommand(DependencyObject obj, ICommand value)
{
obj.SetValue(CancelableCommandProperty, value);
}
public static object GetCancelableCommandParameter(DependencyObject obj)
{
return obj.GetValue(CancelableCommandParameterProperty);
}
public static void SetCancelableCommandParameter(DependencyObject obj, object value)
{
obj.SetValue(CancelableCommandParameterProperty, value);
}
private static void OnCancelableCommandChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
var contentDialog = obj as Windows.UI.Xaml.Controls.ContentDialog;
if (contentDialog != null)
{
contentDialog.Loaded += (sender, routedEventArgs) =>
{
((Windows.UI.Xaml.Controls.ContentDialog)sender).PrimaryButtonClick += OnPrimaryButtonClick;
};
}
}
private static void OnPrimaryButtonClick(Windows.UI.Xaml.Controls.ContentDialog sender, ContentDialogButtonClickEventArgs args)
{
var contentDialog = sender as Windows.UI.Xaml.Controls.ContentDialog;
if (contentDialog != null)
{
var command = GetCancelableCommand(contentDialog);
command?.Execute(GetCancelableCommandParameter(contentDialog));
args.Cancel = GetDialogCancel(contentDialog);
}
}
}
}
内容对话框的 xaml 看起来像这样
<ContentDialog
x:Class="MyClass.Views.MyContentDialog"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:MyClass.Views"
xmlns:controlextensions="using:BSE.UI.Xaml.Controls.Extensions"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
PrimaryButtonText="Button1"
SecondaryButtonText="Button2"
controlextensions:ContentDialog.DialogCancel="{Binding Cancel}"
controlextensions:ContentDialog.CancelableCommandParameter="{Binding}"
controlextensions:ContentDialog.CancelableCommand="{Binding MyCancelableCommand}">
</ContentDialog>
这是视图模型
namespace MyClass.ViewModels
{
public class MyContentDialogViewModel : ViewModelBase
{
private ICommand m_myCancelableCommand;
private bool m_cancel;
public ICommand MyCancelableCommand=> m_myCancelableCommand ?? (m_myCancelableCommand = new RelayCommand<object>(CancelableSave));
public bool Cancel
{
get
{
return m_cancel;
}
set
{
m_cancel = value;
RaisePropertyChanged("Cancel");
}
}
private void CancelableSave(object obj)
{
Cancel = !ValidateDialog();
}
private bool ValidateDialog()
{
return true// if saving successfull otherwise false
}
}
}