WPF: правильная конфигурация для Window с дочерним UserControl (MVVM) - PullRequest
0 голосов
/ 07 августа 2010

Я пытаюсь правильно выполнить следующее.У меня есть UserControl (ProgramView).У него есть модель представления (ProgramViewViewModel).ProgramView используется как дочерний элемент управления в окне (ProgramWindow).ProgramWindow имеет открытое свойство ProgramId, поэтому потребитель окна может указать желаемую Программу (объект данных) для отображения.ProgramView имеет свойство ProgramId, так как его основной задачей является отображение этих данных.ProgramWindow - это чуть больше, чем окно-обертка для этого пользовательского элемента управления.

ProgramViewViewModel также имеет свойство ProgramId.Изменения в этом свойстве вытесняют работу модели представления, которая выходит из модели представления с использованием других свойств, к которым может привязываться ProgramView.

Я пытаюсь скрыть операцию модели представления отПотребитель ProgramView и ProgramWindow.

Этот ProgramId должен быть связан через все эти слои.Изменения в ProgramWindow.ProgramId должны передаваться в ProgramView.ProgramId, а затем в ProgramViewViewModel.ProgramId.Я не могу понять, как правильно это реализовать.

Мой нынешний подход - использовать ProgramId во всех трех классах как DP.В окне я мог бы представить, что экземпляр ProgramView создается следующим образом:

<local:ProgramView ProgramId="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:ProgramWindow}}, Path=ProgramId}" />

Кажется, это действительно работает.В ProgramView я получаю измененные события для свойства, и они, кажется, имеют правильное значение.Кажется, что FindAncestor работает правильно.

Как тогда синхронизировать свойство ProgramViewViewModel.ProgramId?Я вижу два пути.Одним из способов было бы установить Binding для самого экземпляра ProgramViewViewModel, также использовать FindAncestor и найти ProgramId в ProgramViewViewModel. У этого есть два недостатка.Требуется ProgramViewViewModel для отображения ProgramId как свойства зависимости.Я бы предпочел избежать этого, но это может быть приемлемым.В любом случае, я не могу выполнить это в XAML.

<local:View.DataContext>
    <local:ProgramViewViewModel
        ProgramId="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:ProgramView}}, Path=ProgramId}" />
</local:View.DataContext>

Это не работает.Похоже, что я не могу ввести связывающее выражение в экземпляре экземпляра.FindAncestor сообщает, что не может найти ProgramView.Моя теория заключается в том, что экземпляр находится за пределами логического дерева и, следовательно, не может перейти к своему родителю.

Второй вариант, который имеет больше смысла, - это привязка свойства ProgramView.ProgramId к «ProgramId»в DataContext).Я не могу этого сделать, потому что не могу понять, как указать выражение привязки для свойства, определенного в коде.требуется в XAML, но на самом деле существует тип ProgramId.Я не могу понять, как задать это свойство.

Если я вручную (в программном обеспечении ProgramView) создаю экземпляр Binding и вызываю SetBinding (ProgramIdProperty, binding), значение больше не попадает в само представление.Я считаю, что это потому, что я только что заменил привязку на ProgramView.ProgramId, который был ранее установлен ProgramWindow.Одна привязка для каждого свойства?

Мои оставшиеся идеи - предоставить ДВА свойства ProgramId в ProgramView.Один привязан к DataContext, другой общедоступен для привязки потребителем (ProgramWindow), а затем пишет обработчики OnValueChanged, которые синхронизируют два.Это похоже на взлом.Другой - вручную следить за изменениями в ProgramView.ProgramId и ProgramView.DataContext внутри кода программы ProgramView и самому распространять значение.Ни одна из этих идей не кажется идеальной.

Я ищу другие предложения.

1 Ответ

0 голосов
/ 07 августа 2010

Ваше описание кажется подробным, но у меня возникают проблемы с пониманием, почему вам нужно реализовать этот дизайн.Я не могу не думать, что СУХОЙ.

Если вам нужно предоставить свойство зависимости в двух таких моделях представления, связанных с этим, я бы посоветовал вам сделать модель дочернего представления (для представления пользовательского элемента управления) свойствомпервого (для вида окна программы).Что-то вроде:

public class MainViewModel : ViewModelBase
{
   public ProgramViewModel ChildViewModel { get; private set; }

}

public class ProgramViewModel : ViewModelBase
{
   private int _ProgramId;

   public int ProgramId
   {
      get { return _ProgramId; }
      set
      {
         if (value != _ProgramId)
         {
             // set and raise propery changed notification
         }
      }
   }
}

MainView может получить свойство, используя ChildViewModel.ProgramId (контекст данных установлен в MainViewModel).ProgramView обращается к нему по ProgramId (контекст данных установлен в MainViewModel.ChildViewModel).

...