您的位置:首页 > 职场人生

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

2010-07-14 09:39 471 查看

1.开篇前言

首先很高兴这个系列能得到大家的关注和支持,基于对大家负责和对自己负责的态度,我会不断努力写好这个系列,分享自己的微薄技术和经验,希望在帮助别人的
同时也不断提升自己。由于这篇文章很多(现已拆分成2篇,今天这篇只是其中之一),一共花了几个个晚上的休息时间才完成,所以读者花的时间长了一些,也希
望大家能够见谅,这个系列以后会每周发三到四篇左右(主要是写一篇差不多要花几晚上,感觉思维比较发散),除了讲WPF技术本身之外,也会讲一些项目具体
开发,所以敬请关注。
本篇文章取名为WPF千年轮回只因为两个原因:
WPF和当年Win32、WinForm等的到来颇为相似,只是在功能和体验上上进行了提高,所以这是微软产品上的一个轮回;
WPF的学习过程和其他技术一样,譬如ASP.NET,我们在学习的时候会先要了解Asp.Net构架(Http请求处理流程)、
Pipeline、HttpHandler和HttpModule
等内容,这和WPF的Application生命周期相对应,再如WPF的Window生命周期可以和ASP.NET的页面生命周期相对应等。当然你也可
以拿WinForm或者其他技术来举例,这里这是阐述观点。

在前三篇文章中我们对WPF有了一个比较全面的认识,并且也通过一个基本的例子对比了WPF和之前的WinForm程序的区别和联系。那么在本篇文
章当中,除了讲一些理论知识外,更多的是用实际的代码来验证这些理论。

2.本文提纲

·1.开篇前言
·2.本文提纲
·3.Application
·4.Window
·5.Dispatcher及多线程
·6.类继承结构
·7.WPF的逻辑树和视觉树
·8.本文总结
.9.系列进度

3.Application

一.介绍

WPF和传统的WinForm类似,WPF同样需要一个Application来统领一些全局的行为和操作,并且每个Domain
(应用程序域)中只能有一个Application实例存在。和WinForm不同的是WPFApplication默认由两部分组成:
App.xaml和App.xaml.cs,这有点类似于DelphiForm(我对此只是了解,并没有接触过Delphi
),将定义和行为代码相分离。当然,这个和WebForm也比较类似。XAML从严格意义上说并不是一个纯粹的XML格式文件,它更像是一种
DSL(DomainSpecific
Language,领域特定语言),它的所有定义都直接映射成某些代码,只是具体的翻译工作交给了编译器完成而已。WPF应用程序由
System.Windows.Application类来进行管理。

二.创建WPF应用程序

创建WPF应用程序有两种方式:
1、VisualStudio和ExpressionBlend默认的方式,使用App.xaml文件定义启动应用程序
App.xaml文件的内容大致如下所示:



2、可以自已定义类,定义Main方法实现对WPF应用程序的启动
在项目中添加一个类,类的代码如下,在项目选项中,设定此类为启动项。
usingSystem;
usingSystem.Collections.Generic;
usingSystem.Configuration;
usingSystem.Data;
usingSystem.Linq;
usingSystem.Windows;

namespaceWPFApplications
{
///<summary>
///InteractionlogicforApp.xaml
///</summary>
publicpartialclassApp:Application
{
[STAThread]
staticvoidMain()
{
//定义Application对象作为整个应用程序入口
Applicationapp=newApplication();
//方法一:调用Run方法,参数为启动的窗体对象,也是最常用的方法
Window2win=newWindow2();
app.Run(win);

//方法二:指定Application对象的MainWindow属性为启动窗体,然后调用无参数的Run方法
//Window2win=newWindow2();
//app.MainWindow=win;
//win.Show();
//win.Show()是必须的,否则无法显示窗体
//app.Run();

//方法三:通过Url的方式启动
//app.StartupUri=newUri("Window2.xaml",UriKind.Relative);
//app.Run();
}
}
}

三、Application应用程序关闭

