0

我正在创建一个我想使用验证规则的应用程序,但是在某些屏幕上没有足够的空间来显示错误字段旁边的结果错误,所以我想把它放在底部的状态栏中场地。

这个示例来自我从网络上拼凑的几个位,它提供了一个使用不同规则进行验证并以不同方式显示错误的表单,但我看不到如何使用 XAML 将错误消息放入 StatusBarItem。我确信有一种简单的方法可以做到这一点。任何人都可以帮助我吗?

该示例是在 VS2010 中使用 Framework 4.0 编写的。

主窗口.xaml

<Window x:Class="SampleValidation.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:c="clr-namespace:SWL.Libraries.SysText"
        Title="Sample ValidationRule WPF" Height="350" Width="525"
        Loaded="Window_Loaded" WindowStartupLocation="CenterScreen">
  <Window.Resources>
    <ControlTemplate x:Key="validationTemplate">
      <!--
      <TextBlock Foreground="Red" FontSize="20">***</TextBlock>
      -->
      <DockPanel LastChildFill="True">
        <TextBlock DockPanel.Dock="Right" Foreground="Red" Margin="5" FontSize="8pt" Text="***" />
        <AdornedElementPlaceholder />
      </DockPanel>
    </ControlTemplate>
    <Style x:Key="textBoxInError1" TargetType="{x:Type TextBox}">
      <Style.Triggers>
        <Trigger Property="Validation.HasError" Value="true">
          <Setter Property="ToolTip"
                  Value="{Binding RelativeSource={x:Static RelativeSource.Self},
                  Path=(Validation.Errors)[0].ErrorContent}" />
        </Trigger>
      </Style.Triggers>
    </Style>
    <Style x:Key="textBoxInError2"  TargetType="{x:Type TextBox}">
      <Setter Property="Validation.ErrorTemplate">
        <Setter.Value>
          <ControlTemplate>
            <DockPanel LastChildFill="True">
              <TextBlock DockPanel.Dock="Right" Foreground="Red" Margin="5" FontSize="8pt"
                         Text="{Binding ElementName=MyAdorner, Path=AdornedElement.(Validation.Errors)[0].ErrorContent}" />
              <Border BorderBrush="Red" BorderThickness="2">
                <AdornedElementPlaceholder Name="MyAdorner" />
              </Border>
            </DockPanel>
          </ControlTemplate>
        </Setter.Value>
      </Setter>
      <Style.Triggers>
        <Trigger Property="Validation.HasError" Value="true">
          <Setter Property="ToolTip"
                  Value="{Binding RelativeSource={RelativeSource Self},
                  Path=(Validation.Errors)[0].ErrorContent}" />
        </Trigger>
      </Style.Triggers>
    </Style>
  </Window.Resources>
  <Grid>
    <TextBlock Height="24" HorizontalAlignment="Left" Margin="36,73,0,0" Name="textBlock1"
               Text="Node Address:" VerticalAlignment="Top" Width="87" />
    <TextBlock Height="24" HorizontalAlignment="Left" Margin="36,112,0,0" Name="textBlock2"
               Text="Node Name:" VerticalAlignment="Top" Width="78" />
    <TextBox Height="24" HorizontalAlignment="Left" Margin="129,70,0,0" Name="textBox1"
             VerticalAlignment="Top" Width="119" TextChanged="textBox1_TextChanged"
             TabIndex="0" Style="{StaticResource textBoxInError2}"
             Validation.ErrorTemplate="{StaticResource validationTemplate}">
      <Binding Path="NodeAddress" UpdateSourceTrigger="PropertyChanged">
        <Binding.ValidationRules>
          <c:NumberRangeRule Min="1" Max="100" />
        </Binding.ValidationRules>
      </Binding>
    </TextBox>
    <TextBox Height="24" HorizontalAlignment="Left" Margin="129,109,0,0" Name="textBox2"
             VerticalAlignment="Top" Width="119" TextChanged="textBox2_TextChanged"
             TabIndex="1" Style="{StaticResource textBoxInError2}">
      <Binding Path="NodeName" UpdateSourceTrigger="PropertyChanged">
        <Binding.ValidationRules>
          <c:NameFormatRule MinLength="6" MaxLength="9" />
        </Binding.ValidationRules>
      </Binding>
    </TextBox>
    <StatusBar Height="23" HorizontalAlignment="Stretch" Margin="0,0,0,0"
               Name="myStatusBar" VerticalAlignment="Bottom">
      <StatusBarItem x:Name="errorStatusBarItem" Content="No errors" />
    </StatusBar>
    <Button Content="Close" Height="29" HorizontalAlignment="Left" Margin="108,227,0,0"
            Name="btnCLOSE" VerticalAlignment="Top" Width="85" Click="btnCLOSE_Click" TabIndex="3" />
    <Button Content="Apply" Height="29" HorizontalAlignment="Left" Margin="297,227,0,0"
            Name="btnAPPLY" VerticalAlignment="Top" Width="85" Click="btnAPPLY_Click" TabIndex="2" />
  </Grid>
