Средства проверки валидации WPF - показывать, только если элемент управления удерживал фокус - PullRequest
3 голосов
/ 24 февраля 2011

В моем приложении WPF я хочу отображать окно подтверждения только после того, как элемент управления был отредактирован / введен / сфокусирован пользователем. Таким образом, пользователю предоставляется возможность ввести действительный ввод в поле, и только если он этого не сделает, будет отображаться проверка.

Мы хотим, чтобы каждое поле заполнялось, поэтому указание обязательных полей при первом открытии формы может обойти это так, что сразу же пользователь будет склонен просто выполнить то, что ему нужно, чтобы избавиться от большие красные ошибки проверки, которые также могут обойти поток формы.

Есть ли способ узнать, удерживал ли элемент управления фокус еще? Может ли присоединенное свойство работать?

В случае, если это помогает дать более конкретный ответ: вот мой текущий стиль проверки, который отображает красную границу [если элемент управления имеет границу] и небольшой восклицательный знак с всплывающей подсказкой для сообщения об ошибке (на самом деле довольно стандартно):

<Style TargetType="Control">
    <Style.Triggers>
        <Trigger Property="Validation.HasError" Value="true">
            <Setter Property="ToolTip"
                    Value="{Binding RelativeSource={x:Static RelativeSource.Self},
                    Path=(Validation.Errors).CurrentItem.ErrorContent}"/>

            <Setter Property="Validation.ErrorTemplate">
                <Setter.Value>
                    <ControlTemplate>
                        <DockPanel LastChildFill="true">
                            <Image Source="../Resources/Icons/Error.ico" Margin="4" Width="15" ToolTip="{Binding ElementName=customAdorner, Path=AdornedElement.(Validation.Errors).CurrentItem.ErrorContent}" />
                            <AdornedElementPlaceholder Name="customAdorner" VerticalAlignment="Center" >
                                <Border BorderBrush="red" BorderThickness="1" Visibility="{Binding ElementName=customAdorner, Path=AdornedElement.BorderThickness, Converter={StaticResource hasBorderToVisibilityConverter}}" />
                            </AdornedElementPlaceholder>
                        </DockPanel>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Trigger>
        <Trigger Property="IsVisible" Value="False">
            <Setter Property="Validation.ErrorTemplate" Value="{x:Null}"/>
        </Trigger>
    </Style.Triggers>
</Style>

1 Ответ

8 голосов
/ 24 февраля 2011

Вы можете объединить присоединенное поведение с присоединенным свойством, чтобы сделать это. Прикрепленное поведение ObserveFocus подпишется на событие GotFocus, а в обработчике события установите HasHeldFocus Присоединенное свойство на True

Это может быть использовано для установки свойства в ViewModel, как это

<Button local:HasHeldFocusBehavior.ObserveFocus="True"
        local:HasHeldFocusBehavior.HasHeldFocus="{Binding HasHeldFocus, 
                                                          Mode=OneWayToSource}"/>

Вот пример того, как его можно использовать для изменения Background Button после его фокусировки

<Style TargetType="Button">
    <Setter Property="Background" Value="Red"/>
    <Setter Property="local:HasHeldFocusBehavior.ObserveFocus" Value="True"/>
    <Style.Triggers>
        <DataTrigger Binding="{Binding RelativeSource={RelativeSource Self},
                                       Path=(local:HasHeldFocusBehavior.HasHeldFocus)}"
                        Value="True">
            <Setter Property="Background" Value="Green"/>
        </DataTrigger>
    </Style.Triggers>
</Style>

HasHeldFocusBehavior

public static class HasHeldFocusBehavior
{
    public static readonly DependencyProperty ObserveFocusProperty =
        DependencyProperty.RegisterAttached("ObserveFocus",
                                            typeof(bool),
                                            typeof(HasHeldFocusBehavior),
                                            new UIPropertyMetadata(false, OnObserveFocusChanged));
    public static bool GetObserveFocus(DependencyObject obj)
    {
        return (bool)obj.GetValue(ObserveFocusProperty);
    }
    public static void SetObserveFocus(DependencyObject obj, bool value)
    {
        obj.SetValue(ObserveFocusProperty, value);
    }
    private static void OnObserveFocusChanged(DependencyObject dpo,
                                              DependencyPropertyChangedEventArgs e)
    {
        UIElement element = dpo as UIElement;
        element.Focus();
        if ((bool)e.NewValue == true)
        {
            SetHasHeldFocus(element, element.IsFocused);
            element.GotFocus += element_GotFocus;
        }
        else
        {
            element.GotFocus -= element_GotFocus;
        }
    }
    static void element_GotFocus(object sender, RoutedEventArgs e)
    {
        UIElement element = sender as UIElement;
        SetHasHeldFocus(element, true);
    }

    private static readonly DependencyProperty HasHeldFocusProperty =
        DependencyProperty.RegisterAttached("HasHeldFocus",
                                            typeof(bool),
                                            typeof(HasHeldFocusBehavior),
                                            new UIPropertyMetadata(false));
    public static void SetHasHeldFocus(DependencyObject element, bool value)
    {
        element.SetValue(HasHeldFocusProperty, value);
    }
    public static bool GetHasHeldFocus(DependencyObject element)
    {
        return (bool)element.GetValue(HasHeldFocusProperty);
    }
}

Обновление

В вашем случае вы можете заменить Validation.HasError Trigger на MultiTrigger

<Style TargetType="Control">
    <Style.Triggers>
        <MultiDataTrigger>
            <MultiDataTrigger.Conditions>
                <Condition Binding="{Binding RelativeSource={RelativeSource Self},
                                             Path=(Validation.HasError)}"
                           Value="True"/>
                <Condition Binding="{Binding RelativeSource={RelativeSource Self},
                                             Path=(local:HasHeldFocusBehavior.HasHeldFocus)}"
                           Value="True"/>
            </MultiDataTrigger.Conditions>
            <!-- Setters.. -->
        </MultiDataTrigger>
        <!-- ... -->
    </Style.Triggers>
</Style>
...