OnLastWindowClose(默认值):最后一个窗体关闭或调用Application对象的Shutdown()
方法时,应用程序关闭。
OnMainWindowClose启动窗体关闭或调用Application对象的Shutdown()方法时,应用程序关
闭。(和C#的Windows应用程序的关闭模式比较类似)
OnExplicitShutdown只有在调用Application对象的Shutdown()方法时,应用程序才会关闭。
对关闭选项更改的时候,可以直接在App.xaml中更改:
<Applicationx:Class="WPFApplications.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
StartupUri="Window2.xaml"
ShutdownMode="OnExplicitShutdown">
<Application.Resources>
</Application.Resources>
</Application>

同样你也可以在代码文件(App.xaml.cs)中进行更改,但必须注意这个设置写在app.Run()方法之前,如下代码:
app.ShutdownMode=ShutdownMode.OnExplicitShutdown;
app.Run(win);

四、Application对象的事件

名称
描述
Activated
当应用程序成为前台应用程序时发生,即获取焦点。
Deactivated
当应用程序停止作为前台应用程序时发生,即失去焦点。
DispatcherUnhandledException
在异常由应用程序引发但未进行处理时发生。
Exit
正好在应用程序关闭之前发生,且无法取消。
FragmentNavigation
当应用程序中的导航器开始导航至某个内容片断时发生,如果所需片段位于当前内容中,则导航会立即发生;或者,如果所需片段位于不同
内容中,则导航会在加载了源XAML内容之后发生。
LoadCompleted
在已经加载、分析并开始呈现应用程序中的导航器导航到的内容时发生。
Navigated
在已经找到应用程序中的导航器要导航到的内容时发生,尽管此时该内容可能尚未完成加载。
Navigating
在应用程序中的导航器请求新导航时发生。
NavigationFailed
在应用程序中的导航器在导航到所请求内容时出现错误的情况下发生。
NavigationProgress
在由应用程序中的导航器管理的下载过程中定期发生,以提供导航进度信息。
NavigationStopped
在调用应用程序中的导航器的StopLoading方法时发生,或者当导航器在当前导航正在进行期间请求了一个新导航时发生(没大用到)。
SessionEnding
在用户通过注销或关闭操作系统而结束Windows会话时发生。
Startup
在调用Application对象的Run方法时发生。
应用程序的事件处理可以:
1、在App.xaml中做事件的绑定,在App.xaml.cs文件中添加事件的处理方法
在App.xaml文件中:
<Applicationx:Class="WPFApplications.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
StartupUri="Window1.xaml"
Startup="Application_Startup"
Exit="Application_Exit"
DispatcherUnhandledException="Application_DispatcherUnhandledException">
<Application.Resources>

</Application.Resources>
</Application>

在App.xaml.cs文件中:
publicpartialclassApp:Application
{
[STAThread]
staticvoidMain()
{
//定义Application对象作为整个应用程序入口
Applicationapp=newApplication();
//方法一:调用Run方法,参数为启动的窗体对象,也是最常用的方法
Window2win=newWindow2();
app.Run(win);
}

privatevoidApplication_DispatcherUnhandledException(objectsender,System.Windows.Threading.
DispatcherUnhandledExceptionEventArgse)
{

}

privatevoidApplication_Exit(objectsender,ExitEventArgse)
{
}
}

2、在自定义的类中可以做正常的C#的事件绑定:
publicpartialclassApp:Application
{
[STAThread]
staticvoidMain()
{
//定义Application对象作为整个应用程序入口
Applicationapp=newApplication();
//调用Run方法,参数为启动的窗体对象,也是最常用的方法
Window2win=newWindow2();
app.Startup+=newStartupEventHandler(app_Startup);
app.DispatcherUnhandledException+=newSystem.Windows.Threading.
DispatcherUnhandledExceptionEventHandler(app_DispatcherUnhandledException);
app.Run(win);
}

staticvoidapp_DispatcherUnhandledException(objectsender,System.Windows.Threading.
DispatcherUnhandledExceptionEventArgse)
{
thrownewNotImplementedException();
}

staticvoidapp_Startup(objectsender,StartupEventArgse)
{
thrownewNotImplementedException();
}
}

如果通过XAML启动窗体的话,也会编译成为为如下的程序,默认路径为Debug文件夹得App.g.cs文件:

publicpartialclassApp:System.Windows.Application{

///<summary>
///InitializeComponent
///</summary>
[System.Diagnostics.DebuggerNonUserCodeAttribute()]
publicvoidInitializeComponent(){

#line4"..\..\App.xaml"
this.StartupUri=newSystem.Uri("Window5.xaml",System.UriKind.Relative);

#linedefault
#linehidden
}

///<summary>
///ApplicationEntryPoint.
///</summary>
[System.STAThreadAttribute()]
[System.Diagnostics.DebuggerNonUserCodeAttribute()]
publicstaticvoidMain(){
WPFApplications.Appapp=newWPFApplications.App();
app.InitializeComponent();
app.Run();
}
}

五、WPF应用程序生存周期




当然这幅图也只是简单的概括了WPF的执行顺序和生命周期,具体还要细致研究才是。


4.Window

一、窗体类基本概念

对于WPF应用程序,在VisualStudio和Expression
Blend中,自定义的窗体均继承System.Windows.Window类.大家都可能听说过或者看过Applications=Code+
Markup:AGuidetotheMicrosoftWindowsPresentation
Foundation这本书,它里面就是用XAML和后台代码两种形式来实现同一个功能,那么我们这里定义的窗体也由两部分组成:
1、XAML文件,在这里面通常全部写UI的东西(希望大家还记得这两幅图)







2、后台代码文件

namespaceWPFApplications
{
///<summary>
///InteractionlogicforWindow5.xaml
///</summary>
publicpartialclassWindow5:Window
{
publicWindow5()
{
InitializeComponent();
}

privatevoidbtnOK_Click(objectsender,RoutedEventArgse)
{
lblHello.Content="HelloWorldChanged";
}
}
}

也可以将后台代码放在XAML文件中,上面的例子可以改写为:
<Windowx:Class="WPFApplications.Window5"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window5"Height="300"Width="300">
<StackPanel>
<Labelx:Name="lblHello">Hello,World!</Label>
<Buttonx:Name="btnOK"Width="88"Height="22"Content="Click"
Click="btnOK_Click"/>
<x:Code>
<![CDATA[
voidbtnOK_Click(objectsender,System.Windows.RoutedEventArgse)
{
lblHello.Content="HelloWorldChanged";
}
]]>
</x:Code>
</StackPanel>
</Window>

二、窗体的生命周期

1、显示窗体
构造函数

Show()、ShowDialog()方法:Show()方法显示非模态窗口,ShowDialog()方法显示模态窗口,这个基本和
WinForm类似

Loaded事件:窗体第一次Show()或ShowDialog()时引发的事件,通常在此事件中加载窗体的初始化数据,但如果用了MVVM模
式,基本就不在这里面写。

2、关闭窗体
Close()方法:关闭窗体,并释放窗体的资源

Closing事件、Closed事件:关闭时、关闭后引发的事件,通常在Closing事件中提示用户是否退出等信息。

3、窗体的激活
Activate()方法:激活窗体

Activated、Deactivated事件:当窗体激动、失去焦点时引发的事件

4、窗体的生命周期




为了证实上面的结论,我们用下面的代码进行测试:
publicpartialclassWindow3:Window
{
publicWindow3()
{
this.Activated+=newEventHandler(Window1_Activated);
this.Closing+=newSystem.ComponentModel.CancelEventHandler(Window1_Closing);
this.ContentRendered+=newEventHandler(Window1_ContentRendered);
this.Deactivated+=newEventHandler(Window1_Deactivated);
this.Loaded+=newRoutedEventHandler(Window1_Loaded);
this.Closed+=newEventHandler(Window1_Closed);
this.Unloaded+=newRoutedEventHandler(Window1_Unloaded);
this.SourceInitialized+=newEventHandler(Window1_SourceInitialized);

InitializeComponent();
}

voidWindow1_Unloaded(objectsender,RoutedEventArgse)
{
Debug.WriteLine("Unloaded");
}

voidWindow1_SourceInitialized(objectsender,EventArgse)
{
Debug.WriteLine("SourceInitialized");
}

voidWindow1_Loaded(objectsender,RoutedEventArgse)
{
Debug.WriteLine("Loaded");
}

voidWindow1_Deactivated(objectsender,EventArgse)
{
Debug.WriteLine("Deactivated");
}

voidWindow1_ContentRendered(objectsender,EventArgse)
{
Debug.WriteLine("ContentRendered");
}

voidWindow1_Closing(objectsender,System.ComponentModel.CancelEventArgse)
{
Debug.WriteLine("Closing");
MessageBoxResultdr=MessageBox.Show("Cancelthewindow?",
"Answer",MessageBoxButton.YesNo,MessageBoxImage.Question);
if(dr==MessageBoxResult.No)
{
e.Cancel=true;
}
}

voidWindow1_Closed(objectsender,EventArgse)
{
Debug.WriteLine("Closed");
}

voidWindow1_Activated(objectsender,EventArgse)
{
Debug.WriteLine("Activated");
}

}

执行结果为:




WPF窗体的详细的属性、方法、事件请参考MSDN,有很多的属性、方法、事件与Windows应用程序中
System.Windows.Forms.Form类颇为相似,其中常用的一些属性、方法、事件有:
窗体边框模式(WindowStyle属性)和是否允许更改窗体大小(ResizeMode属性)。

窗体启动位置(WindowStartupLocation属性)和启动状态(WindowState属性)等。

窗体标题(Title属性)及图标。

是否显示在任务栏(ShowInTaskbar)

始终在最前(TopMost属性)

5.Dispatcher及多线程

提到这个UI和后台线程交互这个问题,大家都可能在WinForm中遇到过,记得几年前我参加一个外资企业的面试,公司的其中一道题就是说在
WinForm中如何使用后台线程来操作UI,所以对这个问题比较记忆犹新。
WPF线程分配系统提供一个Dispatcher属性、VerifyAccess和CheckAccess
方法来操作线程。线程分配系统位于所有WPF类中基类,大部分WPF元素都派生于此类,如下图的Dispatcher类:





WPF应用程序启动后,会有两个线程:

一个是用来处理UI呈现(处理UI的请求,比如输入和展现等操作)。

一个用来管理UI的(对UI元素及整个UI进行管理)。

与Dispatcher调度对象想对应的就是DispatcherObject,在WPF中绝大部分控件都继承自
DispatcherObject,甚至包括Application。这些继承自DispatcherObject
的对象具有线程关联特征,也就意味着只有创建这些对象实例,且包含了Dispatcher的线程(通常指默认UI
线程)才能直接对其进行更新操作。



当我们尝试从一个非UI
线程更新一个UI元素,会看到如下的异常错误。
XAML代码
<Windowx:Class="WPFApplications.Window2"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window2"Height="300"Width="300">
<StackPanel>
<Labelx:Name="lblHello">Hello,World!</Label>
</StackPanel>
</Window>

后台代码:

publicpartialclassWindow2:Window
{
publicWindow2()
{
InitializeComponent();
Threadthread=newThread(ModifyUI);
thread.Start();
}

privatevoidModifyUI()
{
//模拟一些工作正在进行
Thread.Sleep(TimeSpan.FromSeconds(5));
lblHello.Content="Hello,Dispatcher";
}
}

错误截图:





按照DispatcherObject的限制原则,我们改用Window.Dispatcher.Invoke()
即可顺利完成这个更新操作。
privatevoidModifyUINew()
{
//模拟一些工作正在进行
Thread.Sleep(TimeSpan.FromSeconds(5));
this.Dispatcher.BeginInvoke(DispatcherPriority.Normal,(ThreadStart)delegate()
{
lblHello.Content="Hello,Dispatcher";
});
}


如果在其他工程或者类中,我们可以用
Application.Current.Dispatcher.Invoke方法来完成同样的操作,它们都指向UIThread
Dispatcher这个唯一的对象。
Dispatcher同时还支持BeginInvoke异步调用,如下代码:
privatevoidbtnHello_Click(objectsender,RoutedEventArgse)
{
newThread(()=>
{
Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Normal,
newAction(()=>
{
Thread.Sleep(TimeSpan.FromSeconds(5));
this.lblHello.Content=DateTime.Now.ToString();
}));
}).Start();
}


