TextBox MVVM DataBinding

时间:2012-01-24 21:36:05

标签: c# wpf data-binding mvvm

-->

我正在写一个WPF计算器应用程序。在这个应用程序中,我有TextBox根据OnWindowKeyDown事件获取和设置输入(输入插入/验证并作为用户类型返回到TextBox)。

例如,如果用户键入:

3-> validate == true--> print to TextBox '3'
y-> validate == false -> ignore
3-> validate == true --> print to TeextBox "33"

我想通过char获取输入char(我有一个State Pattern类可以对添加的每个char做出反应),但是我需要将结果作为字符串返回。

如何使用MVVM设计将数据传输到textBox?

3 个答案:

答案 0 :(得分:2)

您可以将TextBoxes绑定到PropertiesViewModel并使用INotifyPropertyChanged事件进行验证/修改/无论如何。

您可以从UI或后台更新属性,并且会自动为用户更新。

答案 1 :(得分:1)

足以将Text属性TextBox绑定到ViewModel的属性。

例如:

//ViewModel 
public class MyViewModel : INotifyPropertyChanged
{
     private string textBoxText = string.Empty;
     public string TextBoxText 
     {
        get {return textBoxText;}
        set {
           textBoxText = value;
           OnPropertyChanged("TextBoxText "..);
        }
     }
}

MyViewModel绑定到DataContext FormTextBox或其他任何内容的TextBox ...简而言之,以某种方式将其提供给Converter

定义Text将调用每个<{em>} TextBoxpublic class TextContenyConverter: IValueConverter { public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { // validate input and return appropriate value } public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { throw new NotSupportedException(); } } 属性的方法。

XAML

考虑到DataContextMyViewModel类型的对象

,在TextContenyConverterObject中绑定后

其中TextContenyConverter是类型为ValueConverters的对象,定义为静态资源。

这只是一个例子。

This是对{{1}}的又一个很好的解释。

答案 2 :(得分:1)

使用ValueConverter的问题在于您依赖于表示层来实现域逻辑。你说你有某种状态模式类,它确实听起来像模型的一部分(MVVM中的第一个'M')。

如果您将绑定设置为{Binding .... UpdateSourceTrigger = PropertyChanged},则每次用户输入单个字符时,您将获得发送给您View Model的值。然后,您需要验证对setter的每次调用。

接下来,TextBox控件中存在一个功能/错误。如果它是更改的源,TextBox将不会监听绑定的PropertyChanged事件。这意味着如果您输入“y”并且您的setter实际上将属性设置为“”然后引发PropertyChanged事件,您仍然会看到“y”:(

有一篇帖子关注此内容(http://stackoverflow.com/questions/3905227/coerce-a-wpf-textbox-not-working-anymore-in-net-4-0)但是他们正在使用事件,他们没有做MVVM。

刚刚为WPF项目做了这个,我最终得到了一个附属财产。我的模型中的所有逻辑都是我的ViewModel包装的。我能够对我的逻辑进行单元测试,并将附加属性添加到样式中,以便我可以多次重复使用。

我写的代码看起来像这样。

public sealed class TextBoxBehaviour : DependencyObject
{
    #region CoerceValue Attached property

    public static bool GetCoerceValue(DependencyObject obj)
    {
        return (bool)obj.GetValue(CoerceValueProperty);
    }
    public static void SetCoerceValue(DependencyObject obj, bool value)
    {
        obj.SetValue(CoerceValueProperty, value);
    }

    /// <summary>
    /// Gets or Sets whether the TextBox should reevaluate the binding after it pushes a change (either on LostFocus or PropertyChanged depending on the binding).
    /// </summary>
    public static readonly DependencyProperty CoerceValueProperty =
        DependencyProperty.RegisterAttached("CoerceValue", typeof(bool), typeof(TextBoxBehaviour), new UIPropertyMetadata(false, CoerceValuePropertyChanged));

    static void CoerceValuePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var textbox = d as TextBox;
        if (textbox == null) 
            return;

        if ((bool)e.NewValue)
        {
            if (textbox.IsLoaded)
            {
                PrepareTextBox(textbox);
            }
            else
            {
                textbox.Loaded += OnTextBoxLoaded;
            }
        }
        else
        {
            textbox.TextChanged -= OnCoerceText;
            textbox.LostFocus-= OnCoerceText;
            textbox.Loaded -= OnTextBoxLoaded;
        }
    }

    static void OnTextBoxLoaded(object sender, RoutedEventArgs e)
    {
        var textbox = (TextBox)sender;
        PrepareTextBox(textbox);
        textbox.Loaded -= OnTextBoxLoaded;
    }

    static void OnCoerceText(object sender, RoutedEventArgs e)
    {
        var textBox = (TextBox)sender;
        var selectionStart = textBox.SelectionStart;
        var selectionLength = textBox.SelectionLength;

        textBox.GetBindingExpression(TextBox.TextProperty).UpdateTarget();

        if (selectionStart < textBox.Text.Length) textBox.SelectionStart = selectionStart;
        if (selectionStart + selectionLength < textBox.Text.Length) textBox.SelectionLength = selectionLength;
    }

    private static void PrepareTextBox(TextBox textbox)
    {
        var binding = textbox.GetBindingExpression(TextBox.TextProperty).ParentBinding;
        var newBinding = binding.Clone();
        newBinding.ValidatesOnDataErrors = true;
        textbox.SetBinding(TextBox.TextProperty, newBinding);

        if (newBinding.UpdateSourceTrigger == UpdateSourceTrigger.PropertyChanged)
        {
            textbox.TextChanged += OnCoerceText;
        }
        else if (newBinding.UpdateSourceTrigger == UpdateSourceTrigger.LostFocus || newBinding.UpdateSourceTrigger == UpdateSourceTrigger.Default)
        {
            textbox.LostFocus += OnCoerceText;
        }
    }
    #endregion
}

然后你必须实现setter(看起来你已经是这样)并将附加属性添加到绑定到ViewModel的文本框中。

<TextBox Text="{Binding Number, UpdateSourceTrigger=PropertyChanged}"
         myNamespace:TextBoxBehaviour.CoerceValue="True"/>