Если вы используете MVVM верно, тогда ваш клик по кнопке ОК должен быть обработан некоторым Command
.Эта команда должна исходить от вашего ViewModel
.Связанные свойства Expliticly
должны снова исходить из вашего ViewModel
.Так что вам мешает.
- Не используйте
Explicit
привязку, но используйте OneWay
привязку. - В вашей кнопке свяжите команду и привяжите параметр команды к
OneWay
bound Dependency свойство. - В обработчике Execute вашей команды (который должен быть некоторым методом из вашей ViewModel) измените свойство ViewModel с приходящим параметром.
- Поднимите
NotifyPropertyChanged
для этого свойстваот вашего ViewModel
.
Например
Предположим, мне нужно обновить текст TextBox обратно в мою модель при нажатии кнопки ОК.
Так что для этого у меня естькласс EmployeeViewModel
, в котором есть свойство EmployeeName
.В собственности есть геттер и сеттер.Сеттер вызывает уведомление об изменении свойства.Модель представления также имеет другое свойство типа ICommand
с именем SaveNameCommand
, которое возвращает мне команду для выполнения.
EmployeeViewModel
- это тип контекста данных моего представления.Myview имеет TextBox
(названный x: Name = "EmployeeNameTxBx") OneWay
, привязанный к EmployeeName
, и кнопку как OK
.Я связываю свойство Button.Command
со свойством EmployeeViewModel.SaveNameCommand
, а свойство Button.CommandParameter
связано со свойством EmployeeNameTxBx.Text
.
<StackPanel>
<TextBox x:Name="EmployeeNameTxBx"
Text="{Binding EmployeeName, Mode=OneWay}" />
<Button Content="OK"
Command="{Binding SaveNameCommand}"
CommandParameter="{Bidning Text, ElementName=EmployeeNameTxBx}" />
</StackPanel>
Внутри моего EmployeeViewModel
у меня есть OnSaveNameCommandExecute(object param)
метод для выполнения моего SaveNameCommand
.
При этом выполнить этот код ...
var text = (string)param;
this.EmployeeName = text;
Таким образом, ТОЛЬКО при нажатии кнопки ОК, текст TextBox возвращается обратно в EmployeeName
свойство модели.
РЕДАКТИРОВАТЬ
Глядя на ваши комментарии ниже, я вижу, что вы пытаетесь реализовать проверку на пользовательском интерфейсе.Теперь это немного меняет дело.
IDataErrorInfo
и связанные с ними проверки работают ТОЛЬКО ЕСЛИ ваши элементы управления вводом (такие как TextBoxes) имеют TwoWay привязанный.Да, вот как это задумано.Итак, теперь вы можете спросить: «Значит ли это, что полная концепция НЕ РАЗРЕШЕНИЯ недопустимых данных на передачу модели бесполезна в MVVM, если мы используем IDataErrorInfo»?
Не совсем!
См. MVVM неприменять правило, согласно которому ТОЛЬКО действительные данные должны возвращаться.Он принимает неверные данные, и именно так IDataErrorInfo
работает и выдает сообщения об ошибках.Дело в том, что ViewModel - это просто мягкая копия вашего представления, поэтому она может быть грязной .Следует убедиться, что эта грязь не зафиксирована для ваших внешних интерфейсов, таких как службы или база данных.
Такой неверный поток данных должен быть ограничен ViewModel
путем тестированияневерные данные.И эти данные появятся, если у нас будет включена привязка TwoWay
.Таким образом, учитывая, что вы реализуете IDataErrorInfo
, вам необходимо иметь TwoWay
привязок, что вполне допустимо в MVVM.
Подход 1:
Чтоесли я хочу явно проверить определенные элементы в пользовательском интерфейсе при нажатии кнопки?
Для этого используйте трюк с отложенной проверкой.В вашей ViewModel есть флаг isValidating.Установите значение false по умолчанию.
В своем свойстве IDataErrorInfo.this
пропустите проверку, проверив флаг isValidating ...
string IDataErrorInfo.this[string columnName]
{
get
{
if (!isValidating) return string.Empty;
string result = string.Empty;
bool value = false;
if (columnName == "EmployeeName")
{
if (string.IsNullOrEmpty(AccountType))
{
result = "EmployeeName cannot be empty!";
value = true;
}
}
return result;
}
}
Затем в обработчике выполненной команды OK проверьте имя сотрудника и затемподнять события уведомления об изменении свойства для того же свойства ...
private void OnSaveNameCommandExecute(object param)
{
isValidating = true;
this.NotifyPropertyChanged("EmployeeName");
isValidating = false;
}
Это запускает проверку ТОЛЬКО при нажатии кнопки ОК.Помните, что EmployeeName
ДОЛЖЕН содержать недействительные данные для проверки работоспособности.
Подход 2:
Что если я хочу явно обновить привязки безДвухсторонний режим в MVVM?
Тогда вам придется использовать Attached Behavior
.Поведение будет привязано к кнопке ОК и будет принимать список всех элементов, для которых необходимо обновить привязки.
<Button Content="OK">
<local:SpecialBindingBehavior.DependentControls>
<MultiBinding Converter="{StaticResource ListMaker}">
<Binding ElementName="EmployeeNameTxBx" />
<Binding ElementName="EmployeeSalaryTxBx" />
....
<MultiBinding>
</local:SpecialBindingBehavior.DependentControls>
</Button>
ListMaker
- это IMultiValueConverter
, который просто преобразует значения в список ...
Convert(object[] values, ...)
{
return values.ToList();
}
В вашем SpecialBindingBehavior
есть обработчик изменения свойства DependentControls
...
private static void OnDependentControlsChanged(
DependencyObject depObj,
DependencyPropertyChangedEventArgs e)
{
var button = sender as Button;
if (button != null && e.NewValue is IList)
{
button.Click
+= new RoutedEventHandler(
(object s, RoutedEventArgs args) =>
{
foreach(var element in (IList)e.NewValue)
{
var bndExp
= ((TextBox)element).GetBindingExpression(
((TextBox)element).Textproperty);
bndExp.UpdateSource();
}
});
}
}
Но я все равно предложу вам использовать мой предыдущий чистый MVVM на основе **Подход 1 .