我正在写一个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?
答案 0 :(得分:2)
您可以将TextBoxes
绑定到Properties
上ViewModel
并使用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
Form
,TextBox
或其他任何内容的TextBox
...简而言之,以某种方式将其提供给Converter
。
定义Text
将调用每个<{em>} TextBox
个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
考虑到DataContext
是MyViewModel
类型的对象
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"/>