</Window>

主窗口.cs

using System;
using System.Collections.Generic;  
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace SampleValidation {
  /// <summary>
  /// Interaction logic for MainWindow.xaml
  /// </summary>
  public partial class MainWindow : Window {
    public int NodeAddress { get; set; }
    public string NodeName { get; set; }
    public bool IsAllLoaded { get; set; }

    public MainWindow() {
      NodeAddress = 1;
      NodeName = "freddy";
      IsAllLoaded = false;
      InitializeComponent();
      btnAPPLY.Visibility = System.Windows.Visibility.Hidden;
      DataContext = this;
    }

    private void btnAPPLY_Click(object sender, RoutedEventArgs e) {
      // if there are no format errors reported by the validation rules
      Validator.ErrorText = "";
      if (Validator.IsValid(this))
        // Save the data
        btnAPPLY.Visibility = System.Windows.Visibility.Hidden; // hide the button indicating nothing new to save
      else
        MessageBox.Show("Cant Save Changes - Error in form\r\n" + Validator.ErrorText, "Save not allowed", MessageBoxButton.OK, MessageBoxImage.Error);
    }

    private void btnCLOSE_Click(object sender, RoutedEventArgs e) {
      if (btnAPPLY.Visibility != System.Windows.Visibility.Hidden) {
        MessageBoxResult myAnswer = MessageBox.Show("Save Changes?", "Confirmation", MessageBoxButton.YesNoCancel);

        if (myAnswer == MessageBoxResult.Cancel)
          return;
        if (myAnswer == MessageBoxResult.Yes)
          btnAPPLY_Click(sender, e);
      }
      this.Close();
    }

    private void Window_Loaded(object sender, RoutedEventArgs e) {
      IsAllLoaded = true;
    }

    private void ShowModified() {
      if (IsAllLoaded)
          btnAPPLY.Visibility = System.Windows.Visibility.Visible;
    } // ShowModified

    private void textBox2_TextChanged(object sender, TextChangedEventArgs e) {
      ShowModified();
    }

    private void textBox1_TextChanged(object sender, TextChangedEventArgs e) {
      ShowModified();
    }
  } // class MainWindow

  public static class Validator {
    public static string ErrorText { get; set; }

    static Validator() {
      ErrorText = "";
    }

    public static bool IsValid(DependencyObject parent) {
      // Validate all the bindings on the parent
      bool valid = true;
      LocalValueEnumerator localValues = parent.GetLocalValueEnumerator();

      while (localValues.MoveNext()) {
        LocalValueEntry entry = localValues.Current;

        if (BindingOperations.IsDataBound(parent, entry.Property)) {
          Binding binding = BindingOperations.GetBinding(parent, entry.Property);

          if (binding.ValidationRules.Count > 0) {
            BindingExpression expression = BindingOperations.GetBindingExpression(parent, entry.Property);
            expression.UpdateSource();

            if (expression.HasError) {
              ErrorText = expression.ValidationError.ErrorContent.ToString();
              valid = false;
            }
          }
        }
      }

      // Validate all the bindings on the children
      System.Collections.IEnumerable children = LogicalTreeHelper.GetChildren(parent);

      foreach (object obj in children) {
        if (obj is DependencyObject) {
          DependencyObject child = (DependencyObject)obj;

          if (!IsValid(child)) {
            valid = false;
          }
        }
      }
      return valid;
    }
  } // class Validator

验证规则.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Globalization;
using System.Windows.Controls;

namespace SWL.Libraries.SysText {
  public class NumberRangeRule : ValidationRule {
    private int _min;
    private int _max;

    public NumberRangeRule() { }

    public int Min {
      get { return _min; }
      set { _min = value; }
    }

    public int Max {
      get { return _max; }
      set { _max = value; }
    }

    public override ValidationResult Validate(object value, CultureInfo cultureInfo) {
      int val = 0;

      try {
        if (((string)value).Length > 0)
          val = Int32.Parse((String)value);
      } catch (Exception e) {
        return new ValidationResult(false, "Illegal Characters or " + e.Message);
      }

      if ((val < Min) || (val > Max)) {
        return new ValidationResult(false, "Please Enter Number in Range: " + Min + " - " + Max + ".");
      } else {
        return new ValidationResult(true, null);
      }
    }
  }

  public class NameFormatRule : ValidationRule {
    private int _minLength;
    private int _maxLength;

    public NameFormatRule() { }

    public int MinLength {
      get { return _minLength; }
      set { _minLength = value; }
    }

    public int MaxLength 
      get { return _maxLength; }
      set { _maxLength = value; }
    }

    public override ValidationResult Validate(object value, CultureInfo cultureInfo) {
      try {
        if (((string)value).Length > 0) {
          if (((string)value).Length < MinLength || ((string)value).Length > MaxLength)
            return new ValidationResult(false, String.Format ("Enter a string of {0} to {1} characters in length", MinLength, MaxLength));
          return new ValidationResult(true, null);
        }
        return new ValidationResult(true, null);
      } catch (Exception e) {
        return new ValidationResult(false, "Illegal Characters or " + e.Message);
      }
    }
  }
}
4

