您的位置:

WPF Prism

一、简介

WPF Prism是一套由微软模式和实践团队(Microsoft Patterns & Practices Team)开发的面向Windows Presentation Foundation (WPF) 的框架。该框架目标是带来可重用、可插入、可测试的模块化代码,以及保持松耦合的应用程序代码。WPF Prism框架用于帮助开发人员构建具有高度模块化、可测试、可维护且易于扩展的WPF桌面应用程序。Prism利用依赖注入 (DI) 容器来实现模块化、命令、事件聚合 (EA)、导航、区域等功能,以提供一致性的解决方案。

二、模块化开发

设计良好的应用程序应该是由若干松耦合的模块组成的,每个模块都能独立、可移植、可重用,并且易于测试。Prism框架通过依赖注入(DI)容器实现模块化,代码创建对象时只需指定对象基类或接口,DI容器会自动寻找并构造该对象所依赖的实例,并返回完整的对象。

实际上,在Prism框架上,模块等同于程序集,而且每个程序集都必须包含一个派生自 IModule 接口的模块类。这个类需要实现 IModule.Initialize 方法,通过该方法可以向程序注入一些功能。下面介绍一些重要的基本模块初始化,以及在模块之间共享数据。

1. 模块初始化

使用Prism框架,您可以使用以下代码,初始化模块

    
// MyApp.ModulA.dll
public class ModulA : IModule
{
    public void Initialize()
    {
        // 初始化视图和服务所需的代码
    }
}

// App.xaml.cs
protected override void RegisterTypes(IContainerRegistry containerRegistry)
{
    // 使用RegisterType注册服务
    containerRegistry.RegisterType<IService, Service>();
}

protected override void ConfigureModuleCatalog(IModuleCatalog moduleCatalog)
{
    // 使用ModuleCatalog添加模块
    moduleCatalog.AddModule(typeof(ModulA));
}
    

2. 模块间共享数据

在模块之间,有时需要共享数据。例如,ModulA 模块需要提供数据,以便其他模块使用,您可以使用以下代码,向其他需要该数据的模块添加服务

    
// MyApp.ModulA.dll
public class ModulA : IModule
{
    // 公共数据
    public static IData Data = new Data();

    public void Initialize()
    {
        // 注册服务
        containerRegistry.RegisterInstance<IData>(Data);
    }
}

// MyApp.ModulB.dll
public class ModulB : IModule
{
    public ModulB(IData data)
    {
        var myData = data as Data;// 编写代码,以便使用Data服务
    }

    public void Initialize()
    {
        // 初始化...
    }
}

// App.xaml.cs
protected override void ConfigureModuleCatalog(IModuleCatalog moduleCatalog)
{
    moduleCatalog.AddModule<ModulA>();// 我们不需要在此处添加ModulB,因为ModulB是自动加载的
}
    

三、依赖注入

依赖注入(Dependency Injection)是一种编程方法,通过该方法,我们可以创建松耦合、可测试且可重用的代码。您只需定义接口或基类,然后使用Prism框架,自动寻找并创建该接口或基类的实现。依赖注入是Prism框架的核心,贯穿整个框架。

1. 构造函数注入

构造函数注入是最简单的依赖注入方法。在该模式下,您仅需使用 Container.Resolve 方法创建实例。以下代码演示使用构造函数注入实例化对象

    
// 定义接口和实现
public interface IService
{
    void DoSomething();
}

public class Service : IService
{
    public void DoSomething()
    {
        Console.WriteLine("Hello World!");
    }
}

// Main
var service = Container.Resolve<IService>();
service.DoSomething();
    

2. 属性注入

属性注入是一种广泛应用的依赖注入方法。该方法在对象创建完成之后,通过反射操作,向对象的属性自动注入相应的实现实例。通过Prism框架,我们使用 [Dependency] 特性注入属性。

    
// 定义接口和实现
public interface IService
{
    void DoSomething();
}

public class Service : IService
{
    public void DoSomething()
    {
        Console.WriteLine("Hello World!");
    }
}

// Main
class MainViewModel
{
    [Dependency]
    public IService Service { get; set; } // 必须是公共属性

    public MainViewModel()
    {

    }

    public void DoSomething()
    {
        Service.DoSomething();
    }
}

// 在Main函数中初始化
var mainViewModel = new MainViewModel();
Container.BuildUp(mainViewModel); // 执行依赖注入
mainViewModel.DoSomething();
    

四、命令

