Здравствуйте, Glas, Вы писали:
G>Можно ли какими-то внутренними средствами Prism узнать активен ли View модуля? Я для навигации использую View injection.
G>G>moduleRegion.Activate(view);
G>
G>Хотелось бы после этого сказать модулю, что он активен. IEventAggregator использовать не хочу, итак много событий между модулями отсылается.
Внутренних средств у Prism для этого нет — надо городить самому.
Очевидно, что сам класс модуля должен реализовать, например, тот же IActiveAware. Теперь вопрос, как изменения активности вида должны "просачиваться" к модулям? Активностью видов управляют в Prism'е регионы (IRegion). Но ни регионы, ни даже виды, ничего не знают о модулях. Вообще, по умолчанию в Prism'е не существует реестра загруженных модулей. А следовательно такой реестр надо придумать и создать.
В Prism'е существует механизм, позволяющий в сам вид, и в его модель пробрасывать информацию об активности вида из региона. Для этого вид или его модель должны реализовать IActiveAware. Но чтобы не городить механизм проброса информации об активности вида в модуль, из которого этот вид был загружен, на наше счастье в Prism'е существует механизм поведений регионов (RegionBehavior), позволяющий нам цепляться к экземплярам регионов, наподобие того как Attached Properties позволяют нам цепляться к элементам WPF-дерева в XAML. Можно для региона написать такое поведение, которое бы при изменении активных видов в регионе, пробрасывало бы эту информацию в модули этих видов.
Остается вопрос, каким образом вид может сообщить поведению региона о том, какому модулю этот вид принадлежит? Это зависит от решения по реестру модулей. Поведение должно иметь доступ к реестру модулей, должно уметь вычитывать из вида информацию о том, какому модулю этот вид принадлежит, и выполнять над соответствующим модулем необходимые операции в части его активации/деактивации.
Если принять, что каждый модуль загружается в приложение только один раз, то для идентификации модуля отлично подойдет некий GUID. В качестве реестра для модулей можно использовать контейнер IUnityContainer, в котором можно сохранять экземпляры модулей под IModule и ключом — тем самым GUID'ом. Чтобы не городить код по сохранению экземпляров модулей в контейнере внутри самих модулей, можно переопределить механизм инициализации модулей, IModuleInitializer, чтобы он делал это за нас. А классы модулей, например, помечать атрибутом IdentifiableModuleAttribute. Вот пример такого инициализатора модулей:
public class IdentifiableModuleInitializer : ModuleInitializer, IModuleInitializer
{
public IdentifiableModuleInitializer(IUnityContainer container)
: base(container.Resolve<IServiceLocator>(), container.Resolve<ILoggerFacade>())
{
Container = container;
}
IUnityContainer Container { get; set; }
public new void Initialize(ModuleInfo moduleInfo)
{
if (moduleInfo == null)
{
throw new ArgumentNullException("moduleInfo");
}
IModule moduleInstance = null;
try
{
moduleInstance = this.CreateModule(moduleInfo);
moduleInstance.Initialize();
var identifiableModuleAttributes = (IdentifiableModuleAttribute[])
moduleInstance.GetType().GetCustomAttributes(typeof(IdentifiableModuleAttribute), false);
if (identifiableModuleAttributes.Length > 0)
{
Container.RegisterInstance<IModule>(identifiableModuleAttributes[0].ModuleId,
moduleInstance, new ContainerControlledLifetimeManager());
}
}
catch (Exception ex)
{
this.HandleModuleInitializationError(moduleInfo, (moduleInstance != null) ? moduleInstance.GetType().Assembly.FullName : null, ex);
}
}
}
Метод Initialize пришлось переопределять при помощи ключевого слова new из-за ошибки в базовом классе Prism'ы. Код нагло стырен из Prism'ы рефлектором с добавлением выделенного жирным фрагмента.
При этом для автоматической регистрации модуля в контейнере, к классу модуля необходимо добавить соответствующий атрибут:
[IdentifiableModule("A52773FA-1579-4D54-B4FC-EFEB7BE65EA8")]
public class Module : ActiveAwareModule
{
Класс ActiveAwareModule — это вспомогательный базовый класс для модулей, так как он реализует IActiveAware, но ничего существенного кроме этого не делает:
Чтобы поведение региона могло вычитать из вида информацию о том, какому модулю этот вид принадлежит, применим к классу вида такой же атрибут:
[IdentifiableModule("A52773FA-1579-4D54-B4FC-EFEB7BE65EA8")]
public partial class View1 : UserControl
{
И, наконец, дирижер всей этой песни — поведение региона ModuleActiveAwareBehavior:
public class ModuleActiveAwareBehavior : RegionBehavior
{
public ModuleActiveAwareBehavior(IUnityContainer container)
{
Container = container;
}
public IUnityContainer Container { get; private set; }
protected override void OnAttach()
{
Region.ActiveViews.CollectionChanged += new NotifyCollectionChangedEventHandler(ActiveViews_CollectionChanged);
}
void ActiveViews_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
var activeModules = Region.ActiveViews
.Where(view => view.GetType().GetCustomAttributes(typeof(IdentifiableModuleAttribute), false).Count() > 0)
.Select(view => Container.Resolve<IModule>(((IdentifiableModuleAttribute[])
view.GetType().GetCustomAttributes(typeof(IdentifiableModuleAttribute), false))[0].ModuleId))
.OfType<IActiveAware>();
var inactiveModules = Container.ResolveAll<IModule>().OfType<IActiveAware>().Except(activeModules).ToArray();
foreach (var activeModule in activeModules)
{
activeModule.IsActive = true;
}
foreach (var activeModule in inactiveModules)
{
activeModule.IsActive = false;
}
}
}
Данное поведение внедряется в архитектуру приложения на стадии Bootstrapper'а в соответствующем методе:
protected override IRegionBehaviorFactory ConfigureDefaultRegionBehaviors()
{
var regionBehaviorFactory = base.ConfigureDefaultRegionBehaviors();
regionBehaviorFactory.AddIfMissing("ModuleActiveAwareBehavior", typeof(ModuleActiveAwareBehavior));
return regionBehaviorFactory;
}
Все. Демонстрационный проект можно скачать
здесь.