WPF验证修饰符 - 仅在控件之前持有焦点时显示

3
在我的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
你可以结合附加行为和附加属性来实现此功能。附加行为ObserveFocus将订阅GotFocus事件,并在事件处理程序中将HasHeldFocus附加属性设置为True
可以像这样使用它来设置ViewModel中的属性:
<Button local:HasHeldFocusBehavior.ObserveFocus="True"
        local:HasHeldFocusBehavior.HasHeldFocus="{Binding HasHeldFocus, 
                                                          Mode=OneWayToSource}"/>

这是一个有关如何在Button获得焦点后改变其Background的示例。
<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);
    }
}

更新

在您的情况下,您可以使用MultiTrigger替换Validation.HasError触发器。

<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>

1
太棒了!我已经将它改为基于“失去焦点”而不是“获得焦点”,但它完美地工作了。 - Reddog
@Reddog:好的,看起来我搞反了 :) 很高兴听到它起作用了! - Fredrik Hedblad

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