MVVM (с WPF) - привязка нескольких представлений к одной и той же модели представления

Недавно я начал исследовать шаблон MVVM с WPF для будущего проекта. Я начал с статьи Джоша Смита в MSDN. У меня есть вопрос (ну, многие , но начнем с одного):

У меня есть IndividualViewModel, который раскрывает свойства модели. Мне нужны два представления «Добавить человека» и «Редактировать человека», которые, как вы можете себе представить, очень похожи. В настоящее время я создал 2 подкласса AddIndividualViewModel и EditIndividualViewModel, которые предоставляют команды «Добавить» и «Редактировать» соответственно. У меня также есть 2 одинаковых именованных представления, которые привязываются к ним.

Теперь этот метод работает, и эти классы в любом случае довольно малы, но мне интересно, могу ли я иметь только одну модель представления, которая предоставляет обе команды. У меня по-прежнему будет 2 представления, которые будут привязаны к этой же модели представления, отображая соответствующую команду в виде кнопки. Я не совсем понимаю, как это сделать. В ресурсах главного окна у меня есть что-то вроде:

        <DataTemplate DataType="{x:Type ViewModels:AddIndividualViewModel}">
            <Views:AddIndividualView />
        </DataTemplate>

С помощью этого метода привязки вы можете иметь привязку только один к одному, то есть одно и то же представление всегда отображается для данной модели представления. Есть ли способ автоматически переключать представление в зависимости от свойства в модели представления (например, IndividualViewModel.Mode). Есть ли другой подход, который мне следует рассмотреть?

Обратите внимание, что в главном окне есть набор моделей представлений, каждая из которых отображается на вкладке.

Спасибо!


person Bimf    schedule 01.01.2010    source источник


Ответы (4)


Итак, вам нужно 2 разных представления на основе значения свойства. Одна вещь, которую следует учитывать, - это реорганизовать код презентации, чтобы вместо значения свойства у вас могут быть реальные подклассы. Затем вы можете использовать 2 разных DataTemplate для каждого класса.

<DataTemplate DataType="{x:Type ViewModels:AddIndividualViewModel}">
  <Views:AddIndividualView />
</DataTemplate>

<DataTemplate DataType="{x:Type ViewModels:EditIndividualViewModel}">
  <Views:EditIndividualView />
</DataTemplate>

Если вы считаете, что это излишество, вы можете использовать триггер и обернуть ваши конкретные представления в ContentPresenter.

<DataTemplate x:Key="AddIndividualTemplate" DataType="{x:Type ViewModels:IndividualViewModel}">
  <Views:AddIndividualView />
</DataTemplate>

<DataTemplate x:Key="EditIndividualTemplate" DataType="{x:Type ViewModels:IndividualViewModel}">
  <Views:EditIndividualView />
</DataTemplate>

<DataTemplate DataType="{x:Type ViewModels:IndividualViewModel}">
  <ContentPresenter Content="{Binding}">
    <ContentPresenter.Style>
      <Style TargetType="ContentPresenter">
        <Setter Property="ContentTemplate" Value="{StaticResource AddIndividualTemplate}" />
        <Style.Triggers>
          <DataTrigger Binding="{Binding Mode}" Value="{x:Static ViewModels:IndividualMode.Edit}">
            <Setter Property="ContentTemplate" Value="{StaticResource EditIndividualTemplate}" />
          </DataTrigger>
        </Style.Triggers>
      </Style>
    </ContentPresenter.Style>
  </ContentPresenter>
</DataTemplate>
person wigy    schedule 11.03.2015
comment
Это классическое решение MVVM. Он определяет оба параметра и не имеет кода. - person Tal Segal; 06.12.2017

Спасибо, что указали мне правильное направление! Я все еще новичок в WPF и изучаю все различные возможности, включая методы привязки. В любом случае, для всех, кто интересуется, вот решение, к которому я пришел для этого конкретного случая:

Я решил, что хочу разделить модели представления на два подкласса AddIndividualViewModel и EditIndividualViewModel, которые предоставляют только команды, а не пытаются управлять состоянием в одном классе. Однако мне нужно было одно представление, чтобы не дублировать XAML. В итоге я использовал два DataTemplate и DataTemplateSelector, чтобы отключить кнопки действий в зависимости от модели представления:

        <DataTemplate x:Key="addTemplate">
            <Button Command="{Binding Path=AddCommand}">Add</Button>
        </DataTemplate>

        <DataTemplate x:Key="editTemplate">
            <Button Command="{Binding Path=UpdateCommand}">Update</Button>
        </DataTemplate>

        <TemplateSelectors:AddEditTemplateSelector
            AddTemplate="{StaticResource addTemplate}"
            EditTemplate="{StaticResource editTemplate}"
            x:Key="addEditTemplateSelector" />

и ведущий контента внизу формы:

        <ContentPresenter Content="{Binding}"
                          ContentTemplateSelector="{StaticResource addEditTemplateSelector}" />

Вот код для селектора шаблонов:

class AddEditTemplateSelector : DataTemplateSelector
{
    public DataTemplate AddTemplate { get; set; }
    public DataTemplate EditTemplate { get; set; }

    public override DataTemplate SelectTemplate(object item, DependencyObject container)
    {
        if (item is AddIndividualViewModel)
        {
            return AddTemplate;
        }
        else if (item is EditIndividualViewModel)
        {
            return EditTemplate;
        }

        return null;
    }
}

Это может быть или не быть тем, как реализовать последнюю вещь (с учетом требований), но приятно видеть, что у меня есть такая возможность.

person Poya M.    schedule 03.01.2010

Нет причин, по которым вы не могли бы этого добиться. Один из способов сделать это - предоставить некоторый флаг в вашей модели представления, указывающий, находитесь ли вы в режиме добавления или в режиме редактирования, и стилизовать ваше представление на основе этого флага с помощью простых привязок, триггеров или селекторов шаблонов.

Для справки вы можете посмотреть DataWrapper класс Саши Барбера, который является частью его Cinch фреймворк (не применим непосредственно к вашему случаю, но это хорошая отправная точка), который обертывает поля данных в модели представления таким образом, чтобы поддерживать флаг для переключения между только чтением (режим просмотра записи) и чтением-записью (редактировать режим записи). Вы можете применить аналогичный подход, чтобы различать добавление и редактирование.

По сути, вместо того, чтобы иметь простые свойства в вашей модели представления, создайте экземпляр класса-оболочки данных, который включает свойство Value и свойство IsAdding. На ваш взгляд, вы можете использовать привязки, триггеры или селекторы шаблонов для изменения шаблонов на основе этого свойства.

person Aviad P.    schedule 01.01.2010

Для этой задачи вам вообще не нужен DataTemplateSelector.

  1. Получите как EditIndividualVM, так и AddINdividualVM из IndividualVM.
  2. Edit- и AddCommands направляют к свойству установки в IndividualVM.
  3. Установщик VM = new AddIndividualVM или VM = new EditIndividualVM в зависимости от того, какая кнопка нажата.
  4. В xaml вы привязываете контентную сетку к свойству виртуальной машины следующим образом:

person msfanboy    schedule 21.03.2010
comment
Кажется, у вас отсутствует код. Не могли бы вы обновить свой ответ фрагментом кода? - person Debajit; 20.12.2013