如何将验证传递给用户控件的子控件

时间:2015-02-02 15:59:33

标签: c# wpf validation xaml mvvm

我创建了一个由Telerik RadMaskedInput和Label组成的用户控件。用户控件是一个浮动标签输入,如下所示: http://css-tricks.com/float-labels-css/ 我遇到的问题是,当控件验证时,整个控件获得红色验证边框。我只需要RadMaskedInput框边框为红色。我已经看过其他几个帖子并帮助解决同一问题,但我似乎无法为我工作。我控制的XAML看起来像这样:

<Grid>
        <Label x:Name="controlLabel" 
               VerticalAlignment="Bottom" 
               Padding="5,0,5,0" 
               HorizontalAlignment="Left" 
               Background="White" 
               Grid.Row="0" 
               Margin="10,0,0,0" 
               Panel.ZIndex="1" 
               IsHitTestVisible="False" 
               Template="{DynamicResource LabelControlTemplate1}"
               Content="{Binding ElementName=floatingLabelTextBox, Path=LabelContent}"
               FontFamily="{Binding ElementName=floatingLabelTextBox, Path=LabelFontFamily}"
               FontSize="{Binding ElementName=floatingLabelTextBox, Path=LabelFontSize}"
               Foreground="{DynamicResource FloatingLabelTextBox.Label.Foreground}"/>

        <telerik:RadMaskedTextInput x:Name="controlMaskedTextBox" 
                                    Mask="" 
                                    TextMode="PlainText" 
                                    FontWeight="Normal" 
                                    BorderBrush="{DynamicResource FloatingLabelTextBox.BorderBrush}" 
                                    BorderThickness="1" 
                                    SelectionOnFocus="SelectAll" 
                                    IsClearButtonVisible="False"
                                    VerticalAlignment="Bottom" 
                                    HorizontalAlignment="Left" 
                                    HorizontalContentAlignment="Right" 
                                    VerticalContentAlignment="Center" 
                                    Value="{Binding TextBoxText,
                                                   Mode=TwoWay,
                                                   UpdateSourceTrigger=LostFocus,
                                                   ValidatesOnDataErrors=True,
                                                   ValidatesOnExceptions=True,
                                                   NotifyOnValidationError=True}"
                                    GotFocus="TextBox_GotFocus" 
                                    LostFocus="TextBox_LostFocus" 
                                    Template="{DynamicResource RadMaskedTextInputControlTemplate1}"/>
</Grid>

RadMaskedTextInput的控件模板如下所示:

