当前位置 : 主页 > 编程语言 > 其它开发 >

WPF企业内训全程实录(下)

来源:互联网 收集:自由互联 发布时间:2022-05-25
摘要 WPF企业内训全程实录由于文章比较长,所以一共拆分成了三篇,上篇WPF企业内训全程实录(上)主要讲了基础,中篇WPF企业内训全程实录(中)主要讲解开发模式、团队协作及应用框
摘要

  WPF企业内训全程实录由于文章比较长,所以一共拆分成了三篇,上篇WPF企业内训全程实录(上)主要讲了基础,中篇WPF企业内训全程实录(中)主要讲解开发模式、团队协作及应用框架,起着承上启下的作用,主要讲解开发模式、团队协作及应用框架。这篇作为该实录的下篇——终结篇,起着总结的作用,主要讲解其他技术的引用、WPF项目及性能优化、部署与更新等议题。

  其实如果大家仔细看目录,可以发现我安排的顺序是首先讲解最基本的概念和基础内容、然后过渡到开发模式及框架、最后结合其他技术和项目实际运用,这也是学习并应用一门技术最好的流程。上篇实际上主要有两个侧重点:一则就是理清脉络——历史渊源、概念引入及基本阐述;二则是讲解WPFBasic——主要细究WPF的每个知识点,基本涵盖了WPF的方方面面;如果大家感兴趣,可以下载代码并进行仔细研究,如果有不懂的地方也可以参考我写的WPF 基础到企业应用系列,这里受篇幅限制,就不详细重复论述。中篇主要围绕WPF开发模式、WPF团队协作和MVVM框架三个议题进行阐述,侧重于为什么要引入MVC/MVP/MVVM模式、从根本上说这些模式是为了解决什么问题、针对不同的开发模式,团队协作会有哪些具体的改变、了解并使用常用的开发框架以及追根索源探究并实现自己的开发框架。下篇则主要总结前面所讲的内容,同时介绍其他技术引入、WPF项目及性能优化与部署与自动更新等概念,其目的在于项目具体应用层次的归纳和总结。

章节纲要

· 1.摘要

· 2.本文提纲

· 3.简要介绍

· 4.WPF介绍

· 5.WPF基础

· 6.WPF工具

· 7.WPF开发模式

· 8.WPF团队协作

· 9.了解并使用MVVM框架

· 10.自己开发MVVM框架

· 11.其他技术引入

· 12.WPF项目及性能优化

· 13.部署与自动更新

· 14.总结

· 15.详细技术索引

十一. 其他技术引入

通过WPF 基础到企业应用和前面两篇文章,我们基本讲解了WPF的基础知识和框架相关细节,可以用以下这幅图进行简要概括:

圣殿骑士 

                                                                                       图1

那么下面我们就来探讨一下WPF和其他技术之间的衔接问题。我们之前做项目都有一个基本流程,大致包括以下几个方面:

1. 基础开发平台与工具:在开发一个中、大型项目之前,我们一般都会有技术选型的过程,比如选择Linux + Apache + PHP + MySQL或者Linux + Apache + Java (WebSphere) + Oracle再或者我们最熟悉的Windows Server 2003/2008 + IIS + C#/ASP.NET + SQL Server,当然这些操作系统、WEB服务器、开发语言和数据库在一定条件下可以任意搭配,比如你想用FreeBSD操作系统,你想用Ruby或者Python语言,你想用DB2或者其他数据库等。决定了开发平台和语言之后,就需要有定制的开发工具,比如Java你可能需要Eclipse或者MyEclipse插件,Net你需要强大的Visual Studio或者MONO环境下使用SharpDevelop,PHP你可能需要强大的Zend。简而言之,不管使用什么开发平台,都需要对应的开发套件与开发工具作为辅助。当然我们今天谈的是WPF项目的开发,所以必然我们会首先选择Windows Server 2003/2008 + IIS + C#/ASP.NET + SQL Server这种搭配,也有人会说可以考虑MONO下的WPF开发和部署,不过很遗憾,MONO下针对WPF并没有完全进行实现。

2. 基础框架及资源:这里的基础框架是指有没有现成的数据访问框架、通用权限框架、异常和日志处理框架、IOC框架、AOP框架、简单的CMS管理框架、Office文档及PDF交互、报表及打印功能等,因为有了这些以后,开发项目就简单了许多,我们只需要关注具体的业务处理就行,这样可以使项目更加高效且稳定的完成。

