.net - WPF 验证装饰器 - 仅显示控件之前是否保持焦点

标签 .net wpf validation adorner

在我的 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>

最佳答案

您可以将附加行​​为与附加属性结合起来执行此操作。附加行为 ObserveFocus 将订阅 GotFocus 事件并在事件处理程序中将 HasHeldFocus 附加属性设置为 True

它可以用来像这样在 ViewModel 中设置一个属性

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

这是一个示例,说明如何使用它来更改 ButtonBackground 一旦它被聚焦

<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 触发器替换为 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>

关于.net - WPF 验证装饰器 - 仅显示控件之前是否保持焦点,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5097449/

相关文章:

c# - 在 C# 中规范化目录名称

c# - 动态链接库,解决方案无法加载程序集

c# - 为什么选择静态类而不是单例实现?

.net - 如何使单个事件处理程序处理ALL Button.Click事件?

wpf - MVVMLight 中的异常处理

jsf - 复合组件内输入字段的验证器永远不会被触发

wpf - 从 WPF 窗口中检索所有数据绑定(bind)

c# - 根据TextWrapping 属性获取TextBlock 的行数?

c# - WPF上下文菜单自动滚动

php - Braintree dropin UI : validate billing address custom fields before form submit