<ControlTemplate x:Key="RadMaskedTextInputControlTemplate1" TargetType="{x:Type telerik:RadMaskedTextInput}">
                <Grid SnapsToDevicePixels="True" UseLayoutRounding="True">
                    <VisualStateManager.VisualStateGroups>
                        <VisualStateGroup x:Name="CommonStates">
                            <VisualState x:Name="Normal">
                                <Storyboard>
                                    <DoubleAnimation Duration="0" To="0" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="ClearButton"/>
                                </Storyboard>
                            </VisualState>
                            <VisualState x:Name="ReadOnly">
                                <Storyboard>
                                    <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Background" Storyboard.TargetName="GridContainer">
                                        <DiscreteObjectKeyFrame KeyTime="0">
                                            <DiscreteObjectKeyFrame.Value>
                                                <SolidColorBrush Color="#5EC9C9C9"/>
                                            </DiscreteObjectKeyFrame.Value>
                                        </DiscreteObjectKeyFrame>
                                    </ObjectAnimationUsingKeyFrames>
                                </Storyboard>
                            </VisualState>
                            <VisualState x:Name="Disabled">
                                <Storyboard>
                                    <!--<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Visibility" Storyboard.TargetName="DisabledVisual">
                                    <DiscreteObjectKeyFrame KeyTime="0">
                                        <DiscreteObjectKeyFrame.Value>
                                            <Visibility>Visible</Visibility>
                                        </DiscreteObjectKeyFrame.Value>
                                    </DiscreteObjectKeyFrame>
                                </ObjectAnimationUsingKeyFrames>-->
                                    <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Foreground" Storyboard.TargetName="DisplayText">
                                        <DiscreteObjectKeyFrame KeyTime="0">
                                            <DiscreteObjectKeyFrame.Value>
                                                <SolidColorBrush Color="#FF8D8D8D"/>
                                            </DiscreteObjectKeyFrame.Value>
                                        </DiscreteObjectKeyFrame>
                                    </ObjectAnimationUsingKeyFrames>
                                    <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Foreground" Storyboard.TargetName="Content">
                                        <DiscreteObjectKeyFrame KeyTime="0">
                                            <DiscreteObjectKeyFrame.Value>
                                                <SolidColorBrush Color="#FF8D8D8D"/>
                                            </DiscreteObjectKeyFrame.Value>
                                        </DiscreteObjectKeyFrame>
                                    </ObjectAnimationUsingKeyFrames>
                                </Storyboard>
                            </VisualState>
                        </VisualStateGroup>
                        <VisualStateGroup x:Name="FocusStates">
                            <VisualState x:Name="Focused">
                                <Storyboard>
                                    <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Visibility" Storyboard.TargetName="DisplayTextContent">
                                        <DiscreteObjectKeyFrame KeyTime="0">
                                            <DiscreteObjectKeyFrame.Value>
                                                <Visibility>Collapsed</Visibility>
                                            </DiscreteObjectKeyFrame.Value>
                                        </DiscreteObjectKeyFrame>
                                    </ObjectAnimationUsingKeyFrames>
                                    <!--<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Visibility" Storyboard.TargetName="FocusedVisual">
                                    <DiscreteObjectKeyFrame KeyTime="0">
                                        <DiscreteObjectKeyFrame.Value>
                                            <Visibility>Visible</Visibility>
                                        </DiscreteObjectKeyFrame.Value>
                                    </DiscreteObjectKeyFrame>
                                </ObjectAnimationUsingKeyFrames>-->
                                </Storyboard>
                            </VisualState>
                            <VisualState x:Name="NotFocused">
                                <Storyboard>
                                    <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Visibility" Storyboard.TargetName="DisplayTextContent">
                                        <DiscreteObjectKeyFrame KeyTime="0">
                                            <DiscreteObjectKeyFrame.Value>
                                                <Visibility>Visible</Visibility>
                                            </DiscreteObjectKeyFrame.Value>
                                        </DiscreteObjectKeyFrame>
                                    </ObjectAnimationUsingKeyFrames>
                                    <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Foreground" Storyboard.TargetName="EditorSite">
                                        <DiscreteObjectKeyFrame KeyTime="0">
                                            <DiscreteObjectKeyFrame.Value>
                                                <SolidColorBrush Color="Transparent"/>
                                            </DiscreteObjectKeyFrame.Value>
                                        </DiscreteObjectKeyFrame>
                                    </ObjectAnimationUsingKeyFrames>
                                </Storyboard>
                            </VisualState>
                        </VisualStateGroup>
                        <VisualStateGroup x:Name="ContentStates">
                            <VisualState x:Name="NotEmpty"/>
                            <VisualState x:Name="Empty">
                                <Storyboard>
                                    <DoubleAnimation Duration="0" To="0" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="DisplayTextContent"/>
                                    <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Visibility" Storyboard.TargetName="EmptyContent">
                                        <DiscreteObjectKeyFrame KeyTime="0">
                                            <DiscreteObjectKeyFrame.Value>
                                                <Visibility>Visible</Visibility>
                                            </DiscreteObjectKeyFrame.Value>
                                        </DiscreteObjectKeyFrame>
                                    </ObjectAnimationUsingKeyFrames>
                                </Storyboard>
                            </VisualState>
                        </VisualStateGroup>
                    </VisualStateManager.VisualStateGroups>
                    <Border x:Name="Border" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" CornerRadius="3" SnapsToDevicePixels="True">
                        <Grid x:Name="GridContainer">
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="*"/>
                                <ColumnDefinition Width="Auto"/>
                            </Grid.ColumnDefinitions>
                            <MaskedInput:PreviewInputTextBox x:Name="EditorSite" AcceptsReturn="{TemplateBinding AcceptsReturn}" Cursor="{TemplateBinding Cursor}" Foreground="{TemplateBinding Foreground}" FontWeight="{TemplateBinding FontWeight}" FontSize="{TemplateBinding FontSize}" FontFamily="{TemplateBinding FontFamily}" Height="{TemplateBinding Height}" IsReadOnly="{TemplateBinding IsReadOnly}" IsEnabled="{TemplateBinding IsEnabled}" MaskedInputControl="{x:Null}" Padding="{TemplateBinding Padding}" Style="{TemplateBinding TextBoxStyle}" TabIndex="{TemplateBinding TabIndex}" VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}">
                                <MaskedInput:PreviewInputTextBox.TextAlignment>
                                    <Binding Path="HorizontalContentAlignment" RelativeSource="{RelativeSource TemplatedParent}">
                                        <Binding.Converter>
                                            <MaskedInput:HorizontalContentAlignmentToTextAlignmentConverter/>
                                        </Binding.Converter>
                                    </Binding>
                                </MaskedInput:PreviewInputTextBox.TextAlignment>
                            </MaskedInput:PreviewInputTextBox>
                            <Border x:Name="DisplayTextContent" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="0" IsHitTestVisible="False" Visibility="Collapsed">
                                <MaskedInput:PreviewInputTextBox x:Name="DisplayText" Cursor="{TemplateBinding Cursor}" Foreground="{TemplateBinding Foreground}" FontWeight="{TemplateBinding FontWeight}" FontSize="{TemplateBinding FontSize}" FontFamily="{TemplateBinding FontFamily}" HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}" Height="{TemplateBinding Height}" IsTabStop="False" IsReadOnly="{TemplateBinding IsReadOnly}" IsEnabled="{TemplateBinding IsEnabled}" MaskedInputControl="{x:Null}" Padding="{TemplateBinding Padding}" Style="{TemplateBinding TextBoxStyle}" Text="{Binding Text, Mode=OneWay, RelativeSource={RelativeSource TemplatedParent}}" VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}">
                                    <MaskedInput:PreviewInputTextBox.TextAlignment>
                                        <Binding Path="HorizontalContentAlignment" RelativeSource="{RelativeSource TemplatedParent}">
                                            <Binding.Converter>
                                                <MaskedInput:HorizontalContentAlignmentToTextAlignmentConverter/>
                                            </Binding.Converter>
                                        </Binding>
                                    </MaskedInput:PreviewInputTextBox.TextAlignment>
                                </MaskedInput:PreviewInputTextBox>
                            </Border>
                            <Border x:Name="EmptyContent" IsHitTestVisible="False" Padding="5,0,0,0" Visibility="Collapsed">
                                <ContentControl x:Name="Content" ContentTemplate="{TemplateBinding EmptyContentTemplate}" Content="{TemplateBinding EmptyContent}" Foreground="{TemplateBinding Foreground}" FontStyle="Italic" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" IsTabStop="False" Margin="{TemplateBinding Padding}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
                            </Border>
                            <telerik:RadButton x:Name="ClearButton" Grid.Column="1" Command="{TemplateBinding ClearCommand}" IsTabStop="False" InnerCornerRadius="3" Opacity="0" Style="{TemplateBinding ClearButtonStyle}">
                                <telerik:RadButton.Visibility>
                                    <Binding Path="IsClearButtonVisible" RelativeSource="{RelativeSource TemplatedParent}">
                                        <Binding.Converter>
                                            <telerik:BooleanToVisibilityConverter/>
                                        </Binding.Converter>
                                    </Binding>
                                </telerik:RadButton.Visibility>
                            </telerik:RadButton>
                        </Grid>
                    </Border>
                    <!--<Border x:Name="FocusedVisual" BorderBrush="#FFFFC92B" BorderThickness="1" Background="{x:Null}" CornerRadius="5" Visibility="Collapsed"/>
                <Border x:Name="DisabledVisual" BorderBrush="#FFD9D9D9" BorderThickness="1" CornerRadius="3" Visibility="Collapsed"/>-->
                </Grid>
                <ControlTemplate.Triggers>
                    <Trigger Property="IsEnabled" Value="False">
                        <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
                        <Setter TargetName="Border" Property="BorderBrush" Value="#FFD9D9D9"/>
                    </Trigger>
                    <Trigger Property="Validation.HasError" Value="true">
                        <Setter TargetName="Border" Property="BorderBrush" Value="Red"/>
                    </Trigger>
                </ControlTemplate.Triggers>
