WPF多绑定按钮.isEnabled与文本框

8

我是WPF的新手,但在网上找不到解决方案。我的问题是,我希望只有四个文本框没有有效性错误时,才启用按钮。

我的代码如下:

<Button Content="Action" Click="Action_Click" >
      <Button.IsEnabled>
          <MultiBinding Converter="{StaticResource ValCon}">
               <Binding ElementName="textBox1" />
               <Binding ElementName="textBox2"/>
               <Binding ElementName="textBox3"/>
               <Binding ElementName="textBox4"/>
          </MultiBinding>
       </Button.IsEnabled>
</Button>

我的多值转换器如下:

class ValidityConverter : IMultiValueConverter
{
    public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        bool b = true;
        foreach (var x in values)
        {
            TextBox y = x as TextBox;
            if (Validation.GetHasError(y))
            {
                b = false;
                break;
            }
        }
        return b;
    }
    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

我注意到这个只在一开始起作用。当用户修改文本框之一时,它就不起作用了。我的值转换器需要实现INotifyPropertyChanged吗?在WPF中做类似这样的事情的正确解决方案是什么?感谢您的帮助。

编辑

我已经做了类似这样的事情,并且它工作正常:

                       <Button Click="Button_Click"  >
                            <Button.Style>
                                <Style TargetType="Button">
                                    <Setter Property="IsEnabled" Value="False"/>
                                    <Style.Triggers>
                                        <MultiDataTrigger>
                                            <MultiDataTrigger.Conditions>
                                                <Condition Binding="{Binding Path=(Validation.HasError), ElementName=textBox}" Value="False"/>
                                                <Condition Binding="{Binding Path=(Validation.HasError), ElementName=textBox1}" Value="False"/>
                                                <Condition Binding="{Binding Path=(Validation.HasError), ElementName=textBox2}" Value="False"/>
                                                <Condition Binding="{Binding Path=(Validation.HasError), ElementName=textBox3}" Value="False"/>
                                            </MultiDataTrigger.Conditions>
                                            <Setter Property="IsEnabled" Value="True"/>
                                        </MultiDataTrigger>
                                    </Style.Triggers>
                                </Style>
                            </Button.Style>
                        </Button>

3
MultiBinding 的参数都没有改变,它们始终是相同的四个文本框。这些绑定只会被评估一次,因此你的 IMultiValueConverter 只会被评估一次。你可以考虑使用 BindingGroup 将所有验证规则汇总到一个范围内。 - Mike Strobel
它只在一开始工作是什么意思?当所有文本框都有效时,按钮是否启用,并且即使您将某个文本框更改为无效,它仍然保持为真? - gilmishal
正如Mike Strobel所说,绑定只在开始时评估一次。按钮在开始时启用,因为Text属性是正确的,并且当用户将文本设置为无效值时,它不会更改。感谢建议,我将尝试使用BindingGroup做些什么。 - Marcin
5个回答

3
我所做的是使用您的MultiTrigger解决方案,但在我的情况下,我希望仅在3个文本框长度为零时激活按钮,因此我的解决方案与下面的代码一起工作。
<Button.Style>
   <Style TargetType="Button">
       <Setter Property="IsEnabled" Value="True"/>
           <Style.Triggers>
                 <MultiDataTrigger>
                     <MultiDataTrigger.Conditions>
                                <Condition Binding="{Binding Path=Text.Length, ElementName=activationKeyTextbox}" Value="0"/>
                     </MultiDataTrigger.Conditions>
                     <Setter Property="IsEnabled" Value="False"/>
                     </MultiDataTrigger>
                     <MultiDataTrigger>
                     <MultiDataTrigger.Conditions>
                                <Condition Binding="{Binding Path=Text.Length, ElementName=serialDayStartbox}" Value="0"/>
                     </MultiDataTrigger.Conditions>
                     <Setter Property="IsEnabled" Value="False"/>
                     </MultiDataTrigger>
                     <MultiDataTrigger>
                     <MultiDataTrigger.Conditions>
                        <Condition Binding="{Binding Path=Text.Length, ElementName=serialNumdaysbox}" Value="0"/>
                 </MultiDataTrigger.Conditions>
                 <Setter Property="IsEnabled" Value="False"/>
             </MultiDataTrigger>
        </Style.Triggers>
    </Style>
 </Button.Style>

2

我不建议使用IMultiValueConverter,因为这里并没有进行任何转换操作。可以考虑使用BindingGroup来实现你的目的。

MVVM/INotifyPropertyChanged同样有效的方法如下:

  1. 将按钮的IsEnabled属性绑定到一个布尔属性(比如IsValid)。
  2. 将所有文本框分别绑定到不同的属性。
  3. 在任一文本框的OnPropertyChange中调用一个公共函数(比如Validate())。
  4. 根据Validate()的结果,可以设置IsValid为true或false,从而切换(启用/禁用)按钮的状态。

5
如果您能提供一个代码示例,那将更好。因为我们是WPF和.NET领域的新手,很难理解。 - Sizzling Code

