Проблема с использованием ValueConverter заключается в том, что вы полагаетесь на слой Presentation в том, что похоже на доменную логику.Вы говорите, что у вас есть какой-то класс шаблонов состояний, который действительно звучит как часть модели (первая буква «M» в MVVM).
Если для привязки установлено значение {Binding .... UpdateSourceTrigger =PropertyChanged} вы будете получать значение, отправляемое вам View Model каждый раз, когда пользователь вводит один символ.Затем вам нужно проверять каждый вызов к установщику.
Далее, есть элемент / ошибка в элементе управления TextBox.TextBox не будет прослушивать события PropertyChanged для привязки, если она была источником изменения.Это означает, что если вы введете «y», и ваш установщик фактически установит свойство на «», а затем вызовет событие PropertyChanged, вы все равно увидите «y»: (
Есть сообщение, которое смотрит на это (/2389790/prinuditelno-ne-rabotaet-bolshe-tekstovogo-polya-wpf-v-net-4-0) но поскольку они используют события, они не выполняют MVVM.
После того, как я только что сделал это для проекта WPF, у меня появилось свойство Attached. Вся логика была в моей модели, которую обернул мой 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
}
тогда вы простонеобходимо реализовать установщик (как вы, кажется, уже) и добавить в текстовое поле прикрепленное свойство, привязанное к вашей модели представления.
<TextBox Text="{Binding Number, UpdateSourceTrigger=PropertyChanged}"
myNamespace:TextBoxBehaviour.CoerceValue="True"/>