关于Dispatcher和WPF多线程,
还有很多要讲,由于篇幅有限且精力有限,我这里只讲一些我们最常见的应用,同时包括Freezable
的处理等问题,大家可以查阅MSDN或者查阅国外相关的专题。

6.类继承结构

在WPF中常用的的控件类继承结构如下图所示(图中圆圈的表示抽象类,方框的表示实体类):



System.Object类:大家都知道在.Net中所有类型的根类型,在图中没有画出来,DispatcherObject
就继承于它,所以它是整个应用系统的基类。

System.Windows.Threading.DispatcherObject类:WPF中的绝大多数对象是从
DispatcherObject派生的,它提供了用于处理并发和线程的基本构造。WPF是基于调度程序实现的消息系统。

System.Windows.DependencyObject类:WPF基本所有的控件都实现了依赖属性,它表示一个参与依赖项属性系统的对
象。

System.Windows.Media.Visual类:为WPF中的呈现提供支持,其中包括命中测试、坐标转换和边界框计算等。

System.Windows.UIElement类:UIElement是WPF核心级实现的基类,该类建立在Windows
PresentationFoundation(WPF)元素和基本表示特征基础上。

System.Windows.FrameworkElement类:为WindowsPresentationFoundation
(WPF)元素提供WPF框架级属性集、事件集和方法集。此类表示附带的WPF框架级实现,它是基于由UIElement定义的WPF
核心级API构建的。
System.Windows.Controls.Control类:表示用户界面(UI)元素的基类,这些元素使用
ControlTemplate来定义其外观。
System.Windows.Controls.ContentControl类:表示包含单项内容的控件。

