UWP - 文本框验证标志

时间:2016-05-21 18:21:45

标签: c# validation textbox uwp sign

我正在尝试实现一个文本框,当输入有效时,该文本框在“X”旁边的末尾有一个验证符号。 DataTemplate实现没有问题,但我不确定如何实现逻辑。

一种方法是TextChanged事件,然后尝试访问DataTemplate中的ValidSign元素。但我不太喜欢这种方法。

有没有办法扩展TextBox逻辑,因为首先必须有一些代码隐藏“X”按钮?

1 个答案:

答案 0 :(得分:3)

您可以使用VisualState实现此目的。这是一个例子:想法是通过继承TextBox类,自定义默认文本框样式,添加视觉状态并在DeleteButton旁边添加一个星形来创建自定义文本框。

此类是继承自TextBox类的自定义文本框。在这里,我们根据依赖项属性HasError的值设置控件的可视状态。如果HasError为true,我们转到InvalidState,否则我们转到ValidState。

  public class ValidatingTextBox : TextBox
  {
    public ValidatingTextBox()
    {
      // Tells this control to use the ValidatingTextBox style. 
      // This will be the customized textbox style below. 
      this.DefaultStyleKey = typeof(ValidatingTextBox);
    }

    public bool HasError
    {
      get { return (bool)GetValue(HasErrorProperty); }
      set { SetValue(HasErrorProperty, value); }
    }

    /// <summary>
    /// This is a dependency property that will indicate if there's an error. 
    /// This DP can be bound to a property of the VM.
    /// </summary>
    public static readonly DependencyProperty HasErrorProperty =
        DependencyProperty.Register("HasError", typeof(bool), typeof(ValidatingTextBox), new PropertyMetadata(false, HasErrorUpdated));


    // This method will update the Validation visual state which will be defined later in the Style
    private static void HasErrorUpdated(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
      ValidatingTextBox textBox = d as ValidatingTextBox;

      if (textBox != null)
      {
        if (textBox.HasError)
          VisualStateManager.GoToState(textBox, "InvalidState", false);
        else
          VisualStateManager.GoToState(textBox, "ValidState", false);
      }
    }
  }

