WPF приложение на MVVM с использованием PRISM и Dependency Container

WPF и Silverlight | создано: 12.12.2019 | опубликовано: 12.12.2019 | обновлено: 13.01.2024 | просмотров: 9024

В этой статье показан пример создания WPF-приложения на основе MVVM паттерна проектирования. За основу используется PRISM 7, как MVVM-фреймворк и вместо Autofac, который теперь не поддерживается в PRISM используется DryIoc как DI-контейнер.

Нота печали

В новой версии PRISM 7 к моему огромному сожалению больше не поддерживается DI-контейнер под название Autofac - и это печальная нота этого поста. Не поддерживается в силу некоторых архитектурных особенностей и самого контейнера, и фреймворка PRISM 7. Именно в седьмой версии появились такие решения, которые больше не позволяют использовать immutable контейнер. Подробности можно найти на сайте Prism. Но, к счастью, в интернете полным-полно контейнеров и...

Создание проекта в Visual Studio

Я буду использовать WPF на платформе NET Core:

Далее вводим название проекта:

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

Установка Nuget-пакетов

Первый устанавливаем Prism.Wpf:

Далее ставим Prism.DryIoc, как альтернатива Autofac (выбывшему из списка поддерживаемых для PRISM 7):

Файлы и папки

Теперь создаем папки Views и ViewModels, чтобы не нарушать договоренности принятые в Prism 7. Далее создаем Shell.xaml в папке Views как главное окно нашего приложения. Если вы не помните, то мы удалили MainWindow.xaml. 

Вот его содержимое в конечном виде:

<Window x:Class="WpfMvvmTemplate.Views.Shell"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d"
        Title="{Binding DisplayName}"
        WindowStartupLocation="CenterScreen"
        ResizeMode="CanMinimize"
        Height="450"
        Width="800"
        xmlns:prism="http://prismlibrary.com/"
        prism:ViewModelLocator.AutoWireViewModel="True">
    <Grid>
        <StackPanel>
            <TextBlock Text="TEST TEXT DEMO"></TextBlock>
            <TextBlock Text="{Binding DisplayName, FallbackValue=\{TITLE\}}"></TextBlock>
        </StackPanel>
    </Grid>
</Window>

Обратите внимание на выделенные строки. Это требуется для того, чтобы Shell.xaml "нашел" свой ViewModel и связал свой binding автоматически (конечно же это сделает за нас Prism 7).

На очереди ViewModel для нашего Shell.xaml. Одна из договоренностей вы уже надеюсь поняли, что... 

Папки Views должны содержать представления (Views) .xaml. А папка ViewModels должна содержать ViewModel'ы для этих представлений (Views).

Другая договоренность гласит. 

Названия ViewModels образуются следующим образом, к названию представления (View) приставляется суффикс ViewModel. Например,  для Shell.xaml должен существовать ShellViewModel.cs. 

Если последняя договоренность не соблюдается, то авто связывание (ViewModelLocator.AutoWireViewModel) не будет работать. Надо отметить, что все договоренности вы можете заменить на своё усмотрение, но при этом надо понимать, что в таком случае, это более комплексный и сложный подход. Вам придется поглубже изучить Prism 7.

Создаём ShellViewModel:

Вот его текстовая сущность:

using Prism.Mvvm;

namespace WpfMvvmTemplate.ViewModels
{
    /// <summary>
    /// Shell ViewModel
    /// </summary>
    public class ShellViewModel : BindableBase {
 
        #region property DisplayName
 
        /// <summary>
        /// Represent DisplayName property
        /// </summary>
        public string DisplayName {
            get => _displayName;
            set => SetProperty(ref _displayName, value);
        }
 
        /// <summary>
        /// Backing field for property DisplayName
        /// </summary>
        private string _displayName = "WPF PRISM (MVVM) + DI (DryIoc)";
 
        #endregion
 
    }
}

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

А где же Prism 7

Для того чтобы всё заработало на фреймворке Prism7, надо поменять тип приложения. Для этого открываем самый главный файл App.xaml и меняем его содержимое:

<Application x:Class="WpfMvvmTemplate.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:local="clr-namespace:WpfMvvmTemplate"
             StartupUri="MainWindow.xaml">
    <Application.Resources>

    </Application.Resources>
</Application>

на новое:

<prism:PrismApplication x:Class="WpfMvvmTemplate.App"
                        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                        xmlns:prism="http://prismlibrary.com/">

</prism:PrismApplication>

Далее надо открыть App.xaml.cs и закончить начатое. Текущее содержимое:

using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;

namespace WpfMvvmTemplate
{
    /// <summary>
    /// Interaction logic for App.xaml
    /// </summary>
    public partial class App : Application
    {
    }
}

меняем на новое:

using System.Windows;
using Prism.Ioc;
using WpfMvvmTemplate.Views;

namespace WpfMvvmTemplate
{
    /// <summary>
    /// Interaction logic for App.xaml
    /// </summary>
    public partial class App
    {
        /// <inheritdoc />
        protected override void RegisterTypes(IContainerRegistry containerRegistry)
        {

        }

        /// <inheritdoc />
        protected override Window CreateShell()
        {
            return Container.Resolve<Shell>();
        }
    }
}

Обратите внимание на выделенную строку. В этой строке мы говорим фреймворку, что является стартовым окном (Window) в нашем приложении. Именно по-этому мы и указали Shell. 

Еще немаловажная информация. Теперь для регистрации своих зависимостей в контейнере можно делать в методе RegisterTypes (строка 13). На самом деле всё просто, потому что сам DryIoc является очень простым и поэтому очень быстрым. Он обеспечивает 99% всех потребностей Dependency Injection. Читайте инструкции!

Запускаем проект

Ничего удивительного... Всё работает.

Заключение

В заключении хотелось бы отметить, что существует возможность использовать Autofac в Prism, но только это уже предыдущая версия фреймворка 6. Я также писал об этом когда-то давно.