Привязка ContentControl к UserControl и повторное использование того же экземпляра

Я пытаюсь привязать ContentControl Content к UserControl, который я создал в своей ViewModel. Я не могу использовать метод с привязкой к ViewModel, а затем сделать UserControl DataTemplate ViewModel, так как мне нужно, чтобы ContentControl мог часто меняться, используя тот же экземпляр UserControls / Views, а не создавать экземпляры представлений при каждом повторном связывании.

Однако при установке свойства UserControl в экземпляр UserControl, а затем при визуализации / привязке представления к данным я получаю: Необходимо отключить указанный дочерний элемент от текущего родительского визуала перед присоединением к новому родительскому визуалу. Несмотря на то, что я ранее никуда не добавлял этот UserControl, я просто создал этот экземпляр ранее и сохранил его в памяти.

Есть ли лучший способ добиться того, что я делаю?

В ViewModel

public class MyViewModel : INotifyPropertyChanged
{
    //...

    private void LoadApps()
    {
        var instances = new List<UserControl>
                          {
                              new Instance1View(),
                              new Instance2View(),
                              new Instance3View(),
                          };
        SwitchInstances(instances);
    }

    private void SwitchInstances(List<UserControl> instances)
    {
        CenterApp = instances[0];
    }

    //...

    private UserControl _centerApp;
    public UserControl CenterApp
    {
        get { return _centerApp; }

        set
        {
            if (_centerApp == value)
            {
                return;
            }

            _centerApp = value;
            OnPropertyChanged("CenterApp");
        }
    }

    //...
}

В View.xaml

<ContentControl Content="{Binding CenterApp}"></ContentControl>

person cederlof    schedule 28.03.2013    source источник
comment
UserControl, который я создал в своей ViewModel. Вы делаете это неправильно. Создайте экземпляр модели вторичного представления в своей модели первичного представления, привяжите к нему элемент управления содержимым и используйте шаблон содержимого для отображения его визуального дерева. Виртуальные машины не должны создавать представления (или зависеть от них).   -  person Kent Boogaart    schedule 28.03.2013
comment
@KentBoogaart Насколько я понимаю (и говорю в своем вопросе): Я не могу использовать метод с привязкой к ViewModel, а затем UserControl должен быть DataTemplate ViewModel, поскольку мне нужно содержимое ContentControl, чтобы иметь возможность часто меняются, используя один и тот же экземпляр UserControls / Views, и не создают экземпляры представлений при каждом повторном связывании. Или я не понимаю, как работает табличка данных?   -  person cederlof    schedule 28.03.2013


Ответы (1)


Слишком долго для комментария.

Основываясь на том, что @Kent заявил в вашем комментарии, весь смысл MVVM состоит в том, чтобы отключить модель представления от связанных с представлением вещей (элементов управления), что блокирует возможность тестирования приложений с графическим интерфейсом. Таким образом, у вас есть UserControl / Button / любой элемент, связанный с графическим представлением, сводящий на нет весь принцип MVVM.

Если вы используете MVVM, вам следует соблюдать его стандарты, а затем повторно решить свою проблему.

  1. С MVVM у вас обычно есть 1 представление ‹-> 1 модель представления
  2. View знает о своей модели представления (обычно через DataContext). Реверс не должен кодироваться.
  3. Вы пытаетесь поместить логику, управляющую представлением, в модель представления, чтобы разрешить логику тестирования (Команды и свойства INPC)

... и многое другое. Это довольно специфично в части модели представления, не имеющей связанных с представлением вещей, например, даже не имеющих свойств в модели представления, таких как Visibility. Обычно вы держите bool, а затем в представлении используете преобразователь, чтобы переключить его на объект Visibility.

Чтение немного больше о MVVM, безусловно, поможет вам,

Теперь кое-что, чтобы решить вашу текущую проблему:

Следуя структуре MVVM,

у вас будут ViewModels, такие как

  • Главный: MyViewModel
  • Получите все экземпляры ViewModels из базы, чтобы они могли храниться в списке.
  • Перечислите или сохраните индивидуально Instance1ViewModel, Instance2ViewModel, Instance3ViewModel в MyViewModel (либо создайте его самостоятельно, либо, если вы используете контейнер IOC, позвольте ему ввести его)
  • Попросите MyViewModel предоставить свойство, как в опубликованном вами примере:

Пример:

// ViewModelBase is the base class for all instance View Models
private ViewModelBase _currentFrame;
public ViewModelBase CurrentFrame {
  get {
    return _currentFrame;
  }

  private set {
    if (value == _currentFrame)
      return;
    _currentFrame = value;
    OnPropertyChanged(() => CurrentFrame);
  }
}
  • Теперь в вашем MyView.xaml файле просмотра вы должны (не обязательно быть на верхнем уровне) установить DataContext верхнего уровня на ваш MyViewModel
  • Затем xaml вашего представления можно объявить следующим образом:

Пример:

...
<Window.Resources>
  <DataTemplate DataType="{x:Type local:Instance1ViewModel}">
    <local:Instance1View />
  </DataTemplate>
  <DataTemplate DataType="{x:Type local:Instance2ViewModel}">
    <local:Instance2View />
  </DataTemplate>
  <DataTemplate DataType="{x:Type local:Instance3ViewModel}">
    <local:Instance3View />
  </DataTemplate>
</Window.Resources>
<Grid>
  <ContentControl Content="{Binding Path=CurrentFrame}" />
</Grid>
...
  • Вот и все!. Теперь вы просто переключаете свойство CurrentFrame в своей модели представления и заставляете его указывать на любую из трех экземпляров моделей представления, и представление будет соответствующим образом обновлено.

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

person Viv    schedule 28.03.2013
comment
Спасибо за обстоятельный ответ! Я все это понимаю, и это была моя первая попытка исправить это - использовать View как DataTemplate для ViewModel. Однако в представлении я привязываю VisualStates к свойствам ViewModel, и когда я переключаю ViewModel, привязанный к моему ContentControl, на другую виртуальную машину, а затем переключаю его обратно - у меня есть два экземпляра View в памяти, оба из которых имеют их ViewState привязан к моей ViewModel. Каждый раз, когда я повторно привязываю ContentControl, он создает новый экземпляр View. - person cederlof; 30.03.2013
comment
Я не могу найти решение, в котором я бы получил тот же экземпляр View, который используется внутри ContentControl. Именно тогда я попытался привязать View-instance к ContentControl вместо ViewModel - и так - нарушив шаблон MVVM. - person cederlof; 30.03.2013
comment
@cederlof Нет проблем. У меня проблема с сохранением представлений при переключении их из модели представления. Вы смотрели ссылку в самом последнем пункте моего сообщения. Это точно такой же вопрос, как и ваш, с некоторыми полезными ответами. Вы можете попробовать пример Рэйчел и применить его к своему контейнеру, чтобы получить свое требование и не нарушать принципы MVVM. Попробуйте, если вы этого не сделали, поскольку он, кажется, решит проблему эффективно. - person Viv; 30.03.2013
comment
Спасибо! Однако эти решения, похоже, не работают для меня, поскольку я хотел бы использовать тот же экземпляр View внутри другого элемента управления ContentPresenter, перемещая его из одного ContentPresenter в другой. Извините, мой вопрос не совсем ясен ... - person cederlof; 08.04.2013