WPF ValidationRule и DataTrigger для отключения кнопки отправки

#wpf #validation #xaml

#wpf #проверка #xaml

Вопрос:

В следующем XAML я использую MultiDataTrigger и ValidationRule, пытаясь удерживать кнопку отправки disabled до тех пор, пока в двух текстовых полях не будут введены допустимые значения (буквенно-цифровые). Это работает нормально, поскольку, когда значение недопустимо (скажем, some@test ), кнопка отключается, и когда вы делаете это значение допустимым (скажем, sometest ), оно включает кнопку.

Проблема: Но при запуске приложения кнопка отображается как включенная — это сводит на нет всю цель, поскольку вы можете отправить кнопку, даже если вы еще не начали вводить значения текстового поля.

Вопрос: Чего мне здесь может не хватать и как мы можем это исправить?

Примечания: Если вы посмотрите (и я протестировал) MultiDataTrigger пример в первой ссылке выше, вы заметите (как показано на двух изображениях), что начальное значение (как и ожидалось) отображается как Unverified red цветное, а когда вы устанавливаете два флажка, текстовое значение меняется на Verified зеленый цвет. Итак, кажется, я явно что-то упускаю в XAML выше. Пожалуйста, обратите внимание, что я установил начальное состояние кнопки на <Setter Property="IsEnabled" Value="False"/> .

 <Window x:Class="WPF_DataValidation.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WPF_DataValidation"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Window.Resources>
        
        <local:MyDataSource x:Key="myds"/>

        <ControlTemplate x:Key="ValidationTemplate">
            <DockPanel>
                <TextBlock Text="{Binding [0].ErrorContent}" Foreground="Red" DockPanel.Dock="Bottom" Grid.Row="1"/>
                <AdornedElementPlaceholder/>
            </DockPanel>
        </ControlTemplate>
        <Style TargetType="Label">
            <Setter Property="Margin" Value="5" />
            <Setter Property="VerticalAlignment" Value="Center" />
        </Style>
        <Style x:Key="DefaultTextBoxStyle" TargetType="TextBox">
            <Setter Property="Margin" Value="5" />
            <Setter Property="VerticalAlignment" Value="Center" />
        </Style>
    </Window.Resources>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto" SharedSizeGroup="Labels" />
            <ColumnDefinition Width="*" />
        </Grid.ColumnDefinitions>
        <Label Name="lblFirstName">First Name:</Label>
        <TextBox Name="txtFirstName" Grid.Column="1" Style="{StaticResource DefaultTextBoxStyle}" Validation.ErrorTemplate="{StaticResource ValidationTemplate}">
            <TextBox.Text>
                <Binding Path="FirstName" Source="{StaticResource myds}" UpdateSourceTrigger="PropertyChanged">
                    <Binding.ValidationRules>
                        <local:AlphanumericOnlyValidationRule/>
                    </Binding.ValidationRules>
                </Binding>
            </TextBox.Text>
        </TextBox>

        <Label Name="lblLastName" Grid.Row="1">Last Name:</Label>
        <TextBox Name="txtLastName" Grid.Row="1" Grid.Column="1" Style="{StaticResource DefaultTextBoxStyle}" Validation.ErrorTemplate="{StaticResource ValidationTemplate}">
            <TextBox.Text>
                <Binding Path="LastName" Source="{StaticResource myds}" UpdateSourceTrigger="PropertyChanged">
                    <Binding.ValidationRules>
                        <local:AlphanumericOnlyValidationRule />
                    </Binding.ValidationRules>
                </Binding>
            </TextBox.Text>
        </TextBox>

        <Button x:Name="btnTest" Content="Test" Grid.Row="2" HorizontalAlignment="Right">
            <Button.Style>
                <Style TargetType="Button">
                    <Setter Property="IsEnabled" Value="False"/>
                    <Style.Triggers>
                        <MultiDataTrigger>
                            <MultiDataTrigger.Conditions>
                                <Condition Binding="{Binding Path=(Validation.HasError), ElementName=txtFirstName}" Value="False"/>
                                <Condition Binding="{Binding Path=(Validation.HasError), ElementName=txtLastName}" Value="False"/>
                            </MultiDataTrigger.Conditions>
                            <Setter Property="IsEnabled" Value="True"/>
                        </MultiDataTrigger>
                    </Style.Triggers>
                </Style>
            </Button.Style>
        </Button>
    </Grid>
