2013-05-16 85 views
5

我有以下文本框:文本框並不總是更新

<TextBox Text="{Binding SearchString, 
       UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" /> 

綁定到以下屬性:

private string _searchString; 
public string SearchString 
{ 
    get 
    { 
     return _searchString; 
    } 
    set 
    { 
     value = Regex.Replace(value, "[^0-9]", string.Empty);    
     _searchString = value; 
     DoNotifyPropertyChanged("SearchString"); 
    } 
} 

類從實現INotifyPropertyChanged

public event PropertyChangedEventHandler PropertyChanged; 
protected void DoNotifyPropertyChanged(string propertyName) 
{ 
    if (PropertyChanged != null) 
     PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); 
} 
基類繼承

我只想要一個快速和骯髒的方式來禁止非數字字符的整數ly文本框(我知道它不完整,僅供演示)。我不想僅僅通知有非法文本或任何東西,我想立即放棄所有不允許輸入的字符。

但是,TextBox行爲異常。我仍然可以輸入任何我想要的文本,它將以輸入的方式顯示,例如「1AAA」。即使在本例中該屬性已被正確清理爲「1」,文本框仍顯示「1aaa」。只有當我輸入一個實際的數字時,會導致_searchString改變,它也會更新顯示的文本,例如當我有「1aaa2」時,它會正確更新爲「12」。這裏有什麼問題?

回答

3

這聽起來像特定於視圖的邏輯,所以我沒有理由不使用視圖後面的代碼來控制它。就我個人而言,我會在TextBox上執行PreviewKeyDown這種行爲,它會丟棄任何非數字字符。

它可能不會傷害有一些通用的,你可以重複使用,如自定義NumbersOnlyTextBox控制,或AttachedProperty你可以連接到您的TextBox指定只允許數字。

事實上,我記得創建一個附加屬性,允許您指定一個文本框的正則表達式,它將限制字符輸入到正則表達式。我有一段時間沒有使用它,所以你可能想要測試它或者更新它,但這裏是代碼。

// When set to a Regex, the TextBox will only accept characters that match the RegEx 
#region AllowedCharactersRegex Property 

/// <summary> 
/// Lets you enter a RegexPattern of what characters are allowed as input in a TextBox 
/// </summary> 
public static readonly DependencyProperty AllowedCharactersRegexProperty = 
    DependencyProperty.RegisterAttached("AllowedCharactersRegex", 
             typeof(string), typeof(TextBoxProperties), 
             new UIPropertyMetadata(null, AllowedCharactersRegexChanged)); 

// Get 
public static string GetAllowedCharactersRegex(DependencyObject obj) 
{ 
    return (string)obj.GetValue(AllowedCharactersRegexProperty); 
} 

// Set 
public static void SetAllowedCharactersRegex(DependencyObject obj, string value) 
{ 
    obj.SetValue(AllowedCharactersRegexProperty, value); 
} 

// Events 
public static void AllowedCharactersRegexChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e) 
{ 
    var tb = obj as TextBox; 
    if (tb != null) 
    { 
     if (e.NewValue != null) 
     { 
      tb.PreviewTextInput += Textbox_PreviewTextChanged; 
      DataObject.AddPastingHandler(tb, TextBox_OnPaste); 
     } 
     else 
     { 
      tb.PreviewTextInput -= Textbox_PreviewTextChanged; 
      DataObject.RemovePastingHandler(tb, TextBox_OnPaste); 
     } 
    } 
} 

public static void TextBox_OnPaste(object sender, DataObjectPastingEventArgs e) 
{ 
    var tb = sender as TextBox; 

    bool isText = e.SourceDataObject.GetDataPresent(DataFormats.Text, true); 
    if (!isText) return; 

    var newText = e.SourceDataObject.GetData(DataFormats.Text) as string; 
    string re = GetAllowedCharactersRegex(tb); 
    re = "[^" + re + "]"; 

    if (Regex.IsMatch(newText.Trim(), re, RegexOptions.IgnoreCase)) 
    { 
     e.CancelCommand(); 
    } 
} 

public static void Textbox_PreviewTextChanged(object sender, TextCompositionEventArgs e) 
{ 
    var tb = sender as TextBox; 
    if (tb != null) 
    { 
     string re = GetAllowedCharactersRegex(tb); 
     re = "[^" + re + "]"; 

     if (Regex.IsMatch(e.Text, re, RegexOptions.IgnoreCase)) 
     { 
      e.Handled = true; 
     } 
    } 
} 

#endregion // AllowedCharactersRegex Property 

它會像這樣使用:

<TextBox Text="{Binding SearchString, UpdateSourceTrigger=PropertyChanged}" 
     local:TextBoxHelpers.AllowedCharactersRegex="[0-9]" /> 

但至於爲什麼它不會更新UI。 UI知道該值實際上沒有改變,因此在接收到PropertyChange通知時不會重新評估綁定。

爲了解決這個問題,你可以嘗試在將其設置爲正則表達式值之前暫時將其值設置爲其他值,並且提出通知以便UI重新評估綁定,但老實說這不是一個理想的解。

private string _searchString; 
public string SearchString 
{ 
    get 
    { 
     return _searchString; 
    } 
    set 
    { 
     value = Regex.Replace(value, "[^0-9]", string.Empty);  

     // If regex value is the same as the existing value, 
     // change value to null to force bindings to re-evaluate 
     if (_searchString == value) 
     { 
      _searchString = null; 
      DoNotifyPropertyChanged("SearchString"); 
     } 

     _searchString = value; 
     DoNotifyPropertyChanged("SearchString"); 
    } 
} 
+0

你是對的。但爲什麼它不這樣工作? – user1064519

+0

@ user1064519當PropertyChange通知被引發時,UI知道值實際上沒有改變,所以它不必費心去重新評估綁定並刷新UI。請參閱我的回答更新:) – Rachel

+1

如果以這種方式工作,最終會出現無限循環:鍵入一個鍵將更改文本框,該文本框設置屬性,該屬性將觸發PropertyChanged事件,該事件將更新綁定,這將改變文本框,這將設置屬性......等等無限。 –

0

我想這與WPF的內置無限循環預防邏輯有關。按照書面說明,您的邏輯會告知WPF,每次調用「Set」時,屬性都會發生更改。當通知WPF該屬性已更改時,它將更新該控件。當控件更新時,它會(根據你的綁定)再次調用「Set」屬性。無限廣告。 WPF的目的是檢測這些類型的循環,並在一定程度上阻止它們 - 這可能是你陷入了陷阱。

我不知道這個邏輯究竟是如何工作的,但我認爲Rachel's answer會得到你最好的結果。一般來說,ViewModel(你綁定的是什麼)應該是View的反映,糟糕的輸入和全部。 ViewModel應該能夠驗證輸入(不知道它來自哪裏或輸入的方式),並防止傳播到模型的錯誤輸入(例如,通過拖曳到「錯誤狀態」)。

你想要做的是控制用戶輸入什麼,這可能是更好的留給查看邏輯。

0

你爲什麼不看

BindingOperations.GetBindingExpressionBase(_textBoxName, TextBox.TextProperty).UpdateTarget(); 

更新您的XAML

<TextBox x:Name="_textBoxName" Text="{Binding SearchString, 
       UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" /> 

這迫使從源代碼更新的目標,你正在使用的DependencyProperty和你的控制不會更新因爲它在發送到綁定源時知道該值。

MSDN:http://msdn.microsoft.com/en-us/library/system.windows.data.bindingexpressionbase.updatetarget.aspx