开发者

Displaying Errors, Having Controls After to Move Downwards

So I am trying to have a message displayed when the input is invalid, suppose I want something other than a ToolTip, something that stays until the error is corrected. I tried having an ErrorTemplate

<Style TargetType="{x:Type TextBox}">
    <Setter Property="Validation.ErrorTemplate">
        <Setter.Value>
            <ControlTemplate>
                <StackPanel>
                    <Border BorderBrush="Red" BorderThickness="1">
                        <AdornedElementPlaceholder x:Name="adornedErrorElement" />
                    </Border>
                    <Label Background="Red" Foreground="White" FontSize="9" Content="{Binding ElementName=adornedErrorElement, Path=AdornedElement.(Validation.Errors)[0].ErrorContent}" />
                </StackPanel>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>开发者_StackOverflow中文版

<StackPanel Margin="20">
    <TextBlock Text="Name" />
    <TextBox Text="{Binding Name}" />

    <TextBlock Text="Email" />
    <TextBox Text="{Binding Path=Email, ValidatesOnDataErrors=True}" />

    <Button Content="Submit" />
</StackPanel>

I get

Displaying Errors, Having Controls After to Move Downwards

Where the label overlays elements after it. How can I have it such that it works just like another element in the stackpanel?

UPDATE: Using The VSM

Now, I want to go 1 step further and animate the error label up and down. I am considering VSM following @robertos answer. I tried implementing in in Blend. A few problems I faced. I tried

<ControlTemplate TargetType="{x:Type TextBox}">
    <StackPanel Orientation="Vertical">
        <Microsoft_Windows_Themes:ListBoxChrome ...>
        <VisualStateManager.VisualStateGroups>
                ...
        </VisualStateManager.VisualStateGroups>
        <ScrollViewer ... />
        </Microsoft_Windows_Themes:ListBoxChrome>
        <Label Content="Error Here" />
    </StackPanel>
</ControlTemplate>

Then I lost access to VisualStates in Blend. Then I tried

<Microsoft_Windows_Themes:ListBoxChrome>
    <StackPanel>
        <ScrollViewer x:Name="PART_ContentHost" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" Margin="2,0,-2,0"/>
    <TextBlock x:Name="textBlock" Background="Red" Foreground="White" FontWeight="Bold" Text="Hello" Visibility="Collapsed" />
    </StackPanel>
</Microsoft_Windows_Themes:ListBoxChrome>

Not ideal as the StackPanel is within the border. Also my attempts at animation looks just weird

http://screenr.com/byk

http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=9,0,115,0' width='560' height='345'>http://screenr.com/Content/assets/screenr_1116090935.swf' >http://screenr.com/Content/assets/screenr_1116090935.swf' flashvars='i=130553' allowFullScreen='true' width='560' height='345' pluginspage='http://www.macromedia.com/go/getflashplayer' >

1st I must make the label hidden instead of collapsed the animate just the opacity. I want the label to appear like its coming out from the textbox


The element would have to be a part of the same StackPanel in the VisualTree which is not the case with the Validation.ErrorTemplate as you noticed. One way to do this would be to retemplate the TextBox and make place for a Collapsed error Label which will turn visible on Validation.HasError. You'll need to add a reference to PresentationFramework.Aero.

xmlns:Microsoft_Windows_Themes="clr-namespace:Microsoft.Windows.Themes;assembly=PresentationFramework.Aero"

Xaml

<LinearGradientBrush x:Key="TextBoxBorder" EndPoint="0,20" StartPoint="0,0" MappingMode="Absolute">
    <GradientStop Color="#ABADB3" Offset="0.05"/>
    <GradientStop Color="#E2E3EA" Offset="0.07"/>
    <GradientStop Color="#E3E9EF" Offset="1"/>
</LinearGradientBrush>
<Style x:Key="LabelValidationTextBox" BasedOn="{x:Null}" TargetType="{x:Type TextBox}">
    <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
    <Setter Property="Background" Value="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"/>
    <Setter Property="BorderBrush" Value="{StaticResource TextBoxBorder}"/>
    <Setter Property="BorderThickness" Value="1"/>
    <Setter Property="Padding" Value="1"/>
    <Setter Property="AllowDrop" Value="true"/>
    <Setter Property="FocusVisualStyle" Value="{x:Null}"/>
    <Setter Property="Validation.ErrorTemplate" Value="{x:Null}"/>
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type TextBox}">
                <StackPanel Orientation="Vertical">
                    <Microsoft_Windows_Themes:ListBoxChrome x:Name="Bd" SnapsToDevicePixels="true" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" RenderFocused="{TemplateBinding IsKeyboardFocusWithin}" RenderMouseOver="{TemplateBinding IsMouseOver}">
                        <ScrollViewer x:Name="PART_ContentHost" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
                    </Microsoft_Windows_Themes:ListBoxChrome>
                    <Label StackPanel.ZIndex="-1" Name="errorLabel" Height="22" Margin="0,-22,0,0" Background="Red" Foreground="White" FontSize="9" Content="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=(Validation.Errors)[0].ErrorContent}" />
                </StackPanel>
                <ControlTemplate.Triggers>
                    <Trigger Property="IsEnabled" Value="false">
                        <Setter Property="Background" TargetName="Bd" Value="{DynamicResource {x:Static SystemColors.ControlBrushKey}}"/>
                        <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
                    </Trigger>
                    <Trigger Property="Validation.HasError" Value="True">
                        <Trigger.EnterActions>
                            <BeginStoryboard>
                                <Storyboard TargetName="errorLabel" TargetProperty="Margin">
                                    <ThicknessAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetProperty="Margin">
                                        <SplineThicknessKeyFrame KeyTime="0:0:0.0" Value="0,-22,0,0"/>
                                        <SplineThicknessKeyFrame KeyTime="0:0:0.5" Value="0,0,0,0"/>
                                    </ThicknessAnimationUsingKeyFrames>
                                </Storyboard>
                            </BeginStoryboard>
                        </Trigger.EnterActions>
                        <Trigger.ExitActions>
                            <BeginStoryboard>
                                <Storyboard TargetName="errorLabel" TargetProperty="Margin">
                                    <ThicknessAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetProperty="Margin">
                                        <SplineThicknessKeyFrame KeyTime="0:0:0.0" Value="0,0,0,0"/>
                                        <SplineThicknessKeyFrame KeyTime="0:0:0.5" Value="0,-22,0,0"/>
                                    </ThicknessAnimationUsingKeyFrames>
                                </Storyboard>
                            </BeginStoryboard>
                        </Trigger.ExitActions>
                    </Trigger>
                </ControlTemplate.Triggers>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

Update

Added Margin animation of the Label. It will "slide out" of the TextBox when Validation.HasError is True and "slide back in" to the TextBox when Validation.HasError is False.


Instead of using ErrorTemplate, you could use the Visual State Manager and customize the Invalid State. By doing that, besides being able to integrate your changes to the actual control (which affects layout), you'll also get the ability to animate state changes very easily as well.

If you need more guidance on Visual States don't hesitate to ask.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