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

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

简介

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


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

  2. Shell:壳-》Window

  3. Region:区域

  4. 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的,例如:

  1. ContentControl,只能显示一个,所有内容都是堆叠的。
  2. ItemsControl,所有内容都是栈的形式布局的,类似StackPanel
  3. ComboBox,所有内容,类似下拉框,下拉列表。
  4. 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。

  1. 构造函数注入IRegionManager,IContainerExtension
  2. 取出Region,_regionManager.Regions["ContentRegion"];
  3. 添加view
  4. 导航到指定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是否是活跃状态

  1. 继承BindableBase, IActiveAware
  2. 实现
public class ViewAViewModel : BindableBase, IActiveAware, 
{
    private bool _isActive;
    public bool IsActive
    {
        get => _isActive;
        set
        {
            _isActive = value;

            if (value)
            {
                System.Diagnostics.Debug.WriteLine("ViewA 激活了");
            }
            else
            {
                System.Diagnostics.Debug.WriteLine("ViewA 失效了");
            }
            IsActiveChanged?.Invoke(this, new EventArgs());
        }
    }

	public event EventHandler IsActiveChanged;
}

Navigation控制View切换,

  1. 写View的UI代码
  2. 写ViewModels的代码
  3. 写对应的Cmd方法
  4. 注入View,App.cs 中的RegisterTypes,注入VIew,containerRegistry.RegisterForNavigation<ViewA>();
  5. 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
{
    get
    {
        if (_viewACommand == null)
            _viewACommand = new DelegateCommand<string>((view) =>
            {
				_regionManager.RequestNavigate("ContentRegion", view);
            });
        ;
        return _viewACommand;
    }
    set { _viewACommand = value; }
}

第三种:使用TabControl

  1. ViewModel增加属性Title
  2. 绑定到View
  3. 和第二中方法的导航一样可以注册到指定的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方法控制

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

第五种:导航传值

  1. NavigationWindowViewModel中的ViewACommand传参

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

//NavigationWindowViewModel中的Command
private DelegateCommand<string> _viewACommand;