</ControlTemplate>

后面代码中的依赖项属性和dp回调:

public String TextBoxText
        {
            get
            {
                return (String)GetValue(TextBoxTextProperty);
            }
            set
            {
                SetValue(TextBoxTextProperty, value);
            }
        }

        // Using a DependencyProperty as the backing store for TextBoxText.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty TextBoxTextProperty =
            DependencyProperty.Register("TextBoxText", typeof(String), typeof(FloatingLabelTextBox), new PropertyMetadata() { PropertyChangedCallback = UpdateTextBoxText });

        private static void UpdateTextBoxText(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            FloatingLabelTextBox control = d as FloatingLabelTextBox;

            if (control != null && e.NewValue != null)
            {
                if (!string.IsNullOrWhiteSpace(e.NewValue as string))
                {
                    control.FloatLabel();
                }
                else
                {
                    control.DockLabel();
                }
            }
        }

最后,我调用/使用控件的代码:

<controls:FloatingLabelTextBox LabelContent="Batch #"
                                       TextBoxText="{Binding BatchNumber.StringValue,
                                       Mode=TwoWay,
                                       UpdateSourceTrigger=LostFocus,
                                       ValidatesOnDataErrors=True,
                                       ValidatesOnExceptions=True,
                                       NotifyOnValidationError=True,
                                       Converter={StaticResource stringToInt}}"
                                       Validation.ErrorTemplate="{x:Null}"
                                       LabelForeground="{DynamicResource FloatingLabelTextBox.Label.Foreground}"
                                       LabelFontSize="12"
                                       TextBoxWidth="150"
                                       TextBoxHeight="30"
                                       HorizontalAlignment="Left"
                                       VerticalAlignment="Bottom"
                                       Grid.Row="1"
                                       IsEnabled="{Binding BatchNumber.IsReadOnly, Mode=OneWay, Converter={StaticResource inverseBool}}" />

请注意,我正在使用MVVM模式。我已经从这里建模了我的代码:Validation Error Templates For UserControl但我没有看到预期的结果。我根本没有看到任何验证边界。

我在这个问题上花了太长时间。任何帮助/建议将不胜感激。

1 个答案:

答案 0 :(得分:0)

我没有得到任何好的答案来解决这个问题。我尝试了所有我能想到的东西。我有信心这是可能的,但有一个截止日期,我需要得到一些东西。我的解决方案是重新控制控件,以便扩展基本控件。在这种情况下,我扩展了Telerik RadMaskedTextInput控件并覆盖了控件模板,将浮动标签添加到模板中,并使用一系列触发器来实现我所需要的。这允许我使用Telerik控件的本机验证,因此不必传递任何内容。对于我的情况,这可能是比创建用户控件更好的解决方案。如果需要更复杂的控制,我对如何完成这项工作没有好的建议,并希望看到评论或建议,以满足我的好奇心。感谢MethodMan花时间回应。