3. 逻辑架构:逻辑架构往往决定了你如何划分模块以及如何来分层,这个要根据项目的具体情况而定,比如项目的大小、项目模块的多少以及开发方式、开发团队等。往往在这一阶段决定项目的整体架构(三层及多层架构、是否有必要搭建ESB与SOA等)。

4. 物理搭建:之前在做WEB应用的时候,会非常重视物理结构及环境的搭建,因为往往它在项目伸缩性、灵活性以及负载方面起着至关重要的作用,其实在决定逻辑架构的时候也必须要考虑到物理架构,我们这里所说的物理架构就是指整个系统或者多个系统在物理环境上的一个部署情况,比如Web Server集群、App Server集群、文件服务器集群、图片服务器集群、流媒体服务器集群、全文检索服务器集群、缓存服务器集群、负载均衡服务器、数据库主从、读写服务器集群等的部署情况。做简单的WPF管理系统也许并不用考虑这么多,如果要做大型的WPF播放器以及大型的WPF应用,这些都得经过仔细的斟酌才行。

5. 框架的选择:这个地方是选择整个应用程序的框架,当然选择的前提还必须参考前面的逻辑与物理结构。具体框架包括数据底层处理框架、公共基础框架以及我们前面所提及的诸如MVC/MVP/MVVM模式等。

6. 其他处理:面向对象设计与实现、面向方面思想、权限系统设计、缓存体系设计、异常及日志框架设计、分布式及负载均衡等都是我们需要考虑的重点和要点。最后要特别注意团队及项目规范、项目整体开发流程、版本与配置管理、项目开发注意细节等问题。

十二. WPF项目及性能优化 一,WPF项目 1)之前的项目架构

在讲WPF项目架构与基础结构之前,我们先来看一下之前搭建的WEB应用程序:

clip_image002

clip_image004

                                                             图2及图3 

上面的两幅图具体概念如下:

01,User Interface即UI层:该层作为数据输入和展示的界面,是与用户交互的有效途径,所以它起着至关重要的作用。往往给人第一印象的就是UI层,在设计的时候也要根据不同的技术或者不同的要求进行斟酌。通常可以把UI分为B/S UI、C/S UI以及WEB服务。在这里就是我们的ASP.NET项目。

02,WebModelCommon:这层作为UI与领域逻辑的中间层,它的充当了桥梁、筛选、过滤和验证的作用。它主要包括两个工程,WebHelper主要提供给UI一些常用操作。WebLogic主要对UI与领域逻辑的数据进行转换、筛选、验证及过滤操作。

03,Business Logic:Domain Model (Data Model Layer)始终是应用程序的核心,必须投入大量精力,按照面向对象的分析和设计 (OOAD) 最佳做法进行设计同时按照OOP进行开发。业务逻辑层根据业务进行文件夹区分,每个文件夹主要包括业务实体、业务接口、业务具体逻辑、业务适配器以及业务自动单元测试。

04,FrameWork:主要包括数据访问框架、通用权限框架、异常和日志处理框架、IOC框架、AOP框架等基础或常用功能。

05,SOA:这一层不是必须的,根据项目的具体情况进行取舍,如果业务比较复杂且交互项目繁多,那么SOA可以减轻我们的负担;如果业务比较单一且相对简单,就可以直接调用或者使用Web Service/Remoting/WCF作为通信框架即可。在实施SOA的过程中,可以自己使用WCF+WF搭建一个小型轻量级的SOA框架,也可以使用诸如Biztalk等软件。

06,Reference:这里主要包括第三方的框架和组件项目,把这些文件分门别类地集中放在此目录下。

07,Solution Items:项目的规范、流程、重要文件等。

08,Test:这里主要放置测试需要的一些信息,如测试版本、测试文档等。

09,Publish:这个文件夹主要放置发布的版本。

这个框架能解决大多数问题,但仍有不完善之处。

