Prism/MVVM (MEF/WPF): отображение навигации [Меню, например] из модулей
Я начинаю свой первый набег в мир Prism v4/MVVM с MEF и WPF. Я успешно создал оболочку и, используя MEF, я могу обнаружить и инициализировать модули. Однако я не уверен относительно правильного способа навигации по представлениям, представленным этими модулями.
Например, скажем, что один из модулей предоставляет три вида, и я хочу отобразить навигацию к этим представлениям в элементе управления меню. До сих пор я успешно просмотрел представление, основанное на MenuItem
, и этот MenuItem
содержит дочерние элементы MenuItem
, таким образом предоставляя иерархию команд, которая может быть использована. Отлично.
Вещь, это кажется неправильным. Я теперь заявляю в своем модуле, что навигация (и, следовательно, оболочка) ДОЛЖНА поддерживать использование меню. Что делать, если я хочу перейти на использование ToolBar
или даже Ribbon
. Затем мне пришлось бы изменить все мои модули, чтобы отобразить соответствующие типы управления для оболочки.
Я посмотрел вокруг, и на некоторых сайтах есть ссылка на "Сервис", чтобы обеспечить навигацию, при которой во время инициализации модуля в службу добавляются параметры навигации, которые, в свою очередь, используются оболочкой для отображения этого навигация в любом формате, который он хочет (ToolBar
, TreeView
, Ribbon
, MenuItem
и т.д.) - но я не могу найти примеров того, как это сделать.
Чтобы представить все это в перспективе, я, в конечном счете, хочу, чтобы иметь возможность выбирать виды из меню и/или другого элемента управления навигацией (возможно, Ribbon
), а затем открывать эти представления по требованию в TabControl. Я уже получил возможность создавать представления в TabControl
во время инициализации модуля, теперь мне нужен следующий шаг.
Что мне нужно знать, так это: что было бы правильным способом разоблачить варианты навигации таким образом, чтобы не настаивать на поддержке определенного элемента управления оболочкой, и если услуга - это путь, то как можно было бы объединить это в шаблоны Prism/MVVM.
Заранее благодарим за любую информацию, которую вы можете предложить.
Ответы
Ответ 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).