最近这段时间一直在看一个开源软件PowerToys的源码,里面使用Modules的开发风格让我特别着迷,感觉比我现在写代码的风格好了太多太多。我尝试把PowerToys的架构分离了出来,但是发现代码维护量比较大,我自己很难维护这一套东西,就想到了同类型的Prism。

之前一直使用MVVMLight进行开发。因为最近要写一个开源的财务软件,想在项目中使用Prism,所以这个系列是财务软件的前置系列Prism从0到入门。主要是对照Prism官方例子去学一遍他的代码并整理成自己的代码。然后尝试理解Prism中体现的编程思想。以及如何在我们自己的项目中更好的使用Prism。

PrismLibrary/Prism-Samples-Wpf: Samples that demonstrate how to use various Prism features with WPF (github.com)

打开连接后有一个README.md。里面是Prism Samples WPF。1到29个示例包含了Prism的主要内容。这个系列我们就学习这个,并在学习完成后结合实践到自己的项目中。

一、浏览PrismSamples中的目录结构了解怎么学Prism;

简单浏览了一下,发现第07的Modules 分为了5种不同的方式,需要梳理的时间比较多,而01-06内容比较少,第一篇就先写到07之前,我们看到示例代码中BootstrapperShell(引导程序外壳)、Regions(区域)、CustomRegions(自定义区域)、ViewDiscovery (我理解为Register View WithRegion区域选项卡查看,把某个视图通过RegionManager放在某个位置)、ViewInjection(视图注入)、ViewActivationDeactivation(视图的激活和取消激活)。

现在开始学习示例代码。打开01BootstrapperShell的代码,我们进行观察;

1、App.xaml中主要修改

删掉了StartupUri属性,没有额外的代码。

2、App.cs中主要修改

App依然继承自Application,在重写OnStartup中实例化了Bootstrapper类,然后调用Bootstrapper实例后的对象的.Run()方法进行启动。

3、查看BootStrapper.cs

双击BootStrapper.cs打开源码看到了。包含了2个重写的方法CreateShell()方法返回一个DependencyObject。和一个没有返回值的RegisterTypes()方法。在学习WPF教程四的时候,我们讲到了DependencyObject对象,我们了解到DependencyObject所有的子对象都支持依赖项属性。包括UI控件。而CreateShell()方法返回的是一个DependencyObject类型,传入了Views文件夹下的MainWindow.xaml。

4、在MainWindow.xaml中主要变化

只有一个ContentControl,显示一串字符串“Hello from Prism”。

5、运行代码

发现打开了一个标题为Shell的窗体显示了Hello from Prism。就是我们的MainWindow。这里CreateShell()函数就是修改启动起始页面的地方。先不分析,继续看下一个Samples。

protected override Window CreateShell()
{
return Container.Resolve<MainWindow>();
}

总结例子01:在CreateShell()中传入的MainWindow是设置默认启动页面,使用容器解析了MainWindw 并返回。(我们只学习代码,不考虑设计启动逻辑,单例等等。不在这里学习,只学Samples代码。)

观察02Regions示例;

1、App .xaml中主要修改

App.xaml中 添加命名空间 xmlns:prism="http://prismlibrary.com/";

更换Application为prism:下的PrismApplication。

移除了StartupUri属性。

2、App.cs中主要修改

修改App继承自PrismApplication;

重写CreateShell()方法;

返回结果是使用Container.Resolve()容器解析出来的MainWindow窗体。

 protected override Window CreateShell()
{
return Container.Resolve<MainWindow>();
}

Views下的MainWindow.xaml

创建了一个ContentControl控件,用于显示内容,并添加了附加依赖项属性prism:RegionManager.RegionName="ContentRegion" 。cs代码中无更新内容。

启动程序,界面无任何内容。

总结:在ContentControl控件中添加了附加依赖项属性,从命名看,是区域管理类下的区域名称设置为ContentRegion。

观察03CustomRegions示例;

1、App.xaml中主要修改

添加了 xmlns:prism="http://prismlibrary.com/"命名空间;修改了Application类为PrismApplication。

2、App.cs中主要修改

