2010-01-17 9 views
12

Tôi đã xác thực được kết nối với mô hình được gắn với vùng chứa TextBox. Khi cửa sổ lần đầu tiên mở lỗi xác nhận xuất hiện khi mô hình trống, tôi không muốn thấy lỗi xác thực cho đến khi gửi cửa sổ hoặc văn bản trong TextBox đã thay đổi hoặc bị mất tiêu điểm.Xác thực Hộp văn bản WPF

Đây là TextBox:

<TextBox Text="{Binding 
        Path=Firstname, 
        UpdateSourceTrigger=PropertyChanged, 
        ValidatesOnDataErrors=True}" 
     Width="124" 
     Height="24"/> 

Làm thế nào điều này có thể đạt được?

Trả lời

22

Điều này thực sự phụ thuộc vào thực hiện lại IDataErrorInfo. Nếu bạn căn cứ nó xung quanh một từ điển thông báo lỗi bạn có thể kiểm soát khi xác thực chạy mà thêm vào danh sách đó. Bạn thường sẽ muốn làm điều đó từ setters tài sản của bạn (như bất cứ khi nào bạn gọi PropertyChange), đây gọi CheckValidationState:

public string this[string columnName] 
    { 
     get 
     { 
      return ValidateProperty(columnName); 
     } 
    } 

    public Dictionary<string, string> Errors { get; private set; } 

    protected void SetError(string propertyName, string errorMessage) 
    { 
     Debug.Assert(!String.IsNullOrEmpty(propertyName), "propertyName is null or empty."); 
     if (String.IsNullOrEmpty(propertyName)) 
      return; 

     if (!String.IsNullOrEmpty(errorMessage)) 
     { 
      if (Errors.ContainsKey(propertyName)) 
       Errors[propertyName] = errorMessage; 
      else 
       Errors.Add(propertyName, errorMessage); 
     } 
     else if (Errors.ContainsKey(propertyName)) 
      Errors.Remove(propertyName); 

     NotifyPropertyChanged("Errors"); 
     NotifyPropertyChanged("Error"); 
     NotifyPropertyChanged("Item[]"); 
    } 

    protected virtual string ValidateProperty(string propertyName) 
    { 
     return Errors.ContainsKey(propertyName) ? Errors[propertyName] : null; 
    } 

    protected virtual bool CheckValidationState<T>(string propertyName, T proposedValue) 
    { 
     // your validation logic here 
    } 

Bạn có thể sau đó cũng bao gồm một phương pháp mà xác nhận tất cả các thuộc tính của bạn (như trong một tiết kiệm):

protected bool Validate() 
    { 
     if (Errors.Count > 0) 
      return false; 

     bool result = true; 
     foreach (PropertyInfo propertyInfo in GetType().GetProperties()) 
     { 
      if (!CheckValidationState(propertyInfo.Name, propertyInfo.GetValue(this, null))) 
       result = false; 
      NotifyPropertyChanged(propertyInfo.Name); 
     } 
     return result; 
    } 

UPDATE:

tôi sẽ khuyên bạn nên đặt đoạn code trên vào một lớp cơ sở ViewModel để bạn có thể tái sử dụng nó. Sau đó bạn có thể tạo ra một lớp có nguồn gốc như thế này:

public class SampleViewModel : ViewModelBase 
{ 
    private string _firstName; 

    public SampleViewModel() 
    { 
     Save = new DelegateCommand<object>(SaveExecuted); 
    } 

    public DelegateCommand<object> Save { get; private set; } 

    public string FirstName 
    { 
     get { return _firstName; } 
     set 
     { 
      if (_firstName == value) 
       return; 

      CheckValidationState("FirstName", value); 

      _firstName = value; 
      NotifyPropertyChanged("FirstName"); 
     } 
    } 

    public void SaveExecuted(object obj) 
    { 
     bool isValid = Validate(); 
     MessageBox.Show(isValid ? "Saved" : "Validation Error. Save canceled"); // TODO: do something appropriate to your app here 
    } 

    protected override bool CheckValidationState<T>(string propertyName, T proposedValue) 
    { 
     // your validation logic here 
     if (propertyName == "FirstName") 
     { 
      if (String.IsNullOrEmpty(proposedValue as String)) 
      { 
       SetError(propertyName, "First Name is required."); 
       return false; 
      } 
      else if (proposedValue.Equals("John")) 
      { 
       SetError(propertyName, "\"John\" is not an allowed name."); 
       return false; 
      } 
      else 
      { 
       SetError(propertyName, String.Empty); // clear the error 
       return true; 
      } 
     } 
     return true; 
    } 
} 

