Как выполнить валидацию по клику MVVM

229
14 октября 2018, 07:20

Первоначально при запуске TextBox пустой, если я начинаю в нем что то печатать, а потом удаляю все, чтобы он был пустой, то появляется ошибка валидации. Но мне бы хотелось, чтобы эта ошибка появлялась и при нажатии на кнопку (если в TextBox совсем ничего не было напичатано). Мне не понятно, что нужно написать в Command или CanExecute, чтобы заставить TextBox запустить валидацию.

<UserControl x:Class="ExpenseTracker.Views.AccountsView"
         x:ClassModifier="internal"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:validationRules="clr-namespace:ExpenseTracker.ValidationResultRules">
<StackPanel>
    <TextBox x:Name="accountName">
        <TextBox.Text>
            <Binding Path="Input" 
                     Mode="TwoWay" 
                     UpdateSourceTrigger="PropertyChanged">
                <Binding.ValidationRules>
                    <validationRules:NotEmpty></validationRules:NotEmpty>
                </Binding.ValidationRules>
            </Binding>
        </TextBox.Text>
    </TextBox>
    <Button Grid.Row="4" Grid.Column="2"
            Command="{Binding AddAccountCommand}">
        <Button.Style>
            <Style TargetType="Button">
                <Setter Property="IsEnabled" Value="False"/>
                <Style.Triggers>
                    <MultiDataTrigger>
                        <MultiDataTrigger.Conditions>
                            <Condition Binding="{Binding Path=(Validation.HasError), ElementName=accountName}" Value="False"/>
                        </MultiDataTrigger.Conditions>
                        <Setter Property="IsEnabled" Value="True"/>
                    </MultiDataTrigger>
                </Style.Triggers>
            </Style>
        </Button.Style> Add Account
    </Button>
</StackPanel>

internal sealed class NotEmpty :ValidationRule
{
    public override ValidationResult Validate(object value, CultureInfo cultureInfo)
    {
        ValidationResult result = ValidationResult.ValidResult;
        if(((string)value).Length==0)
        {
            result = new ValidationResult(false, "String is empty");
        }
        return result;
    }
}
Answer 1

На самом деле использовать ValidationRule в MVVM неправильно, потому что это всё-таки вьюшная валидация и VM о ней ничего не знает. Вам же нужна валидация в VM, которая реализуется с помощью интерфейсов IDataErrorInfo/INotifyDataErrorInfo (см. этот ответ на enSO).

"Малой кровью" вашу задачу можно решить включив в правиле валидации ValidatesOnTargetUpdated:

...
<validationRules:NotEmpty ValidatesOnTargetUpdated="True"/>
...

В правиле валидации только измените условие на такое (может ведь и null придти):

...
if (string.IsNullOrEmpty((string)value))
...

Тогда ваш TextBox будет красным прямо при запуске

Еще вариант (который я нашел здесь), написать такой класс для принудительной валидации:

public static class Validator
{
    /// <summary>
    /// This method forces WPF to validate the child controls of the control passed in as a parameter.
    /// </summary>
    /// <param name="parent">Type: DependencyObject. The control which is the descendent root control to validate.</param>
    /// <returns>Type: bool. The validation result</returns>
    public static bool IsValid(DependencyObject parent)
    {
        // Validate all the bindings on the parent
        bool valid = true;
        LocalValueEnumerator localValues = parent.GetLocalValueEnumerator();
        while (localValues.MoveNext())
        {
            LocalValueEntry entry = localValues.Current;
            if (BindingOperations.IsDataBound(parent, entry.Property))
            {
                Binding binding = BindingOperations.GetBinding(parent, entry.Property);
                foreach (ValidationRule rule in binding.ValidationRules)
                {
                    ValidationResult result = rule.Validate(parent.GetValue(entry.Property), null);
                    if (!result.IsValid)
                    {
                        BindingExpression expression = BindingOperations.GetBindingExpression(parent, entry.Property);
                        Validation.MarkInvalid(expression, new ValidationError(rule, expression, result.ErrorContent, null));
                        valid = false;
                    }
                }
            }
        }
        // Validate all the bindings on the children
        for (int i = 0; i != VisualTreeHelper.GetChildrenCount(parent); ++i)
        {
            DependencyObject child = VisualTreeHelper.GetChild(parent, i);
            if (!IsValid(child))
            {
                valid = false;
            }
        }
        return valid;
    }
}

И использовать его, например, при загрузке окна:

Loaded += (s, e) => Validator.IsValid(this);

Или в обработчике клика по кнопке.

Правда, как взаимодействовать с ним из VM я не знаю (почему — см. первый абзац ответа).

READ ALSO
Отправка AJAX формы .Net MVC

Отправка AJAX формы .Net MVC

Всем добрый деньПытаюсь отправить AJAX форму на сервер, но метод контроллера, которым должна обрабатываться форма, не вызывается

219
Считывать &ldquo;свайпы&rdquo; пальцем по экрану Unity C#

Считывать “свайпы” пальцем по экрану Unity C#

Мне нужно отслеживать свайп пальцем на "Unity c#"Мне надо чтобы когда игрок делал свайп по экрану, игровая камера двигалась по карте, но не выходила...

195