字段中的长度验证触发错误图标

时间:2016-12-07 13:57:51

标签: c# wpf xaml user-interface user-experience

当用户尝试输入比字段应接受的字符串更长的字符串时,我尝试在字段中显示图标,工具提示或聊天气泡。我已经广泛搜索了这一点,我感到非常惊讶,这似乎没有简单的解决方案。我找到了简单的解决方案,可以在输入无效时更改字段外观,但没有一个可以闪现警告并在字段中保留合法的先前值。如何实现这一目标?

1 个答案:

答案 0 :(得分:0)

正确的方法是使用ErrorTemplateValidationRule s。

首先,你需要一个ValidationRule,在你的情况下需要检查一个字符串的长度。

public class StringLengthRule : ValidationRule
{
    public int MaxLength { get; set; }

    public override ValidationResult Validate(object value, CultureInfo cultureInfo)
    {
        if (((string)value).Length > MaxLength)
            return new ValidationResult(false, "Input too long!");
        return new ValidationResult(true, null);
    }
}

您可以将其附加到视图的绑定中,例如:

<TextBox>
    <TextBox.Text>
        <Binding RelativeSource="{RelativeSource AncestorType=local:MainWindow}"
                 UpdateSourceTrigger="PropertyChanged" <!-- or LostFocus -->
                 Path="Text">
            <Binding.ValidationRules>
                <local:StringLengthRule MaxLength="15"/>
            </Binding.ValidationRules>
        </Binding>
    </TextBox.Text>
</TextBox>

根据您的样式/模板,这看起来像这样: (我在这个答案的底部包含了样式和模板)

Error Template

这也适用于内部错误。如果绑定到int,例如:

Example 2

在关闭对话框之前,您可以使用相同的Validations来检查所有输入是否有效。

public static class ValidationHelper
{
    public static bool IsValid(DependencyObject obj)
    {
        // The dependency object is valid if it has no errors and all
        // of its children (that are dependency objects) are error-free.
        return !Validation.GetHasError(obj) &&
        LogicalTreeHelper.GetChildren(obj)
        .OfType<DependencyObject>()
        .All(IsValid);
    }

    public static bool ShowValidHint(DependencyObject dependencyObject)
    {
        if (IsValid(dependencyObject)) return true;

        MessageBox.Show(Strings.WarningInput, Strings.InputError, MessageBoxButton.OK, MessageBoxImage.Warning);
        return false;
    }
}

用法:

private void btnOk_Click(object sender, RoutedEventArgs e)
{
    ((Button)sender).Focus();
    if (ValidationHelper.ShowValidHint(this))
        DialogResult = true;
    else
        // show error
}

至于保留合法的先前值部分。最好通过PropertyChangedCallback的{​​{1}}或CoerceValueCallback或(如果您使用的是PropertyMetadata)来设置属性设置器。我个人会建议反对它。如果您更改该值,该值将再次有效,键入其键盘的人可能甚至不知道他们做错了什么。

不记得我从哪里获得这个模板,但是我已经将它用于这样的案例多年了。

控制模板

INotifyPropertyChanged

样式

<ControlTemplate x:Key="TextBoxErrorTemplate">
    <StackPanel Orientation="Horizontal">
        <!-- Defines TextBox outline border and the ToolTipCorner -->
        <Border x:Name="border" BorderThickness="1.25" BorderBrush="#FFDC000C">
            <Grid>
                <Polygon x:Name="toolTipCorner" Grid.ZIndex="2" Margin="-1" Points="9,9 9,0 0,0" Fill="#FFDC000C" HorizontalAlignment="Right"
                    VerticalAlignment="Top" IsHitTestVisible="True"/>
                <Polyline Grid.ZIndex="3" Points="10,10 0,0" Margin="-1" HorizontalAlignment="Right" StrokeThickness="1.5" 
                              StrokeEndLineCap="Round" StrokeStartLineCap="Round" Stroke="White" VerticalAlignment="Top" IsHitTestVisible="True"/>
                <AdornedElementPlaceholder x:Name="adorner"/>
            </Grid>
        </Border>
        <!-- Defines the Popup -->
        <Popup x:Name="placard" AllowsTransparency="True" PopupAnimation="Fade" Placement="Top" PlacementTarget="{Binding ElementName=toolTipCorner}" PlacementRectangle="10,-1,0,0">
            <!-- Used to reposition Popup when dialog moves or resizes -->
            <Popup.Style>
                <Style TargetType="{x:Type Popup}">
                    <Style.Triggers>
                        <!-- Shows Popup when TextBox has focus -->
                        <DataTrigger Binding="{Binding ElementName=adorner, Path=AdornedElement.IsFocused}" Value="True">
                            <Setter Property="IsOpen" Value="True"/>
                        </DataTrigger>
                        <!-- Shows Popup when mouse hovers over ToolTipCorner -->
                        <DataTrigger Binding="{Binding ElementName=toolTipCorner, Path=IsMouseOver}" Value="True">
                            <Setter Property="IsOpen" Value="True"/>
                        </DataTrigger>
                        <!-- Hides Popup when window is no longer active -->
                        <DataTrigger Binding="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, Path=IsActive}" Value="False">
                            <Setter Property="IsOpen" Value="False"/>
                        </DataTrigger>
                    </Style.Triggers>
                </Style>
            </Popup.Style>
            <Border x:Name="errorBorder"
            Background="#FFDC000C"
            Margin="0,0,8,8"
            Opacity="1"
            CornerRadius="4"
            IsHitTestVisible="False"
            MinHeight="24"
            MaxWidth="267">
                <Border.Effect>
                    <DropShadowEffect ShadowDepth="4"
                          Color="Black"
                          Opacity="0.6"
                          Direction="315"
                          BlurRadius="4"/>
                </Border.Effect>
                <TextBlock Text="{Binding ElementName=adorner, Path=AdornedElement.(Validation.Errors).CurrentItem.ErrorContent}"
                 Foreground="White"
                 Margin="8,3,8,3"
                 TextWrapping="Wrap"/>
            </Border>
        </Popup>
    </StackPanel>
</ControlTemplate>