2011-04-25 61 views
1

我有一個窗體上的WPF文本框,以允許輸入一個URI。使用WPF文本框與URI轉換器,無效輸入擦除文本框

我試圖用數據轉換器來做到這一點。問題是當文本框綁定更新和文本框不包含有效的URI時,

  • 數據轉換器返回空;
  • 它將我的模型屬性設置爲null;
  • 這會導致屬性更改事件觸發;
  • 該文本框值設置爲空字符串,擦了用戶輸入無效

我是一個新手WPF,我茫然地找到使用數據轉換器的簡單圖案不會導致這種行爲。我認爲必須有一個標準模式來使用,我知道我是否是一個有經驗的WPF程序員。看看棱鏡4中包含的例子,似乎有兩種不同的方法使用。我不喜歡他們兩個。

第一種方法是當我的模型屬性設置爲null時引發異常,並將其捕獲並顯示爲驗證錯誤。問題是我希望該屬性能夠設置爲空 - 每次打開表單時,這些字段都設置爲其先前的值。如果應用程序以前從未運行過,則URI將被設置爲空 - 這不會引發異常。另外,使用例外進行驗證非常困難。

第二種方法是當屬性設置爲null時,將模型的驗證狀態設置爲包含屬性無效但不實際更新屬性。我認爲這很糟糕。它導致模型內部不一致,聲稱DCSUri無效,但包含之前有效的DCSUri值。

我用來避免這些問題的方法是在我的ViewModel中有一個字符串DCSUri,它只更新我的模型的Uri類型DCSUri屬性(如果它是有效的URI)。但我更喜歡使用轉換器並將我的文本框直接綁定到我的模型的方法。

我的轉換代碼:

/// <summary> 
/// Converter from Uri to a string and vice versa. 
/// </summary> 
public class StringToUriConverter : IValueConverter 
{ 
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 
    { 
     return value; 
    } 

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 
    { 
     Uri uri = null; 
     string stringValue = value as string; 
     if (stringValue != null) 
      Uri.TryCreate(stringValue, UriKind.Absolute, out uri); 
     return uri; 
    } 
} 

的XAML的文本框:

<TextBox Grid.Row="1" Grid.Column="1" Name="DCSUriTextBox" 
      Text="{Binding Path=DCSLoadSettings.DCSUri, Mode=TwoWay, UpdateSourceTrigger=LostFocus, ValidatesOnExceptions=True, NotifyOnValidationError=True, ValidatesOnDataErrors=True, Converter={StaticResource StringToUriConverter} }" 
      HorizontalAlignment="Stretch" Height="Auto" VerticalAlignment="Center" Margin="5,0,20,0" IsReadOnly="{Binding Path=IsNotReady}" Grid.ColumnSpan="2" /> 

而對於我的模型內的DCSUri屬性的代碼:

/// <summary> 
    /// The Uri of the DCS instance being provided configuration 
    /// </summary> 
    public Uri DCSUri 
    { 
     get 
     { 
      return mDCSUri; 
     } 
     set 
     { 
      if (!Equals(value, mDCSUri)) 
      { 
       mDCSUri = value; 
       this["DCSUri"] = value == null 
        ? "Must provide a Uri for the DCS instance being provided configuration" 
        : string.Empty; 
       RaisePropertyChanged(() => DCSUri); 
      } 
     } 
    } 

回答

5

您應該使用ValidationRules驗證,並以正確的方式命名您的轉換器;我想接近它像這樣(假設你希望能夠到URI設置爲null):

public class UriToStringConverter : IValueConverter 
{ 
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) 
    { 
     Uri input = value as Uri; 
     return input == null ? 
      String.Empty : input.ToString(); 
    } 

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) 
    { 
     string input = value as string; 
     return String.IsNullOrEmpty(input) ? 
      null : new Uri(input, UriKind.Absolute); 
    } 
} 
public class UriValidationRule : ValidationRule 
{ 
    public override ValidationResult Validate(object value, System.Globalization.CultureInfo cultureInfo) 
    { 
     string input = value as string; 
     if (String.IsNullOrEmpty(input)) // Valid input, converts to null. 
     { 
      return new ValidationResult(true, null); 
     } 
     Uri outUri; 
     if (Uri.TryCreate(input, UriKind.Absolute, out outUri)) 
     { 
      return new ValidationResult(true, null); 
     } 
     else 
     { 
      return new ValidationResult(false, "String is not a valid URI"); 
     } 
    } 
} 

然後使用它像這樣(或通過定義轉換器和規則作爲一種資源某處):

<TextBox MinWidth="100"> 
    <TextBox.Text> 
     <Binding Path="Uri"> 
      <Binding.ValidationRules> 
       <vr:UriValidationRule /> 
      </Binding.ValidationRules> 
      <Binding.Converter> 
       <vc:UriToStringConverter/> 
      </Binding.Converter> 
     </Binding> 
    </TextBox.Text> 
</TextBox> 

如果輸入文本沒有通過驗證,轉換器不會被調用,這就是爲什麼我沒有TryCreate或類似的東西在裏面。

在CodeProject上有a decent article about input validation,您可能會發現它有幫助。


要爲空測試值,你可以用另一種轉換器和一個輔助的TextBlock:

public class NullToStringConverter : IValueConverter 
{ 
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) 
    { 
     return value == null ? 
      "NULL" : value; 
    } 

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) 
    { 
     throw new NotSupportedException(); 
    } 
} 
<TextBlock> 
    <TextBlock.Text> 
     <Binding Path="Uri"> 
      <Binding.Converter> 
       <vc:NullToStringConverter/> 
      </Binding.Converter> 
     </Binding> 
    </TextBlock.Text> 
</TextBlock> 
+0

感謝HB ......我必須承認,我發現微軟的例子惱人的狀態有時候,因爲我一直使用的實踐來自於Prism包含的參考實現。他們對ValidationRules等沒有提及,並鼓勵您實現IDataErrorInfo或INotifyDataErrorInfo。有沒有IDataErrorInfo/INotifyDataErrorInfo的地方,還是應該使用ValidationRules完成驗證? – SamStephens 2011-04-26 01:00:38

+0

我無法回答,因爲我對這些接口沒有經驗,對不起。從查看MSDN引用看,它看起來像IDataErrorInfo是古老的,而ValiationRule只有參考版本回到.NET 3.0,所以我懷疑ValidationRules是首選,特別是因爲它被集成到綁定引擎中。 – 2011-04-26 01:10:57

+0

http://stackoverflow.com/questions/2980853/idataerrorinfo-vs-validationrule-vs-exception/2982656#2982656有一個很好的解釋。基本上,在模型中使用IDataErrorInfo來處理錯誤,在視圖中使用ValidationRule來處理錯誤。就我而言,無效的Uri在視圖中是錯誤的,所以您給我的ValidationRule實現是合適的。謝謝您的幫助! – SamStephens 2011-04-26 21:16:37