System.Windows.Controls.ItemsControl类:表示一个可用于呈现项的集合的控件。

System.Windows.Controls.Panel类:为所有Panel元素(布局)提供基类。使用Panel元素在
WindowsPresentationFoundation(WPF)应用程序中放置和排列子对象。

System.Windows.Sharps.Sharp类:为Ellipse、Polygon和Rectangle
之类的形状元素提供基类。

除了上面的图以外,还有几个命名空间也很重要,如下:

System.Windows.Controls.Decorator类:提供在单个子元素(如Border或
Viewbox)上或周围应用效果的元素的基类。

System.Windows.Controls.Image类:表示显示图像的控件。

System.Windows.Controls.MediaElement类:表示包含音频和/或视频的控件。

7.WPF的逻辑树和视觉树

关于这部分的内容讲起来就比较多了,正如上次大家的留言里说的一样,这个内容如果拉开来讲肯定就要开几个篇幅,所以我们今天主要以讲清楚概念为重
点,先看下面的一个XAML代码的例子:
<Windowx:Class="WPFApplications.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1"Height="300"Width="300">
<StackPanel>
<Label>Hello,World!</Label>
</StackPanel>
</Window>

上面这个UI非常的简单,Window是一个根结点,它有一个子结点StackPanel,StackPanel有一个子结点Label。注意
Label下还有一个子结点string(LabelText),它同时也是一个叶子结点。这就构成了窗口的一个逻辑树。逻辑树始终存在于WPF的UI
中,不管UI是用XAML编写还是用代码编写。WPF的每个方面(属性、事件、资源等等)都是依赖于逻辑树的。