1)WPF或Silverlight架构

  在MVVM模式中,你需要一个为View量身定制的model,那么这个model实际上就是上图ViewModel。ViewModel包含所有UI所需要的接口、属性和命令,这样只需要通过Binding使他们进行关联,就可以使二者之间达到松散耦合,所以这样一来,UI就可以由UI专业人员用 Design和Blend来实现(当然很多效果还是需要用传统的制图软件,所以我们都称这种想法叫理想状态),代码人员也可以专心写他的逻辑和业务代码,所以这样分工和协作变得更轻松、更愉快了。更漂亮的是View完全可以由(Unit/Automatic Test)所取代,所以单元测试也变得相对简单。这对于我们的开发人员和测试人员无疑是一个很好的解脱,同时也提高了系统的可测性、稳定性和维护性。数据绑定系统同时也提供了标准化的方式传输到视图的错误验证和输入验证(但是个人觉得不是很好用,所以我们在实际的项目当中会写一套自己的验证框架)。

2010-12-8 22-07-25

                                                    图4

那么根据上面这幅图,我们具体可以得出如下结论:

1,首先介绍的是UI层,该层作为数据输入和展示的界面,是与用户交互的有效途径,所以它起着至关重要的作用。往往给人第一印象的就是UI层,在设计的时候也要根据不同的技术或者不同的要求进行斟酌。通常可以把UI分为B/S UI、C/S UI以及WEB服务。

2,ViewModel作为用户界面和业务领域模型的中间层,它往往起着类似于桥梁的作用(UI和领域业务逻辑层之间的桥梁以及耦合的隔离者)。

3,Domain Model (Data Model Layer)始终是应用程序的核心,必须投入大量精力,按照面向对象的分析和设计 (OOAD) 最佳做法进行设计同时按照OOP进行开发。业务逻辑层根据业务进行文件夹区分,每个文件夹主要包括业务实体、业务接口、业务具体逻辑、业务适配器以及业务自动单元测试。

4,Model、View 和 ViewModel 层之间实施严格的分离,也强调了它们之间是一种松散耦合的关系。

5,每一层或者每一个模块都有自己完整的单元测试,这样即提高了代码质量,同时也增强了稳定性和可维护性。

6,不要为了MVVM而MVVM,不要强调UI端不产生一句后台代码而把所有代码都扔进ViewModel,因为有的操作如果不参与逻辑流程,放在UI端处理会更好,这也符合UI和逻辑隔离的最终原则。

7,数据底层操作层可以随意的搭配,既可以使用传统的方式也可以用ORM的方式,这个根据团队或项目的具体情况作抉择。如传统的存储过程或者脚本、借助于企业库、IBATIS.NET、Nhibernate,Active Record,Entity framework,Linq To Sql,entity framework或者Custom ORM等。

8,Database做为最底层,它对应用程序起着最为关键的作用,项目没有数据就等于失去了最本质的东西,除非这个项目和数据没有太多关联,所以必须投入大量的时间和精力在这一层,这也是数据库库团队存在的必要。最典型的问题包括数据库优化、数据库拆分、分离、同步、数据挖掘以及数据库的备份与复原等。

前段时间本来要重构一个项目,但由于诸多原因,这个项目没有能够在整体架构上进行调整,这里也把项目整体结构图贴出来,供大家参考

Arc1
  • Arc2

                                                     图5及图6 

上面的两幅图具体概念如下:

01,UI:该层作为数据输入和展示的界面,是与用户交互的有效途径,所以它起着至关重要的作用。往往给人第一印象的就是UI层,在设计的时候也要根据不同的技术或者不同的要求进行斟酌。通常可以把UI分为B/S UI、C/S UI以及WEB服务。在这里就是我们的WPF项目。

02,ViewModel作为用户界面和业务领域模型的中间层,它往往起着类似于桥梁的作用(UI和领域业务逻辑层之间的桥梁以及耦合的隔离者),这里没有把它从UI里面独立出来,所以它放在UI或者独立出来均可。

03,LogicBusiness:Domain Model (Data Model Layer)始终是应用程序的核心,必须投入大量精力,按照面向对象的分析和设计 (OOAD) 最佳做法进行设计同时按照OOP进行开发。

04,Common:主要包括数据访问框架、通用权限框架、异常和日志处理框架、IOC框架、AOP框架等基础或常用功能。

05,SOA Service:这一层不是必须的,根据项目的具体情况进行取舍,如果业务比较复杂且繁多,那么SOA可以减轻我们的负担;如果业务比较单一且相对简单,就可以直接调用或者使用Web Service/Remoting/WCF作为通信框架即可。在实施SOA的过程中,可以自己使用WCF+WF搭建一个小型轻量级的SOA框架,也可以使用诸如Biztalk等软件。

