2012-05-31 42 views
0

我正在創建一個應用程序,我想要使用驗證規則,但在某些屏幕上沒有足夠的空間顯示出錯的字段旁邊出現的錯誤,所以我想把它放在字段底部的狀態欄中。從TextBox綁定ValidationRule到StatusBarItem並顯示錯誤信息

這個示例是我從網上拼湊出來的幾個比特,它給出了一個使用不同規則驗證並以不同方式顯示錯誤的窗體,但是我不能看到如何使用XAML將錯誤消息導入到StatusBarItem中。我確信有一個簡單的方法來做到這一點。任何人都可以幫助我嗎?

該示例是使用Framework 4.0在VS2010中編寫的。

MainWindow.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> 

MainWindow.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 

ValidationRules.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); 
     } 
    } 
    } 
} 

回答

0

UPDATE 我最近不得不處理中顯示,當多個錯誤​​信息日e鼠標懸停了一個錯誤圖標。我讓我的viewmodel實現IDataErrorInfo。然後我創建了一個類級字典,由每個控件的唯一標識符(我只是使用了一個枚舉)來控制,在我的viewmodel中爲每個控件保存一個錯誤。然後我創建了一個字符串類型的公共屬性並將其稱爲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屬性。

+0

沒有,不做任何事情因爲它沒有指向包含驗證錯誤的東西,並且可能有幾個項目在窗體上不只是1或2在窗體上的項目。我認爲驗證錯誤需要發送到狀態欄 - 或狀態欄中的文本塊,其方式與在windows資源中的觸發器和設置器區域中設置工具提示的方式相同,但我似乎正在研究如何製作 - 是文本框樣式指向狀態欄或其他外部項目的一部分 –

+0

剛更新了答案。看看這是否有助於你。 – Josh

+0

我剛剛回來了這個項目,所以將進一步調查並根據這個想法創建一個解決方案。謝謝 –