修改了App继承自PrismApplication。重写了CreateShell()方法,并且返回值修改为Window,同样使用了Container.Resolve()的方式设置默认启动窗体。重写了ConfigureRegionAdapterMappings()方法,我们在PrismApplication使用F12跳转过去,看到在PrismApplication下没有ConfigureRegionAdapterMappings()方法,继续PrismAoolicationBase上F12,我们看到了ConfigureRegionAdapterMappings()方法点开注释,看到提示(翻译后):配置要在应用程序中使用的默认区域适配器映射,以便使XAML中定义的UI控件适配为使用区域并自动注册。可以在派生类中重写,以添加应用程序所需的特定映射。 返回结果(翻译后)为包含所有映射的Prism.Regions.RegionAdapterMappings实例。在搭配这个方法名和参数,我们大概了解到这个方法应该是配置区域适配器映射。我们搭配代码详细看一下。调用了父类的ConfigureRegionAdapterMappings()方法后,用使用传入参数的regionAdapterMappings.RegisterMapping()方法,传入了stackPanel,和Container.resolve(),使用regionAdapterMappings注册一个容器解析的StackPanelRegionAdapter的类。因为regionAdapterMappings是传入的对象,StackPanel又是布局的面便,所以我们去分析StackPanelRegionAdapter。

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

3、分析StackPanelRegionAdapter

我们看到StackPanelRegionAdapter是是在Prism文件夹。继承自RegionAdapterBase。

一个公共的空的构造函数;

重写Adapt方法,并且传入了IRegion和StackPanel类型的参数,我们使用F12看到region.Views主要是获取区域中视图集合,所有添加视图的集合对象。这方法中使用传入的region.Views注册了CollectionChanged事件,这个事件主要是处理集合变更的消息;从这段代码看主要逻辑是传入的region如果集合发生了变化,如果是Add操作则把时间传入的控件添加到regionTarget中。

  protected override void Adapt(IRegion region, StackPanel regionTarget)
{
region.Views.CollectionChanged += (s, e) =>
{
if (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Add)
{
foreach (FrameworkElement element in e.NewItems)
{
regionTarget.Children.Add(element);
}
} //handle remove
};
}

一个CreateRegion()方法,F12看方法说明,是创建一个新的IRegion实例,用于调整对象。

  protected override IRegion CreateRegion()
{
return new AllActiveRegion();
}

3、Views 下的MainWindow.xaml

我们看到在MainWindow下有一个StackPanel。设置了一个附加依赖项属性,并命名为ContentRegion。cs文件中什么也没有新增。

 <Grid>
<StackPanel prism:RegionManager.RegionName="ContentRegion" />
</Grid>

4、我们运行代码。界面什么也没有。

总结:这个例子在讲如何注册StakPanel,并通过在App下重写ConfigureRegionAdapterMappings()方法的方式,使用 regionAdapterMappings.RegisterMapping(typeof(StackPanel), Container.Resolve());的方式设置自定义面板区域适配器,然后再UI的XAML中通过附加依赖项属性的方式regionManager.regionName设置Name。先总结到这里,我们去看一下篇。

观察04ViewDiscovery示例

1、App.xaml

添加命名空间xmlns:prism="http://prismlibrary.com/"

修改Application为prism:PrismApplication

去掉StartupUri属性

2、App.cs

重写CreateShell()方法。返回MainWindow。

 protected override Window CreateShell()
{
return Container.Resolve<MainWindow>();
}

3、Views下的MainWindow.xaml

添加了ContentControl控件,设置了附加依赖项属性 prism:RegionManager.RegionName="ContentRegion"

4、Views下的MainWindow.cs

再构造函数中,使用传入的regionManager使用RegisterViewWithRegion方法,传入了一个ContentRegion字符串,和ViewA。

 public MainWindow(IRegionManager regionManager)
{
InitializeComponent();
//view discovery
regionManager.RegisterViewWithRegion("ContentRegion", typeof(ViewA));
}

我们在RegisterViewWithRegion上F12看注释,通过注册类型将视图与区域相关联。当显示区域get时,将使用ServiceLocator将此类型解析为一个具体实例。实例将被添加到区域的视图集合中,两个参数分别是与视图关联的区域的名称,要注册到的视图的类型;

我就知道了,这里原来时使用regionMananger去注册关联一个区域名称,和一个视图对象。ContentRegion字符串是我们的ContentControl控件的附加依赖项属性,ViewA是我们的一个UserControl。