06,DataAccess:数据库访问组件。

07,AddedDll:这里主要包括第三方的框架和组件项目,把这些文件分门别类地集中放在此目录下。

08,UnitTest:对每层及每个模块进行单元测试。

  从上面WEB与WPF两个项目中可以看到具体的差别与联系:联系在于公共的东西都是不会变的;差别在于WEB与WPF都有各自的特点,所以在处理上也有一些细微的区别!

二,性能优化

  关于性能优化,在任何项目都会有所涉及,使用WPF的项目也不例外(或者更甚)。那么针对WPF项目,我们如何来做好性能优化呢?其实这个问题比较大,因为往往某个细节的操作会影响到整个WPF应用程序的性能,所以在做项目的时候一般都会针对这些性能优化操作列一个表(详细说明性能优化点及注意事项)。WPF性能优化就像之前做WEB优化一样,都有一些“银弹”可供参考,WEB项目有Yahoo团队实践分享——网站性能优化的条黄金守则和业界的一些经验;WPF作为出道不久的技术,主要参考微软提供的性能优化注意事项以及自己的实践经验。本来是想罗列一些性能优化点,无奈由于时间仓促且怕罗列不全面或不正确,所以把它留在以后心情好的时候再续,不然就会起到不能帮助读者反而误导读者的效果。

十三. 部署与自动更新

  一个优良的软件都会有自己完整的部署与自动更新流程,C/S应用程序尤其如此,所以如何解决客户端的部署与自动升级问题便是一个非常重要的问题。大家都知道ClickOnce 无疑是微软对Client/Server模式部署的最佳解决方案,但正是因为它的功能特别强大而且又要使用相当简单,所以在产品的封装上就特别严实,基本上就暴露了一些简单的操作接口,这样用户就无法做定制化的操作,所以我们下面主要探讨不使用ClickOnce的操作流程。

一,打包操作

  关于打包操作,现在市面上有很多的打包工具与代码,但最为常用的要数Installshield和微软自带的打包工具软件,两者在这方面都有不俗的表现,前者强调打包的定制化以及多样性,后者强调易操作及快速性。如果你想要更多漂亮及定制化的操作,可以选择Installshield+脚本的方式;如果你想做一般的效果,那么微软自带的打包工具软件就特别适合;当然这也不是绝对的定律,只是一般情况下的选择,下面我们就主要来讲解微软自带的打包工具软件。

1) 创建安装和部署项目:

1,右键点击当前解决方案—>Add—>New Project,在弹出的窗口中选择Other Project Types—>Setup and Deployment—>Setup Project;然后输入项目的名字,点击“OK”按钮。新添加的安装和部署项目就会出现在解决方案列表中。

2) 添加项目输出及制作安装界面:

1,首先把你要安装的项目加进来,也就是加入到应用程序文件夹,由于VS SetUp设置比较简单,你只需要按照操作步骤一步一步设置即可。在完成应用程序文件夹之后设置开始菜单和用户桌面的快捷方式。

2,右键点击安装项目,选择View—>User Interface可以看到VS SetUp为我们提供的一些默认安装界面,当然你也可以创建、修改或者移除这些窗口,总之你可以定制化你的安装步骤和页面。

3) 注册表问题:

和前面的操作类似,对于注册表的设置也比较简单,右键点击安装项目,选择View->Registry即可创建、修改及移除注册表键值。

4) 创建组件注册项目:

1,右击当前解决方案—>Add—>New Project,在弹出的窗口中选择 Visual C#—>Class Library;然后在下方文本框中输入Name,点击“OK”按钮。新添加的项目会出现在解决方案列表中。

2,右键点击该项目—>Add—>New Item,在弹出的窗口中选择Installer Class;在下方文本框中输入名字,点击“Add”按钮添加文件并关闭窗口。

3,选中刚添加的文件,按F7或者View Code转到代码页,在代码页面添加以下方法:

namespace RegsvrDll
{
[RunInstaller(true)]
public partial class InstallerReg : Installer
{
public InstallerReg()
{
InitializeComponent();
}

public override void Install(System.Collections.IDictionary stateSaver)
{
base.Install(stateSaver);
}

protected override void OnAfterInstall(IDictionary savedState)
{
base.OnAfterInstall(savedState);

string LogicDir = Context.Parameters["LogicDir"];

// Close the writer and underlying file.
// 注册CDO组件
// /s 关闭注册成功的提示窗口显示,/c退出cmd窗口
System.Diagnostics.Process.Start("cmd", @"/c regsvr32 " + @LogicDir + @"ComUtilities.dll /s");

string path = @LogicDir + @"App.exe";
using (RegistryKey regWrite = Registry.LocalMachine.CreateSubKey("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\App Paths"))
{
regWrite.SetValue("App.exe", path);
regWrite.Close();
}
}

protected override void OnBeforeInstall(IDictionary savedState)
{
base.OnBeforeInstall(savedState);
}
}
}

注意:需要加入using System.Configuration.Install;

5) 在安装和部署项目中添加要安装的项目和文件:

1, 添加项目:右击项目—>View—>File System,在出现的界面中,右击左列的Application Folder—>Add—>Project Output,在弹出的窗口中选择相应的Project,然后点击“OK”按钮。多个项目重复多次。在这里我们假设我们开发的项目为 MainProject,注册组件的项目名称为RegDll,那么我在弹出的窗口中先选择项目MainProject,点击“OK”后,我在重复添加项目 RegDll。

2,添加文件:右击项目—>View—>File System,在出现的界面中,右击左列的Application Folder—>Add—>File,在弹出的窗口中选择C:\WINDOWS\system32 \misexec.exe;

3,创 建快捷方式:在右列中选择Primary output from MainProject(Active),右击 选择Create Shortcut to Primary output from MainProject(Active),你可以按F2给新添加的快捷方式更名。右击msiexec.exe 选择 Create Shortcut to misexec.exe,你可以按F2将刚生成的快捷方式更名为“卸载”;

4,创建程序组:右击左列的User’s Programs Menu选择 Add—>Fold,你可以将新创建的文件夹更名,当软件安装完毕后它将出现在程序组中,然后将上一步创建的两个快捷方式拖到新创建的文件夹中。

5,实现“卸载”的功能:选中安装和部署项目,按F4,复制ProductCode的内容,然后选中“卸载”快捷方式,按F4,将Arguments 的内容更改为:/x 刚复制的ProductCode的内容。

6) 安装时注册组件问题:

  右击安装和部署项目—>View—>Custom Actions,在出现的界面中右击左列的Install—>Add Custom Action,选择Application Folder中的Primary output from RegDll(Active)。

clip_image002

                                                    图7

修改“主输出来自RegDll (活动)”的CustomActionData属性为 /LogicDir="[TARGETDIR]\"

clip_image004

                                                    图8

7) 自动检测当前系统中MDAC、.Net Framework版本、Windows Installer3.1是否满足版本要求,如果不满足则安装:

  右击安装和部署项目选择属性,在弹出的窗口中点击按钮 “Prerequisites”。选中MDAC2.8、.Net Framework2.0、Windows Installer3.1;然后选中Download prerequisites from the same location as my application 单选按钮,点击“OK”,再点击“OK”。

8) 重新编译项目生成项目安装包,这个时候就可以把安装包Host在网上供用户下载。

二,自动更新 1),简要介绍

  众所周知,对于一般的软件开发,在开始的时候都会有一个技术选型的阶段,最大的选型就是首先要确定是选择Client/Server模式还是 Browser/Server模式。综合而论:两者各有优劣,在很多方面都不能被对方互相取代,如在适用Internet、维护工作量等方面,B/S比C /S要强很多;但在运行速度、数据安全、人机交互等方面,B/S就远不如C/S那么强大。所以综上所述,凡是C/S的强项,便是B/S的弱项,反之亦然。由于今天讨论的是自动更新组件,所以接下来我们就往这方面细讲,既然C/S模式在运行速度、数据安全、人机交互有这么多的优点,尤其是客户端技术日益发展的今天,如何解决客户端的部署与自动升级问题便是一个非常重要的问题。

