Как создать отношения между родителями и потомками MVVM между виртуальными машинами в одном окне? - PullRequest
1 голос
/ 13 июля 2011

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

Производительность тестирования приложения алгоритмом для определения минимума / максимума срезов из больших данных сбора, Вот как это выглядит.

ViewModel главного окна генерирует коллекцию со многими значениями.Я хочу указать диапазон для среза, а затем выполнить несколько версий минимального / максимального алгоритма, чтобы проверить правильность и время выполнения.

Части в групповых полях внизу - это два экземпляра UserControl, который я назовуребенок контролирует.Это имело смысл для меня, потому что я знал, что мне понадобится несколько, и не хотел копировать / вставлять кластеры в главном окне.Вот где возникает проблема.

В главном окне VM есть свойства для коллекции и диапазона фрагментов.Мне нужно, чтобы дочерние виртуальные машины имели доступ к этим свойствам.Это сложно.В идеале я бы хотел что-то вроде этого:

<local:AlgorithmTester RangeStart="{Binding RangeStart}"
                       RangeEnd="{Binding RangeEnd}"
                       Values="{Binding Values}" />

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

  1. Не используйте UserControls;скопируйте / вставьте код для каждого тестировщика.
  2. Настройте виртуальные машины для тестировщиков в коде таким образом, чтобы лямбда-код мог захватывать необходимые свойства в замыкании.(Делаю это прямо сейчас.) Это заставляет меня чувствовать себя грязно, так как лямбды должны принадлежать другому классу.
  3. Друг Твиттера предложил агрегатор событий.Дочерние виртуальные машины будут подписываться на агрегированное событие RangeStartChanged (и другие). Мне это не нравится, потому что я чувствую, что отношения между дочерней виртуальной машиной и родительской виртуальной машиной должны быть более ясными;может быть, это старые привычки умирать?
  4. Вместо UserControl, используйте шаблонный ItemsControl для класса с соответствующими свойствами.Мне все еще нужно было бы найти способ сообщить об изменениях в основных свойствах ВМ, но это было бы логично для внутренней ВМ.Единственная причина, по которой мне не нравится этот подход, это то, что он кажется мне странным;это может быть многолетний опыт WinForms, сдерживающий меня.(Чем больше я думаю об этом, тем больше он мне нравится.)

Существуют ли другие решения?Я слишком разборчив в том, что существует?Что бы вы сделали?

Ответы [ 2 ]

4 голосов
/ 14 июля 2011

Я бы хотел, чтобы ваш ParentViewModel содержал ChildViewModel, и использовал бы DataTemplate, чтобы сообщить WPF, что он должен рисовать ChildViewModel, используя local:AlgorithmTester Control

Sample ViewModel

public class ParentViewModel
{
    // Actual implementation omitted for sake of simplicity
    public ChildViewModel TestViewModelA { get; set; }
    public ChildViewModel TestViewModelB { get; set; }
}

Sample XAML

<Window>
    <Window.Resources>
        <!-- Data Template to tell WPF how to draw the ViewModels -->
        <DataTemplate DataType="{x:Type local:ChildViewModel}">
            <local:AlgorithmTester />
        </DataTemplate>
    <Window.Resources>

    <!-- Put ViewModels in ContentControls and let WPF figure out how to display them -->
    <UniformGrid Columns="2">
        <ContentControl Content="{Binding TestViewModelA}" />
        <ContentControl Content="{Binding TestViewModelA}" />
    </UniformGrid>
</Window>

Ваши свойства алгоритма (StartRange, EndRange и т. Д.) И методы (Calculate ()) хранятся в ChildViewModel, а AlgoritmTester является представлением, которое делает ViewModel удобной для пользователя.Например, это выглядело бы следующим образом:

<UniformGrid Columns="2" Rows="4">
    <TextBlock Text="Minimum:" />
    <TextBox Text="{Binding StartRange}" />
    <TextBlock Text="Maximum:" />
    <TextBox Text="{Binding EndRange}" />
    <TextBlock Text="Elapsed:" />
    <TextBox Text="{Binding Elapsed}" />
    <Button Content="Calculate" Command="{Binding CalculateCommand}" />
</UniformGrid>

РЕДАКТИРОВАТЬ

Что касается комментария по вышеуказанному вопросу, ваш ParentViewModel будет отвечать за пропуск диапазонав ChildViewModels.

Например, если диапазон является статическим, вы должны установить его при создании:

TestViewModelA = new ChildViewModel();
TestViewModelA.StartRange = 0;
TestViewModelA.EndRange = 10;

Если он динамический, я бы зарегистрировал обработчик события PropertyChanged, который установитдиапазон

void ParentViewModel_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
    switch (e.PropertyName)
    {
        case "StartRange":
            TestViewModelA.StartRange = this.StartRange;
            TestViewModelB.StartRange = this.StartRange;
            break;
        case "EndRange":
            TestViewModelA.EndRange = this.EndRange ;
            TestViewModelB.EndRange = this.EndRange ;
            break;
    }
}
1 голос
/ 14 июля 2011

Я бы лично сделал их пользовательскими элементами управления со свойствами зависимостей и связал бы их с моделью основного представления, как показано. Альтернативой, и то, что я на самом деле сделал бы, это использование инфраструктуры MVVM, такой как Caliburn.Micro , которая делает просмотр композиции невероятно простым.

В случае Caliburn.Micro у вас будет 2 общедоступных свойства в вашей модели основного вида, каждое из которых имеет тип AlgorithmTesterViewModel, а в вашем главном представлении 2 ContentControl называется так же, как ваши 2 общедоступных свойства.

Caliburn.Micro автоматически определит местонахождение AlgorithmTesterView в соответствии с соглашением об именах, внедрит представление в 2 ContentControl (за кулисами через DataTemplates) и свяжет элементы управления для каждого со свойствами в AlgorithmTesterViewModel.

Затем вы создадите экземпляр двух AlgorithmTesterViewModel в модели основного представления, передав соответствующие данные, и назначите их двум открытым свойствам.

Кстати, вы очень похожи на Дэвида Митчелла. Это не оскорбление или дополнение. Просто наблюдение факта.

...