4、View下的ViewA.xaml

包含了一个38号大的字体显示内容为View A的TextBlock控件。CS文件中没有更改。

5、运行程序

界面上显示了View A。

总结:App启动时通过重写CreateShell()方法使用Container.Resolve方法返回一个MainWindow;MainWindow界面有一个ContentControl显示控件,有一个附加依赖项属性,设置了区域名称为ContentRegion。在MainWindow的有参构造函数中使用区域管理器,注册了视图和显示区域的关联,传入了区域名称和视图类。视图类是自己的显示内容。通过这样的流程在MainWindow下通过RegisterViewWithRegion方法,关联了具有ContenRegion名称的附加依赖项属性和视图类,界面上就显示了关联的视图。

观察05ViewInjection示例

1、App.xaml

添加了xmlns:prism="http://prismlibrary.com/" 命名空间

修改Application为prism:PrismApplication。

去掉StartupUri属性

2、App.cs

App继承自PrismApplication

重写了CreateShell()方法,返回对象为Container.Resolve(); 解析后的MainWindow窗体,

3、Views下的MainWindow.xaml

创建了DockPanel ,设置最后一个元素填充剩余空间。创建Button按钮,填充在DockPanel面板的顶部,显示为Add View,创建Click事件,创建了ContentControl显示控件,设置附加依赖项属性RegionName,名字叫做ContentRegion。

   <DockPanel LastChildFill="True">
<Button DockPanel.Dock="Top" Click="Button_Click">Add View</Button>
<ContentControl prism:RegionManager.RegionName="ContentRegion" />
</DockPanel>

4、Views下的MainWindow.cs

使用F12跳转到对应的接口说明

IContainerExtension _container 对象是对容器的抽象。

IRegionManager _regionManager; 是定义一个接口来管理一组区域并将区域附着到对象;

构造函数中初始化了这2个对象,使用是在这个Button的点击事件中,我们看到了使用容器对象Resolve解析了ViewA界面,然后使用了IRegionManager对象找到了在XAML中定义的附加依赖项属性名为ContentRegion的控件,然后使用Add方法添加了容器中的ViewA

 public partial class MainWindow : Window
{
IContainerExtension _container;
IRegionManager _regionManager; public MainWindow(IContainerExtension container, IRegionManager regionManager)
{
InitializeComponent();
_container = container;
_regionManager = regionManager;
} private void Button_Click(object sender, RoutedEventArgs e)
{
var view = _container.Resolve<ViewA>();
IRegion region = _regionManager.Regions["ContentRegion"];
region.Add(view);
}
}

5、Views下的ViewA.xaml

是一个UserControl,里面只有一个TextBlock 显示内容为View A 字号为38。cs文件中没有额外的东西。

6、运行代码,界面显示View A

总结:在程序启动时更改Application类为PrismApplication,在重写CreateShell()方法时使用Container解析MainWindow窗体,并返回,作为启动窗体,在MainWindow被启动时,在构造函数中初始化了容器接口和区域管理对象。MainWindow界面中设置了显示控件和点击控件、显示控件设置了附加依赖项属性,用于做区域名称ContentRegion。在点击事件中,使用容器接口解析ViewA用户控件,并使用区域管理器查找名字为ContentRegion的区域(我们界面上的ContentControl),然后再调用这个区域返回对象的Add方法再找到的这个区域中显示我们刚才解析的ViewA自定义控件。

观察06ViewActivationDeactivation示例

1、App.xaml

替换Application为PrismApplication,使用Prism框架的时候,我看到现在每个都替换了。所以这一部分后面再写到的时候我都...处理。

(和前面一样的部分我就省略了)

2、App.cs

重写CreateShell(),解析MainWindow并返回,作为启动页面。

(....)

3、MainWindow.xaml

创建DockPanel,用StackPanel嵌套4个Button,并 设置了显示控件ContenControl,添加附加依赖属性RegionName为ContentRegion。Button 从名字理解,主要是激活ViewA (Button_Click)取消激活ViewA(Button_Click_1);激活ViewB(Button_Click_2) 取消激活ViewB(Button_Click_3)。

  <DockPanel LastChildFill="True">
