最近在学习 Prsim 和WPF,在油管上找到了一个 PrsimOutlook 项目,作者是 Brian Lagunas ,是当年 Prsim 搬家到GitHub时交给社区的三位贡献者之一.
照着视频学习加上有源代码,作者就是Prsim的作者之一,对Prsim相当的了解.
相关链接
- PrsimOutlook 油管视频链接
- Prsim官网文档
- Prsim官方提供的一些例程代码
- PrsimOutlook 油管视频配套代码 Github
- PrsimOutlook视频中用到的UI框架,可以在Nuget中使用试用版
- Brian Lagunas的个人博客,里面有很多文章
视频一 Setting up the Prism Project 学习记录
视频一 程序最后的结果是这样的
使用 Prsim 提供的扩展创建模板,使用此模板可以快速创建 Prsim项目和 Prsim Module
PrsimOutlook 是 Prsim WPF项目的空模板
Modules 目录下三个项目是 Prsim WPF Mudule 的空模板
PrsimOutlook.Core 是用来放 共享的一些的基础设施
创建程序目录结构如下
- PrsimOutlook 是 Prsim WPF项目的空模板
- Modules 目录下三个项目是 Prsim WPF Mudule 的空模板
- PrsimOutlook.Core 是用来放 共享的一些的基础设施
窗体分为3个Region,定义在 PrismOutlook.Core.RegionNames 类中
namespace PrismOutlook.Core
{
public static class RegionNames
{
public static string ContentRegion = "ContentRegion";
public static string OutlookGroupRegion = "OutlookGroupRegion";
public static string RibbonRegion = "RibbonRegion";
}
}
在MainWindow.xaml中 使用这3个 Region
<ig:RibbonWindowContentHost>
<ig:RibbonWindowContentHost.Ribbon>
<ig:XamRibbon prism:RegionManager.RegionName="{x:Static core:RegionNames.RibbonRegion}">
</ig:XamRibbon>
</ig:RibbonWindowContentHost.Ribbon>
<DockPanel LastChildFill="True">
<ig:XamOutlookBar prism:RegionManager.RegionName="{x:Static core:RegionNames.OutlookGroupRegion}" DockPanel.Dock="Left" Width="200"></ig:XamOutlookBar>
<ContentControl prism:RegionManager.RegionName="{x:Static core:RegionNames.ContentRegion}" />
</DockPanel>
</ig:RibbonWindowContentHost>
最上方是 RibbonRegion ,左下侧是 OutlookGroupRegion,右侧是 ContentRegion
具体Region的内容,写在 Module中,这样可以降低耦合
首先将 PrsimOutlook 和Module 中的View 关联起来
这需要在App.xaml.cs override ConfigureModuleCatalog函数 添加 Module
protected override void ConfigureModuleCatalog(IModuleCatalog moduleCatalog)
{
moduleCatalog.AddModule<MailModule>();
moduleCatalog.AddModule<ContactsModule>();
}
其次需要在 MailModule.cs 和 ContactsModule.cs 中 将View 注册到视图上
由于Prsim有Unity,可以在通过这种方式获得 IRegionManager 的实例
private readonly IRegionManager _regionManager;
public MailModule(IRegionManager regionManager)
{
_regionManager = regionManager;
}
MailModule
namespace PrismOutlook.Modules.Mail
{
public class MailModule : IModule
{
private readonly IRegionManager _regionManager;
public MailModule(IRegionManager regionManager)
{
_regionManager = regionManager;
}
public void OnInitialized(IContainerProvider containerProvider)
{
//TODO: remove
_regionManager.RegisterViewWithRegion(RegionNames.ContentRegion, typeof(ViewA));
_regionManager.RegisterViewWithRegion(RegionNames.RibbonRegion, typeof(HomeTab));
_regionManager.RegisterViewWithRegion(RegionNames.OutlookGroupRegion, typeof(MailGroup));
}
public void RegisterTypes(IContainerRegistry containerRegistry)
{
}
}
}
ContactsModule
namespace PrismOutlook.Modules.Contacts
{
public class ContactsModule : IModule
{
private readonly IRegionManager _regionManager;
public ContactsModule(IRegionManager regionManager)
{
_regionManager = regionManager;
}
public void OnInitialized(IContainerProvider containerProvider)
{
_regionManager.RegisterViewWithRegion(RegionNames.OutlookGroupRegion, typeof(ContactsGroup));
}
public void RegisterTypes(IContainerRegistry containerRegistry)
{
}
}
}
这里面发现
ViewA 已经创建了,但 HomeTab , ContactsGroup , MailGroup 还没创建
MailGroup和 ContactsGroup 是注册到 OutlookGroupRegion
HomeTab 是注册到 RibbonRegion
现在要创建 MailGroup View
<ig:OutlookBarGroup x:Class="PrismOutlook.Modules.Mail.Menus.MailGroup"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:PrismOutlook.Modules.Mail.Menus"
xmlns:ig="http://schemas.infragistics.com/xaml/wpf"
Header="Mail">
<Grid>
<TextBlock Text="Testing"/>
</Grid>
</ig:OutlookBarGroup>
窗体类型是 OutlookBarGroup
现在创建 ContactsGroup
<ig:OutlookBarGroup x:Class="PrismOutlook.Modules.Contacts.Menus.ContactsGroup"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:PrismOutlook.Modules.Contacts.Menus"
xmlns:ig="http://schemas.infragistics.com/xaml/wpf"
Header="Contacts">
<Grid>
<TextBlock Text="Tetsing again from Contacts" />
</Grid>
</ig:OutlookBarGroup>
窗体类型也是 OutlookBarGroup
这里面有一个需要注意的地方,让我们回到 MainWindow.xaml
<ig:RibbonWindowContentHost>
<ig:RibbonWindowContentHost.Ribbon>
<ig:XamRibbon prism:RegionManager.RegionName="{x:Static core:RegionNames.RibbonRegion}">
</ig:XamRibbon>
</ig:RibbonWindowContentHost.Ribbon>
<DockPanel LastChildFill="True">
<ig:XamOutlookBar prism:RegionManager.RegionName="{x:Static core:RegionNames.OutlookGroupRegion}" DockPanel.Dock="Left" Width="200"></ig:XamOutlookBar>
<ContentControl prism:RegionManager.RegionName="{x:Static core:RegionNames.ContentRegion}" />
</DockPanel>
</ig:RibbonWindowContentHost>
Prsim 对于 ContentControl,可以自动的将View 注入到 Region中
但对于其他的控件,需要自己写 RegionAdapter
所以对于 XamOutlookBar 需要写 XamOutlookBar 控件的 RegionAdapter
对于这个 RegionAdapter 我还不清楚是怎么写的,好在我们有源码,直接copy过来看看
using Infragistics.Windows.OutlookBar;
using Prism.Regions;
namespace PrismOutlook.Core.Regions
{
public class XamOutlookBarRegionAdapter : RegionAdapterBase<XamOutlookBar>
{
public XamOutlookBarRegionAdapter(IRegionBehaviorFactory factory)
: base(factory)
{
}
protected override void Adapt(IRegion region, XamOutlookBar regionTarget)
{
region.Views.CollectionChanged += ((x, y) =>
{
switch (y.Action)
{
case System.Collections.Specialized.NotifyCollectionChangedAction.Add:
{
foreach (OutlookBarGroup group in y.NewItems)
{
regionTarget.Groups.Add(group);
//The WPF XamOutlookBar does not automatically select the first group in it's collection.
//So we must manually select the group if it is the first one in the collection, but we don't
//want to excute this code every time a new group is added, only if the first group is the current group being added.
if (regionTarget.Groups[0] == group)
{
regionTarget.SelectedGroup = group;
}
}
break;
}
case System.Collections.Specialized.NotifyCollectionChangedAction.Remove:
{
foreach (OutlookBarGroup group in y.OldItems)
{
regionTarget.Groups.Remove(group);
}
break;
}
}
});
}
protected override IRegion CreateRegion()
{
return new SingleActiveRegion();
}
}
}
一顿操作猛如虎,大意就是 往 XamOutlookBar 加入Region
对于这个项目来说也就是 往 XamOutlookBar 加入 MailGroup 和 ContactsGroup
同时还需要写 XamRibbonRegionAdapter
代码如下
using Infragistics.Windows.Ribbon;
using Prism.Regions;
using System;
using System.Collections.Specialized;
namespace PrismOutlook.Core.Regions
{
public class XamRibbonRegionAdapter : RegionAdapterBase<XamRibbon>
{
public XamRibbonRegionAdapter(IRegionBehaviorFactory regionBehaviorFactory)
: base(regionBehaviorFactory)
{
}
protected override void Adapt(IRegion region, XamRibbon regionTarget)
{
if (region == null) throw new ArgumentNullException(nameof(region));
if (regionTarget == null) throw new ArgumentNullException(nameof(regionTarget));
region.Views.CollectionChanged += (s, e) =>
{
if (e.Action == NotifyCollectionChangedAction.Add)
{
foreach (var view in e.NewItems)
{
AddViewToRegion(view, regionTarget);
}
}
else if (e.Action == NotifyCollectionChangedAction.Remove)
{
foreach (var view in e.OldItems)
{
RemoveViewFromRegion(view, regionTarget);
}
}
};
}
protected override IRegion CreateRegion()
{
return new SingleActiveRegion();
}
static void AddViewToRegion(object view, XamRibbon xamRibbon)
{
if (view is RibbonTabItem ribbonTabItem)
{
xamRibbon.Tabs.Add(ribbonTabItem);
}
}
static void RemoveViewFromRegion(object view, XamRibbon xamRibbon)
{
if (view is RibbonTabItem ribbonTabItem)
{
xamRibbon.Tabs.Remove(ribbonTabItem);
}
}
}
}
然后还需要在 App.xaml.cs 中 告诉 Prsim 一下,注册一下
protected override void ConfigureRegionAdapterMappings(RegionAdapterMappings regionAdapterMappings)
{
base.ConfigureRegionAdapterMappings(regionAdapterMappings);
regionAdapterMappings.RegisterMapping(typeof(XamOutlookBar), Container.Resolve<XamOutlookBarRegionAdapter>());
regionAdapterMappings.RegisterMapping(typeof(XamRibbon), Container.Resolve<XamRibbonRegionAdapter>());
}
总结
一路写下来,思路一下清晰了起来,照着视频学的时候还是一片懵懂,我会坚持写下去的
视频一 主要是把 程序框架搭了起来,将Module注册到了 MainWindow中,这样 程序就知道了各个Module.
然后将 View 通过 RegisterViewWithRegion 注册到 各个Region中.
Prsim 是有Unity的,所以程序可以使用依赖注入的方式,很方便
对于 ContentControl,Prsim 为我们提供了默认的方式 将View 注册到Region中
但对于 其他的控件,就需要自己来写 RegionAdapter,同时还需要在 ConfigureRegionAdapterMappings中注册一下