当 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
    }
  }
}