<StackPanel>
<Button Content="Activate ViewA" Click="Button_Click"/>
<Button Content="Deactivate ViewA" Click="Button_Click_1"/>
<Button Content="Activate ViewB" Click="Button_Click_2"/>
<Button Content="Deactivate ViewB" Click="Button_Click_3"/>
</StackPanel>
<ContentControl prism:RegionManager.RegionName="ContentRegion" HorizontalAlignment="Center" VerticalAlignment="Center" />
</DockPanel>

4、Views下的MainWindow.cs

定义了变量容器接口对象,区域管理对象,区域对象和两个ViewA、ViewB页面对象;

构造函数中初始化了容器接口和区域管理对象、注册了loaded事件;

再Loaded事件中,使用容器解析ViewA和ViewB并放入类的ViewA和ViewB对象;

使用区域管理器获取界面上的ContentRegion区域对象,复制给类的_region对象,并再区域中添加ViewA和ViewB视图;

Button下4个Click使用_region的Activate和Deactivate方法显示对应的ViewA和ViewB;

 private void Button_Click(object sender, RoutedEventArgs e)
{
//activate view a
_region.Activate(_viewA);
} private void Button_Click_1(object sender, RoutedEventArgs e)
{
//deactivate view a
_region.Deactivate(_viewA);
} private void Button_Click_2(object sender, RoutedEventArgs e)
{
//activate view b
_region.Activate(_viewB);
} private void Button_Click_3(object sender, RoutedEventArgs e)
{
//deactivate view b
_region.Deactivate(_viewB);
}

5、Views下的ViewA.xaml和ViewB.xaml无特殊代码,就是显示对应的ViewA和ViewB;

6、运行代码,点击不同的Button可以切换显示ViewA和ViewB,

总结:MainWindow在构造函数中初始化了容器接口和区域管理对象,

在loaded事件中初始化了用于显示的容器解析对象ViewA和ViewB,使用区域管理区查找我们在界面上通过附加依赖项属性定义的ContentRegion显示控件,并返回给一个IRegion区域对象,使用IRegion区域对象的Add方法,添加资源到区域中。

在4个Button的Click事件中,主要是通过Activate和Deactivate方法来设置显示不同的ViewA和ViewB;

因为07示例分为了5个不同加载Modules方式,我们第一篇就先学到这里。下面打开我们最开始创建的WPFPrismDemo,我们写一下代码回忆一下前面的内容;

我们通过一个DEMO来掌握01-06的示例内容。

二、创建PrismSamplesDemo

打开VS创建WPF项目,我使用了.NET Framewrok 4.8框架。

打开PrismLibrary/Prism: Prism is a framework for building loosely coupled, maintainable, and testable XAML applications in WPF, Xamarin Forms, and Uno / Win UI Applications.. (github.com) 这个是Prism的地址

我们看到了。在WPF下,有一个Prism.DryIoc和Prism.Unity,我看评论说都说DryIoc的性能比Unity的好,所以我选用了DryIoc(我并没有验证这个性能问题,我只是找一个入手开始进入学习。)

在创建的PrismSamplesDemo项目中点击解决方案右键=》管理Nuget包=》浏览中输入Prism.DryIoc=》选择安装最新版。就完成了Prism的安装,接下来我们学习示例。

1、修改App.xaml

添加xmlns:prism="http://prismlibrary.com/"命名空间

修改Application为prism:PrismApplication

移除StartupUri属性。

2、修改App.cs

修改App继承自PrismApplication

重写CreateShell()用于设置初始显示窗口

  protected override Window CreateShell()
{
return Container.Resolve<MainWindow>();
}
//这个方法如果不重写则会编译报错。
protected override void RegisterTypes(IContainerRegistry containerRegistry)
{ }

如果不重写RegisterTypes的话,编译会报错。

3、修改MainWindow.xaml,使用ContentControl尝试设置显示区域。

<Window x:Class="WPFPrismDemo.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:prism="http://prismlibrary.com/"
xmlns:local="clr-namespace:WPFPrismDemo"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<DockPanel>
<StackPanel>
<Button Content="Activate View A" Click="ActivateViewA_Click"/>
<Button Content="Deactivate View A" Click="DeactivateViewA_Click"/>
<Button Content="Activate View B" Click="ActivateViewB_Click"/>
<Button Content="Deactivate View B" Click="DeactivateViewB_Click"/>
</StackPanel>
<ContentControl prism:RegionManager.RegionName="ContentRegion" FontSize="30" VerticalAlignment="Center" HorizontalAlignment="Center"/>
</DockPanel>
</Window>

