繁体   English   中英

在不禁用命令的情况下禁用按钮单击事件?

[英]Disable button click event without disabling command?

我正在使用 MVVM 在 WPF 中创建应用程序,其中我显示一个带有保存文件名和路径文本框的页面。 这些文本框经过大量实时验证(路径太短、太长、无效字符等),并且“保存”按钮只有在文件和路径都有效时才会变为活动状态。 但是,我将留下一些最终检查,例如目录是否存在、文件是否已存在等,直到用户点击保存按钮。

问题是我有点作弊,并使用Navigation Service的按钮clickcommand在 ViewModel 中进行最后一分钟的保存或清理。 在这种情况下,该命令将处理,但当它完成时,无论验证结果如何,单击都会立即触发。 所以,不知何故,我需要启用或禁用 click 事件,直到我们知道最终的验证结果。

看法:

<TextBlock>
    Backup Name:
</TextBlock>

<TextBlock Text="{Binding Path=(Validation.Errors)/ErrorContent, 
                   ElementName=BackupFileNameTextBox}"/>

<TextBox x:Name="BackupFileNameTextBox"
         Text="{Binding BackupFileName, Mode=TwoWay, 
                UpdateSourceTrigger=PropertyChanged, 
                ValidatesOnNotifyDataErrors=True}"/>

... File Path Code same as above...

<Button Click="Pg9SaveBtnClick"
        Command="{Binding SaveBtnCommand}">
    Save
</Button>

代码背后

public Pg9BackupIntro()
{
   InitializeComponent();
}

private void Pg9SaveBtnClick(object sender, RoutedEventArgs e)
{
   // What I want to do
   if(binding.ViewModel.SaveClickEnabled) //<- Something like this
   {
       if (NavigationService.CanGoForward)
           NavigationService.GoForward();
       else
       {
           Pg10Backup page = new Pg10Backup();
           NavigationService.Navigate(page);
       }
   }
}

private void Pg9BackBtnClick(object sender, RoutedEventArgs e)
{
    NavigationService.GoBack();
}

视图模型:

public RelayCommand SaveBtnCommand { get; private set; }

public Pg9BackupIntroViewModel()
{
   SetupBackupSave();
   SaveBtnCommand = new RelayCommand(SetBackupFileData, CanSetBackupFileData);    
}

private string _backupFileName;
public string BackupFileName
{
    get { return _backupFileName; }
    set
    {
        _backupFileName = value;
        validateFileName();
        RaisePropertyChanged();
     }
}

... BackupFilePath prop same as above...

private bool _saveClickEnabled = false;
public bool SaveClickEnabled
{
    get { return _saveClickEnabled; }
    set
    {
        _saveClickEnabled = value;
        RaisePropertyChanged();
    }
}

private bool CanSetBackupFileData(object obj)
{
    if(HasErrors)
        return false;

    return true;
}

private void SetBackupFileData(object obj)
{
    finalValidation();

    if(!HasErrors)
       SaveClickEnabled = true;
    else 
       SaveClickEnabled = false;
}

我能想到的最简单的事情(如上所示)是在 VM 中创建一个名为“SaveClickEnable”的属性,并在绑定到该属性的代码中。 然后我们可以简单地检查 click 事件处理程序中的属性,如果一切正常,就调用Navigation Service 不幸的是,我看过的所有教程都只涉及通过文本框等 UI 元素进行绑定,而不是绑定到变量背后的代码。 这是我尝试的绑定方法:

public Pg9BackupIntro()
{
   InitializeComponent();
   Binding binding = new Binding();
   binding.Source = ViewModel or Datacontext;  //<- don't understand syntax here
   binding.Path = ViewModel.SaveClickEnabled;
}

另一个想法是可能使用 2 个按钮; 1 表示command ,1 表示click click按钮将永远不可见,但会根据 VM 中设置的属性以某种方式触发单击。

这些都可能吗? 我试过两者都做,但我的菜鸟妨碍了。 我也尝试绑定到IsHitTestVisible但这也会禁用command

<Button IsHitTestVisible="{Binding SaveClickEnabled}" //<- Disables command too
        Click="Pg9SaveBtnClick"
        Command="{Binding SaveBtnCommand}">
    Save
</Button>

有没有一种简单的方法可以做到这一点?

我通过创建一个隐藏的复选框并将其IsChecked属性绑定到SaveClickEnabled解决了这个问题。 然后我只需检查 click 方法中复选框的值,如果出现错误,它会绕过导航代码。

看法:

<CheckBox x:Name="ClickEnabledCheckbox" 
                  Visibility="Collapsed" 
                  IsChecked="{Binding SaveClickEnabled}"/> 

代码隐藏:

private void Pg9BackupBtnClick(object sender, RoutedEventArgs e)
{
    if(ClickEnabledCheckbox.IsChecked == true)
    {
        ...Navigate to next View
    }
} 

视图模型:

private bool _saveClickEnabled = false;
public bool saveClickEnabled
{
    get { return _backupClickEnabled; }
    set
    {
        if (HasErrors)
            _backupClickEnabled = false;

         _backupClickEnabled = true;
         RaisePropertyChanged();
     }
}

它的美妙之处在于,我不想在用户可能正在输入有效文件路径或我们可以为他们创建的路径的整个过程中对他们咆哮。 所以这只检查点击/命令上的那些类型的错误,当用户试图纠正他/她的错误时,错误就会消失,直到下一次按下。

我唯一的愿望是它更干净一些,似乎应该有办法消除复选框。

如果我对您的理解正确,那么您需要在 ViewModel 中执行命令之后(即在 SetBackupFileData 方法之后)调用某个方法(在您的代码中是 Pg9SaveBtnClick)。
从概念上讲,您面临的问题是:“ViewModel 如何调用它不知道的方法?”。
如果您稍微考虑一下,那么它的答案就很自然了:“有必要将有关此方法的信息传递给 ViewModel。” 方法信息通过委托传递。
通常,这种信息传输发生在 ViewModel 实例化时,称为“依赖注入”。

以下是此类实现的示例:

public class ViewModel
{
    private readonly Action executeAfterSaving;
    public ViewModel(Action executeAfterSaving)
       => this.executeAfterSaving = executeAfterSaving;

    private void SetBackupFileData(object obj)
    {
        finalValidation();

        if(!HasErrors)
            executeAfterSaving?.Invoke();
    }

     // Continuation of the code
}

Window后面的代码:

     {
        // Some code
 
        DataContext = new ViewModel(GoForwardAfterSaving);
     }

     private void GoForwardAfterSaving()
     {
         if (NavigationService.CanGoForward)
             NavigationService.GoForward();
         else
         {
             Pg10Backup page = new Pg10Backup();
             NavigationService.Navigate(page);
         }
     }

代码以一般术语显示。 对于更具体的示例,我们需要有关您的实施的更多详细信息。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM