2013-07-16 1 views
2

У меня есть ViewModel, который реализует интерфейс IDataErrorInfo. Он просто имеет свойство: MyNumber.MVVM: Сделать привязку обновления ViewModel, только когда данные VALID

На мой взгляд есть TextBox, в котором пользователь может ввести любое число, но действительны только числа от 0 до 9.

До сих пор текстовое поле отмечено красным цветом, когда проверка не удалась, но у меня есть проблема: это не позволяет избежать связанного свойства в ViewModel, который будет установлен.

В нескольких словах свойство MyNumber устанавливается независимо от того, является ли вход действительным или нет. Я не хочу, чтобы MyNumber получал недопустимые данные.

Как можно достичь этого в чистом MVVM-подходе?

Большое спасибо!

+3

В принципе, вы не можете. И вы не должны этого хотеть, потому что это не так, как предполагается. Если 'MyNumber' не будет установлен, как вы знаете, что он был установлен с недопустимым значением? У вас не было бы никакой ценности для проверки, поэтому ваша реализация IDataErrorInfo не будет работать. –

+0

Это хороший момент. Ну, реальный случай использования заключается в том, что у меня есть свойство MyNumber также представляет BorderThickness графика. Например, у меня нет такой границы, чтобы быть размером более 5 пикселей. Пользователь может ввести 100, а ввод текста показывает ошибку, но поскольку свойство MyNumber действительно получает 100, граница увеличивается до 100, нарушая мое правило. – SuperJMN

+0

Я подумал об установке стиля DataTrigger, который проверяет, действительно ли это значение или нет, и соответственно устанавливает привязку, но я не знал, все ли в порядке. Есть идеи? Благодаря! – SuperJMN

ответ

0

Через некоторое время исследования, я пришел к очень удовлетворяющего решения:

Поскольку я использую Cinch MVVM Framework by Sacha Barber свойства в моей модели представления, которые требуют проверки использовать очень удобный класс, который называется DataWrapper. Он обертывает фактическое свойство и предоставляет некоторые полезные функции. Фактически, MyProperty, о котором я упоминал ранее, является DataWrapper. Я добавил в свою коллекцию «Правила» желаемое правило «x < = 5».

Очень хорошая вещь в классе DataWrapper заключается в том, что он содержит свойство «IsValid», которое указывает, нарушает ли фактическое значение правила, которые вы ему установили.

Это инкапсулирует в оболочку все свойства, необходимые для проверки!

Следующий шаг настроить привязки в XAML к DataWrapper, но я решил, что Binding будет применяться только когда DataWrapper содержит допустимое значение.

ПРЕДУПРЕЖДЕНИЕ. сложное объяснение впереди.

Вы можете спросить меня, почему мне это нужно. Это немного сложно. Вид похож на мастера, в котором пользователь может выбирать размеры, цвета и другие графические аспекты модели. Модель представлена ​​в представлении с помощью элемента управления. Чтобы упростить понимание (это немного сложнее, чем это), вы можете предположить, что модель класс под названием Portrait, и она будет представлена ​​в представлении с помощью управления Border. TextBox, в котором пользователь вводит толщину портрета, имеет привязку к свойству в ViewModel (вышеупомянутый DataWrapper), который я назову «BorderThicknessWrapper». Фактически, привязка установлена ​​в BorderThicknessWraper.DataValue, поэтому ввод текста в TextBox изменяет фактическое значение данных, хранящихся в DataWrapper.

При этом у нас есть значение, которое пользователь вводит в TextBox в ViewModel.

Теперь мы хотим сделать элемент управления, который представляет модель, чтобы отразить изменения. Мы сказали, что у нас будет Граница как изображение нашего портрета, поэтому каждое изменение BorderThicknessWrapper изменит BorderThickness границы. Но подождите, мы ХОТИТЕ его представлять действительные значения, поэтому в XAML у меня есть это!Магический трюк;)

<Border> 
    <Border.Style> 
    <Style TargetType="{x:Type Border}"> 
     <Style.Triggers> 
      <DataTrigger Binding="{Binding BorderThickessWrapper.IsValid}" Value="True"> 
       <Setter Property="BorderThickness" Value="{Binding GutterWidth.DataValue}" /> 
      </DataTrigger> 
     </Style.Triggers> 
    </Style> 
</Border.Style> 

Как вы можете видеть, Border имеет стиль с DataTrigger, который гласит «когда обертка имеет действительное значение, установите привязку». Если свойство недействительно, применяется значение по умолчанию.

Что нам нужно до сих пор!

Надеюсь, вы понимаете решение. Это чистый MVVM на основе: D и довольно SOLID!

1

Я бы предположил, что вы идете по этому пути неправильно.

Если вы хотите ограничить ввод только номерами, не используйте стандартное текстовое поле. Вместо этого используйте элемент управления, который ограничивает допустимый ввод/формат, например IntegerUpDown/DecimalUpDown/DoubleUpDown из набора инструментов WPF Control Toolkit - доступный через Nuget. Затем вы можете привязать значение этого элемента управления к полю соответствующего числового типа в модели представления.

+0

Поскольку вы правы с этой точки зрения, когда дело доходит до числовых значений (это хорошее решение проблемы), если у вас есть другие значения которые являются строками, цветами или другими типами, мне более удобно, когда ViewModel отвечает за проверку. Он чувствует себя более элегантным, чем привязка значений max/min к свойствам в ViewModel. В любом случае я голосовал за вас :) – SuperJMN

+1

В целом, я бы сказал, не допускайте ввода текста в свободном формате для данных, которые вы должны проверить таким образом. Так, например, для значения цвета, либо дать пользователю список выбора предварительно определенных значений цветов, либо предоставить элемент управления выбора цвета, который позволяет только возвращать действительные значения в ViewModel. Аналогично для дат и т. Д. – Peregrine