Установите для UpdateSourceTrigger значение «Явный» в ShowDialog (WPF MVVM) - PullRequest
6 голосов
/ 05 октября 2011

Я видел этот пример - Свойство Binding.UpdateSourceTrigger

в примере UpdateSourceTrigger установлен в Explicit, а затем в коде представления он вызывает UpdateSource имени TextBox.

Но если я использую MVVM dp, я не хочу, чтобы имена для моих элементов управления находились в ВМ, а не в представлении, поэтому как правильно связать элементы управления со свойствами ВМ и установить для UpdateSourceTrigger явное значение?

Я хочу сделать это, потому что в моем случае это окно ShowDialog, и я хочу, чтобы источник обновлялся только в том случае, если пользователь нажал «ok»

Заранее спасибо!

Ответы [ 2 ]

11 голосов
/ 05 октября 2011

Если вы используете MVVM верно, тогда ваш клик по кнопке ОК должен быть обработан некоторым Command.Эта команда должна исходить от вашего ViewModel.Связанные свойства Expliticly должны снова исходить из вашего ViewModel.Так что вам мешает.

  1. Не используйте Explicit привязку, но используйте OneWay привязку.
  2. В вашей кнопке свяжите команду и привяжите параметр команды к OneWay bound Dependency свойство.
  3. В обработчике Execute вашей команды (который должен быть некоторым методом из вашей ViewModel) измените свойство ViewModel с приходящим параметром.
  4. Поднимите 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 .

1 голос
/ 05 марта 2015

Это старый вопрос, но я все же хочу предложить альтернативный подход для других пользователей, которые наткнулись на этот вопрос ... В моих моделях представления я не показываю свойства модели непосредственно в методах get / set Property. Я использую внутренние переменные для всех свойств. Затем я связываю все свойства в двух направлениях. Таким образом, я могу выполнить всю проверку как «обычную», потому что изменяются только внутренние переменные. В конструкторе модели представления у меня есть объект модели в качестве параметра, и я устанавливаю внутренние переменные в значения моей модели. Теперь, когда я нажимаю на кнопку «Сохранить» (-> запускает команду «Сохранить» в моем представлении модели) и ошибок нет, я устанавливаю все свойства моей модели на значения соответствующей внутренней переменной. Если я нажимаю кнопку «Canel / Undo» (-> Команда «Отмена» в моих моделях представления), я устанавливаю внутренние переменные в значения моей нетронутой модели (используя установщики свойств модели представления так, чтобы NotifyPropertyChanged был вызывается и вид показывает изменения = старые значения).

Еще один подход заключается в реализации Memento-Support в модели, поэтому перед началом редактирования вы вызываете функцию в модели для сохранения текущих значений, а если вы отмените редактирование, то вызываете функцию для восстановления этих значений. Таким образом, вы будете иметь поддержку отмены / отмены везде, а не только в одной модели представления ... Я реализовал оба метода в разных проектах, и оба работают нормально, это зависит от требований проекта ...

...