2012-06-27 38 views
15

我試圖綁定TextBoxdouble某些對象的屬性與UpdateSourceTrigger=PropertyChanged。我們的目標是在編輯過程中立即使輸入的值保持在允許範圍內(如果不是,則顯示錯誤)。我想要在模型層面實現驗證,即通過IDataErrorInfo驗證綁定到雙字段

當我綁定到int屬性時,所有的工作都很好,但是如果屬性爲double,那麼會出現令人沮喪的編輯行爲:在擦除數字小數部分的最後一位有效數字後 - 小數點分隔符會自動擦除(所有可能的小數零) 。例如,在從數字'12.03'擦除數字'3'之後,文本被改變爲'12'而不是'12.0'。

請幫忙。

下面是示例代碼:

MainWindow.xaml:

<Window x:Class="BindWithValidation.MainWindow" 
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
     Title="MainWindow" Height="80" Width="200" WindowStartupLocation="CenterOwner"> 

    <StackPanel> 
    <TextBox Width="100" Margin="10" Text="{Binding DoubleField, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}"> 
     <TextBox.Style> 
     <Style TargetType="TextBox"> 
      <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> 
     </TextBox.Style> 
    </TextBox> 
    </StackPanel> 
</Window> 

MainWindow.xaml.cs:

namespace BindWithValidation 
{ 
    public partial class MainWindow : Window 
    { 
    private UISimpleData _uiData = new UISimpleData(); 

    public MainWindow() 
    { 
     InitializeComponent(); 
     DataContext = _uiData; 
    } 
    } 
} 

UISimpleData.cs:

namespace BindWithValidation 
{ 
    public class UISimpleData : INotifyPropertyChanged, IDataErrorInfo 
    { 
    private double _doubleField = 12.03; 

    public double DoubleField 
    { 
     get 
     { 
     return _doubleField; 
     } 
     set 
     { 
     if (_doubleField == value) 
      return; 

     _doubleField = value; 
     RaisePropertyChanged("DoubleField"); 
     } 
    } 

    public string this[string propertyName] 
    { 
     get 
     { 
     string validationResult = null; 
     switch (propertyName) 
     { 
      case "DoubleField": 
      { 
      if (DoubleField < 2 || DoubleField > 5) 
       validationResult = "DoubleField is out of range"; 
      break; 
      } 

      default: 
      throw new ApplicationException("Unknown Property being validated on UIData"); 
     } 

     return validationResult; 
     } 
    } 

    public string Error { get { return "not implemented"; } } 

    public event PropertyChangedEventHandler PropertyChanged; 

    protected void RaisePropertyChanged(string property) 
    { 
     if (PropertyChanged != null) 
     PropertyChanged(this, new PropertyChangedEventArgs(property)); 
    } 
    } 
} 
+0

我想這是用格式化 - 因爲12相當於12.00,你試過使用StringFormat綁定? – Charleh

+0

是的,我試過了,但不喜歡編輯如何使用它。 StringFormat適合呈現,但在編輯期間,我想避免它。 – arudoy

回答

1

嘗試在你的bi上使用StringFormat nding:

<TextBox Width="100" Margin="10" Text="{Binding DoubleField, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True, StringFormat='0.0'}"> 

不知道該字符串格式是正確的,甚至因爲我沒有做過一期一會,但它只是試圖格式化帶小數位的值的例子

+0

感謝您的建議,但StringFormat不方便(請參閱上面的註釋)。如果事實上,我打算在TextBox將鬆散焦點後應用StringFormat,但是雖然它有焦點,但我希望避免StringFormat – arudoy

5

這可能很奇怪,但因爲你總是會有N位小數。

<TextBox.Text> 
    <Binding Path="DoubleField" StringFormat="{}{0:0.00}" UpdateSourceTrigger="PropertyChanged" ValidatesOnDataErrors="True"/> 
</TextBox.Text> 

如果有固定的小數位數不夠好,你可能必須編寫對待值作爲一個字符串,並將其轉換回雙轉換器。

+0

是的,我試過了。啓用S​​tringFormat編輯工作也不是很好。例如,使用從'12 .00'刪除鍵的StringFormat擦除小數點分隔符會導致'1200.00',光標位於'12'後面。所以,擦除整個小數部分是不可能的,這對用戶來說可能是相當令人驚訝的。 – arudoy

