【问题标题】:TextBox MVVM DataBinding文本框 MVVM 数据绑定
【发布时间】:2012-01-24 21:36:04
【问题描述】:

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

例如,如果用户输入:

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

我想按字符获取输入字符(我有一个状态模式类,它对添加的每个字符做出反应),但我需要将结果作为字符串返回。

如何使用 MVVM 设计将数据从文本框传输到文本框?

【问题讨论】:

    标签: c# wpf data-binding mvvm


    【解决方案1】:

    您可以在ViewModel 上将TextBoxes 绑定到Properties,并使用INotifyPropertyChanged 事件来进行验证/修改/其他操作。

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

    【讨论】:

    • 您只能从 Dispatcher 引发 PropertyChanged 事件(即不能从后台线程)。如果您尝试这样做,它将引发异常。
    【解决方案2】:

    应该TextBoxText 属性绑定到ViewModel 的属性就足够了。

    例如:

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

    MyViewModel 绑定到DataContextFormTextBox 或其他...简而言之,以某种方式将其提供给您的TextBox

    定义一个Converter,在TextBox 集合的Text 属性中每次 都会调用其方法。

    public 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 类型的对象

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

    这只是一个例子。

    This 是对ValueConverters 的另一个很好的解释。

    【讨论】:

      【解决方案3】:

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

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

      接下来,TextBox 控件中有一个功能/错误。如果 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"/>
      

      【讨论】:

        猜你喜欢
        • 2014-01-29
        • 2015-10-16
        • 2012-11-24
        • 1970-01-01
        • 2011-09-30
        • 2011-07-01
        • 2013-12-04
        • 1970-01-01
        • 2012-02-11
        相关资源
        最近更新 更多