MVVM: Как спроектировать окно для размещения разных приложений? - PullRequest
1 голос
/ 05 октября 2010

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

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

В настоящее время мой MainWindowViewModel имеет свойство «CurrentViewModel» объекта типа, которое может быть установлено либо в качестве модели выбора метода, либо в одну из моделей представления результатов анализа, которые затем отображаются с использованием шаблонов данных.

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

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

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

Как бы я оптимально спроектировал это?

Ответы [ 3 ]

2 голосов
/ 05 октября 2010

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

  • К вашему первому пункту, наше приложение или «оболочка» нуждается в явном перечислении «доступных» методов.Мы можем предоставить это любым способом, который пожелаем, явно (через config) или неявно (через отражение).В качестве предпочтения я предпочитаю первое, поскольку оно менее «волшебно».

  • Во-вторых, в дополнение к явному перечислению наша оболочка должна поддерживать карту от выбора до реализации.Опять же, это может быть выполнено любым количеством способов, но обычно наше перечисление представляет собой список «Типов», и когда тип выбирается, мы запрашиваем реализацию этого типа у фабрики.Самым простым способом реализации этого шаблона является использование контейнера Inversion of Control, такого как Castle Windsor, Unity, Ninject и т. Д. Если честно, я не помню, что мы использовали внутри.

Например, рассмотрим,

// a simple Plain Old C Object that describes business methods.
public class BusinessMethod
{
    // user-friendly name
    public string Name { get; set; }
    // type that actually implements
    public Type ImplementationType { get; set; }
}

// ... meanwhile, back on the ranch ...
public void OnBusinessMethodSelection ()
{
    // 1. if selected 
    if (BusinessMethodList.SelectedItem != null)
    {

        // 2. retrieve selected item
        BusinessMethod selected = 
            (BusinessMethod)(BusinessMethodList.SelectedItem);

        // 3. request implementation of selected item from
        // IoC container
        object implementation = 
            _container.Resolve (selected.ImplementationType);
    }
}
  • К третьему пункту нам нужен способ связи между разными частями.К сожалению, мы не можем полагаться на методы времени разработки (например, привязки команд и данных), поэтому мы должны реализовать нашу собственную службу агрегации событий.В основном это «синглтон» (как в одиночном экземпляре , а не статический класс), который знает о подписчиках и издателях, и, предпочтительно, реализация, которая предлагает строгую типизацию аргументов.К счастью для нас, многие великие люди ушли перед нами, и мы можем извлечь пользу из их опыта.Проверьте концентратор событий Кент Бугаарта .

Вот пример того, как мы будем использовать агрегатор событий

// an example of a strongly typed subject. notice how subject
// defines content. semanticly, when someone creates and publishes
// an instance of this subject, they are requesting someone show
// an analysis view based on data content,
public class AnalysisSubject
{
    // subject content, in this case a data result from
    // a business method
    public object Data { get; set; }
}

public class MainWindow : ISubscriber<AnalysisSubject> ...
{

    // use whatever implementation of an IoC container we like
    // here i assume we abstract from implementation and use a
    // custom interface IContainer that exposes functionality 
    // that we need
    private readonly IContainer _container = null;
    public class MainWindow ()
    {
        // we're teh r00tz! we create an instance of IoC
        // container for use throughout application
        IContainer _container = new CustomContainer ();

        // our container exposes both parameterized and
        // type-parameterized resolve methods
        IEventHub events = _container.Resolve<IEventHub> ();
        events.Subscribe<AnalysisSubject> (this);
    }

    #region ISubscriber<AnalysisSubject>

    // part of strongly typed subscriptions is that we
    // may now handle strongly typed publications! yay!
    public void Receive (AnalysisSubject subject)
    {
        // 1. request to display analysis of data
        Type analysisType = subject.Data.GetType ();

        // 2. get view control based on payload type
        // 
        // NOTE: implicit usage below is not consistent
        // with previous invocations, here we are submitting
        // a type of something we already have, and actually
        // want back something that knows how to handle it.
        // most IoC containers can provide this functionality
        // through "facilities" add ons that accept a 
        // parameter\discriminator like below, and produce 
        // something in return.
        Control control = (Control)(_container.Resolve (analysisType));

        // [alternatively] if the above is too "magical" where
        // IAnalysisFactory is an interface we define for this
        // express purpose
        //IAnalysisFactory factory = _container.Resolve<IAnalysisFactory> ();
        //Control control = factory.GetAnalysisControlFor (analysisType);

        // 3. assign subject data to control
        Control.DataContext = subject.Data;

        // 4. display control
    }

    #endregion

}

И пример публикации

public class SomeBusinessView
{

    private readonly IEventHub _events = null;

    // we cannot function without an event aggregator of
    // some kind, so we declare our dependency as a contructor
    // dependency
    public SomeBusinessView (IEventHub events)
    {
        _events = events;
    }

    public void DoMyThang ()
    {
        // 1. do some business
        MyBusinessData data = SomeBusinessFunction ();
        // 2. publish complete event
        AnalysisSubject subject = new AnalysisSubject () { Data = data, };
        _events.Publish (subject);
    }

}
1 голос
/ 05 октября 2010

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

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

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

0 голосов
/ 05 октября 2010

Может ли источник данных выбора быть привязан к имеющимся у вас типам ViewModel, а путь может быть именем каждого (будь то строковое свойство для отформатированного имени или имя типа в явном виде). После выбора у вас будет соответствующий объект, который был выбран.

Возможно, каждая из ViewModels ссылается на общего поставщика, который, как вы говорите, выполняет анализ результатов, и два разных представления просто отображают одни и те же данные по-разному.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...