Подтвердить что ты не робот

Prism/MVVM (MEF/WPF): отображение навигации [Меню, например] из модулей

Я начинаю свой первый набег в мир Prism v4/MVVM с MEF и WPF. Я успешно создал оболочку и, используя MEF, я могу обнаружить и инициализировать модули. Однако я не уверен относительно правильного способа навигации по представлениям, представленным этими модулями.

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

Вещь, это кажется неправильным. Я теперь заявляю в своем модуле, что навигация (и, следовательно, оболочка) ДОЛЖНА поддерживать использование меню. Что делать, если я хочу перейти на использование ToolBar или даже Ribbon. Затем мне пришлось бы изменить все мои модули, чтобы отобразить соответствующие типы управления для оболочки.

Я посмотрел вокруг, и на некоторых сайтах есть ссылка на "Сервис", чтобы обеспечить навигацию, при которой во время инициализации модуля в службу добавляются параметры навигации, которые, в свою очередь, используются оболочкой для отображения этого навигация в любом формате, который он хочет (ToolBar, TreeView, Ribbon, MenuItem и т.д.) - но я не могу найти примеров того, как это сделать.

Чтобы представить все это в перспективе, я, в конечном счете, хочу, чтобы иметь возможность выбирать виды из меню и/или другого элемента управления навигацией (возможно, Ribbon), а затем открывать эти представления по требованию в TabControl. Я уже получил возможность создавать представления в TabControl во время инициализации модуля, теперь мне нужен следующий шаг.

Что мне нужно знать, так это: что было бы правильным способом разоблачить варианты навигации таким образом, чтобы не настаивать на поддержке определенного элемента управления оболочкой, и если услуга - это путь, то как можно было бы объединить это в шаблоны Prism/MVVM.

Заранее благодарим за любую информацию, которую вы можете предложить.

4b9b3361

Ответ 1

Я предполагаю, что у вас есть основной модуль, содержащий общие интерфейсы. Вы можете создать простой интерфейс, например

public interface IMenuService {
    void AddItem(string name, Action action);
    IEnumerable<MenuItemViewModel> GetItems { get; }
}

Создайте 1 реализацию и один экземпляр.

public class MenuService : IMenuService {

    private readonly IList<MenuItemViewModel> items = new List<MenuItemViewModel>();

    void AddItem(string name, Action action) {
        items.Add(new MenuItemViewModel {
            Name = name,
            Action = action
        });
    }

    IEnumerable<MenuItemViewModel> GetItems {
        get { return list.AsEnumerable(); }
    }
}

В ваших модулях используйте MEF для разрешения этого экземпляра и вызывайте AddItem() для регистрации ваших просмотров. Свойство Action является простым делегатом, чтобы активировать представление или делать что-либо еще.

Затем в вашей оболочке или любом представлении вам просто нужно вызвать свойство GetItems для заполнения вашего меню.

Ответ 2

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

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

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

Поэтому мои модули (в настоящее время) выставляют "RibbonView" (RibbonTab) с необходимыми значками, кнопками и командами и т.д., чтобы разоблачить функциональность модуля. Каждый "RibbonView" зарегистрирован в "RibbonRegion" оболочки вместе с подсказками для упорядочения, и это затем отображается внутри оболочки.

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

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

Мне было бы интересно услышать, есть ли у кого-нибудь мнения по этому поводу.

Ответ 3

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

Чтобы обойти это, я решил делегировать эту ответственность стороннему компоненту LayoutManager. LayoutManager находится между оболочкой и модулем и определяет "что происходит". Это конкретная реализация и действительно определяет реализацию. И представление оболочки, и модуль остаются общедоступными.

Посмотрите: http://rgramann.blogspot.com/2009/08/layout-manager-for-prism-v2.html

Что может дать вам некоторые идеи вокруг этой проблемы.

Надеюсь, что это поможет.

Ответ 4

В этой статье используется абстракция (IMenuItem) для представления ViewModels для ваших вариантов меню. Как вы фактически визуализируете эти импортированные объекты, действительно зависит от хост-приложения. В этом примере используется меню WPF, но вы можете сделать его каким-либо образом, потому что IMenuItem достаточно абстрактно.

Если вы изменили IMenuItem на INavigationItem, у вас есть то, что вы хотите.

В этой статье, когда конкретный элемент навигации получает уведомление о том, что он был "запущен", он обычно создает экземпляр ViewModel для документа или "pad" и передает его службе ILayoutManager. Он имеет подключаемую архитектуру, поэтому вы можете заменить службу LayoutManager для другого механизма компоновки (по умолчанию это оболочка вокруг AvalonDock).