Индикатор MVVM, вводимый в основном в приложении области уведомлений

#architecture #mvvm #ninject #mvvm-light

#архитектура #mvvm #ninject #mvvm-light

Вопрос:

Мне нужен совет по архитектуре приложения.

Я создаю настольное приложение .Net 4 WPF с поддержкой значков области уведомлений.

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

Значок области уведомлений — это чисто элемент управления WPF, который я получил из этого примера codeproject.

Поскольку мое приложение должно оставаться запущенным, даже когда все окна закрыты, я установил

 ShutdownMode="OnExplicitShutdown"
  

в App.xaml.

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

В App.xaml.cs я создаю Ninject StandardKernel , давайте назовем его appKernel и загрузим в него модули Ninject. Сначала Ninject должен разрешить только один интерфейс — heartbeatManager экземпляр. HeartbeatManager — это класс, который я планирую использовать для:

а) Размещение моего экземпляра NotifyIcon в качестве переменной поля, чтобы он был видимым до тех пор, пока экземпляр класса находится в памяти.

б) Реализация события завершения работы, на которое я подпишусь в app.xaml.cs, и явное завершение работы приложения, когда класс heartbeat запрашивает его.

На этом этапе создается heartbeatManager экземпляр, который просто остается висеть в памяти.

В App.xaml я установил StartupUri="MainWindow.xaml" , чтобы было создано и показано главное окно. Следуя шаблону MVVM Light ViewModelLocator, MainWindow пытается разрешить контекст данных из статического экземпляра ViewModelLocator, определенного в App.xaml:

 <Application.Resources>
    <ViewModels:ViewModelLocator x:Key="Locator" d:IsDataSource="True" />
</Application.Resources>
  

Создается экземпляр ViewModelLocator, и в конструкторе он инициализирует другое стандартное ядро (есть ли какой-либо другой тип ядра, который я использую на данный момент?) это будет использоваться для разрешения привязок модели представления. Модель представления MainWindow предоставляется для удовлетворения запроса Windows, и все приложение продолжает работать.

Очень важным следствием является то, что экземпляр hearbeatManger привязан к определению интерфейса в однотонной области, так что модели просмотра, которые требуют его в качестве параметра конструктора, могут разрешать и получать уже созданный экземпляр. Будет ли работать такое разрешение зависимостей, если модель представления и heartbeatManager загружены в разные ядра? Если нет, как я могу решить эту проблему?

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

Комментарии:

1. Мне трудно понять, в чем здесь реальная проблема. Вы говорите о завершении работы приложения и зависании экземпляров в памяти, но вы не объяснили, почему вас это беспокоит. Класс приложения будет сохраняться, потому что он создан в сгенерированном Main методе, и ваш контейнер / ядро должны сохраняться, если вы поместите его в свой класс приложения, а классы, которые вы создаете, должны сохраняться до тех пор, пока контейнер работает (особенно одиночные элементы). Почему у вас есть два ядра / контейнера? Кроме того, это может быть лучше для codereview.stackexchange.com .

2. Проблема заключается в наличии двух контейнеров. Я вижу это, когда я приступил к попытке реализовать все это. Но как я могу передать ядро, которое я создаю в app.xaml.cs, конструктору viewmodel locator, чтобы избежать создания второго ядра для разрешения viewmodel?

3. Я не уверен, почему вы даже позволяете какому-либо вашему коду знать о ядре. Что особенного в вашем приложении, которое требует от вас прямого использования ядра (за исключением верхнего уровня)? Похоже, вы можете использовать его в качестве локатора служб, когда он действительно предназначен для внедрения зависимостей. Просто у вас возникла проблема с выяснением того, как заставить контейнер / ядро удовлетворять DataContext , то есть Object , и не позволяет вам определять атрибуты, специфичные для просмотра?

4. Я использую ninject в качестве локатора службы в классе локатора viewmodel, как описано здесь: kellabyte.com/2010/08/23 /…

5. Это неправильный способ определения местоположения моих моделей просмотра? Я почти реализовал описанную выше архитектуру, остается единственная проблема, как передать ядро, которое я создаю на верхнем уровне, конструктору viewmodellocator.

Ответ №1:

…На этом этапе создается экземпляр heartbeatManager, который просто остается висеть в памяти…

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

…Следуя шаблону MVVM Light ViewModelLocator, MainWindow пытается разрешить контекст данных из статического экземпляра ViewModelLocator, определенного в App.xaml…

Я не уверен, что это будет хорошо сочетаться с NInject. Это контейнер для внедрения зависимостей, а не контейнер для поиска служб (хотя он использует его под капотом). Основная цель NInject (и аналогичных фреймворков) — избежать необходимости использования шаблона поиска служб и вместо этого внедрить все ваши зависимости.

…Создается экземпляр ViewModelLocator, и в конструкторе он инициализирует другое стандартное ядро…

Если ваш сценарий не является довольно сложным (что на самом деле не для этого приложения), тогда я предлагаю придерживаться одного ядра NInject.

Предлагаемое решение

Сам класс view model locator мало что делает для вас, кроме как позволяет разделить вашу логику инициализации и выполнять выборочную инициализацию в зависимости от того, находитесь ли вы в режиме разработки или в режиме выполнения. Вы могли бы достичь того же с помощью модулей NInject (которые уже используются в статье view model locator, на которую вы ссылались в комментариях).

Я предлагаю вместо использования локатора модели представления указать все свои компоненты в своем классе модуля и выполнить IsInDesignMode логику в Load методе.

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

Есть несколько способов решить эту проблему непосредственно в NInject, вместо того, чтобы прибегать к локатору служб:

  • Используйте внедрение зависимостей в свой вид, чтобы для него требовалась модель представления.
    Если это не работает с использованием внедрения конструктора (как вы упомянули в своих комментариях), вместо этого используйте внедрение свойств с пользовательским свойством, которое перенаправляет его установщик на DataContext .
  • Используйте методы NInject factory ( ToMethod ) для создания своего представления и привязывайте модель представления к представлению в каждом заводском методе.
    Например. Bind<MainView>().ToMethod(context => new MainView() { DataContext = new MainViewModel() });

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