</Window>
  

MyDataSource.cs:

 public class MyDataSource
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
}
  

AlphanumericOnlyValidationRule.cs

 using System.Windows.Controls;
using System.Linq;
using System.Globalization;

namespace WPF_DataValidation
{
    public class AlphanumericOnlyValidationRule: ValidationRule
    {
        public override ValidationResult Validate(object value, CultureInfo cultureInfo)
        {
            string sVal = value as string;

            if (string.IsNullOrEmpty(sVal))
            {
                return new ValidationResult(false, "Please enter some text");
            }

            if (!sVal.All(char.IsLetterOrDigit))
            {
                //Note: this will also return false if text has a blank space
                return new ValidationResult(false, "This field must contain alphanumeric characcters only");
            }

            return new ValidationResult(true, null);
        }
    }
}
  

НАБЛЮДЕНИЕ

Проблема, похоже, в том, что при загрузке приложения в первый раз оно, вероятно, сначала отключает кнопку отправки. Но значение Validation.HasError в MultiDataTrigger.Conditions блоке XAML предполагает, что ошибки проверки нет (поскольку приложение только что загружено, а пользователь еще не предпринял никаких действий), и, следовательно, оно снова включает кнопку отправки. Это всего лишь мое наблюдение, основанное на следующем XAML, который я использовал, изменив пример MultiDataTrigger, как показано ниже. Здесь я просто изменил значение TextBlock на Button вместе с соответствующими значениями и заметил, что изначально кнопка отключена (поскольку флажки по умолчанию не установлены), и когда вы устанавливаете оба флажка, кнопка включается (как и ожидалось). Итак, нам, вероятно, нужно посмотреть, как (в приведенном выше XAML) мы можем отключить кнопку при начальной загрузке приложения.

MiltiDataTrigger Пример модифицированного XAML из онлайн:

 <Button.Style>
    <Style TargetType="Button">
         <Setter Property="IsEnabled" Value="False"/>
            <Style.Triggers>
                <MultiDataTrigger>
                    <MultiDataTrigger.Conditions>
                        <Condition Binding="{Binding Path=(Validation.HasError), ElementName=txtFirstName}" Value="False"/>
                        <Condition Binding="{Binding Path=(Validation.HasError), ElementName=txtLastName}" Value="False"/>
                    </MultiDataTrigger.Conditions>
                  <Setter Property="IsEnabled" Value="True"/>
              </MultiDataTrigger>
            </Style.Triggers>
         </Style>
</Button.Style>
  

Ответ №1:

Это ожидаемое поведение, поскольку происходит проверка

(…) когда значение целевого объекта передается свойству источника привязки.

Источник

В вашем случае, чтобы принудительно выполнить проверку, прежде чем даже обновлять значение элементов управления, вы можете добавить ValidatesOnTargetUpdated="True" свойство ValidationRule , и оно запустит проверку при обновлении целевого объекта привязки.

 <TextBox
    Name="txtLastName"
    Grid.Row="1"
    Grid.Column="1"
    Style="{StaticResource DefaultTextBoxStyle}"
    Validation.ErrorTemplate="{StaticResource ValidationTemplate}"
    >
    <TextBox.Text>
        <Binding
            Path="LastName"
            Source="{StaticResource myds}"
            UpdateSourceTrigger="PropertyChanged"
            >
            <Binding.ValidationRules>
                <local:AlphanumericOnlyValidationRule ValidatesOnTargetUpdated="True" />
            </Binding.ValidationRules>
        </Binding>
    </TextBox.Text>
</TextBox>
  

ValidationRule.ValidatesOnTargetUpdated — MS DOC

Комментарии:

1. Ваше предложение сработало как шарм. Добро пожаловать в StackOverflow — и благодарю вас за NICELY объяснение причины проблемы со всеми соответствующими ссылками, поскольку это было важно для меня (и, надеюсь, для некоторых других читателей вашего ответа).