2),ClickOnce与自定义之间的权衡

  在前面的摘要中我们简单介绍了自动更新功能的重要性,在这一小节里我们来谈一下为什么不使用微软给我们提供的自动更新组件ClickOnce,大家都知道ClickOnce给我们提供了很多功能:简单说来,ClickOnce 应用程序就是任何使用 ClickOnce 技术发布的 Windows 窗体或控制台应用程序。可以采用三种不同的方法发布 ClickOnce 应用程序:从网页发布、从网络文件共享发布或是从媒体(如 CD-ROM)发布。ClickOnce 应用程序既可以安装在最终用户的计算机上并在本地运行(即使当计算机脱机时也可以运行),也可以仅以联机模式运行,而不在最终用户的计算机上永久安装任何内容。ClickOnce 应用程序可以自行更新;这些应用程序可以在较新版本变为可用时检查较新版本,并自动替换所有更新的文件。开发人员可以指定更新行为;网络管理员也可以控制更新策略,如将更新标记为强制性的。最终用户或管理员还可以对更新进行回滚,使应用程序恢复到早期的版本。

从上面大家可以看出ClickOnce 无疑是微软对Client/Server模式部署的最佳解决方案,但正是因为它的功能特别强大而且又要使用相当简单,所以在产品的封装上就特别严实,基本上就暴露了一些简单的操作接口,这样就无形把一些定制化的操作拒之于门外,比如:

  • 用户不能自己指定安装路径。
  • 对自动更新流程不能做定制化的操作。
  • 对自动更新的UI不能定制化的设计。

正因为这几个原因,所以很多企业都会做一些定制化的组件来实现自动更新的功能,基于此,我们这里也实现了一个非常简单的自动更新组件.

3),自定义更新操作流程

其实自动更新的原理很简单,分析起来无非就是简单的几步操作,当然实现方式也是大同小异,这里我们就选一种较简单的方式:

1.启动主程序,主程序里面调用升级程序,升级程序连接到IIS或者FTP。

2.升级程序获取服务器端XML配置文件中新版本程序的更新日期或版本号或文件大小。

3.升级程序获取原有客户端应用程序的最近一次更新日期或版本号或文件大小,然后两者进行比较;如果新版本日期>原有程序的最新日期,则提示用户是否升级;或如果新版本版本号>原有程序的版本号,则提示用户是否升级;再或如果新版本文件大小>原有程序的文件大小,则提示用户是否升级。本文主要采用一般的做法,就是通过版本号来进行对比。

4.如果用户选择升级,则获取下载文件列表;

5.在本地建立与远程IIS或者FTP相应的临时目录,然后下载到这个临时目录文件下;

6.删除旧的主程序,拷贝临时文件夹中的文件到相应的位置;

8.结束升级流程并重新启动主程序。  

4),自动更新效果图

当我们运行主程序(WinForm或者WPF),如果服务器上有最新的版本,就会弹出如下页面进行提示并让用户选择是否更新。

2010-10-13

                                                    图9

当用户不需要更新时,可以选择Skip按钮跳过并继续主程序流程,反之则进入如下页面。

2010-10-13

                                                    图10

在下载的过程中,用户可以选择Cancel停止下载并重新回到主流程。

十四,总结

  上篇WPF企业内训全程实录(上)主要讲解历史渊源、概念引入、基本阐述以及WPF的每个知识点。中篇WPF企业内训全程实录(中)主要主要围绕WPF开发模式、WPF团队协作和MVVM框架三个议题进行阐述。下篇WPF企业内训全程实录(下)将着重强调结合其他技术共同打造WPF项目、相关性能优化、以及部署与更新问题。另外如果有不懂的地方也可以参考之前写的WPF 基础到企业应用系列,最后声明一下,由于圣殿骑士才识浅薄,所以以上观点只是个人的看法与心得,遗漏和错误之处也敬请海涵。怀着技术分享与交流的态度分享出来,希望各位多多指教!

十五,详细技术索引

· 1. WPF 基础到企业应用系列1——开篇有益

· 2. WPF 基础到企业应用系列2——WPF前世今生

· 3. WPF 基础到企业应用系列3——WPF开发漫谈

· 4. WPF 基础到企业应用系列4——WPF千年轮回

· 5. WPF 基础到企业应用系列5——WPF千年轮回 续前缘

· 6. WPF 基础到企业应用系列6——WPF布局全接触

· 7. WPF 基础到企业应用系列7——深入剖析依赖属性(核心篇)

· 8. WPF 基础到企业应用系列8——依赖属性之“风云再起”

WPF企业内训全程实录(上)

WPF企业内训全程实录(中)

WPF企业内训全程实录(下)


上一篇:设计模式系列
下一篇:没有了
网友评论