Trong trường hợp này tôi đang sử dụng một DelegateCommand để kích hoạt các hoạt động tiết kiệm nhưng nó có thể là bất cứ điều gì mà làm cho một lời gọi phương thức để thực hiện tiết kiệm. Thiết lập này cho phép trạng thái trống ban đầu hiển thị là hợp lệ trong giao diện người dùng nhưng thay đổi hoặc cuộc gọi để Lưu cập nhật trạng thái xác thực. Bạn cũng có thể nhận được tổng quát hơn và phức tạp hơn theo cách bạn thực sự thực hiện xác thực để nó không kết thúc bằng một phương pháp (ở đây có một số giả định về loại) nhưng điều này được đơn giản hóa để giúp dễ dàng bắt đầu với .

+0

Cảm ơn bạn đã trả lời John, bạn có thể mở rộng câu trả lời của mình một chút được không. Tôi đã cố gắng thực hiện những gì bạn đã thảo luận và bị mất một chút. Cảm ơn ... – Burt

+0

Burt - Tôi đã cập nhật để thêm mã khác để cho biết cách một lớp có thể triển khai thực hiện điều này mà tôi hy vọng sẽ điền vào một số lỗ cho bạn. Điều đó có giúp bạn không? –

+0

Cảm ơn John thực sự đánh giá cao nó, đã đi lên bầu cử bạn nhưng bằng cách nào đó quản lý để loại bỏ một phiếu bầu nó đã không cho phép tôi thêm nó một lần nữa. – Burt

-1

Những gì tôi làm, tôi donno nếu đây là cách chính xác (tôi rất vui khi tìm hiểu, bây giờ là một cơ hội), nhưng trong initializer của thực thể hoặc mô hình tôi chạy tất cả các validators.

7

Nếu bạn đang triển khai IDataErrorInfo, tôi đã đạt được điều này bằng cách kiểm tra các giá trị null trong việc thực hiện logic xác thực. Khi tạo một cửa sổ mới, việc kiểm tra null sẽ ngăn không cho logic xác nhận của bạn bị kích hoạt. Ví dụ:

public partial class Product : IDataErrorInfo 
{ 
    #region IDataErrorInfo Members 

    public string Error 
    { 
     get { return null; } 
    } 

    public string this[string columnName] 
    { 
     get 
     { 
      if (columnName == "ProductName") 
      { 
       // Only apply validation if there is actually a value 
       if (this.ProductName != null) 
       { 
        if (this.ProductName.Length <= 0 || this.ProductName.Length > 25) 
         return "Product Name must be between 1 and 25 characters"; 
       } 
      } 

      return null; 
     } 
    } 

    #endregion 
} 

Ngoài ra, nếu bạn muốn bắn xác nhận trên TextBox.LostFocus, thay đổi liên kết của bạn để LostFocus, như sau:

<TextBox Text="{Binding    
       Path=Firstname,    
       UpdateSourceTrigger=LostFocus,    
       ValidatesOnDataErrors=True}"    
    Width="124"    
    Height="24"/> 
+0

Nó có thể có giá trị đối với một số lĩnh vực để được null mặc dù điều gì xảy ra trong trường hợp đó? – Burt

+1

Nếu null là một mục nhập hợp lệ, khi đó việc xác thực sẽ không trả lại lỗi nếu bạn không cho phép. Tất cả phụ thuộc vào cách bạn triển khai logic nghiệp vụ của mình. Ví dụ: nếu trường có thể là rỗng hoặc trống, chỉ cho phép các ngoại lệ đó được thực hiện trong logic của bạn. Bạn có thể cho chúng tôi một ví dụ về tất cả những gì bạn muốn kiểm tra trên một lĩnh vực cụ thể không? – Brent

4

trong tệp app.xaml của bạn, bạn cần sử dụng kiểu hộp văn bản tùy chỉnh để xác thực hộp văn bản mà không cần bất kỳ thành phần nào của bên thứ ba.

enter image description here

<Setter Property="Template"> 
    <Setter.Value> 
     <ControlTemplate TargetType="{x:Type TextBox}"> 
      <Grid Name="test"> 
       <Border Background="{StaticResource TextBackColor}" 
         BorderBrush="#FF888888" 
         x:Name="Bd" 
         CornerRadius="1" 
         BorderThickness="1"> 
        <ScrollViewer Margin="0" x:Name="PART_ContentHost"/> 
       </Border> 
       <Image Name="ErrorImage" 
         Width="15" 
         Height="15" 
         Margin="0,0,4,0" 
         Source="Images/validate.png" 
         HorizontalAlignment="Right"> 
       </Image> 
      </Grid> 
     </ControlTemplate> 
    </Setter.Value> 
</Setter>