2013-05-16 12 views
5

を更新していない私は、次のテキストボックスを持っているのTextBoxは常に

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)); 
} 
を実装基本クラスから継承します

私が望むのは、整数型の非数値文字を禁止するためのすばやく汚れた方法ですテキストボックス(私はそれが完全ではないことを知っている、デモのためだけに)。私は、不法なテキストや何かがあるという単なる通知を望んでいません、私は許可されていない入力上のすべての文字をすぐに破棄したいと思います。

しかし、TextBoxは奇妙な動作をしています。私はまだ私が望むテキストを入力することができます、それは入力されたように表示されます。 "1aaa"。この例ではプロパティが適切に "1"にクリーンアップされていても、テキストボックスには "1aaa"と表示されます。 _searchStringを変更する実際の数字を入力した場合にのみ、表示されたテキストも更新されます。たとえば、「1aaa2」の場合、「12」に正しく更新されます。ここで何が問題なの?

答えて

3

これはビュー固有のロジックのように聞こえるので、ビューを制御するためにコードビハインドを使用しない理由はありません。個人的に私はPreviewKeyDownTextBoxに数字以外の文字を捨てて、この種の動作を実装します。 コントロールなどの一般的なものを持っていることや、TextBoxに添付して数値のみを許可することを指定できるAttachedPropertyなどは、おそらく害ではないでしょう。

実際には、テキストボックスの正規表現を指定することができる添付プロパティを作成することを覚えています。文字入力をその正規表現だけに制限します。私はしばらくそれを使用していないので、おそらくそれをテストしたり、それを更新したりしたいと思うでしょうが、ここにコードがあります。

// 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がバインディングを再評価するようにPropertyChange通知を呼び出すことができます。しかし、実際は理想的ではありません溶液。

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 UIは、PropertyChange通知が発生したときに値が実際に変更されていないことを知っているため、バインディングを再評価してUIを更新する必要はありません。私の答えへの更新を参照してください:) – Rachel

+1

この方法でうまくいけば、無限ループになります。キーを入力するとPropertyChangedイベントを発生させるプロパティを設定するテキストボックスが変更され、これはテキストボックスを変更し、プロパティを設定します...など、広告の無限になります。 –

0

これは、WPFの組み込みの無限ループ防止ロジックと関係があります。書かれているように、あなたのロジックはWPFにプロパティが "Set"が呼び出されるたびに変更されたことを通知します。プロパティが変更されたことがWPFに通知されると、コントロールが更新されます。コントロールが更新されると、(バインドに従って) "Set"プロパティを再度呼び出します。広告無限。 WPFはこれらの種類のループを検出してある程度防ぐために設計されています。これはおそらくあなたが閉じ込めたトラップです。

このロジックの仕組みはわかりませんが、私はRachel's answerあなたは最高の結果をもたらします。一般的に、ViewModel(バインドするもの)は、View、入力の悪さ、およびすべてを反映する必要があります。 ViewModelは入力を検証し(入力元や入力方法を知らずに)、不正入力がモデルに伝播するのを防ぐことができます(たとえば、「エラー状態」に移行する)。

あなたがしようとしているのは、ユーザーが入力しているものを制御することです。これは、おそらくViewロジックの方が良いでしょう。

あなたが

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

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

あなたのXAMLを更新する。これは、あなたがたDependencyPropertyを使用していて、あなたのコントロールが更新されません、ターゲットにソースからの更新を強制的に見ていないのはなぜ