接下来,我们需要复制default TextBox style并通过添加可视状态和开始标记来自定义它,这将指示存在验证错误。

  <Style TargetType="local:ValidatingTextBox">
    <Setter Property="MinWidth" Value="{ThemeResource TextControlThemeMinWidth}"/>
    <Setter Property="MinHeight" Value="{ThemeResource TextControlThemeMinHeight}"/>
    <Setter Property="Foreground" Value="{ThemeResource SystemControlForegroundBaseHighBrush}"/>
    <Setter Property="Background" Value="{ThemeResource SystemControlBackgroundAltHighBrush}"/>
    <Setter Property="BorderBrush" Value="{ThemeResource SystemControlForegroundChromeDisabledLowBrush}"/>
    <Setter Property="SelectionHighlightColor" Value="{ThemeResource SystemControlHighlightAccentBrush}"/>
    <Setter Property="BorderThickness" Value="{ThemeResource TextControlBorderThemeThickness}"/>
    <Setter Property="FontFamily" Value="{ThemeResource ContentControlThemeFontFamily}"/>
    <Setter Property="FontSize" Value="{ThemeResource ControlContentThemeFontSize}"/>
    <Setter Property="ScrollViewer.HorizontalScrollMode" Value="Auto"/>
    <Setter Property="ScrollViewer.VerticalScrollMode" Value="Auto"/>
    <Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Hidden"/>
    <Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Hidden"/>
    <Setter Property="ScrollViewer.IsDeferredScrollingEnabled" Value="False"/>
    <Setter Property="Padding" Value="{ThemeResource TextControlThemePadding}"/>
    <Setter Property="Template">
      <Setter.Value>
        <ControlTemplate TargetType="local:ValidatingTextBox">
          <Grid>
            <Grid.Resources>
              <Style x:Name="DeleteButtonStyle" TargetType="Button">
                <Setter Property="Template">
                  <Setter.Value>
                    <ControlTemplate TargetType="Button">
                      <Grid x:Name="ButtonLayoutGrid" BorderBrush="{ThemeResource TextBoxButtonBorderThemeBrush}"
                                                  BorderThickness="{TemplateBinding BorderThickness}"
                                                  Background="{ThemeResource TextBoxButtonBackgroundThemeBrush}">
                        <VisualStateManager.VisualStateGroups>

                          <VisualStateGroup x:Name="CommonStates">
                            <VisualState x:Name="Normal"/>
                            <VisualState x:Name="PointerOver">
                              <Storyboard>
                                <ObjectAnimationUsingKeyFrames Storyboard.TargetName="GlyphElement"
                                                           Storyboard.TargetProperty="Foreground">
                                  <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SystemControlHighlightAccentBrush}"/>
                                </ObjectAnimationUsingKeyFrames>
                              </Storyboard>
                            </VisualState>
                            <VisualState x:Name="Pressed">
                              <Storyboard>
                                <ObjectAnimationUsingKeyFrames Storyboard.TargetName="ButtonLayoutGrid"
                                                           Storyboard.TargetProperty="Background">
                                  <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SystemControlHighlightAccentBrush}"/>
                                </ObjectAnimationUsingKeyFrames>
                                <ObjectAnimationUsingKeyFrames Storyboard.TargetName="GlyphElement"
                                                           Storyboard.TargetProperty="Foreground">
                                  <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SystemControlHighlightAltChromeWhiteBrush}"/>
                                </ObjectAnimationUsingKeyFrames>
                              </Storyboard>
                            </VisualState>
                            <VisualState x:Name="Disabled">
                              <Storyboard>
                                <DoubleAnimation Storyboard.TargetName="ButtonLayoutGrid"
                                             Storyboard.TargetProperty="Opacity"
                                             To="0"
                                             Duration="0"/>
                              </Storyboard>
                            </VisualState>
                          </VisualStateGroup>
                        </VisualStateManager.VisualStateGroups>
                        <TextBlock x:Name="GlyphElement"
                                Foreground="{ThemeResource SystemControlForegroundChromeBlackMediumBrush}"
                                VerticalAlignment="Center"
                                HorizontalAlignment="Center"
                                FontStyle="Normal"
                                FontSize="12"
                                Text="&#xE10A;"
                                FontFamily="{ThemeResource SymbolThemeFontFamily}"
                                AutomationProperties.AccessibilityView="Raw"/>
                      </Grid>
                    </ControlTemplate>
                  </Setter.Value>
                </Setter>
              </Style>
            </Grid.Resources>
            <VisualStateManager.VisualStateGroups>
              <VisualStateGroup x:Name="CommonStates">
                <VisualState x:Name="Disabled">
                  <Storyboard>
                    <ObjectAnimationUsingKeyFrames Storyboard.TargetName="HeaderContentPresenter"
                                                 Storyboard.TargetProperty="Foreground">
                      <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SystemControlDisabledBaseMediumLowBrush}"/>
                    </ObjectAnimationUsingKeyFrames>
                    <ObjectAnimationUsingKeyFrames Storyboard.TargetName="BackgroundElement"
                                               Storyboard.TargetProperty="Background">
                      <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SystemControlDisabledTransparentBrush}"/>
                    </ObjectAnimationUsingKeyFrames>
                    <ObjectAnimationUsingKeyFrames Storyboard.TargetName="BorderElement"
                                               Storyboard.TargetProperty="Background">
                      <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SystemControlBackgroundBaseLowBrush}"/>
                    </ObjectAnimationUsingKeyFrames>
                    <ObjectAnimationUsingKeyFrames Storyboard.TargetName="BorderElement"
                                               Storyboard.TargetProperty="BorderBrush">
                      <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SystemControlDisabledBaseLowBrush}"/>
                    </ObjectAnimationUsingKeyFrames>
                    <ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentElement"
                                               Storyboard.TargetProperty="Foreground">
                      <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SystemControlDisabledChromeDisabledLowBrush}"/>
                    </ObjectAnimationUsingKeyFrames>
                    <ObjectAnimationUsingKeyFrames Storyboard.TargetName="PlaceholderTextContentPresenter"
                                               Storyboard.TargetProperty="Foreground">
                      <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SystemControlDisabledChromeDisabledLowBrush}"/>
                    </ObjectAnimationUsingKeyFrames>
                  </Storyboard>
                </VisualState>
                <VisualState x:Name="Normal"/>
                <VisualState x:Name="PointerOver">
                  <Storyboard>
                    <ObjectAnimationUsingKeyFrames Storyboard.TargetName="BorderElement"
                                               Storyboard.TargetProperty="BorderBrush">
                      <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SystemControlHighlightChromeAltLowBrush}"/>
                    </ObjectAnimationUsingKeyFrames>
                    <ObjectAnimationUsingKeyFrames Storyboard.TargetName="BackgroundElement"
                                               Storyboard.TargetProperty="Opacity">
                      <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextControlBackgroundHoverOpacity}"/>
                    </ObjectAnimationUsingKeyFrames>
                  </Storyboard>
                </VisualState>
                <VisualState x:Name="Focused">
                  <Storyboard>
                    <ObjectAnimationUsingKeyFrames Storyboard.TargetName="PlaceholderTextContentPresenter"
                                               Storyboard.TargetProperty="Foreground">
                      <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SystemControlPageTextChromeBlackMediumLowBrush}"/>
                    </ObjectAnimationUsingKeyFrames>
                    <ObjectAnimationUsingKeyFrames Storyboard.TargetName="BackgroundElement"
                                               Storyboard.TargetProperty="Background">
                      <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SystemControlBackgroundChromeWhiteBrush}"/>
                    </ObjectAnimationUsingKeyFrames>
                    <ObjectAnimationUsingKeyFrames Storyboard.TargetName="BackgroundElement"
                                               Storyboard.TargetProperty="Opacity">
                      <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextControlBackgroundFocusedOpacity}"/>
                    </ObjectAnimationUsingKeyFrames>
                    <ObjectAnimationUsingKeyFrames Storyboard.TargetName="BorderElement"
                                               Storyboard.TargetProperty="BorderBrush">
                      <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SystemControlHighlightAccentBrush}"/>
                    </ObjectAnimationUsingKeyFrames>
                    <ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentElement"
                                               Storyboard.TargetProperty="Foreground">
                      <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SystemControlForegroundChromeBlackHighBrush}"/>
                    </ObjectAnimationUsingKeyFrames>
                    <ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentElement"
                                               Storyboard.TargetProperty="RequestedTheme">
                      <DiscreteObjectKeyFrame KeyTime="0" Value="Light"/>
                    </ObjectAnimationUsingKeyFrames>
                  </Storyboard>
                </VisualState>
              </VisualStateGroup>
              <VisualStateGroup x:Name="ButtonStates">
                <VisualState x:Name="ButtonVisible">
                  <Storyboard>
                    <ObjectAnimationUsingKeyFrames Storyboard.TargetName="DeleteButton"
                                               Storyboard.TargetProperty="Visibility">
                      <DiscreteObjectKeyFrame KeyTime="0">
                        <DiscreteObjectKeyFrame.Value>
                          <Visibility>Visible</Visibility>
                        </DiscreteObjectKeyFrame.Value>
                      </DiscreteObjectKeyFrame>
                    </ObjectAnimationUsingKeyFrames>
                  </Storyboard>
                </VisualState>
                <VisualState x:Name="ButtonCollapsed"/>
              </VisualStateGroup>
              <VisualStateGroup x:Name="ValidationState">
                <VisualState x:Name="InvalidState">
                  <Storyboard>
                    <ObjectAnimationUsingKeyFrames Storyboard.TargetName="ValidationMark"
                                               Storyboard.TargetProperty="Visibility">
                      <DiscreteObjectKeyFrame KeyTime="0">
                        <DiscreteObjectKeyFrame.Value>
                          <Visibility>Visible</Visibility>
                        </DiscreteObjectKeyFrame.Value>
                      </DiscreteObjectKeyFrame>
                    </ObjectAnimationUsingKeyFrames>
                  </Storyboard>
                </VisualState>
                <VisualState x:Name="ValidState">
                  <Storyboard>
                    <ObjectAnimationUsingKeyFrames Storyboard.TargetName="ValidationMark"
                                               Storyboard.TargetProperty="Visibility">
                      <DiscreteObjectKeyFrame KeyTime="0">
                        <DiscreteObjectKeyFrame.Value>
                          <Visibility>Collapsed</Visibility>
                        </DiscreteObjectKeyFrame.Value>
                      </DiscreteObjectKeyFrame>
                    </ObjectAnimationUsingKeyFrames>
                  </Storyboard>
                </VisualState>
              </VisualStateGroup>
            </VisualStateManager.VisualStateGroups>
            <Grid.ColumnDefinitions>
              <ColumnDefinition Width="*"/>
              <ColumnDefinition Width="Auto"/>
              <ColumnDefinition Width="Auto"/>
            </Grid.ColumnDefinitions>
            <Grid.RowDefinitions>
              <RowDefinition Height="Auto"/>
              <RowDefinition Height="*"/>
            </Grid.RowDefinitions>
            <Border x:Name="BackgroundElement"
                Grid.Row="1"
                Background="{TemplateBinding Background}"
                Margin="{TemplateBinding BorderThickness}"
                Opacity="{ThemeResource TextControlBackgroundRestOpacity}"
                Grid.ColumnSpan="3"
                Grid.RowSpan="1"/>
            <Border x:Name="BorderElement"
                Grid.Row="1"
                BorderBrush="{TemplateBinding BorderBrush}"
                BorderThickness="{TemplateBinding BorderThickness}"
                Grid.ColumnSpan="3"
                Grid.RowSpan="1"/>
            <ContentPresenter x:Name="HeaderContentPresenter"
                          x:DeferLoadStrategy="Lazy"
                          Visibility="Collapsed"
                          Grid.Row="0"
                          Foreground="{ThemeResource SystemControlForegroundBaseHighBrush}"
                          Margin="0,0,0,8"
                          Grid.ColumnSpan="2"
                          Content="{TemplateBinding Header}"
                          ContentTemplate="{TemplateBinding HeaderTemplate}"
                          FontWeight="Normal"/>
            <ScrollViewer x:Name="ContentElement"
                      Grid.Row="1"
                      HorizontalScrollMode="{TemplateBinding ScrollViewer.HorizontalScrollMode}"
                      HorizontalScrollBarVisibility="{TemplateBinding ScrollViewer.HorizontalScrollBarVisibility}"
                      VerticalScrollMode="{TemplateBinding ScrollViewer.VerticalScrollMode}"
                      VerticalScrollBarVisibility="{TemplateBinding ScrollViewer.VerticalScrollBarVisibility}"
                      IsHorizontalRailEnabled="{TemplateBinding ScrollViewer.IsHorizontalRailEnabled}"
                      IsVerticalRailEnabled="{TemplateBinding ScrollViewer.IsVerticalRailEnabled}"
                      IsDeferredScrollingEnabled="{TemplateBinding ScrollViewer.IsDeferredScrollingEnabled}"
                      Margin="{TemplateBinding BorderThickness}"
                      Padding="{TemplateBinding Padding}"
                      IsTabStop="False"
                      AutomationProperties.AccessibilityView="Raw"
                      ZoomMode="Disabled"/>
            <ContentControl x:Name="PlaceholderTextContentPresenter"
                      Grid.Row="1"
                      Foreground="{ThemeResource SystemControlPageTextBaseMediumBrush}"
                      Margin="{TemplateBinding BorderThickness}"
                      Padding="{TemplateBinding Padding}"
                      IsTabStop="False"
                      Grid.ColumnSpan="2"
                      Content="{TemplateBinding PlaceholderText}"
                      IsHitTestVisible="False"/>
            <Button x:Name="DeleteButton"
                Grid.Row="1"
                Style="{StaticResource DeleteButtonStyle}"
                BorderThickness="{TemplateBinding BorderThickness}"
                Margin="{ThemeResource HelperButtonThemePadding}"
                IsTabStop="False"
                Grid.Column="1"
                Visibility="Collapsed"
                FontSize="{TemplateBinding FontSize}"
                MinWidth="34"
                VerticalAlignment="Stretch"/>
            <TextBlock Text="*" FontSize="24" FontWeight="SemiBold" HorizontalAlignment="Center" VerticalAlignment="Center" Grid.Row="1" Grid.Column="2"
                       Foreground="Red" Margin="5,2,5,0"
                       x:Name="ValidationMark"/>
          </Grid>
        </ControlTemplate>
      </Setter.Value>
    </Setter>
  </Style>

