二十五、Prism框架

1、Prism的概念,应用场景

  • Modules:Modules是能够独立开发、测试、部署的功能单元,Modules可以被设计成实现特定业务逻辑的模块(如Profile Management),也可以被设计成实现通用基础设施或服务的模块(如Logging、Exception Management)。
  • ModuleCatalog:在Prism中,Module Catalog指明了要Load哪些Module,和用什么样的顺序去Load这些Module。
  • Shell:Shell是宿主应用程序(host application),modules将会被load到Shell中。Shell定义了应用程序的整体布局和结构,而不关心寄宿其中的Module,Shell通常实现通用的application service和infrastructure,而应用的逻辑则实现在具体的Module中,同时,Shell也提供了应用程序的顶层窗口。
  • Views:Views是应用中展现特定功能的视图,它展现UI、定义交互行为,并且通过数据绑定的方式与ViewModel进行交互。
  • ViewModelPresenters:View Model用来封装应用程序的UI逻辑及其状态。
  • Model:Model被用来封装数据和相应的验证,以及相关的业务规则来保证数据的一致性和正确性。
  • Commands:Command被用来封装应用程序功能,Prism提供了DelegateCommandCompositeCommand两个类。
  • Regions(区域):Regions是应用程序UI的逻辑区域,它很像一个PlaceHolder,Views在Regions中展现,很多种控件可以被用作Region:ContentControl、ItemsControl、ListBox、TabControl。Views能在Regions编程或者自动呈现,Prism也提供了Region导航的支持。


2、Prism使用过程(PrismApplication,Prism项目模板),Prism实现Mvvm(BindableBase,RaisePropertyChanged(),DelegateCommand,SetProperty())

创建Prism项目时可以先创建WPF,然后修改App.xaml内容。也可以使用项目模板,需要在拓展中安装"Prism Tempalte Pack",然后创建新项目时,选择Prism Full App(WPF)项目即可:

下面一个WPF修改成Prism项目为例,简单使用Prism项目: 

  • 创建WPF窗体项目

  •  下载prism.Dryioc依赖包

  •  在App.xaml命名空间中添加prism依赖:xmlns:prism="http://prismlibrary.com/"。将App.xaml文件中的Application标签修改成为 prism:PrismApplication  。(wfp项目应用的是Application对象,使用prism框架的项目应用的是PrismApplication。Application对象如何启动主页面, StartupUri, PrismApplication启动主页面可以靠StartupUri(可删去),也可不需要,推荐使用App .xaml.cs中CreateShell()方法中的依赖注入)
<prism:PrismApplication x:Class="PrismDemo.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:local="clr-namespace:PrismDemo"
             xmlns:prism="http://prismlibrary.com/">
    <Application.Resources>
    </Application.Resources>
</prism:PrismApplication>
  • 创建Views和ViewModels文件夹,将项目创建时自动创建的MainWindow.xaml视图文件,移动到Views文件夹下(别忘了修改MainWindow.xaml和MainWindow.xaml.cs中的命名空间,要加上.Views)
<Window x:Class="Demo1.MainWindow"></Window>
修改为:
<Window x:Class="Demo1.Views.MainWindow"></Window>
  • 修改App.xaml.cs继承的类,将继承的Window类改成PrismApplication类,实现PrismApplication类中的CreateShell()方法和RegisterTypes()方法
namespace PrismDemo
{
    /// <summary>
    /// App.xaml 的交互逻辑
    /// </summary>
    public partial class App : PrismApplication
    {
        // 关系:PrismApplication 继承于 PrismApplicationBase 继承于 Application
        // 快捷键:查看对象源码:fn+f12,快速实现接口,方法,抽象类:alt+enter+enter
        // 快速创建构造函数:ctor

        // Window窗体, StartupUri可以启动一个窗体
        // CreateShell()主要负责启动一个主页面。Main
        protected override Window CreateShell()
        {
            // 启动一个窗体MainWindow
            return Container.Resolve<Main>();
        }