这里你可以尝试使用其他的控件替换掉ContentControl,会报错。这个就是03示例里讲到的CustomRegions,Prism中实现了几个,但是有一些没有实现,所以需要自己写了,就参考02的例子,去写自己的就可以。

4、添加Views文件夹,并添加ViewA和ViewB用户控件,用于作为显示对象。

ViewA 添加TextBlock 显示ViewA

ViewB 添加TextBlock 显示ViewB

5、修改MainWindow.cs

不要看06示例的代码,凭理解自己去写,有报错,就去对照示例代码,我在写的时候忘记了IContainerExtension怎么拼写、忘记了_container.Resolve解析视图后需要在__region中add这对象。不然无法Activate和Deactivate。

代码如下:

using Prism.Ioc;
using Prism.Regions;
using System.Windows;
using WPFPrismDemo.Views; namespace WPFPrismDemo
{
/// <summary>
/// MainWindow.xaml 的交互逻辑
/// </summary>
public partial class MainWindow : Window
{
IContainerExtension _container;
IRegionManager _regionManager;
IRegion _region;
ViewA _viewA;
ViewB _viewB;
public MainWindow()
{
InitializeComponent();
}
public MainWindow(IContainerExtension container, IRegionManager regionManager)
{
InitializeComponent();
_container = container;
_regionManager = regionManager;
this.Loaded += MainWindow_Loaded;
} private void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
_region = _regionManager.Regions["ContentRegion"];
_viewA = _container.Resolve<ViewA>();
_viewB = _container.Resolve<ViewB>();
_region.Add(_viewA);
_region.Add(_viewB);
} private void ActivateViewA_Click(object sender, RoutedEventArgs e)
{
_region.Activate(_viewA);
} private void DeactivateViewA_Click(object sender, RoutedEventArgs e)
{
_region.Deactivate(_viewA);
} private void ActivateViewB_Click(object sender, RoutedEventArgs e)
{
_region.Activate(_viewB);
} private void DeactivateViewB_Click(object sender, RoutedEventArgs e)
{
_region.Deactivate(_viewB);
}
}
}

03示例的CustomRegions,目前我们还没有用到,所以这里先不做例子去分析了。

以上是01-06示例的学习,一共有29个示例。01-06我们学习了怎么修改PrismApplication、怎么重写启动页面,怎么设置区域附加依赖项属性,怎么实例化IRegionManager、IContainerExtension、IRegion,怎么使用Resolev、_region.Add、和Activate、Deactivate。目前就先学习这么多。

我创建了一个C#相关的交流群。用于分享学习资料和讨论问题,这个propuev也在群文件里。欢迎有兴趣的小伙伴:QQ群:542633085