+0

我也試着寫我自己的轉換器。從字符串到雙精度的轉換工作正常,但後向轉換存在問題:轉換器如何知道它應該將小數點分隔符插入到值12的字符串表示形式中?用戶可以輸入'12'或'12'。甚至'12.0',並且這些全部具有值12,它被傳遞給轉換器 – arudoy

+0

這正是默認行爲如此做的原因。如果UI沒有「狀態」,很難猜測可見值應該是多少 - 也許一個自定義控件可以完成這項工作:/ –

9

我意識到我對派對有點晚了,但我發現了一個(我認爲)這個問題相當乾淨的解決方案。

一個聰明的轉換器,記住最後一個字符串轉換爲double並返回,如果它存在應該做你想做的一切。

請注意,當用戶更改文本框的內容時,ConvertBack將存儲用戶輸入的字符串,解析雙精度字符串,並將該值傳遞給視圖模型。緊接着,調用Convert以顯示新更改的值。此時,存儲的字符串不爲空,並將返回。

如果應用程序而不是用戶導致雙改只調用Convert。這意味着緩存的字符串將爲空,並且將在雙精度上調用標準的ToString()。

以這種方式,用戶在修改文本框的內容時避免了奇怪的意外,但應用程序仍然可以觸發更改。

public class DoubleToPersistantStringConverter : IValueConverter 
{ 
    private string lastConvertBackString; 

    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) 
    { 
     if (!(value is double)) return null; 

     var stringValue = lastConvertBackString ?? value.ToString(); 
     lastConvertBackString = null; 

     return stringValue; 
    } 

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) 
    { 
     if (!(value is string)) return null; 

     double result; 
     if (double.TryParse((string)value, out result)) 
     { 
      lastConvertBackString = (string)value; 
      return result; 
     } 

     return null; 
    } 
} 
+0

這是一個很好的解決方案。但是,在使用此轉換器的多個TextBoxes上同時調用NotifyPropertyChanged時,它會弄得一團糟。我建議將該屬性作爲轉換器的參數傳遞,並用'Dictionary '替換'lastConvertBackString'。這樣你就可以記住每個屬性的最後一個字符串。 – Octan

2

我遇到了同樣的問題,並且已經找到了一個非常簡單的解決方案:使用自定義的驗證,不返回「有效」時,文本中結束「」或「0」:

double val = 0; 
string tmp = value.ToString(); 

if (tmp.EndsWith(",") || tmp.EndsWith("0") || tmp.EndsWith(".")) 
{ 
    return new ValidationResult(false, "Enter another digit, or delete the last one."); 
} 
else 
{ 
    return ValidationResult.ValidResult; 
} 
+0

我認爲它會顯示錯誤,如果我輸入100 –

+0

正確。添加一個''tmp.Contains(',')|| tmp.Contains('。')''檢查'EndsWith(「。」)「'以避免這種情況。 – Digifaktur

3

問題是每當值更改時都要更新屬性。當您更改12.03〜12.0它是由xaml通過改變TextBox提供delay這樣

<TextBox Width="100" Margin="10" Text="{Binding DoubleField, UpdateSourceTrigger=PropertyChanged,Delay=500, ValidatesOnDataErrors=True}"> 

delay將通知和延遲時間後,設置屬性四捨五入到12

你可以看到的變化在米利秒。 更好地利用StringFormat這樣

<TextBox Width="100" Margin="10" Text="{Binding DoubleField, UpdateSourceTrigger=PropertyChanged,StringFormat=N2, ValidatesOnDataErrors=True}"> 
5

的浮點值結合到文本框的行爲已經改變 從.NET 4至4.5。對於.NET 4.5,默認情況下,不能再使用'UpdateSourceTrigger = PropertyChanged'輸入 分隔符(逗號或點)。

微軟表示,這(是)意圖

如果你仍然想使用「UpdateSourceTrigger =的PropertyChanged」,你 可以強制在.NET 4.5的應用程序的.NET 4的行爲,加入 以下行代碼到你App.xaml.cs的構造函數:

public App() 
{ 
    System.Windows.FrameworkCompatibilityPreferences 
       .KeepTextBoxDisplaySynchronizedWithTextProperty = false; 
} 

(塞巴斯蒂安力士 - 從here逐字複製)