基于 TextBox 值 (WPF) 启用按钮

Posted

技术标签:

【中文标题】基于 TextBox 值 (WPF) 启用按钮【英文标题】:Enable button based on TextBox value (WPF) 【发布时间】:2011-01-26 12:48:16 【问题描述】:

这是 MVVM 应用程序。有一个窗口和相关的视图模型类。

表格上有TextBoxButtonListBox。按钮绑定到具有CanExecute 功能的DelegateCommand。想法是用户在文本框中输入一些数据,按下按钮并将数据附加到列表框。

当用户在TextBox 中输入正确数据时,我想启用命令(和按钮)。事情现在是这样的:

CanExecute() 方法包含检查绑定到文本框的属性中的数据是否正确的代码。 文本框绑定到视图模型中的属性 UpdateSourceTrigger 设置为 PropertyChanged 并且视图模型中的属性在用户按下每个键后更新。

问题是当用户在文本框中输入数据时CanExecute() 不会触发。即使文本框失去焦点也不会触发。

我怎样才能做到这一点?

编辑: Re Yanko 的评论: Delegate 命令在 MVVM 工具包模板中实现,当您创建新的 MVVM 项目时,解决方案中有 Delegate 命令。正如我在 Prism 视频中看到的那样,这应该是同一类(或至少非常相似)。

这里是 XAML sn-p:

    ...
    <UserControl.Resources>
      <views:CommandReference x:Key="AddObjectCommandReference" 
                              Command="Binding AddObjectCommand" />
   </UserControl.Resources>

   ...
   <TextBox Text="Binding ObjectName, UpdateSourceTrigger=PropertyChanged"> </TextBox>
   <Button Command="StaticResource AddObjectCommandReference">Add</Button>
   ...

查看模型:

   // Property bound to textbox
   public string ObjectName
    
        get  return objectName; 
        set  
            objectName = value;
            OnPropertyChanged("ObjectName");
        
    


    // Command bound to button
    public ICommand AddObjectCommand
     
        get 
        
            if (addObjectCommand == null)
            
                addObjectCommand = new DelegateCommand(AddObject, CanAddObject);
            
            return addObjectCommand;
         
    

    private void AddObject()
    
        if (ObjectName == null || ObjectName.Length == 0)
            return;
        objectNames.AddSourceFile(ObjectName);
        OnPropertyChanged("ObjectNames"); // refresh listbox
    

    private bool CanAddObject()
    
        return ObjectName != null && ObjectName.Length > 0;
    

正如我在问题的第一部分中所写,以下事情有效:

ObjectName 的属性设置器在文本框中的每次按键时触发 如果我将return true; 放入CanAddObject(),则命令处于活动状态(按钮)

在我看来绑定是正确的。

我不知道的是如何从上面的代码中使CanExecute()ObjectName 属性的设置器中触发。

Re Ben 和 Abe 的回答:

CanExecuteChanged() 是事件处理程序,编译器报错:

事件 'System.Windows.Input.ICommand.CanExecuteChanged' 只能出现在左侧 += 或 -=

ICommand 中只有两个成员:Execute()CanExecute()

您是否有一些示例说明如何进行命令调用CanExecute()

我在DelegateCommand.cs 中找到了命令管理器助手类,我会研究一下,也许有一些机制可以提供帮助。

无论如何,为了根据用户输入激活命令,需要在属性设置器代码中“轻推”命令对象的想法看起来很笨拙。它将引入依赖关系,而 MVVM 的一大要点就是减少它们。

编辑 2:

我尝试通过从上面的代码调用addObjectCommand.RaiseCanExecuteChanged()ObjectName 属性设置器来激活CanExecute。这也无济于事。 CanExecute() 在表单初始化时被触发了几次,但之后它就再也不会被执行了。这是代码:

   // Property bound to textbox
   public string ObjectName
    
        get  return objectName; 
        set  
            objectName = value;
            addObjectCommand.RaiseCanExecuteChanged();              
            OnPropertyChanged("ObjectName");
        
    

编辑 3:解决方案

正如Yanko Yankov 和JerKimball 所写,问题是静态资源。当我像 Yanko 建议的那样更改按钮绑定时:

<Button Command="Binding AddObjectCommand">Add</Button>

事情立即开始运作。我什至不需要RaiseCanExecuteChanged()。现在CanExecute 会自动触发。

我为什么首先使用静态资源? 原始代码来自WPF MVVM toolkit 手册。该手册中的示例将命令定义为静态资源,然后将其绑定到菜单项。不同之处在于,在我的示例中,MVVM 手册不是使用字符串属性,而是使用ObservableCollection

编辑 4:最终解释

我终于明白了。我需要做的就是阅读CommandReference 类中的评论。它说:

/// <summary>
/// This class facilitates associating a key binding in XAML markup to a command
/// defined in a View Model by exposing a Command dependency property.
/// The class derives from Freezable to work around a limitation in WPF when 
/// databinding from XAML.
/// </summary>

所以,CommandReference 用于KeyBinding,它不适用于视觉元素中的绑定。在上面的代码中,资源中定义的命令引用适用于 KeyBinding,我在此用户控件上没有。 当然,WPF MVVM 工具包附带的示例代码是正确的,但我看错了,在可视元素绑定中使用了CommandReference。 这个 WPF MVVM 有时真的很棘手。