        protected override void RegisterTypes(IContainerRegistry containerRegistry)
        {
        }
       
    }
}
  • 页面:

点击更改Name按钮后,会与mvvm框架实现效果一样,修改文本框名字,实现视图和后台修改时数据交互。

  • MainWindow.xaml视图中测试代码编写:
<Window x:Class="PrismDemo.Views.MainWindow"
        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"
        xmlns:local="clr-namespace:PrismDemo"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Grid>
        <!--xaml中binding数据来源于ViewModel,约定:ViewModel放到ViewModels文件夹,命名:以ViewModel结尾,以窗体的名称开头,如:MainWindowViewModel-->

        <StackPanel HorizontalAlignment="Left">
            <TextBox Text="{Binding Name}" Width="100"/>
            <!--准备把xaml视图上的业务逻辑和视图解耦-->
            <Button Content="更改Name" Command="{Binding ChangeNameCommand}" CommandParameter="王五"/>
        </StackPanel>
    </Grid>
</Window>
  •  MainWindow.xaml.cs的DataContext绑定ViewModel类中编写的数据
namespace PrismDemo.Views
{
    /// <summary>
    /// Main.xaml 的交互逻辑
    /// </summary>
    public partial class MainWindow : Window
    {
        MainWindowViewModel viewModel = new MainWindowViewModel();

        public Main()
        {
            InitializeComponent();
            // 把ViewModel和当前窗体MainWindow建立联系
            // 只能保证:ViewModel提供的数据源可以在xaml视图上使用。
            DataContext = viewModel;
        }
    }
}
  •  MainWindowViewModel.cs中代码:

Prism.Mvvm中的BindableBase 相当于CommunityToolkit.Mvvm中的ObservableObject 

Prism.Mvvm.BindableBase中的RaisePropertyChanged()相当于CommunityToolkit.Mvvm中的ObservableObject.OnPropertyChanged() 。和视图数据交互的还可以使用SetProperty(ref _title, value);实现

Prism.Commands.DelegateCommand ===> CommunityToolkit.Mvvm中的RelayCommand

namespace PrismDemo.ViewModels
{
    public class MainWindowViewModel : BindableBase
    {
        // DelegateCommand 
        public DelegateCommand<string> ChangeNameCommand { get; private set; }
        public MainWindowViewModel()
        {
            Name = "张三";

            ChangeNameCommand = new DelegateCommand<string>(ChangeName);
        }

        private void ChangeName(string obj)
        {
            Name = obj;
        }

        private string name;

        public string Name
        {
            // 通过BindableBase.RaisePropertyChanged()可以保证xaml视图上数据变化,通知ViewModel, 重新渲染到视图。
            get { return name; }
            //set { name = value; RaisePropertyChanged(); }
            set { SetProperty(ref name, value); }   
        }
    }
}

3、自动绑定ViewModel

  • 实现数据自动绑定(不在视图.xaml.cs中使用this.DataContent绑定)需要满足三条要求
    • 窗体|页面|用户控件必须放到Views文件夹下
    • 模型必须放到ViewModels文件夹下
    • 模型名称的名称,必须是窗体名称开头,且以ViewModel结尾
  • 满足以上三条,不需要XXX.xaml.cs中手动绑定DataContext,实现自动绑定ViewModel。

如下图目录结构:

 4、Prism区域(负责管理视图中所有定义的区域),实现简单页面点击导航展示新页面

  参考上面第3部分,创建WPF项目,将项目改成Prism项目,需要修改如下:

  • App.xaml.cs中重写的RegisterTypes()方法中,将导航点击展示需要展示的子窗体,注册进项目(以便在主页面按钮点击时给ContentControl控件使用prism:RegionManager.RegionName="RegionName"绑定后台设置控件内容的操作)。
protected override void RegisterTypes(IContainerRegistry containerRegistry)
{
      containerRegistry.RegisterForNavigation<PageA>();
      containerRegistry.RegisterForNavigation<PageB>();
}
  • Main.xml视图:

需要引入prism的命名依赖:xmlns:prism="http://prismlibrary.com/",以供控件使用。 ContentControl 只能展示用户控件窗体

<Window x:Class="ModelDemo.Views.Main"
        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"
        xmlns:local="clr-namespace:ModelDemo"
        xmlns:prism="http://prismlibrary.com/"
        mc:Ignorable="d"
        Title="主页面" Height="450" Width="800">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="60"></RowDefinition>
            <RowDefinition></RowDefinition>
        </Grid.RowDefinitions>

        <StackPanel Grid.Row="0" HorizontalAlignment="Left" Orientation="Horizontal" Background="AliceBlue" Width="800">
            <Button Content="PageA" Command="{Binding ChangeCommand}" Width="100" Margin="10" CommandParameter="PageA"/>
            <Button Content="PageB " Command="{Binding ChangeCommand}" Width="100" Margin="10" CommandParameter="PageB"/>
        </StackPanel>
<!-- ContentControl 只能展示用户控件窗体 -->
        <ContentControl Grid.Row="1" prism:RegionManager.RegionName="RegionName"/>
    </Grid>
</Window>

  • PageA.xaml视图:
<UserControl x:Class="ModelDemo.Views.PageA"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:ModelDemo.Views"
             mc:Ignorable="d" 
             d:DesignHeight="450" d:DesignWidth="800" Background="Blue">
    <Grid>
        <TextBlock Text="PageA窗体" FontSize="60"/>
    </Grid>
</UserControl>

  •  PageB.xaml视图:
<UserControl x:Class="ModelDemo.Views.PageB"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:ModelDemo.Views"
             mc:Ignorable="d" 
             d:DesignHeight="450" d:DesignWidth="800" Background="Yellow">
    <Grid>
        <TextBlock Text="PageB窗体" FontSize="60"/>
    </Grid>
</UserControl>

  • MainViewModel.cs需要继承BindableBase,在构造函数中注入IRegionManager,才能使用区域块Regions调用在App.xaml.cs中RegisterTypes()方法中注册的用户控件子窗体,实现跳转到主窗体中的ContentControl控件中。
namespace ModelDemo.ViewModels
{
    public class MainViewModel:BindableBase
    {
        private readonly IRegionManager regionManager;

        public DelegateCommand<string> ChangeCommand { set; get; }

        //构造函数里注入IRegionManager类,调用对象才能使用区域块跳转窗体
        public MainViewModel(IRegionManager region)
        {
            ChangeCommand = new DelegateCommand<string>((param) =>
            {
                regionManager.Regions["RegionName"].RequestNavigate(param);
            });
            this.regionManager = region;
        }
    }
}

 5、Prism模块化

        模块化的意思就是,将上面4中的PageA和PageB页面拆分出去,单独封装为类库模块,在主程序中调用这两个模块,实现导航栏页面展示。

模块类库创建步骤: 

  1. WPF类库项目(也可以用WPF(.Net Framework)应用程序---点击项目属性---改成类库项目)
  2. 每个模块都要安装程序包Prism.DryIoc
  3. 按约定编写每个模块库的Views(视图使用用户控件才能在主程序的<ContentControl/>中使用本模块的视图)和ViewModels(没有视图操作可以不写)
  4. 每个模块库都要创建配置文件XXXProfile配置文件,实现IMoudle接口,并在实现的RegisterTypes()方法中注册该页面。
  5. 注意: containerRegistry.RegisterForNavigation<PageA>();  没有对视图操作的可以不用写PageAViewModel参数。例如下面MoudelA的MoudelAProfile配置文件:
namespace ModuleA
{
    public class ModuleAProfile : IModule
    {
        public void OnInitialized(IContainerProvider containerProvider)
        {
            
        }

        public void RegisterTypes(IContainerRegistry containerRegistry)
        {
            // 注册用户控件,让这个模块的用户控件视图将来在主程序中可以实现导航跳转
            // RegisterForNavigation<视图,视图模型>
            containerRegistry.RegisterForNavigation<PageA, PageAViewModel>();
        }
    }
}