视觉树基本上是逻辑树的一种扩展。逻辑树的每个结点都被分解为它们的核心视觉组件。逻辑树的结点对我们来说是不可见的。而视觉树不同,它暴露了视觉
的实现细节。下面是VisualTree结构就表示了上面四行XAML代码的视觉树结构(下面这幅图片来源于WPF揭秘):





当然并不是所有的逻辑树结点都可以扩展为视觉树结点。只有从
System.Windows.Media.Visual或者System.Windows.Media.Visual3D继承的元素才能被视觉树所包
含。其他的元素不能包含是因为它们本身没有自己的提交(Rendering)行为。在WindowsVistaSDK
Tools当中的XamlPad提供查看Visual
Tree的功能。需要注意的是XamlPad目前只能查看以Page为根元素,并且去掉了SizeToContent属性的XAML文档。如下图所示:

在visualstudio的命令行中输入xamlpad就可以进入如下的界面:







通过上图我们可以看到Visual
Tree确实比较复杂,其中还包含有很多的不可见元素,比如ContentPresenter等。Visual
Tree虽然复杂,但是在一般情况下,我们不需要过多地关注它。我们在从根本上改变控件的风格、外观时,需要注意Visual
Tree的使用,因为在这种情况下我们通常会改变控件的视觉逻辑。比如我们在自己写一些控件的时候,再比如我们对某些外观进行特别订制的时候。
WPF
中还提供了遍历逻辑树和视觉树的辅助类:System.Windows.LogicalTreeHelper和
System.Windows.Media.VisualTreeHelper。注意遍历的位置,逻辑树可以在类的构造函数中遍历。但是,视觉树必须在经
过至少一次的布局后才能形成。所以它不能在构造函数遍历。通常是在OnContentRendered进行,这个函数为在布局发生后被调用。