public DelegateCommand<string> ViewACommand
{
    get
    {
        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两个对象
    // 触发时机
}

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

  1. ViewAViewModel继承INavigationAware,实现对应的OnNavigatedTo
    1. 向前:journal.GoForward();
    2. 向后:journal.GoBack();
  2. ViewAViewModel实现字段IRegionNavigationJournal _journal;
  3. 添加一个Command调用_journal.GoBack();回到之前的页面

也可以NavigationWindowViewModel构造函数注入,_regionManager也可以达到相同的控制效果

  1. 构造函数注入:IRegionNavigationService
  2. 实现ForwordCommand、BackCommand

_regionManager也可以达到相同的控制效果

IRegionManager _regionManager;
IRegionNavigationService _regionNavigationService;

public NavigationWindowViewModel(IRegionManager regionManager, IRegionNavigationService regionNavigationService)
{
    _regionManager = regionManager;
    _regionNavigationService = regionNavigationService;
}

private DelegateCommand _forwordCommand;

public DelegateCommand ForwordCommand
{
    get
    {
        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
{
    get
    {
        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
{
    get
    {
        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

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

完整示例:

XAML

<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>
        <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
    {
        get
        {
            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
    {
        get
        {
            if (_viewBCommand == null)
                _viewBCommand = new DelegateCommand(() =>
                {
                    // 导航到 ViewB
                    _regionManager.RequestNavigate("ContentRegion", "ViewB");
                });
            ;
            return _viewBCommand;
        }
        set { _viewBCommand = value; }
    }



    private DelegateCommand _forwordCommand;

    public DelegateCommand ForwordCommand
    {
        get
        {
            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
    {
        get
        {
            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

<Grid>
    <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;
        set
        {
            _isActive = value;

            if (value)
            {
                System.Diagnostics.Debug.WriteLine("ViewA 激活了");
            }
            else
            {
                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里进行注册。

定义

  1. Model中创建View
  2. Model中创建一个Module的管理类,负责注册:MainModule
    1. 继承:IModule实现两个接口
      1. OnInitialized
      2. RegisterTypes
[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));
        region.RegisterViewWithRegion("TopDockRegion", typeof(HeaderView));


        //containerProvider.Resolve<IContainerExtension>().Register<ILeftMenuModel, LeftMenuModel>();
    }

    public void RegisterTypes(IContainerRegistry containerRegistry)
    {

    }
}

注册与发现

配置文件注册

配合App.config注册Module

  1. App.config,配置文件注册与发现Model**
    1. 添加configSections节点:碰到module节点,就会实例化Prism.Modularity.ModulesConfigurationSection,然后把module加载出来。
    2. 添加modules节点
    3. 添加module节点
      1. assemblyFile:程序集名
      2. moduleType:通过Type进行反射,程序集全名称+程序集名称
      3. moduleName:Module名
      4. startupLoaded:加载的时机
        1. fals 按需加载,需要了再加载
<?xml version="1.0" encoding="utf-8" ?>
<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>
  1. 注册
    1. App.xaml.cs中重写ConfigureModuleCatalog
protected override void ConfigureModuleCatalog(IModuleCatalog moduleCatalog)
{
    base.ConfigureModuleCatalog(moduleCatalog);

    moduleCatalog.AddModule<MainModule.MainModule>();
    moduleCatalog.AddModule<SysModule.SysModule>();
}
  1. 重新生成Module程序集,并把dll文件复制到Shell主程序文件目录下。

配合Xml文件注册Moddule

  1. 增加一个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>
  1. 需要注意App.xaml.cs中的CreateModuleCatalog返回值需要重新写
protected override IModuleCatalog CreateModuleCatalog()
{
    return new XamlModuleCatalog("ModuleConfig.xml");
}

代码注册

代码基础注册

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

代码按需加载

  1. 添加module引用
  2. 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任意对象中进行处理

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

自动扫描目录注册

  1. 手动把Module生成的dll,移动到指定目录
  2. 通过程序集=》属性=》生成=》配置生成路径指定
  3. 通过程序集=》属性=》生成事件=》配置生成路径
  1. 定义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)
    {

    }
}
  1. bin.debug中添加目录Modules

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

  3. App.xaml.cs中ConfigureModuleCatalog注册

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

Dialog弹出窗口

  1. 添加一个用户控件(作为弹出窗口的内容)
  2. Module类中注册Dialog弹出窗口
public class SysModule : IModule
{
    public void OnInitialized(IContainerProvider containerProvider){ }
    public void RegisterTypes(IContainerRegistry containerRegistry)
    {
        containerRegistry.RegisterDialog<UserEditView>();
    }
}
  1. 实现对应的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)
    {
        
    }
}
  1. 构造函数注入:IDialogService,

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

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

        });
});

改变弹窗样式

  1. 通过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>
<Grid >
    <TextBlock Text="UserEdit" FontSize="30"/>
</Grid>

自定义弹窗

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

  1. 添加一个窗口
    1. xaml文件中只处理样式
    2. .cs文件中继承接口IDialogWindow
public partial class DialogWindow : Window, IDialogWindow
{
    public DialogWindow()
    {
        InitializeComponent();
    }

    public IDialogResult Result { get; set; }
}
  1. 在App.xaml.cs中注册
protected override void RegisterTypes(IContainerRegistry containerRegistry)
{
    containerRegistry.RegisterDialogWindow<DialogWindow>();
    //containerRegistry.RegisterDialogWindow<DialogWindow>("DialogWindow");
}
  1. 命令调用
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>
<Grid>
    <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>();
    }
}
登峰造极的成就源于自律
原文地址:https://www.cnblogs.com/fishpond816/p/15216767.html