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

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

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

4

3 回答 3

3

这听起来像是特定于视图的逻辑,所以我认为没有理由不使用视图隐藏代码来控制它。就我个人而言,我会在丢弃任何非数字字符的PreviewKeyDownon 上实现这种行为。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");
    }
}
于 2013-05-16T14:19:56.777 回答
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

于 2013-05-16T14:38:59.117 回答
0

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

我不知道这个逻辑到底是如何工作的,但我认为雷切尔的回答会给你带来最好的结果。一般来说,ViewModel(你要绑定的)应该是 View、错误输入和所有内容的反映。ViewModel 应该能够验证输入(不知道它来自哪里或如何输入)并防止错误输入传播到模型(例如,通过转换到“错误状态”)。

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

于 2013-05-16T14:29:00.513 回答