Prism框架模块化思想、Shell、Region、Moduel

这是一篇朝夕Jovan老师讲Prism的课堂学习笔记。

PrismBootstrapper&PrismApplication【全局资源管控,项目启动】

Shell:壳-》Window

Region:区域

Module:模块(View,ViewModel,Model)

项目的基本结构:Shell=>Region=>Module

Shell

protected override Window CreateShell()
    return Container.Resolve<NavigationWindow>();

Region

如果只更新数据源,UI不变则可以使用prism:RegionManager.RegionContext=""

一般的WPF容器是无法创建Region的,例如:Grid,UniformGrid,DockPanel,Canvas等。

可以创建Region的,例如:

  • ContentControl,只能显示一个,所有内容都是堆叠的。
  • ItemsControl,所有内容都是栈的形式布局的,类似StackPanel
  • ComboBox,所有内容,类似下拉框,下拉列表。
  • TabControl,类似TabControl控件
  • 总结:具有RegionAdapter才可以创建Region,可以自定义Region

    ContentControl->ContentControlRegionAdapter

    ItemsControl->ItemsControlRegionAdapter

    ComboBox->SelectorRegionAdapter

    TabControl->TabControlRegionAdapter

    Region的多种注册方式

    第一种:Xaml方式注册

    <!--第一种情况,Xaml方式注册一个区域-->
    <!--<ContentControl prism:RegionManager.RegionName="ContentRegion"/>-->
    

    第二种通过C#代码注册

    Region中的都是UserControl,所以应该是创建一个ViewA,然后再添加到Region中。

    <!--第二种方式,代码注册-->
    <ContentControl Name="cc"/>
    
    public MainWindow(IRegionManager regionManager)
        InitializeComponent();
        RegionManager.SetRegionName(this.cc, "ContentRegion");
        /// 将相关View指定到特定的Region
        /// 第一个参数:Region的名称
        /// 第二个参数:View的类型
        regionManager.RegisterViewWithRegion("ContentRegion", typeof(ViewA));
    

    创建自己的RegionAdapter

    定义RegionAdapter

    public class StackPanelRegionAdapter : RegionAdapterBase<StackPanel>
        public StackPanelRegionAdapter(IRegionBehaviorFactory regionBehaviorFactory) : base(regionBehaviorFactory)
        protected override void Adapt(IRegion region, StackPanel regionTarget)
            region.Views.CollectionChanged += (se, ev) =>
                if (ev.Action == NotifyCollectionChangedAction.Add)
                    foreach (UIElement item in ev.NewItems)
                        regionTarget.Children.Add(item);
        protected override IRegion CreateRegion() => new AllActiveRegion();
    

    注册RegionAdapter

    public partial class App : PrismApplication
        protected override Window CreateShell()
            return Container.Resolve<NavigationWindow>();
        protected override void ConfigureRegionAdapterMappings(RegionAdapterMappings regionAdapterMappings)
            base.ConfigureRegionAdapterMappings(regionAdapterMappings);
            regionAdapterMappings.RegisterMapping<StackPanel>(Container.Resolve<StackPanelRegionAdapter>());
    

    Region视图切换、Activate

    Activate&Deactivate有针对,并不是所有Adapter适用,例如:ContentControlRegionAdapter可以,如果使用ItemsControlRegionAdapter则会报错。

    Region视图切换,Navigation。

  • 构造函数注入IRegionManager,IContainerExtension
  • 取出Region,_regionManager.Regions["ContentRegion"];
  • 添加view
  • 导航到指定view
  • Activate&Deactivate有针对,并不是所有Adapter适用,ContentControlRegionAdapter可以,但是ItemsControlRegionAdapter->报错。

    public partial class MainView : Window
        IRegionManager _regionManager;
        IContainerExtension _containerExtension;
        ViewB viewB;
        public MainView(IRegionManager regionManager, IContainerExtension containerExtension)
            InitializeComponent();
            _regionManager = regionManager;
            _containerExtension = containerExtension;
            /// Region视图切换
            /// Activete&Deactivated
            /// 视图切换-》另一种方式:Navigation
            this.Loaded += (se, ev) =>
                var _region = _regionManager.Regions["ContentRegion"];
                viewB = _containerExtension.Resolve<ViewB>();
                _region.Add(_containerExtension.Resolve<ViewA>());
                _region.Add(viewB);
        private void Button_Click(object sender, RoutedEventArgs e)
            var _region = _regionManager.Regions["ContentRegion"];
            _region.Activate(ViewB);
            // Activate&Deactivate有针对,并不是所有Adapter适用
            // ContentControlRegionAdapter
            // ItemsControlRegionAdapter->报错
    

    判断View是否是活跃状态

  • 继承BindableBase, IActiveAware
  • public class ViewAViewModel : BindableBase, IActiveAware, 
        private bool _isActive;
        public bool IsActive
            get => _isActive;
                _isActive = value;
                if (value)
                    System.Diagnostics.Debug.WriteLine("ViewA 激活了");
                    System.Diagnostics.Debug.WriteLine("ViewA 失效了");
                IsActiveChanged?.Invoke(this, new EventArgs());
    	public event EventHandler IsActiveChanged;
    
    

    Navigation控制View切换,

  • 写View的UI代码
  • 写ViewModels的代码
  • 写对应的Cmd方法
  • 注入View,App.cs 中的RegisterTypes,注入VIew,containerRegistry.RegisterForNavigation<ViewA>();
  • ViewModel的构造函数中注入IRegionManager,需要的导航到指定的View:_regionManager.RequestNavigate("ContentRegion", "ViewB");
  • 如果是Module形式的注册,则可以在Module类中通过RegisterForNavigation注册

    public class SysModule : IModule
        public void OnInitialized(IContainerProvider containerProvider)
        public void RegisterTypes(IContainerRegistry containerRegistry)
            containerRegistry.RegisterForNavigation<MenuManagementView>("mmView");
            containerRegistry.RegisterForNavigation<UserManagementView>();
            containerRegistry.RegisterDialog<UserEditView>();
    

    第一种:基本导航

    _regionManager.RequestNavigate("ContentRegion", "ViewB");
    

    第二种:使用参数替代字符串的导航

    <Button Content="ViewA" Command="{Binding ViewACommand}" CommandParameter="ViewA"/>
    private DelegateCommand<string> _viewACommand;
    public DelegateCommand<string> ViewACommand
            if (_viewACommand == null)
                _viewACommand = new DelegateCommand<string>((view) =>
    				_regionManager.RequestNavigate("ContentRegion", view);
            return _viewACommand;
        set { _viewACommand = value; }
    

    第三种:使用TabControl

  • ViewModel增加属性Title
  • 绑定到View
  • 和第二中方法的导航一样可以注册到指定的Region内
  • <Window.Resources>
        <Style TargetType="TabItem">
            <Setter Property="Header" Value="{Binding DataContext.Title}"/>
        </Style>
    </Window.Resources>
    <TabControl prism:RegionManager.RegionName="ContentRegion" Grid.Row="1"/>
    

    第四种:判断窗口是否重现还是新增

    继承INavigationAware,IsNavigationTarget方法控制

  • INavigationAware,实现三个接口方法
  • IsNavigationTarget:View是重现(返回True)还是新建(返回False)
  • OnNavigatedTo:导航传值,导航日志
  • OnNavigatedFrom:从这个页面到那个页面,关注的是出发时机。
  • 第五种:导航传值

    NavigationWindowViewModel中的ViewACommand传参

    ViewA中接收参数,OnNavigatedTo中:var param = navigationContext.Parameters["value"];

    //NavigationWindowViewModel中的Command
    private DelegateCommand<string> _viewACommand;
    public DelegateCommand<string> ViewACommand
            if (_viewACommand == null)
                _viewACommand = new DelegateCommand<string>((view) =>
                    // 导航到 ViewA
                    // 传值
                    // 导航某个页面,添加判断,是否是可以导航过去
                    NavigationParameters param = new NavigationParameters();
                    param.Add("value", "123");
                    _regionManager.RequestNavigate("ContentRegion", view,param);
            return _viewACommand;
        set { _viewACommand = value; }
    //ViewA中的OnNavigatedTo
    public void OnNavigatedTo(NavigationContext navigationContext)
        // 导航传值
        var param = navigationContext.Parameters["value"];
        // 历史记录
        _journal = navigationContext.NavigationService.Journal;
        //_journal.GoForward();
        //_journal.GoBack();
    public bool IsNavigationTarget(NavigationContext navigationContext)
        // 控件View是重现(返回True)还是新建(返回False)
        return true;
    public void OnNavigatedFrom(NavigationContext navigationContext)
        // navigationContext可以拿ViewA和ViewB两个对象
        // 触发时机
    

    第六种:导航日志控制导航

  • ViewAViewModel继承INavigationAware,实现对应的OnNavigatedTo
  • 向前:journal.GoForward();
  • 向后:journal.GoBack();
  • ViewAViewModel实现字段IRegionNavigationJournal _journal;
  • 添加一个Command调用_journal.GoBack();回到之前的页面
  • 也可以NavigationWindowViewModel构造函数注入,_regionManager也可以达到相同的控制效果

  • 构造函数注入:IRegionNavigationService
  • 实现ForwordCommand、BackCommand
  • _regionManager也可以达到相同的控制效果

    IRegionManager _regionManager;
    IRegionNavigationService _regionNavigationService;
    public NavigationWindowViewModel(IRegionManager regionManager, IRegionNavigationService regionNavigationService)
        _regionManager = regionManager;
        _regionNavigationService = regionNavigationService;
    private DelegateCommand _forwordCommand;
    public DelegateCommand ForwordCommand
            if (_forwordCommand == null)
                _forwordCommand = new DelegateCommand(() =>
                    //if (_regionNavigationJournal.CanGoForward)
                    //    _regionNavigationJournal.GoForward();
                    var j = _regionManager.Regions["ContentRegion"].NavigationService.Journal;
                    //var j = _regionNavigationService.Journal;
                    if (j.CanGoForward)
                        j.GoForward();
            return _forwordCommand;
        set { _forwordCommand = value; }
    private DelegateCommand _backCommand;
    public DelegateCommand BackCommand
            if (_backCommand == null)
                _backCommand = new DelegateCommand(() =>
                    //var j = _regionNavigationService.Journal;
                    var j = _regionManager.Regions["ContentRegion"].NavigationService.Journal;
                    if (j.CanGoBack)
                        j.GoBack();
            return _backCommand;
        set { _backCommand = value; }
    
    IRegionNavigationJournal _journal;
    public DelegateCommand GoBackCommand { get; set; }
    public ViewAViewModel()
        this.Title = "VIewA";
        GoBackCommand = new DelegateCommand(GoBack);
    private void GoBack()
        _journal.GoBack();
    public void OnNavigatedTo(NavigationContext navigationContext)
        // 导航传值
        var param = navigationContext.Parameters["value"];
        // 历史记录
        _journal = navigationContext.NavigationService.Journal;
        //_journal.GoForward();
        //_journal.GoBack();
    

    第七种:判断导航是否结束

    _regionManager.RequestNavigate第三个参数表示导航结束,可以写个Action进行处理,是导航的最后一站。

    private DelegateCommand<string> _viewACommand;
    public DelegateCommand<string> ViewACommand
            if (_viewACommand == null)
                _viewACommand = new DelegateCommand<string>((view) =>
                    // 导航到 ViewA
                    // 传值
                    // 导航某个页面,添加判断,是否是可以导航过去
                    NavigationParameters param = new NavigationParameters();
                    param.Add("value", "123");
                    _regionManager.RequestNavigate("ContentRegion", view,
                        new Action<NavigationResult>((result) =>
                        param);
            return _viewACommand;
        set { _viewACommand = value; }
    

    第八种:判断能不能导航过去:IConfirmNavigationRequest

  • 继承接口:IConfirmNavigationRequest
  • 实现接口:ConfirmNavigationRequest
  • public void ConfirmNavigationRequest(NavigationContext navigationContext, Action<bool> continuationCallback)
        bool result = false;
        if (MessageBox.Show("数据没保存,是否离开", "确认", MessageBoxButton.YesNo) == MessageBoxResult.Yes)
            // 允许导航
            result = true;
        // 不允许导航过来
        continuationCallback(result);
    

    完整示例:

    <Window x:Class="YuTang.PrismLesson.Views.NavigationWindow"
            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:YuTang.PrismLesson.Views"
            xmlns:prism="http://prismlibrary.com/"
            prism:ViewModelLocator.AutoWireViewModel="True"
            mc:Ignorable="d"
            Title="NavigationWindow" Height="450" Width="800">
        <Window.Resources>
            <Style TargetType="TabItem">
                <Setter Property="Header" Value="{Binding DataContext.Title}"/>
            </Style>
        </Window.Resources>
            <Grid.RowDefinitions>
                <RowDefinition Height="40"/>
                <RowDefinition />
            </Grid.RowDefinitions>
            <StackPanel Orientation="Horizontal">
                <Button Content="ViewA" Command="{Binding ViewACommand}" CommandParameter="ViewA"/>
                <Button Content="ViewB" Command="{Binding ViewBCommand}"/>
                <Button Content="Forword" Command="{Binding BackCommand}"/>
                <Button Content="Back" Command="{Binding ForwordCommand}"/>
            </StackPanel>
            <ContentControl prism:RegionManager.RegionName="ContentRegion" Grid.Row="1"/>
            <!--<TabControl prism:RegionManager.RegionName="ContentRegion" Grid.Row="1"/>-->
        </Grid>
    </Window>
    
    public partial class App : PrismApplication
        protected override Window CreateShell()
            return Container.Resolve<NavigationWindow>();
        protected override void RegisterTypes(IContainerRegistry containerRegistry)
            containerRegistry.RegisterForNavigation<ViewA>();
            containerRegistry.RegisterForNavigation<ViewB>();
    

    ViewModel

    public class NavigationWindowViewModel : BindableBase
        IRegionManager _regionManager;
        IRegionNavigationService _regionNavigationService;
        public NavigationWindowViewModel(IRegionManager regionManager, IRegionNavigationService regionNavigationService)
            _regionManager = regionManager;
            _regionNavigationService = regionNavigationService;
        private DelegateCommand<string> _viewACommand;
        public DelegateCommand<string> ViewACommand
                if (_viewACommand == null)
                    _viewACommand = new DelegateCommand<string>((view) =>
                        // 导航到 ViewA
                        // 传值
                        // 导航某个页面,添加判断,是否是可以导航过去
                        NavigationParameters param = new NavigationParameters();
                        param.Add("value", "123");
                        _regionManager.RequestNavigate("ContentRegion", view,
                            new Action<NavigationResult>((result) =>
                            param);
                return _viewACommand;
            set { _viewACommand = value; }
        private DelegateCommand _viewBCommand;
        public DelegateCommand ViewBCommand
                if (_viewBCommand == null)
                    _viewBCommand = new DelegateCommand(() =>
                        // 导航到 ViewB
                        _regionManager.RequestNavigate("ContentRegion", "ViewB");
                return _viewBCommand;
            set { _viewBCommand = value; }
        private DelegateCommand _forwordCommand;
        public DelegateCommand ForwordCommand
                if (_forwordCommand == null)
                    _forwordCommand = new DelegateCommand(() =>
                        //if (_regionNavigationJournal.CanGoForward)
                        //    _regionNavigationJournal.GoForward();
                        var j = _regionManager.Regions["ContentRegion"].NavigationService.Journal;
                        //var j = _regionNavigationService.Journal;
                        if (j.CanGoForward)
                            j.GoForward();
                return _forwordCommand;
            set { _forwordCommand = value; }
        private DelegateCommand _backCommand;
        public DelegateCommand BackCommand
                if (_backCommand == null)
                    _backCommand = new DelegateCommand(() =>
                        //var j = _regionNavigationService.Journal;
                        var j = _regionManager.Regions["ContentRegion"].NavigationService.Journal;
                        if (j.CanGoBack)
                            j.GoBack();
                return _backCommand;
            set { _backCommand = value; }
    

    需要导航的内容

    ViewA.XAML

    <TextBlock Text="ViewA" FontSize="30"/> <Button Content="Back" Command="{Binding GoBackCommand}" Height="40" Width="120"/> </Grid>

    ViewAViewModel.cs

    public class ViewAViewModel : BindableBase, IActiveAware, INavigationAware, IConfirmNavigationRequest
        IRegionNavigationJournal _journal;
        public ViewAViewModel()
            this.Title = "VIewA";
            GoBackCommand = new DelegateCommand(GoBack);
        private void GoBack()
            _journal.GoBack();
        private string _title;
        public string Title
            get { return _title; }
            set { SetProperty(ref _title, value); }
        public DelegateCommand GoBackCommand { get; set; }
        // ===================================================IActiveAware
        private bool _isActive;
        public bool IsActive
            get => _isActive;
                _isActive = value;
                if (value)
                    System.Diagnostics.Debug.WriteLine("ViewA 激活了");
                    System.Diagnostics.Debug.WriteLine("ViewA 失效了");
                IsActiveChanged?.Invoke(this, new EventArgs());
        public event EventHandler IsActiveChanged;
        /// <summary>
        /// =====================================================================INavigationAware========
        /// </summary>
        /// <param name="navigationContext"></param>
        public void OnNavigatedTo(NavigationContext navigationContext)
            // 导航传值
            var param = navigationContext.Parameters["value"];
            // 历史记录
            _journal = navigationContext.NavigationService.Journal;
            //_journal.GoForward();
            //_journal.GoBack();
        public bool IsNavigationTarget(NavigationContext navigationContext)
            // 控件View是重现(返回True)还是新建(返回False)
            return true;
        public void OnNavigatedFrom(NavigationContext navigationContext)
            // navigationContext可以拿ViewA和ViewB两个对象
            // 触发时机
        // ========================================================================IConfirmNavigationRequest
        public void ConfirmNavigationRequest(NavigationContext navigationContext, Action<bool> continuationCallback)
            bool result = false;
            if (MessageBox.Show("数据没保存,是否离开", "确认", MessageBoxButton.YesNo) == MessageBoxResult.Yes)
                // 允许导航
                result = true;
            // 不允许导航过来
            continuationCallback(result);
    

    Model

    Model其实是一系列View的集合,是PrismApplication的扩展延伸。

    是资源管理的延伸(继承IModule实现的两个方法很好的体现出来),一个程序集里View的注册,有了Module,可以由Module分别进行View的注册,Module再在PrismApplication里进行注册。

  • Model中创建View
  • Model中创建一个Module的管理类,负责注册:MainModule
  • 继承:IModule实现两个接口
  • OnInitialized
  • RegisterTypes
  • public class MainModule : IModule public void OnInitialized(IContainerProvider containerProvider) var region = containerProvider.Resolve<IRegionManager>(); //Region region.RegisterViewWithRegion("LeftDockRegion", typeof(LeftMenuView)); region.RegisterViewWithRegion("TopDockRegion", typeof(HeaderView)); //containerProvider.Resolve<IContainerExtension>().Register<ILeftMenuModel, LeftMenuModel>(); public void RegisterTypes(IContainerRegistry containerRegistry)

    注册与发现

    配置文件注册

    配合App.config注册Module

  • App.config,配置文件注册与发现Model**
  • 添加configSections节点:碰到module节点,就会实例化Prism.Modularity.ModulesConfigurationSection,然后把module加载出来。
  • 添加modules节点
  • 添加module节点
  • assemblyFile:程序集名
  • moduleType:通过Type进行反射,程序集全名称+程序集名称
  • moduleName:Module名
  • startupLoaded:加载的时机
  • fals 按需加载,需要了再加载
  • <configuration> <configSections> <section name="modules" type="Prism.Modularity.ModulesConfigurationSection, Prism.Wpf"/> </configSections> <startup> <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.8" /> </startup> <modules> <module assemblyFile="YuTang.PrismLesson.MainModule.dll" moduleType="YuTang.PrismLesson.MainModule.MainModule, YuTang.PrismLesson.MainModule" moduleName="MainModule" startupLoaded="True"/> </modules> </configuration>
  • App.xaml.cs中重写ConfigureModuleCatalog
  • protected override void ConfigureModuleCatalog(IModuleCatalog moduleCatalog)
        base.ConfigureModuleCatalog(moduleCatalog);
        moduleCatalog.AddModule<MainModule.MainModule>();
        moduleCatalog.AddModule<SysModule.SysModule>();
    
  • 重新生成Module程序集,并把dll文件复制到Shell主程序文件目录下。
  • 配合Xml文件注册Moddule

  • 增加一个ModuleConfig.xml文件
  • <?xml version="1.0" encoding="utf-8" ?>
    <m:ModuleCatalog xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                        xmlns:m="clr-namespace:Prism.Modularity;assembly=Prism.Wpf">
    	<m:ModuleInfo ModuleName="MainModule"
                      ModuleType="YuTang.PrismLesson.MainModule.MainModule, YuTang.PrismLesson.MainModule, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
    </m:ModuleCatalog>
    
  • 需要注意App.xaml.cs中的CreateModuleCatalog返回值需要重新写
  • protected override IModuleCatalog CreateModuleCatalog()
        return new XamlModuleCatalog("ModuleConfig.xml");
    

    代码基础注册

  • 添加module引用
  • App.xaml.cs中ConfigureModuleCatalog注册
  • protected override void ConfigureModuleCatalog(IModuleCatalog moduleCatalog)
        base.ConfigureModuleCatalog(moduleCatalog);
        moduleCatalog.AddModule<MainModule.MainModule>();
    

    代码按需加载

  • 添加module引用
  • App.xaml.cs中ConfigureModuleCatalog注册
  • protected override void ConfigureModuleCatalog(IModuleCatalog moduleCatalog)
        base.ConfigureModuleCatalog(moduleCatalog);
        moduleCatalog.AddModule(new ModuleInfo
            ModuleName = "MainModule",
            ModuleType = typeof(MainModule.MainModule).AssemblyQualifiedName,
            //加载时机 是否按需加载
            InitializationMode = InitializationMode.OnDemand
    

    代码IModuleManager任意对象中进行处理

  • 添加module引用
  • App.xaml.cs中ConfigureModuleCatalog注册
  • 还有一个Module加载完成的事件
  • protected override void ConfigureModuleCatalog(IModuleCatalog moduleCatalog)
        base.ConfigureModuleCatalog(moduleCatalog);
        // 任意对象中进行处理,通过对象的构造函数注入ModuleManager的实例
        IModuleManager moduleManager = null;
        moduleManager.LoadModule<MainModule.MainModule>();
        moduleManager.LoadModuleCompleted += (se, ev) => { };
    

    自动扫描目录注册

  • 手动把Module生成的dll,移动到指定目录
  • 通过程序集=》属性=》生成=》配置生成路径指定
  • 通过程序集=》属性=》生成事件=》配置生成路径
  • 定义Module,通过特性控制是否按需加载
  • [Module(ModuleName = "MainModule", OnDemand = false)]
    public class MainModule : IModule
        public void OnInitialized(IContainerProvider containerProvider)
            var region = containerProvider.Resolve<IRegionManager>();
            //Region
            region.RegisterViewWithRegion("LeftDockRegion", typeof(LeftMenuView));
        public void RegisterTypes(IContainerRegistry containerRegistry)
    

    bin.debug中添加目录Modules

    把module生成的dll,放到目录中

    App.xaml.cs中ConfigureModuleCatalog注册

    protected override IModuleCatalog CreateModuleCatalog()
        // 扫描目录
        return new DirectoryModuleCatalog() { ModulePath = @".\Modules" };
    

    Dialog弹出窗口

  • 添加一个用户控件(作为弹出窗口的内容)
  • Module类中注册Dialog弹出窗口
  • public class SysModule : IModule
        public void OnInitialized(IContainerProvider containerProvider){ }
        public void RegisterTypes(IContainerRegistry containerRegistry)
            containerRegistry.RegisterDialog<UserEditView>();
    
  • 实现对应的ViewModel,并实现IDialogAware接口
  • public class UserEditViewModel : IDialogAware
        public string Title => "用户信息编辑";
        public event Action<IDialogResult> RequestClose;
        //是否可以关闭
        public bool CanCloseDialog()
            return true;
        public void OnDialogClosed()
        public void OnDialogOpened(IDialogParameters parameters)
    

    构造函数注入:IDialogService,

  • 包括Show、ShowDialog
  • 第二个参数可以给弹出窗口传参
  • 回调,是窗口状态(在弹出窗口的ViewModel中处理才可以)
  • 创建打开命令,通过注入的IDialogService方法ShowDialog打开弹窗

    public DelegateCommand OpenDialogCommand { get; private set; }
    OpenDialogCommand = new DelegateCommand(() =>
        DialogParameters param = new DialogParameters();
        // View的注册名称 - 参数键值对 - 弹窗回调 - 指定弹出窗口的注册名称
        dialogService.ShowDialog("UserEditView", param,
            (result) =>
    

    自定义弹窗传参

  • 创建自定义控件弹窗
  • 实现自定义控件的ViewModel,继承IDialogAware
  • app.cs注入弹窗
  • ViewModel中调用弹窗
  • <UserControl x:Class="PrismBlankCoreApp.Views.DialogUC"
                 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:PrismBlankCoreApp.Views"
                 xmlns:prism="http://prismlibrary.com/"
                 prism:ViewModelLocator.AutoWireViewModel="True"
                 mc:Ignorable="d"
                 d:DesignHeight="450"
                 d:DesignWidth="800">
        <StackPanel>
            <TextBox  Height="30"
                      Width="200"
                      Background="Orange"
                      Text="{Binding PropertyName}" />
            <Button Content="确定"
                    Command="{Binding OkCommand}" />
            <Button Content="取消"
                    Command="{Binding CancelCommand}" />
        </StackPanel>
    </UserControl>
    

    ViewModel

    namespace PrismBlankCoreApp.ViewModels
        public class DialogUCViewModel : BindableBase, IDialogAware
            public string Title => "传参弹窗测试";
            public event Action<IDialogResult> RequestClose;
            public bool CanCloseDialog()
                return true;
            public void OnDialogClosed()
                //关闭触发
            public void OnDialogOpened(IDialogParameters parameters)
                //打开时触发
            private string fieldName;
            public string PropertyName
                get { return fieldName; }
                set { SetProperty(ref fieldName, value); }
            private DelegateCommand _okCommand;
            public DelegateCommand OkCommand =>
                _okCommand ?? (_okCommand = new DelegateCommand(ExecuteOkCommand));
            private void ExecuteOkCommand()
                    var result = ButtonResult.OK;
                    DialogParameters parameters = new DialogParameters();
                    parameters.Add("参数名", PropertyName);
                    RequestClose?.Invoke(new DialogResult(result, parameters));
                catch (Exception ex)
                    Console.WriteLine(ex.Message);
                    throw;
            private DelegateCommand _cancelCommand;
            public DelegateCommand CancelCommand =>
                _cancelCommand ?? (_cancelCommand = new DelegateCommand(ExecuteCancelCommand));
            private void ExecuteCancelCommand()
                RequestClose?.Invoke(new DialogResult(ButtonResult.No));
    
    public partial class App
        protected override Window CreateShell()
            return Container.Resolve<MainWindow>();
        protected override void RegisterTypes(IContainerRegistry containerRegistry)
            containerRegistry.RegisterDialog<DialogUC>();
    
    public class MainWindowViewModel : BindableBase
            private readonly IDialogService _dialogService;
            private string _title = "Prism Application";
            public string Title
                get { return _title; }
                set { SetProperty(ref _title, value); }
            private string fieldName;
            public string PropertyName
                get { return fieldName; }
                set { SetProperty(ref fieldName, value); }
            private DelegateCommand _dialogCommand;
            public DelegateCommand DialogCommand =>
                _dialogCommand ?? (_dialogCommand = new DelegateCommand(ExecuteDialogCommand));
            private void ExecuteDialogCommand()
                    DialogParameters param = new DialogParameters();
                    _dialogService.ShowDialog("DialogUC", param, (result) =>
                        if (result.Result == ButtonResult.OK)
                            string str = result.Parameters.GetValue<string>("参数名");
                            PropertyName = str;
                catch (System.Exception ex)
                    System.Console.WriteLine(ex.Message);
            public MainWindowViewModel(IDialogService dialogService)
                _dialogService = dialogService;
    

    改变弹窗样式

  • 通过prism:Dialog.WindowStyle修改弹窗样式,比较少用,一般使用的是自定义弹窗。
  • <prism:Dialog.WindowStyle>
        <Style TargetType="Window">
            <Setter Property="WindowStyle" Value="None"/>
            <Setter Property="Width" Value="500"/>
            <Setter Property="Height" Value="300"/>
            <Setter Property="SizeToContent" Value="Manual"/>
        </Style>
    </prism:Dialog.WindowStyle>
        <TextBlock Text="UserEdit" FontSize="30"/>
    </Grid>
    

    自定义弹窗

    多个程序或地方使用,创建一个程序集。

  • 添加一个窗口
  • xaml文件中只处理样式
  • .cs文件中继承接口IDialogWindow
  • public partial class DialogWindow : Window, IDialogWindow
        public DialogWindow()
            InitializeComponent();
        public IDialogResult Result { get; set; }
    
  • 在App.xaml.cs中注册
  • protected override void RegisterTypes(IContainerRegistry containerRegistry)
        containerRegistry.RegisterDialogWindow<DialogWindow>();
        //containerRegistry.RegisterDialogWindow<DialogWindow>("DialogWindow");
    
    OpenDialogCommand = new DelegateCommand(() =>
        DialogParameters param = new DialogParameters();
        // View的注册名称 - 参数键值对 - 弹窗回调 - 指定弹出窗口的注册名称
        dialogService.ShowDialog("UserEditView", param,
            (result) =>
    

    TabControl示例

    实现一个TabControl,带关闭按钮。

    <Window.Resources>
            <Style TargetType="Button" x:Key="TabCloseButtonStyle">
                <Setter Property="Template">
                    <Setter.Value>
                        <ControlTemplate TargetType="Button">
                            <Border VerticalAlignment="Center" HorizontalAlignment="Center" CornerRadius="3" Background="Transparent"
                                    Name="back">
                                <Path Data="M0 0 8 8M0 8 8 0" Margin="5"
                                      Stroke="{TemplateBinding Foreground}" StrokeThickness="1"/>
                            </Border>
                            <ControlTemplate.Triggers>
                                <Trigger Property="IsMouseOver" Value="True">
                                    <Setter Property="Background" Value="#19000000" TargetName="back"/>
                                </Trigger>
                            </ControlTemplate.Triggers>
                        </ControlTemplate>
                    </Setter.Value>
                </Setter>
            </Style>
            <Style TargetType="TabItem">
                <Setter Property="Header" Value="{Binding DataContext.Title}"/>
                <Setter Property="Background" Value="Transparent"/>
                <Setter Property="Margin" Value="2,0"/>
                <Setter Property="Foreground" Value="#444"/>
                <Setter Property="Template">
                    <Setter.Value>
                        <ControlTemplate TargetType="TabItem">
                            <Grid Background="{TemplateBinding Background}" Height="30">
                                <Grid.ColumnDefinitions>
                                    <ColumnDefinition/>
                                    <ColumnDefinition Width="30"/>
                                </Grid.ColumnDefinitions>
                                <TextBlock Text="{TemplateBinding Header}" VerticalAlignment="Center" Margin="10,5,5,5"/>
                                <Button Grid.Column="1" Style="{StaticResource TabCloseButtonStyle}" Foreground="{TemplateBinding Foreground}"
                                        Visibility="Collapsed" Name="close_btn"
                                        Command="{Binding DataContext.CloseCommand}"/>
                            </Grid>
                            <ControlTemplate.Triggers>
                                <Trigger Property="IsMouseOver" Value="True">
                                    <Setter Property="Visibility" Value="Visible" TargetName="close_btn"/>
                                </Trigger>
                                <Trigger Property="IsSelected" Value="True">
                                    <Setter Property="Visibility" Value="Visible" TargetName="close_btn"/>
                                </Trigger>
                            </ControlTemplate.Triggers>
                        </ControlTemplate>
                    </Setter.Value>
                </Setter>
                <Style.Triggers>
                    <Trigger Property="IsMouseOver" Value="True">
                        <Setter Property="Background" Value="#EEE"/>
                    </Trigger>
                    <Trigger Property="IsSelected" Value="True">
                        <Setter Property="Background" Value="orange"/>
                        <Setter Property="Foreground" Value="White"/>
                    </Trigger>
                </Style.Triggers>
            </Style>
        </Window.Resources>
        <DockPanel>
            <ContentControl DockPanel.Dock="Left" Width="220" prism:RegionManager.RegionName="LeftDockRegion"/>
            <ContentControl DockPanel.Dock="Top" Height="80" prism:RegionManager.RegionName="TopDockRegion"/>
            <TabControl prism:RegionManager.RegionName="ContentRegion"/>
        </DockPanel>
    </Grid>
    
    public class MenuManagementViewModel : BindableBase, INavigationAware
        private string UriPath = "";
        private string _title;
        public string Title
            get { return _title; }
            set { _title = value; }
        public DelegateCommand<string> CloseCommand { get; private set; }
        public DelegateCommand SaveCommand { get; private set; }
        public MenuManagementViewModel(IRegionManager regionManager, IUnityContainer unityContainer, ICompositeCommands compositeCommands)
            this.Title = "菜单管理";
            CloseCommand = new DelegateCommand<string>((param) =>
                // 把所在Region里面的当前ViewModel对应的View移除掉
                // 操作Region
                // 通过Unity顶层容器获取对应的类型
                var obj = unityContainer.Registrations.Where(v => v.Name == UriPath).FirstOrDefault();
                string name = obj.MappedToType.Name;
                if (!string.IsNullOrEmpty(name))
                    var region = regionManager.Regions["ContentRegion"];
                    var view = region.Views.Where(v => v.GetType().Name == UriPath).FirstOrDefault();
                    if (view != null)
                        region.Remove(view);
            SaveCommand = new DelegateCommand(() =>
                // 菜单保存
            compositeCommands.DoCommand.RegisterCommand(SaveCommand);
        public void OnNavigatedTo(NavigationContext navigationContext)
            UriPath = navigationContext.Uri.ToString();
        public bool IsNavigationTarget(NavigationContext navigationContext)
            return true;
        public void OnNavigatedFrom(NavigationContext navigationContext)
    
    public class SysModule : IModule
        public void OnInitialized(IContainerProvider containerProvider)
        public void RegisterTypes(IContainerRegistry containerRegistry)
            containerRegistry.RegisterForNavigation<MenuManagementView>("mmView");
            containerRegistry.RegisterForNavigation<UserManagementView>();
            containerRegistry.RegisterDialog<UserEditView>();