上面的代码是整个样式,但这里是你需要关注的行。

这是我们添加视觉状态的部分。我们添加了几个视觉状态,即 ValidState InvalidState 。在 ValidState 上时,名为“ ValidationMark ”的元素的可见性设置为可见。在 InvalidState 上时,它应为折叠

   <VisualStateGroup x:Name="ValidationState">
                <VisualState x:Name="InvalidState">
                  <Storyboard>
                    <ObjectAnimationUsingKeyFrames Storyboard.TargetName="ValidationMark"
                                               Storyboard.TargetProperty="Visibility">
                      <DiscreteObjectKeyFrame KeyTime="0">
                        <DiscreteObjectKeyFrame.Value>
                          <Visibility>Visible</Visibility>
                        </DiscreteObjectKeyFrame.Value>
                      </DiscreteObjectKeyFrame>
                    </ObjectAnimationUsingKeyFrames>
                  </Storyboard>
                </VisualState>
                <VisualState x:Name="ValidState">
                  <Storyboard>
                    <ObjectAnimationUsingKeyFrames Storyboard.TargetName="ValidationMark"
                                               Storyboard.TargetProperty="Visibility">
                      <DiscreteObjectKeyFrame KeyTime="0">
                        <DiscreteObjectKeyFrame.Value>
                          <Visibility>Collapsed</Visibility>
                        </DiscreteObjectKeyFrame.Value>
                      </DiscreteObjectKeyFrame>
                    </ObjectAnimationUsingKeyFrames>
                  </Storyboard>
                </VisualState>
              </VisualStateGroup>