在Prism框架中,命令(Command)是一项非常强大的功能,它可以轻松解决许多典型的WPF应用程序需求,例如向视图添加命令,创建分离的命令对象,将命令绑定到控件或 ViewModel 等。

在Prism框架内部,命令使用 DelegateCommand / DelegateCommand<T> 类来实现。例如,以下是使用 DelegateCommand 类创建命令的示例代码

    
// 定义命令
public DelegateCommand MyCommand { get; private set; }

public MainViewModel()
{
    // 添加命令
    MyCommand = new DelegateCommand(Execute, CanExecute);
}

void Execute()
{
    Debug.WriteLine("执行命令");
}

bool CanExecute()
{
    return true;
}
    

五、导航

在WPF应用程序中,导航(navigation)是一项关键的功能。该功能使我们能够通过显示给定的页面或视图,让用户在视觉上移动,以便在不同的屏幕之间进行切换。在Prism框架中,导航需要两个主要组件: 导航器(NavigationService)和导航目的地(NavigationTarget)。

导航器处理导航请求,而导航目的地则对导航请求进行响应。

    
// 在模块中添加导航
public class AppModule : IModule
{
    public AppModule(INavigationService navigationService)
    {
        this.navigationService = navigationService;
    }

    public void Initialize()
    {
        navigationService.RegisterViewWithRegion("MainRegion", typeof(ViewA));
    }
}

// 在ViewModel中实现导航
public class ViewAViewModel : BindableBase // BindableBase是Prism中的一个基类,实现了 INotifyPropertyChanged
{
    public ViewAViewModel(INavigationService navigationService)
    {
        NavigateCommand = new DelegateCommand(() => navigationService.Navigate("ViewB"));
    }

    private DelegateCommand _navigateCommand;
    public DelegateCommand NavigateCommand
    {
        get => _navigateCommand;
        set => SetProperty(ref _navigateCommand, value);
    }
}

// 在View中,创建一个按钮并将它绑定到导航命令


// 显示View
navigationService.Navigate("ViewA");
    

六、区域

在WPF Prism中,区域(Region)是指一个可以在其中显示一个或多个视图的窗体区域。您可以将区域视为桌面应用程序中的子窗口,因为您可以将视图放入区域,并在不同的区域中切换视图。

以下示例演示如何创建区域,并将视图添加到该区域

    
// App.xaml.cs
public partial class App : PrismApplication
{
    protected override Control CreateShell()
    {
        return Container.Resolve
   ();
    }

    protected override void RegisterTypes(IContainerRegistry containerRegistry)
    {
        containerRegistry.Register();
    }

    protected override void ConfigureModuleCatalog(IModuleCatalog moduleCatalog)
    {
        moduleCatalog.AddModule
    ();
    }

    protected override void ConfigureRegionAdapterMappings(RegionAdapterMappings regionAdapterMappings)
    {
        base.ConfigureRegionAdapterMappings(regionAdapterMappings);
        regionAdapterMappings.RegisterMapping(typeof(StackPanel), Container.Resolve
     ());
    }
}

// 注册StackPanel区域和Adapter
public class StackPanelRegion : Region
{
}

public class StackPanelRegionAdapter : IRegionAdapter
{
    public IRegion Initialize(object regionTarget, string regionName)
    {
        var region = new StackPanelRegion();
        region.Name = regionName;

        var stackpanel = regionTarget as StackPanel;
        RegionManager.SetRegionManager(stackpanel, ContainerLocator.Current.Resolve
      ());
        RegionManager.UpdateRegions();
        return region;
    }

    public bool IsAdaptable(object regionTarget)
    {
        return regionTarget is StackPanel;
    }

    public void AdaptView(object regionTarget)
    {

    }
}

// 主模块
public class MainModule : IModule
{
    public void Initialize()
    {
        IRegionManager regionManager = ServiceLocator.Current.GetInstance
       
        (); regionManager.RegisterViewWithRegion("TopArea", typeof(TopView)); } } // 创建具有指定名称的区域,以及包含“TopArea”名称的StackPanel控件 
        
        
       
      
     
    
   

七、总结

通过本文的介绍,我们可以看到WPF Prism框架的强大功能:模块化开发、依赖注入、命令、导航和区域。通过这些功能,我们可以轻松地构建可扩展、可重用、可测试的WPF应用程序。当然,除了本文介绍的这些内容之外,WPF Prism框架还提供了许多其他的功能和特性,如事件聚合(event aggregator),属性注入、策略等。