【问题标题】:Disable button click event without disabling command?在不禁用命令的情况下禁用按钮单击事件?
【发布时间】:2020-08-14 01:10:48
【问题描述】:

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

问题是我有点作弊并使用按钮clickNavigation Servicecommand 在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 个按钮; command 为 1,click 为 1。 click 按钮将永远不可见,但会根据 VM 中设置的属性以某种方式触发单击。

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

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

有没有简单的方法来做到这一点?

【问题讨论】:

  • “问题是我有点作弊,并使用导航服务的按钮单击” 不要作弊 ;-)
  • 如果您向我们展示代码 sn-p 以及您尝试执行的操作,我们会更清楚
  • @Rans:我更新了问题以显示代码。后面代码中的绑定是我知道会起作用的东西,但我就是想不出在没有 UI 元素的情况下绑定的措辞。我不需要 SaveClickEnabled 绑定说 Textbox.Text,只是一个变量或绑定本身应该可以工作。对吗?

标签: c# wpf mvvm data-binding


【解决方案1】:

我通过创建一个隐藏的复选框并将其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();
     }
}

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

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

【讨论】:

    【解决方案2】:

    如果我理解正确,那么在 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
    }
    

    窗口后面的代码:

         {
            // Some code
     
            DataContext = new ViewModel(GoForwardAfterSaving);
         }
    
         private void GoForwardAfterSaving()
         {
             if (NavigationService.CanGoForward)
                 NavigationService.GoForward();
             else
             {
                 Pg10Backup page = new Pg10Backup();
                 NavigationService.Navigate(page);
             }
         }
    

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-03-24
      • 2011-05-19
      • 2016-03-20
      • 1970-01-01
      • 2015-09-05
      • 1970-01-01
      相关资源
      最近更新 更多