Prism 2.1 Внедрение модулей в ViewModel

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

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

Вот мой файл Boostrapper, со временем я буду добавлять больше модулей, но пока он просто содержит мой довольно надуманный "ProductAModule":

public class Bootstrapper : UnityBootstrapper
{
    protected override void ConfigureContainer()
    {
        Container.RegisterType<IProductModule>();

        base.ConfigureContainer();
    }

    protected override IModuleCatalog GetModuleCatalog()
    {
        return new ModuleCatalog()
            .AddModule(typeof(ProductAModule));
    }

    protected override DependencyObject CreateShell()
    {
        var view = Container.Resolve<ShellView>();
        var viewModel = Container.Resolve<ShellViewModel>();
        view.DataContext = viewModel;
        view.Show();

        return view;
    }
}

Исходя из этого, вот модель ViewModel моей оболочки:

public class ShellViewModel : ViewModelBase
{
    public List<IProductModule> Modules { get; set; }

    public ShellViewModel(List<IProductModule> modules)
    {
        modules.Sort((a, b) => a.Name.CompareTo(b));
        Modules = modules;
    }
}

Как видите, я пытаюсь внедрить список IProductModule (которому ProductAModule наследует некоторые его свойства и методы), чтобы затем его можно было привязать к моему представлению оболочки. Есть ли что-то ДЕЙСТВИТЕЛЬНО простое, чего мне не хватает, или это нельзя сделать с помощью Unity IoC? (Я видел это с расширением StructureMap для Prism)

Еще одна вещь ... При запуске приложения в момент, когда ShellViewModel разрешается Контейнером в Bootstrapper, я получаю следующее исключение:

Ошибка разрешения зависимости, type = "PrismBasic.Shell.ViewModels.ShellViewModel", name = "". Сообщение об исключении: текущая операция сборки (ключ сборки Build Key [PrismBasic.Shell.ViewModels.ShellViewModel, null]) не удалась: не удалось разрешить модули параметров при попытке вызвать конструктор PrismBasic.Shell.ViewModels.ShellViewModel (System.Collections .Generic.List`1 [[PrismBasic.ModuleBase.IProductModule, PrismBasic.ModuleBase, Version = 1.0.0.0, Culture = нейтральный, PublicKeyToken = null]] модули). (Тип стратегии BuildPlanStrategy, индекс 3)

Во всяком случае, просто, да ... Выглядит сбитым с толку ...

Любая помощь будет принята с благодарностью!

Роб


person Robert Reid    schedule 21.01.2010    source источник


Ответы (4)


Я думаю, вы могли бы просто сделать это:

public class Bootstrapper : UnityBootstrapper
{
    protected override void ConfigureContainer()
    {
        Container.RegisterType<IProductModule>();

        base.ConfigureContainer();
    }

    private static ObservableCollection<IProductModule> _productModules = new Obser...();
    public static ObservableCollection<IProductModule> ProductModules
    { 
         get { return _productModules; } 
    }
    protected override IModuleCatalog GetModuleCatalog()
    {
        var modCatalog = new ModuleCatalog()
            .AddModule(typeof(ProductAModule));
        //TODO: add all modules to ProductModules collection

        return modCatalog;
    }

   ...
}

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


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

public class MyViewModel : ViewModel
{

      public ObservableCollection<string> ModuleNames { ... }
      public MyViewModel(IModuleCatalog catalog)
      {
           ModuleNames = new ObservableCollection<string>(catalog.Modules.Select(mod => mod.ModuleName));
      }
}

Это почти все. IModuleCatalog и IModuleManager - единственные вещи, которые настраиваются в контейнере, и вы можете получить доступ к ним с точки зрения модулей. Однако, как я уже сказал, вы не получите никаких данных об экземпляре, потому что эти модули (надеюсь) еще не созданы. Вы можете получить доступ только к данным типа.

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

person Anderson Imes    schedule 21.01.2010
comment
Привет, Андерсон, есть две причины, по которым я этого не делаю: 1) Я пишу это в надежде демистифицировать Prism с помощью MVVM, поэтому я надеюсь сохранить все свои привязки в модели просмотра (а именно ShellViewModel ). 2) Если бы я предпринял // TODO: add ... Я бы не стал использовать ModuleCatalog, зарегистрированный в UnityContainer ... Я все же понимаю, откуда вы пришли, и благодарю вас за ваше предложение! - person Robert Reid; 22.01.2010
comment
Проблема, с которой я столкнулся, - это перевод ModuleInfo в IProductModule. Они наследуются от IProductModule, который наследуется от IModule. От чего вся ситуация становится еще более раздражающей! - person Robert Reid; 22.01.2010
comment
@ Роберт Рид: В предложенном мною решении нет ничего, кроме MVVM. Эта статика просто сделана доступной для ViewModel для использования и предоставления ее представлению ИЛИ для прямого использования View с помощью {x: Static ...}. Что касается модулей, API не хранит экземпляр IModule (или IProductModule, в вашем случае), только Type (ModuleInfo.ModuleType) в виде строки. К этому времени он уже получил ModuleName (либо имя типа без пространств имен, либо имя, которое вы указали в атрибуте). Вы можете запросить в качестве зависимости IModuleCatalog или IModuleManager, но это все. - person Anderson Imes; 22.01.2010
comment
@ Роберт Рид: Я опубликую образец использования IModuleCatalog, чтобы получить список модулей, доступных для загрузки, но это в значительной степени будет вашим ограничением ... пока вы не инициализируете модули, они еще не существуют . - person Anderson Imes; 22.01.2010
comment
Еще раз привет, Андерсон, это был бы очень признателен. Конечно, я знаю, что это не нарушит передовой опыт MVVM (если бы был строгий), я просто думал о том, чтобы мои ViewModel (-ы) не зависели от классов, отличных от их моделей для получения информации. Хотя нет ничего, что говорило бы, что Bootstrapper не может быть, я полагаю! Большое спасибо за ваш вклад. - person Robert Reid; 22.01.2010
comment
@ Роберт Рид: Я приложил образец. Я надеюсь, что это проясняет, к чему у вас есть доступ и каковы ваши ограничения. - person Anderson Imes; 22.01.2010
comment
@Robert Reid: Также, что касается вопроса о модели, я бы обычно не создавал статику прямо в классе загрузчика ... Я сделал это для простоты. Обычно я создаю хранилище моделей для этой информации, например Model.ModuleStore или что-то в этом роде, для чего можно извлечь и внести свой вклад. Я точно вижу, что меня тошнит по поводу извлечения напрямую из Bootstrapper.Modules. - person Anderson Imes; 22.01.2010
comment
Уф! Я начал сомневаться в своем здравом уме, когда вы упомянули об этом! Не отставать от этого передового края - тяжелая работа! Спасибо, что приложили образец, и спасибо, что поддерживаете связь! - person Robert Reid; 22.01.2010
comment
@ Роберт Рид: Похоже, вам нужно немного больше информации о модулях, чем доступно до их создания. Если это статическая информация, вы можете рассмотреть возможность использования атрибутов вашего типа для предоставления этой информации. Вы можете выполнить TypeDescriptor.GetAttributes (Type.GetType (moduleInfo.ModuleType)), чтобы получить коллекцию атрибутов, оформленных на тип модуля. - person Anderson Imes; 22.01.2010

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

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

То, как вы хотите развертывать свои представления и сервисы в терминах модулей, больше зависит от того, какой уровень модульности вы ищете, т.е. если вы хотите иметь возможность развертывать представления и сервисы ModuleA независимо от представлений и сервисов ModuleB и так далее. В вашем случае может быть достаточно зарегистрировать все в одном модуле.

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

Причина, по которой ваши примеры представляют собой пример, заключается в том, что ваша ShellViewModel зависит от List и этот тип не зарегистрирован в Unity. Кроме того, вы регистрируете IProductModule в Unity, что не имеет смысла, поскольку интерфейс не может быть сконструирован.

person scim    schedule 01.03.2010

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

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

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


Изменить: вам необходимо применить атрибут DependencyAttribute к свойствам, чтобы использовать внедрение установщика в Unity; вы можете прочитать об этом здесь.

person Rory    schedule 21.01.2010
comment
Привет, Рори, спасибо за ответ. Извините, я мог быть слишком двусмысленным в своем вопросе ... Я бы хотел, чтобы эти модули были включены в ShellViewModule, я займусь службами где-нибудь в будущем! Порядок выполнения инициализации в Bootstrapper следующий: - ConfigureContainer - GetModuleCatalog - CreateShell. Итак, я определенно получил свой массив ModuleInfo под рукой, они просто недостаточно полезны для привязки без дополнительных свойств, содержащихся в моем IProductModule. Спасибо, Рори! - person Robert Reid; 22.01.2010
comment
Еще раз привет, я использовал атрибут [Dependency] ранее (для внедрения свойств). Я пытаюсь ввести здесь конструктор, я предполагаю, что даже если бы я использовал Property Injection, это не было бы серебряной пулей! - person Robert Reid; 22.01.2010
comment
Привет, Роберт, извини за недоразумение. Я только что заметил, что документы для этого атрибута в любом случае устарели, поэтому сейчас я удалю ссылку. Если вы посмотрите на исходный код UnityBootloader, вы заметите, что контейнер настроен с помощью ModuleCatalog, но не с отдельными модулями. Лучше всего, вероятно, переопределить ConfigureContainer и самостоятельно зарегистрировать модули в контейнере, просто не забудьте вызвать базовую реализацию. - person Rory; 22.01.2010
comment
Спасибо за ваш совет, Рори, я немного не решаюсь добавлять модули вручную, потому что тогда я отвечаю за регистрацию и создание LoggerFacade и добавление всех RegistersTypeIfMissings и т. Д. (Потому что вызов base.ConfigureContainer () будет повторным загрузить все мои модули, заставив их инициализировать (и, что более важно) добавить свои представления в области ShellView дважды). Тем не менее, я очень ценю вашу помощь. - person Robert Reid; 22.01.2010
comment
Не уверен, что это поможет, но интерфейс IModuleCatalog имеет свойство enumerable Modules. Если вам нужна информация только о модулях, вы можете заставить контейнер вводить IModuleCatalog и просто использовать элементы ModuleInfo, которые он содержит, для перечисления модулей. - person Rory; 22.01.2010
comment
Сначала я так думал, но мой IProductModule (интерфейс, из которого в конечном итоге будут производиться все мои модули) содержит некоторые полезные методы для запуска потока процесса контроллера модуля и т.д ... Ах, что за рассол! - person Robert Reid; 22.01.2010
comment
О боже, это проблема. Извините, что у меня нет идей на ночь (кроме повторной реализации большей части функциональности в ModuleManager для самостоятельной загрузки модулей, но это перебор). Если вам удастся найти решение, дайте мне знать. Из любопытства, есть ли причина, по которой вы не можете перенести упомянутые функции на службы внутри различных модулей? В общем, уже завтра, где я, и мне нужно спать, спокойной ночи и удачи от меня. - person Rory; 22.01.2010
comment
Большое спасибо за вашу помощь, Рори, я очень ценю вашу настойчивость! Буду держать вас в курсе! - person Robert Reid; 22.01.2010
comment
Привет, Роберт, я надеялся получить ответ на свой вопрос, переместив полезные методы из IProductModule в одну из служб, которые регистрирует каждый модуль. Идея состоит в том, чтобы каждый модуль мог зарегистрировать службу типа IProductModuleInitializer, используя свое имя модуля. Затем вы можете получить все экземпляры, используя метод ResolveAll<T> Unity. Вы также можете получить конкретный экземпляр, используя имя модуля (доступно через IModuleCatalog.Modules). Очевидно, что только загруженные модули будут иметь доступ к своим службам, но то же самое будет верно и для фактического экземпляра модуля. - person Rory; 25.01.2010

var modules = new IProductModule[]
{
    Container.Resolve<ProductAModule>()
    //Add more modules here...
};
Container.RegisterInstance<IProductModule[]>(modules);

Вот и все! Используя этот код, я могу вставлять свои модули в ShellViewModel и отображать каждый модуль как кнопку в моем приложении!

ТАКОЕ простое разрешение! От отличного парня из группы обсуждения CompositeWPF. Безоговорочно рекомендую ^ _ ^

person Robert Reid    schedule 25.01.2010
comment
За исключением того, что вы создаете несколько экземпляров каждого модуля, если собираетесь активировать их на основе отображаемой информации. Скорее всего, это сработает, но все, что вы делаете в своем констрикторе для каждого модуля, будет выполняться дважды, И у вас будет постоянно оставаться в памяти 2 экземпляра каждого типа модуля. Вот почему я не предлагал такой подход. Это показывает непонимание того, как на самом деле работает Prism. - person Anderson Imes; 25.01.2010
comment
Привет, Андерсон, мне ДЕЙСТВИТЕЛЬНО не хватает понимания с Prism! Я проверил вашу теорию, и действительно есть два экземпляра моих модулей. Но это лучшее решение, которое мне удалось придумать до сих пор (которое позволяет мне привязать мои модули к их собственным кнопкам, каждая с командой). - person Robert Reid; 26.01.2010