一、从GitHub浏览Prism示例代码的方式入门WPF下的Prism的更多相关文章

  1. 二、从GitHub浏览Prism示例代码的方式入门WPF下的Prism之Modules的几种加载方式

    这一篇梳理Prism中07示例Module的几种加载方式. 07示例分为了5个,有5种不同的Module加载方式. 我们开始学习加载Modules 观察07-Modules-Appconfig示例 分 ...

  2. 三、从GitHub浏览Prism示例代码的方式入门WPF下的Prism之Mvvm的08-12示例

    这一篇是学习了前2篇RegionManager关联视图,和通过不同的方式加载Module示例之后的开始进入MVVM了. 从第08示例开始,进入了MVVM部分. 从08示例开始学习Prism下的MVVM ...

  3. 四、从GitHub浏览Prism示例代码的方式入门WPF下的Prism之Mvvm的13示例

    上一篇之分析了示例,没有最终写DEMO,把这一篇分析完,总结后一起写Prism下的MVVM例子. 这一篇开始分析从13示例开始,分析到MVVM主要部分结束然后写一个分析后的总结DEMO 添加一段新的内 ...

  4. 五、从GitHub浏览Prism示例代码的方式入门WPF下的Prism之MVVM中的EventAggregator

    这一篇我们主要再看完示例12.13后,写了个例子,用于再Modules下执行ApplicationCommands,使用IActiveAware执行当前View的Commands,或者Applicat ...

  5. 用git从github网站上下载代码的方式

    原本单击如下下载按钮即可 但有时候github异常,该按钮无效,可以使用如下方法: 1.复制url,如https://github.com/ulli-kroll/mt7610u 2.进入要存放该代码的 ...

  6. AIR32F103(五) FreeRTOSv202112核心库的集成和示例代码

    目录 AIR32F103(一) 合宙AIR32F103CBT6开发板上手报告 AIR32F103(二) Linux环境和LibOpenCM3项目模板 AIR32F103(三) Linux环境基于标准外 ...

  7. 微软的.NET示例代码放在Github上了

    博客搬到了fresky.github.io - Dawei XU,请各位看官挪步.最新的一篇是:微软的.NET示例代码放在Github上了.

  8. ActiveMQ笔记(1):编译、安装、示例代码

    一.编译 虽然ActiveMQ提供了发布版本,但是建议同学们自己下载源代码编译,以后万一有坑,还可以尝试自己改改源码. 1.1 https://github.com/apache/activemq/r ...

  9. 看完48秒动画,让你不敢再登录HTTP网站(附完整示例代码)

    在我的 单点登录SSO示例代码 一文中,强烈不建议部署HTTP的SSO服务站点. 在此写个基于网络包嗅探的HTTP会话劫持程序,给大家一个直观的危害性展示. 示例中,我在一台Mac上登录58同城,被另 ...

随机推荐

  1. 由Chromium内核引起的微信内置浏览器rce漏洞复现

    背景 chrome浏览器爆出漏洞,github上公开了poc:https://github.com/r4j0x00/exploits/tree/master/chrome-0day,在关闭chrome ...

  2. 深入剖析 MySQL 自增锁

    之前的文章把 InnoDB 中的所有的锁都介绍了一下,包括意向锁.记录锁...自增锁巴拉巴拉的.但是后面我自己回过头去看的时候发现,对自增锁的介绍居然才短短的一段. 其实自增锁(AUTO-INC Lo ...

  3. VMware vSphere 7.0 Update 2 发布 - 数据中心虚拟化和 Kubernetes 云原生应用引擎

    2021 年 3 月 9 日,VMware 发布了 vSphere 7 Update 2.它可以通过 VMware Customer Connect 和 vSphere Lifecycle Manag ...

  4. 如何让Android 支持HEIF 图片解码和加载(免费的方法)

    字节跳动火山引擎ImageX提供了一种能力,可以支持客户端android 直接解码HEIF 和HEIC图片,经过测试发现,可以免费使用: 一.阅前准备 HEIF图片格式是什么? 高效率图像格式(Hig ...

  5. Docker学习(6) 获取和推送镜像

    查找镜像 拉取镜像 推送镜像 总结

  6. 3DPytorch-API NVIDIA Kaolin

    3DPytorch-API NVIDIA Kaolin NVIDIA Kaolin library provides a PyTorch API for working with a variety ...

  7. SOC,System on-a-Chip技术初步

    SOC,System on-a-Chip技术初步 S O C(拼作S-O-C)是一种集成电路,它包含了电子系统在单个芯片上所需的所有电路和组件.它可以与传统的计算机系统形成对比,后者由许多不同的组件组 ...

  8. 最全JVM知识点思维导图,看这一篇就够了

    此处是转发别人的,别人花了二个月, 我花一天时间看完, 觉得很有用 https://www.processon.com/view/link/5eea141cf346fb1ae56a44e7

  9. 【NX二次开发】按层查找工作部件中的对象 UF_LAYER_cycle_by_layer

    第一次调用 :返回第一个启用层中的第一个对象. 第二次调用 :返回下一个已启用层中的下一个对象. 最后一次调用:当所有对象都被耗尽时,将返回object_tag = NULL_TAG. 在循环数据库时 ...

  10. 从零开始学架构(三)UML建模

    文章大纲 1.  文章介绍 2.  UML概述 3.  静态模型 4.  动态模型 5.  UML建模的一般过程 一.文章介绍 1.1为什么学习UML (1)UML是一种软件架构的模型表现方法,用于项 ...