实每个Tree结点元素本身也包含了遍历的方法。比如,Visual类包含了三个保护成员方法VisualParent、
VisualChildrenCount、GetVisualChild。通过它们可以访问Visual的父元素和子元素。而对于
FrameworkElement,它通常定义了一个公共的Parent属性表示其逻辑父元素。特定的FrameworkElement子类用不同的方式
暴露了它的逻辑子元素。比如部分子元素是Children
Collection,有是有时Content属性,Content属性强制元素只能有一个逻辑子元素。
为了弄清楚这些概念,我们就通过如下代码作为演示:
publicpartialclassWindow1:Window
{
publicWindow1()
{
InitializeComponent();
PrintLogicalTree(0,this);
}

protectedoverridevoidOnContentRendered(EventArgse)
{
base.OnContentRendered(e);
PrintVisualTree(0,this);
}

voidPrintLogicalTree(intdepth,objectobj)
{
//打印空格,方便查看
Debug.WriteLine(newstring('',depth)+obj);

//如果不是DependencyObject,如string等类型
if(!(objisDependencyObject))return;

//递归打印逻辑树
foreach(objectchildinLogicalTreeHelper.GetChildren(
objasDependencyObject))
{
PrintLogicalTree(depth+1,child);
}
}

voidPrintVisualTree(intdepth,DependencyObjectobj)
{
//打印空格,方便查看
Debug.WriteLine(newstring('',depth)+obj);

//递归打印视觉树
for(inti=0;i<VisualTreeHelper.GetChildrenCount(obj);i++)
{
PrintVisualTree(depth+1,VisualTreeHelper.GetChild(obj,i));
}
}
}

结果为:




8.本文总结

本篇主要对Application、window、多线程、类继承结构、逻辑树与可视树等的理论和实际Demo进行了探讨,通过这一篇文章,我们可
以大概了解WPF在这些元素上的处理,同时也给我后面的内容奠定了基础,后面会逐渐牵涉到实际的一些案例和新的概念,所以如果有不熟悉且对这个专题感兴趣
的朋友可以仔细看一下这篇文章,在文章后面也会把本文用到的代码附加上去,大家可以下载下来进行测试。
最后圣殿骑士会
尽心尽力写好这个系列,同时由于是自己对这些技术的使用总结和心得体会,错误之处在所难免,怀着技术交流的心态,在博客园和51CTO发表出来,所以希望大家能够多多指点,
这样在使一部分人受益的同时也能纠正我的错误观点,以便和各位共同提高,后续文章敬请关注!

9.系列进度(红色标示已发布)

·1.
WPF基础到企业应用系列1——开篇有益
·
2.WPF基础到企业应用系列2——WPF前世今生
·
3.WPF基础到企业应用系列3——WPF开发漫谈
·
4.WPF基础到企业应用系列4——WPF千年轮回
·5.使用面板做布局(几种布局控件的XAML及CS代码,综合布局等)
·6.依赖属性、附加属性(基本、继承、元数据)
·7.路由事件、附加事件
·8.命令
·9.WPF控件分类介绍与使用技巧(ContentControl、HeaderedContentControl……
Decorator)
·10.尺寸缩放、定位与变换元素
·11.资源
·12.数据绑定(基本、值转换、验证、集合的筛选、排序、分组、主从、数据提供者)
·13.样式
·14.模板
·15.多语言、皮肤和主题
·16.2D图形
·17.3D图形
·18.动画(几种动画的应用)
·19.音频、视频、语音
·20.文档、打印、报表
·21.用户控件和自定义控件
·22.Win32、WindowsForm以及ActiveX之间的互用性
·23.构建并部署应用程序(ClickOnce部署、微软setup/InstallShield+自动更新组件)
·24.WPF的模式讲解及实例(MVCDemo)
·25.WPF的模式讲解及实例(MVPDemo)
·26.WPF的模式讲解及实例(MVVMDemo)
·27.性能优化(WPF项目的瓶颈)
·28.一个完整WPF项目(普通架构版)
·39.一个完整WPF项目(MVVM架构版)
·30.WPF4.0新功能
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  职场 休闲 WPF