主程序添加模块步骤:

1.创建WPF(.Net Framework)应用程序项目,将这个项目改成Prism项目。

2.按照约定创建Views和ViewModels文件夹存放视图和视图模型。

3.App.xaml.cs中通过重写ConfigureModuleCatalog()方法,将创建的模块库添加进主程序:

namespace ModuleMain
{
    /// <summary>
    /// App.xaml 的交互逻辑
    /// </summary>
    public partial class App : PrismApplication
    {
        protected override Window CreateShell()
        {
            return Container.Resolve<Main>();
        }

        protected override void RegisterTypes(IContainerRegistry containerRegistry)
        {
        }

//=== 方式1: ConfigureModuleCatalog配合Profile(IModule)
// 需要在主项目中把依赖的两个模块库ModuleA和ModuleB的dll依赖引入
// ConfigureModuleCatalog()主要负责把依赖的两个模块添加到Prism框架中进行管理.
        protected override void ConfigureModuleCatalog(IModuleCatalog moduleCatalog)
        {
            moduleCatalog.AddModule<MoudelAProfile>();
            moduleCatalog.AddModule<MoudelBProfile>();
            base.ConfigureModuleCatalog(moduleCatalog);
        }

//===方式2: ModuleMain项目完全不依赖ModuleA和ModuleB,完全解耦。
//在当前项目的bin/debug目录下新建一个Modules文件夹将模块ModuleA、ModuleB的dll复制到此文件夹下,然后下面代码里设置查找改文件夹即可
        //protected override IModuleCatalog CreateModuleCatalog()
        //{ 
        //    return new DirectoryModuleCatalog() { ModulePath = @".\Modules" };
        //}

//===方式3:通过配置文件app.config实现模块化,提醒:需要把ModuleA.dll和ModuleB.dll放到主项目的bin/debug文件夹中
        //protected override IModuleCatalog CreateModuleCatalog()
        //{
        //    return new ConfigurationModuleCatalog();
        //}

    }
}

4.主视图中引用命名依赖:xmlns:prism="http://prismlibrary.com/"。定义区域块:<ContentControl Grid.Row="1" prism:RegionManager.RegionName="ContentRegion"/>,用来实现用户控件跳转展示

<Window x:Class="MoudelMain.Views.Main"
        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"
        xmlns:prism="http://prismlibrary.com/"
        xmlns:local="clr-namespace:MoudelMain.Views"
        mc:Ignorable="d"
        Title="Main" Height="450" Width="800">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="auto"/>
            <RowDefinition/>
        </Grid.RowDefinitions>

        <WrapPanel>
            <Button Content="打开PageA" Command="{Binding OpenPage}" CommandParameter="PageA" Margin="10"/>
            <Button Content="打开PageB" Command="{Binding OpenPage}" CommandParameter="PageB" Margin="10"/>
        </WrapPanel>

        <ContentControl Grid.Row="1" prism:RegionManager.RegionName="ContentRegion"/>
    </Grid>
</Window>

5.MainViewModel主程序中视图模型中:

        如果需要后台和视图数据交互时,视图模型就需要继承BindableBasea类,在字段的set里就可以编写:RaisePropertyChanged()实现交互。

namespace MoudelMain.ViewModels
{
    public class MainViewModel
    {
        public DelegateCommand<string> OpenPage { get; set; }

        private readonly IRegionManager regionManager;


        //构造函数中注入IRegionManager类实现区域管理。
        //Regions["ContentRegion"]找到视图中定义的区域。
        //RequestNavigate("PageA")实现页面导航跳转展示
        public MainViewModel(IRegionManager regionManager)
        {
            OpenPage = new DelegateCommand<string>(Open);

            this.regionManager = regionManager;
        }

        private void Open(string obj)
        {
            regionManager.Regions["ContentRegion"].RequestNavigate(obj);
        }
    }
}

效果展示:


 6、扩展:依赖注入DI,控制反转Ioc

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值