Отключить кнопку «Сохранить» в WPF, если проверка не удалась

Я принял то, что кажется стандартным способом проверки текстовых полей в WPF с использованием интерфейса и стилей IDataErrorInfo, как показано ниже. Однако как я могу отключить кнопку «Сохранить», когда страница становится недействительной? Это как-то через триггеры делается?

Default Public ReadOnly Property Item(ByVal propertyName As String) As String Implements IDataErrorInfo.Item
    Get
        Dim valid As Boolean = True
        If propertyName = "IncidentCategory" Then
            valid = True
            If Len(IncidentCategory) = 0 Then
                valid = False
            End If
            If Not valid Then
                Return "Incident category is required"
            End If
        End If

        Return Nothing

    End Get
End Property

<Style TargetType="{x:Type TextBox}">
    <Setter Property="Margin" Value="3" />
    <Setter Property="Height" Value="23" />
    <Setter Property="HorizontalAlignment" Value="Left" />
    <Setter Property="Validation.ErrorTemplate">
        <Setter.Value>
            <ControlTemplate>
                <DockPanel LastChildFill="True">
                    <Border BorderBrush="Red" BorderThickness="1">
                        <AdornedElementPlaceholder Name="MyAdorner" />
                    </Border>
                </DockPanel>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
    <Style.Triggers>
        <Trigger Property="Validation.HasError" Value="true">
            <Setter Property="ToolTip"  Value="{Binding RelativeSource={RelativeSource Self}, Path=(Validation.Errors)[0].ErrorContent}" />
        </Trigger>
    </Style.Triggers>
</Style>

person Mitch    schedule 16.04.2009    source источник


Ответы (2)


Несколько вещей:

Во-первых, я бы рекомендовал использовать RoutedCommand ApplicationCommands.Save для реализации обработки кнопки сохранения.

Если вы еще не ознакомились с моделью команд WPF, вы можете получить обзор здесь.

<Button Content="Save" Command="Save">

Теперь для реализации функционала можно добавить привязку команды к Window/UserControl или к самой кнопке:

    <Button.CommandBindings>
        <CommandBinding Command="Save" 
                        Executed="Save_Executed" CanExecute="Save_CanExecute"/>
    </Button.CommandBindings>
</Button>

Реализуйте их в коде позади:

private void Save_Executed(object sender, ExecutedRoutedEventArgs e)
{
}

private void Save_CanExecute(object sender, CanExecuteRoutedEventArgs e)
{
}

В Save_CanExecute установите e.CanExecute на основе допустимости привязки текстового поля.

Если вы хотите реализовать шаблон проектирования MVVM (Model-View-ViewModel), ознакомьтесь с сообщением Джоша Смита на странице . CommandSinkBinding.

И последнее замечание: если вы хотите, чтобы включение/отключение обновлялось сразу после изменения значения в TextBox, установите UpdateSourceTrigger="PropertyChanged" в привязке для TextBox.

РЕДАКТИРОВАТЬ: Если вы хотите проверить/аннулировать на основе всех привязок в элементе управления, вот несколько предложений.

1) Вы уже внедряете IDataErrorInfo. Попробуйте реализовать свойство IDataErrorInfo.Error таким образом, чтобы оно возвращало строку, недопустимую для всех свойств, к которым вы привязываетесь. Это будет работать только в том случае, если весь ваш элемент управления привязан к одному объекту данных. Установить e.CanExecute = string.IsNullOrEmpty(data.Error);

2) Используйте отражение, чтобы получить все общедоступные статические свойства DependencyProperties для соответствующих элементов управления. Затем вызовите BindingOperations.GetBindingExpression(relevantControl, DependencyProperty) в цикле для каждого свойства, чтобы вы могли проверить проверку.

3) В конструкторе вручную создайте коллекцию всех связанных свойств на вложенных элементах управления. В CanExecute выполните итерацию по этой коллекции и проверьте каждую комбинацию DependencyObject/DepencyProperty, используя BindingOperation.GetBindingExpression() для получения выражений, а затем исследуя BindingExpression.HasError.

person Josh G    schedule 16.04.2009
comment
Работает большое спасибо. Однако есть еще одна вещь. Я могу проверить отдельные элементы управления с помощью следующего кода. If Validation.GetHasError(myTextbox) Then e.CanExecute = False Есть ли способ проверить действительность всех элементов управления, а не по отдельности? - person Mitch; 17.04.2009
comment
+1 за предлагаемое использование команды. Команды — это то, что заставило WPF начать по-настоящему щелкать для меня. - person Greg D; 20.04.2009
comment
Например, простота твоего первого предложения, Джош. Я попробую. Еще раз спасибо - person Mitch; 22.04.2009

Я создал прикрепленное свойство только для этого:

public static class DataErrorInfoHelper
{
    public static object GetDataErrorInfo(ButtonBase obj)
    {
        return (object)obj.GetValue(DataErrorInfoProperty);
    }

    public static void SetDataErrorInfo(ButtonBase obj, object value)
    {
        obj.SetValue(DataErrorInfoProperty, value);
    }

    // Using a DependencyProperty as the backing store for DataErrorInfo.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty DataErrorInfoProperty =
        DependencyProperty.RegisterAttached("DataErrorInfo", typeof(object), typeof(DataErrorInfoHelper), new PropertyMetadata(null, OnDataErrorInfoChanged));

    private static void OnDataErrorInfoChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var button = d as ButtonBase;

        if (button.Tag == null)
            button.Tag = new DataErrorInfoContext { Button = button };

        var context = button.Tag as DataErrorInfoContext;

        if(e.OldValue != null)
        {
            PropertyChangedEventManager.RemoveHandler(((INotifyPropertyChanged)e.OldValue), context.Handler, string.Empty);
        }

        var inotify = e.NewValue as INotifyPropertyChanged;
        if (inotify != null)
        {
            PropertyChangedEventManager.AddHandler(inotify, context.Handler, string.Empty);
            context.Handler(inotify, new PropertyChangedEventArgs(string.Empty));
        }
    }

    private class DataErrorInfoContext
    {
        public ButtonBase Button { get; set; }

        public void Handler(object sender, PropertyChangedEventArgs e)
        {
            var dei = sender as IDataErrorInfo;

            foreach (var property in dei.GetType().GetProperties())
            {
                if (!string.IsNullOrEmpty(dei[property.Name]))
                {
                    Button.IsEnabled = false;
                    return;
                }
            }
            Button.IsEnabled = string.IsNullOrEmpty(dei.Error);
        }
    }
}

Я использую это так в своих формах:

<TextBlock  Margin="2">e-mail:</TextBlock>
<TextBox  Margin="2" Text="{Binding Email, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}"/>
<!-- other databindings--->
<Button Margin="2" local:DataErrorInfoHelper.DataErrorInfo="{Binding}"  Commands="{Binding SaveCommand}">Create account</Button>
person ghord    schedule 16.01.2014