这是在DeleteButton之后添加星标的部分。

<Button x:Name="DeleteButton"
            Grid.Row="1"
            Style="{StaticResource DeleteButtonStyle}"
            BorderThickness="{TemplateBinding BorderThickness}"
            Margin="{ThemeResource HelperButtonThemePadding}"
            IsTabStop="False"
            Grid.Column="1"
            Visibility="Collapsed"
            FontSize="{TemplateBinding FontSize}"
            MinWidth="34"
            VerticalAlignment="Stretch"/>
        <TextBlock Text="*" FontSize="24" FontWeight="SemiBold" HorizontalAlignment="Center" VerticalAlignment="Center" Grid.Row="1" Grid.Column="2"
                   Foreground="Red" Margin="5,2,5,0"
                   x:Name="ValidationMark"/>

现在在xaml中,我们只是将HasError依赖项属性绑定到viewmodel中的一个属性,该属性将告诉它是否有效。

  <ctrl:ValidatingTextBox Text="{Binding TextValue, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" HasError="{Binding IsInvalid, UpdateSourceTrigger=PropertyChanged}"/>

例如在视图模型中,我们说它在文本为“有效”时有效,而不是它将显示星形。

     public class TestViewModel : ViewModelBase   {
        private bool _IsInvalid;
        public bool IsInvalid
        {
          get { return _IsInvalid; }
          set { SetProperty(ref _IsInvalid, value); }
        }

        private string _TextValue;
        public string TextValue
        {
          get
          {
            return _TextValue;
          }
          set
          {
            if (SetProperty(ref _TextValue, value))
            {
              Validate(_TextValue);
            }
          }
        }

        private void Validate(string text)
        {
          if (text == "Valid")
          {
            IsInvalid = false;
          }
          else
          {
            IsInvalid = true;
          }
        }   
}

enter image description here

enter image description here