【问题讨论】:

理论上这应该可行。既然您提到了 DelegateCommand,我们应该假设您使用的是 Prism/CAL,还是您自己实现 ICommand?一些源代码将有助于查看。例如,按钮的绑定是什么样的(在代码中)。您如何连接 DelegateCommand,或者如果您自己实现它,该实现是什么样的,等等。 【参考方案1】:

如果 ElementName 绑定不起作用,请使用:

<Entry x:Name="Number1" Text="Binding Number1Text" Keyboard="Numeric"></Entry>
<Entry x:Name="Number2" Text="Binding Number2Text" Keyboard="Numeric"></Entry>
<Button Text="Calculate" x:Name="btnCalculate" Command="Binding CalculateCommand" IsEnabled="Binding Source=x:Reference Number1, Number2, Path=Text.Length, Mode=OneWay"></Button>

或使用:

<Entry x:Name="Number1" Text="Binding Number1Text" Placeholder="Number 1" Keyboard="Numeric"></Entry>
<Entry x:Name="Number2" Text="Binding Number2Text" Placeholder="Number 2" Keyboard="Numeric"></Entry>

<Button VerticalOptions="Center" Text="Calculate" x:Name="btnCalculate" Command="Binding CalculateCommand">
<Button.Triggers>
  <DataTrigger TargetType="Button"
               Binding="Binding Source=x:Reference Number1, Number2,
                                 Path=Text.Length"
               Value="x:Null">
      <Setter Property="IsEnabled" Value="False" />
  </DataTrigger>
</Button.Triggers>

【讨论】:

【参考方案2】:

我知道这是一个老问题,但我个人认为将文本框 Length 绑定到按钮的 IsEnabled 属性更容易,例如:

<TextBox Name="txtbox" Width="100" Height="30"/>
<Button Content="SomeButton " Width="100" Height="30" 

  IsEnabled="Binding ElementName=txtbox, Path=Text.Length, Mode=OneWay"></Button>

【讨论】:

【参考方案3】:

由于您使用的是 DelegateCommand,因此您可以在文本属性更改时调用它的 RaiseCanExecuteChanged 方法。我不确定你想用你的 CommandReference 资源来完成什么,但通常你只是将命令直接绑定到按钮元素的 Command 属性:

<TextBox Text="Binding ObjectName, UpdateSourceTrigger=ValueChanged" />
<Button Command="Binding AddObjectCommand" Content="Add" />

这将是您的视图模型的相关部分:

public string ObjectName

    get  return objectName; 
    set
    
        if (value == objectName) return;
        value = objectName;
        AddObjectCommand.RaiseCanExecuteChanged();
        OnPropertyChanged("ObjectName");
    

【讨论】:

请再次检查问题,我添加了代码和一些关于您的答案的 cmets【参考方案4】:

现在经过编辑,事情看起来更清晰了,谢谢!这可能是一个愚蠢的问题(我有点厌倦了一整天的工作),但是为什么不直接绑定到命令,而不是通过静态资源呢?

<Button Command="Binding AddObjectCommand">Add</Button>

【讨论】:

这也是我的第一个预感 - 因为静态资源不会被不断地重新评估,即使调用 RaiseCanExecuteChanged 也不会做太多。 是的,似乎他正在将按钮绑定到视图模型的静态实例,该实例与文本框绑定的实例不同,并且很可能前一个实例缺少任何连接到外面的世界开始。 你是对的。问题在于静态资源。解释请参见Edit3。【参考方案5】:

在这里呼应安倍,但在这里采取的“正确”路径是使用:

public void RaiseCanExecuteChanged();

在 DelegateCommand 上公开。就依赖关系而言,我认为当命令依赖于 ViewModel 中的属性发生变化时,通过引发这个,你真的不会做任何“坏事”。在这种情况下,耦合或多或少完全包含在 ViewModel 中。

因此,以您上面的示例为例,在“ObjectName”的设置器中,您将在命令“AddObjectCommand”上调用 RaiseCanExecuteChanged。

【讨论】:

Jer,您对 Yanko 回答的评论是正确的。解释请参见Edit3。【参考方案6】:

当您的财产发生变化时,请尝试提高 CanExecuteChanged。命令绑定与属性绑定确实不同,绑定到命令的按钮会通过CanExecuteChanged 事件提醒状态发生变化。

在您的情况下,您可以在对将评估它的绑定属性执行PropertyChanged 并设置命令的内部CanExecute 标志然后引发CanExecuteChanged 时触发检查。更多的是“推动”ICommand 对象而不是“拉动”。

【讨论】:

请再次检查问题,我添加了代码和一些关于您的答案的 cmets

以上是关于基于 TextBox 值 (WPF) 启用按钮的主要内容,如果未能解决你的问题,请参考以下文章

wpf怎么给textbox的text属性指定默认值

WPF中的禁用按钮?

wpf textbox 加按钮

WPF inputbox的问题

wpf 如何获取textbox 里按钮的点击事件

wpf 自定义控件 的问题 点了按钮以后 反应非常慢 请大神帮助