1

我知道这篇文章有点旧了,但是我也遇到了类似的问题,不过我不想写bindinggroup和validation...注意,这是我的第一个wpf应用程序,也是我第一次使用mvvm!

<Button Name="AddBtn" Content="Add" Grid.Row="2" Grid.Column="2" Height="30" Width="100" Command="{Binding AddCmd}" CommandParameter="{Binding Pending}" >
  <Button.IsEnabled>
     <MultiBinding Converter="{StaticResource multiButtonEnabled}" >
          <Binding ElementName="tb1" Path="(Validation.Errors)[0]" />
          <Binding ElementName="tb2" Path="(Validation.Errors)[0]"  />
          <Binding ElementName="tb3" Path="(Validation.Errors)[0]" />
     </MultiBinding>
  </Button.IsEnabled>
</Button>

转换器长这样。
[ValueConversion(typeof(ValidationError), typeof(bool))]
class TxtBoxMultiEnableConverter : IMultiValueConverter
{
    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
    {
        int i=0;
        foreach (var item in values)
        {
            if (item as ValidationError != null)
            {
                i++;
            }
        }
        if (i!=0)
        {
            return false;
        }
        return true;
    }

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
    {
        throw new NotSupportedException();
    }
}

我之前使用IDataErrorInfo来捕捉模型中的错误,并将其绑定到一个double类型。WPF很好地为绑定抛出解析错误,但它们并没有提供一种方式让您钩入以更改显示给用户的消息或提供属性以检查是否有标记了错误,这很令人烦恼。然而,这样可以让您在不必检查验证规则中的解析错误的情况下捕获解析错误。我知道我的无知正在显露,但我们都要从某个地方开始学习!

0

正如Mike所说,文本框没有改变。

你应该绑定在textbox.Text属性上。

在你的例子中:

<Button Content="Action" Click="Action_Click" >
      <Button.IsEnabled>
          <MultiBinding Converter="{StaticResource ValCon}">
               <Binding ElementName="textBox1" Path="Text"/>
               <Binding ElementName="textBox2" Path="Text" />
               <Binding ElementName="textBox3" Path="Text"/>
               <Binding ElementName="textBox4" Path="Text"/>
          </MultiBinding>
       </Button.IsEnabled>
</Button>

0

免责声明

这是我在StackOverflow上的第一个回答,所以可能不准确或需要调整以遵循StackOverflow协议。

至于这个评论:https://dev59.com/fX_aa4cB1Zd3GeqP22tr#23504690(无法直接评论)

我尝试使用了这个建议;但是,没有另一种手动更新MultiBinding的机制是不够的。 我的建议是:

FormConverter.cs:

using System;
using System.Globalization;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;

namespace WpfApp.Converters
{
    /// <summary>
    /// This class will be used to determine whether the form submission button should be enabled
    /// </summary>
    [ValueConversion(typeof(Control), typeof(bool))]
    public class FormConverter : IMultiValueConverter
    {
        public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
        {
            foreach (Control control in values)
            {
                //Relevant inside the scope of the current iteration only
                string text = null;

                //Any validation error
                if (Validation.GetHasError(control))
                    return false;

                //No value selected
                if (control is DatePicker datePicker)
                    if (datePicker.SelectedDate == DateTime.Today)
                        return false;
                    else continue;
                if (control is ComboBox comboBox)
                    if (comboBox.SelectedIndex == -1)
                        return false;
                    else continue;

                //Left blank
                if (control is TextBox textBox)
                    text = textBox.Text;
                else if (control is PasswordBox passwordBox)
                    text = passwordBox.Password;
                if (text != null && text.Length == 0)
                    return false;
            }
            return true;
        }

        public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
        {
            throw new NotSupportedException();
        }
    }
}

MainWindow.xaml.cs:

using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
namespace WpfApp
{
    class MainWindow : Window
    {
        private MultiBindingExpression buttonBinding = null;
        public MainWindow()
        {
            InitializeComponent();
            buttonBinding = BindingOperations.GetMultiBindingExpression(button, button.IsEnabledProperty);
        }

        private void UpdateButton(object sender, RoutedEventArgs e)
        {
            if (registerButtonBinding == null)
                return;
            buttonBinding.UpdateTarget();
        }
    }
}

MainWindow.xaml:

<Window x:Class="WpfApp.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:converters="clr-namespace:WpfApp.Converters">
  <Window.Resources>
    <converters:formConverter x:Key="formConverter"/>
  </Window.Resources>

  <StackPanel HorizontalAlignment="Center">

    <TextBlock Text="First name"/>
    <TextBox x:Name="firstNameTextBox"/>

    <Button x:Name="button">
      <Button.IsEnabled>
        <MultiBinding Converter="{StaticResource formConverter}">
          <Binding ElementName="firstName"/>
        </MultiBinding>
      </Button.IsEnabled>
    </Button>

  </StackPanel>

</Window>

来源:


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接