1 回答 1

0

更新 我最近不得不处理当鼠标悬停在错误图标上时显示的多个错误消息。我让我的视图模型实现了 IDataErrorInfo。然后我在我的视图模型中创建了一个类级别的字典,由每个控件的唯一标识符(我只是使用了一个枚举)作为键,以保存每个控件的一个错误。然后我创建了一个字符串类型的公共属性并将其命名为ErrorText。ErrorText 的 getter 迭代错误字典并将所有错误构建到一个字符串中。

这是一个例子,它可能需要调整。我知道这是非常简化的,但应该让你朝着一个方向前进。对于复杂的验证,您仍然可以使用您创建的 ValidationRule 对象进行验证,然后只需检查返回的 ValidationResult 对象的 IsValid 属性。

// NOTE:  The enum member name matches the name of the property bound to each textbox
public enum ControlIDs
{
    TextBox1Value = 0,
    TextBox2Value
}

public class MyViewModel : IDataErrorInfo
{
    private readonly Dictionary<ControlIDs, string> errors;

    public string ErrorText
    {
        get
        {
            if (errors.ContainsKey(ControlIDs.TextBox1) && errors.ContainsKey(ControlIDs.TextBox2))
            {
                return "Errors: " + errors[ControlsIDs.TextBox1] + ", " + errors[ControlsIDs.TextBox2];
            }

            if (errors.ContainsKey(ControlIDs.TextBox1))
            {
                return "Error: " + errors[ControlsIDs.TextBox1];
            }

            if (errors.ContainsKey(ControlIDs.TextBox2))
            {
                return "Error: " + errors[ControlsIDs.TextBox2];
            }
        }
    }

    public MyViewModel()
    {
        errors = new Dictionary<ControlIDs, string>();
    }

    private void UpdateErrorCollection(ControlIDs fieldKey, string error)
    {
        if (errors.ContainsKey(fieldKey))
        {
            errors[fieldKey] = error;
        }
        else
        {
            errors.Add(fieldKey, error);
        }

        OnPropertyChanged("ErrorText");
    }

    #region IDataErrorInfo

    public string Error
    {
        get { throw new NotImplementedException(); }
    }

    public string this[string columnName]
    {
        string error = string.Empty;
        if (columnName == ControlIDs.TextBox1Value.ToString())
        {
            if (string.IsNullOrWhiteSpace(TextBox1Value))
            {
                error = "TextBox1 must contain a value";
                UpdateErrorCollection(ControlIDs.TextBox1Value, error);
            }
            else
            {
                errors.Remove(ControlIDs.TextBox1Value);
            }
        }
        else if (columnName == ControlIDs.TextBox2Value))
        {
            if (string.IsNullOrWhiteSpace(TextBox2Value))
            {
                error = "TextBox2 must contain a value";
                UpdateErrorCollection(ControlIDs.TextBox2Value, error);
            }
            else
            {
                errors.Remove(ControlIDs.TextBox2Value);
            }
        }

        // returning null indicates success
        return !string.IsNullOrWhiteSpace(error) ? error : null;
    }

    #endregion
}

现在只需将您的 StatusBarItem TextBlock 绑定到 ErrorText 属性。

于 2012-05-31T16:57:28.083 回答