TextBox并不总是更新

时间:2013-05-16 14:02:23

标签: c# wpf mvvm

我有以下TextBox:

<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));
}

我想要的只是一种快速而肮脏的方法来禁止非整数文本框的非数字字符(我知道它不完整,只是为了演示)。我不想仅仅通知有非法文本或任何内容,我想立即丢弃所有不允许输入的字符。

然而,TextBox表现得很奇怪。我仍然可以输入我想要的任何文字,它将显示为输入,例如“1AAA”。即使在此示例中已将属性正确清除为“1”,文本框仍显示“1aaa”。只有当我输入一个会导致_searchString更改的实际数字时,它才会更新显示的文本,例如当我有“1aaa2”时它会正确地更新为“12”。这是怎么回事?

3 个答案:

答案 0 :(得分:3)

这听起来像特定于视图的逻辑,因此我认为没有理由不使用视图后面的代码来控制它。就个人而言,我会在PreviewKeyDown TextBox上实现这种行为,丢弃任何非数字字符。

拥有可以重复使用的通用内容(例如自定义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通知时不会重新评估绑定。

要解决这个问题,您可以尝试暂时将该值设置为其他值,然后再将其设置为正则表达式值,并提出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");
    }
}

答案 1 :(得分:0)

我猜这与WPF的内置无限循环预防逻辑有关。如上所述,您的逻辑将通知WPF,每次调用“Set”时,属性都已更改。当WPF收到通知属性已更改时,它将更新控件。当控件更新时,它将(根据您的绑定)再次调用“Set”属性。无限的。 WPF旨在检测这些类型的循环并在某种程度上阻止它们 - 这可能是你最终陷入的陷阱。

我不确切知道这个逻辑是如何工作的,但我认为Rachel's answer会让你获得最好的结果。通常,ViewModel(您绑定的内容)应该是View,错误输入和所有内容的反映。 ViewModel应该能够验证输入(不知道它来自何处或如何输入)并防止错误的输入传播到模型(例如,通过传递到“错误状态”)。

您要做的是控制用户输入的内容,最好留给View逻辑。

答案 2 :(得分: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