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

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 不同的是 WPF Application 默认由两部分组成 : App.xaml 和 App.xaml.cs,这有点类似于 Delphi Form(我对此只是了解,并没有接触过Delphi ),将定义和行为代码相分离。当然,这个和WebForm 也比较类似。XAML 从严格意义上说并不是一个纯粹的 XML 格式文件,它更像是一种 DSL(Domain Specific Language,领域特定语言),它的所有定义都直接映射成某些代码,只是具体的翻译工作交给了编译器完成而已。WPF应用程序由System.Windows.Application类来进行管理。

二.创建WPF应用程序

创建WPF应用程序有两种方式:

1、Visual Studio和Expression Blend默认的方式,使用App.xaml文件定义启动应用程序

App.xaml文件的内容大致如下所示:

2、可以自已定义类,定义Main方法实现对WPF应用程序的启动

在项目中添加一个类,类的代码如下,在项目选项中,设定此类为启动项。

using System;

using System.Collections.Generic;

using System.Configuration;

using System.Data;

using System.Linq;

using System.Windows;

namespace WPFApplications

{

///

/// Interaction logic for App.xaml

///

public partial class App : Application

{

[STAThread]

static void Main()

{

// 定义Application对象作为整个应用程序入口

Application app = new Application();

// 方法一:调用Run方法,参数为启动的窗体对象 ,也是最常用的方法

Window2 win = new Window2();

app.Run(win);

// 方法二:指定Application对象的MainWindow属性为启动窗体,然后调用无参数的Run方法

//Window2 win = new Window2();

//app.MainWindow = win;

//win.Show();

// win.Show()是必须的,否则无法显示窗体

//app.Run();

// 方法三:通过Url的方式启动

//app.StartupUri = new Uri("Window2.xaml", UriKind.Relative);

//app.Run();

}

}

}    三、Application应用程序关闭  OnLastWindowClose(默认值):最后一个窗体关闭或调用Application对象的Shutdown() 方法时,应用程序关闭。OnMainWindowClose启动窗体关闭或调用Application对象的Shutdown()方法时,应用程序关闭。(和C#的Windows应用程序的关闭模式比较类似) OnExplicitShutdown只有在调用Application对象的Shutdown()方法时,应用程序才会关闭。

对关闭选项更改的时候,可以直接在App.xaml中更改:

<Application x: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文件中:

<Application x: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文件中:

public partial class App : Application

{

[STAThread]

static void Main()

{

// 定义Application对象作为整个应用程序入口

Application app = new Application();

// 方法一:调用Run方法,参数为启动的窗体对象 ,也是最常用的方法

Window2 win = new Window2();

app.Run(win);

}

private void Application_DispatcherUnhandledException(object sender, System.Windows.Threading.DispatcherUnhandledExceptionEventArgs e)

{ }

private void Application_Exit(object sender, ExitEventArgs e)

{ }

}

2、在自定义的类中可以做正常的C#的事件绑定:

public partial class App : Application

{

[STAThread]

static void Main()

{

// 定义Application对象作为整个应用程序入口

Application app = new Application();

// 调用Run方法,参数为启动的窗体对象 ,也是最常用的方法

Window2 win = new Window2();

app.Startup += new StartupEventHandler(app_Startup);

app.DispatcherUnhandledException += new System.Windows.Threading.DispatcherUnhandledExceptionEventHandler(app_DispatcherUnhandledException);

app.Run(win);

}

static void app_DispatcherUnhandledException(object sender, System.Windows.Threading.DispatcherUnhandledExceptionEventArgs e)

{

throw new NotImplementedException();

}

static void app_Startup(object sender, StartupEventArgs e)

{

throw new NotImplementedException();

}

}

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

public partial class App : System.Windows.Application {

///

/// InitializeComponent

///

[System.Diagnostics.DebuggerNonUserCodeAttribute()]

public void InitializeComponent() {

#line 4 "..\..\App.xaml"

this.StartupUri = new System.Uri("Window5.xaml", System.UriKind.Relative);

#line default

#line hidden

}

///

/// Application Entry Point.

///

[System.STAThreadAttribute()]

[System.Diagnostics.DebuggerNonUserCodeAttribute()]

public static void Main() {

WPFApplications.App app = new WPFApplications.App();

app.InitializeComponent();

app.Run();

}

}    五、WPF应用程序生存周期

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

4.Window     一、窗体类基本概念

对于WPF应用程序,在Visual Studio和Expression Blend中,自定义的窗体均继承System.Windows.Window类.大家都可能听说过或者看过Applications = Code + Markup: A Guide to the Microsoft Windows Presentation Foundation这本书,它里面就是用XAML和后台代码两种形式来实现同一个功能,那么我们这里定义的窗体也由两部分组成:

1、 XAML文件,在这里面通常全部写UI的东西(希望大家还记得这两幅图)

2、后台代码文件

namespace WPFApplications

{

///

/// Interaction logic for Window5.xaml

///

public partial class Window5 : Window

{

public Window5()

{

InitializeComponent();

}

private void btnOK_Click(object sender, RoutedEventArgs e)

{

lblHello.Content = "Hello World Changed";

}

}

}

也可以将后台代码放在XAML文件中,上面的例子可以改写为:

<Window x: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>

<Label x:Name="lblHello">Hello,World!Label>

<Button x:Name="btnOK" Width="88" Height="22" Content="Click"

Click="btnOK_Click"/>

<x:Code>

<br /> void btnOK_Click(object sender, System.Windows.RoutedEventArgs e) <br /> { <br /> lblHello.Content = "Hello World Changed"; <br /> } <br />

x:Code>StackPanel>

Window>     二、窗体的生命周期

1、显示窗体

构造函数 Show()、ShowDialog()方法:Show()方法显示非模态窗口,ShowDialog()方法显示模态窗口,这个基本和WinForm类似 Loaded事件:窗体第一次Show()或ShowDialog()时引发的事件,通常在此事件中加载窗体的初始化数据,但如果用了MVVM模式,基本就不在这里面写。

2、关闭窗体

Close()方法:关闭窗体,并释放窗体的资源 Closing事件、Closed事件:关闭时、关闭后引发的事件,通常在Closing事件中提示用户是否退出等信息。

3、窗体的激活

Activate()方法:激活窗体 Activated、Deactivated事件:当窗体激动、失去焦点时引发的事件

4、窗体的生命周期

为了证实上面的结论,我们用下面的代码进行测试:

public partial class Window3 : Window

{

public Window3()

{

this.Activated += new EventHandler(Window1_Activated);

this.Closing += new System.ComponentModel.CancelEventHandler(Window1_Closing);

this.ContentRendered += new EventHandler(Window1_ContentRendered);

this.Deactivated += new EventHandler(Window1_Deactivated);

this.Loaded += new RoutedEventHandler(Window1_Loaded);

this.Closed += new EventHandler(Window1_Closed);

this.Unloaded += new RoutedEventHandler(Window1_Unloaded);

this.SourceInitialized += new EventHandler(Window1_SourceInitialized);

InitializeComponent();

}

void Window1_Unloaded(object sender, RoutedEventArgs e)

{

Debug.WriteLine("Unloaded");

}

void Window1_SourceInitialized(object sender, EventArgs e)

{

Debug.WriteLine("SourceInitialized");

}

void Window1_Loaded(object sender, RoutedEventArgs e)

{

Debug.WriteLine("Loaded");

}

void Window1_Deactivated(object sender, EventArgs e)

{

Debug.WriteLine("Deactivated");

}

void Window1_ContentRendered(object sender, EventArgs e)

{

Debug.WriteLine("ContentRendered");

}

void Window1_Closing(object sender, System.ComponentModel.CancelEventArgs e)

{

Debug.WriteLine("Closing");

MessageBoxResult dr = MessageBox.Show("Cancel the window?",

 "Answer", MessageBoxButton.YesNo, MessageBoxImage.Question);

if (dr == MessageBoxResult.No)

{

e.Cancel = true;

}

}

void Window1_Closed(object sender, EventArgs e)

{

Debug.WriteLine("Closed");

}

void Window1_Activated(object sender, EventArgs e)

{

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代码:

<Window x: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>

<Label x:Name="lblHello">Hello,World!Label>

StackPanel>

Window>

后台代码:

public partial class Window2 : Window

{

public Window2()

{

InitializeComponent();

Thread thread = new Thread(ModifyUI);

thread.Start();

}

private void ModifyUI()

{

// 模拟一些工作正在进行

Thread.Sleep(TimeSpan.FromSeconds(5));

lblHello.Content = "Hello,Dispatcher";

}

}

错误截图:

按照 DispatcherObject 的限制原则,我们改用 Window.Dispatcher.Invoke() 即可顺利完成这个更新操作。

private void ModifyUINew()

{

// 模拟一些工作正在进行

Thread.Sleep(TimeSpan.FromSeconds(5));

this.Dispatcher.BeginInvoke(DispatcherPriority.Normal,(ThreadStart)delega

时间: 2024-08-08 00:36:49

WPF 基础到企业应用系列4WPF千年轮回的相关文章

WPF 基础到企业应用系列5WPF千年轮回2

一,摘要 首先很高兴这个系列能得到大家的关注和支持,前端时间身体状况不适,所以暂停了更新,对此表示非常抱歉,以后会逐渐加快进度,不过由于这是一个很长的系列,我也想把它写好,所以以后也会慢慢来,在这个系列的过程中也会穿插发一些其他文章,比如Windows Azure.设计模式.WCF.Silverlight等,同时也会发一些自己的技术随感和心得,反正只要自己写得开心且对大家有帮助就行.由于自己才疏学浅且是对这些技术的使用总结和心得体会,错误之处在所难免,怀着技术交流的心态,在这里发表出来,所以希望

一起谈.NET技术,WPF 基础到企业应用系列4——WPF千年轮回

1.开篇前言      首先很高兴这个系列能得到大家的关注和支持,基于对大家负责和对自己负责的态度,我会不断努力写好这个系列,分享自己的微薄技术和经验,希望在帮助别人的同时也不断提升自己.由于这篇文章很多(现已拆分成2篇,今天这篇只是其中之一),一共花了几个个晚上的休息时间才完成,所以读者花的时间长了一些,也希望大家能够见谅,这个系列以后会每周发三到四篇左右(主要是写一篇差不多要花几晚上,感觉思维比较发散),除了讲WPF技术本身之外,也会讲一些项目具体开发,所以敬请关注.     本篇文章取名为

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

1.开篇前言 关于本人--圣殿骑士刚入住博客园和51CTO写技术博客,目前主要在一家外资企业从事项目管理.技术架构及企业技术培训工作.由于工作和项目需要,所以对一些技术进行了较为深入的研究,之前在整个公司做过一些技术专场的培训,由于每次时间较短且人员较多的关系,没能讲得很透彻,所以挺对不住那些同事的.现在在园子里开一个博客,希望能把所学的微薄知识书写出来,以供大家参考.近期将针对这些培训专场推出"OO到设计模式"."WCF基础到企业应用"."WPF基础到企

一起谈.NET技术,WPF 基础到企业应用系列2——WPF前世今生

1.开篇前言      很多时候了解一项新技术的历史和趋势往往比这项技术的本身价值还要重要.WPF作为一项新技术(已经三年多了,或者应该叫老技术了),我们都有必要了解它的来龙去脉,尤其是公司的CTO.技术总监.架构师等决策层,因为他们对技术的选型及应用具有决定权.对于开发者来说,了解自己正在从事的这个技术的前世今生,有助于我们更好的认识技术本身的价值,也可以避免我们少走一些弯路(圣殿骑士 就走过很多弯路,所以对此比较感慨).从IT技术发展的这些年可以看出,技术对于各大公司只是竞争的一种手段,而对

一起谈.NET技术,WPF 基础到企业应用系列3——WPF开发漫谈

1.开篇前言      首先很高兴这个系列能得到大家的关注和支持,基于对大家负责和对自己负责的态度,我会不断努力写好这个系列,分享自己的微薄技术和经验,希望在帮助别人的同时也不断提升自己.由于这篇文章稍多,所以读者花的时间长了一些,也希望大家能够见谅,这个系列以后会每周发三到四篇左右(主要是写一篇差不多要花几晚上,感觉思维比较发散),除了讲WPF技术本身之外,也会讲一些项目具体开发,所以敬请关注.在前两次的文章中我们对WPF有了一个比较全面的认识,那么在本篇文章当中,除了讲一些理论知识外,我们会

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

1.开篇前言 很多时候了解一项新技术的历史和趋势往往比这项技术的本身价值还要重要.WPF作为一项新技术(已经三年多了,或者应该叫老技术了),我们都有必要了解它的来龙去脉,尤其是公司的CTO.技术总监.架构师等决策层,因为他们对技术的选型及应用具有决定权.对于开发者来说,了解自己正在从事的这个技术的前世今生,有助于我们更好的认识技术本身的价值,也可以避免我们少走一些弯路(圣殿骑士 就走过很多弯路,所以对此比较感慨).从IT技术发展的这些年可以看出,技术对于各大公司只是竞争的一种手段,而对于大多数程

一起谈.NET技术,WPF 基础到企业应用系列1——开篇有益

1.开篇前言 关于本人--圣殿骑士刚入住博客园和51CTO写技术博客,目前主要在一家外资企业从事项目管理.技术架构及企业技术培训工作.由于工作和项目需要,所以对一些技术进行了较为深入的研究,之前在整个公司做过一些技术专场的培训,由于每次时间较短且人员较多的关系,没能讲得很透彻,所以挺对不住那些同事的.现在在园子里开一个博客,希望能把所学的微薄知识书写出来,以供大家参考.近期将针对这些培训专场推出"OO到设计模式"."WCF基础到企业应用"."WPF基础到企

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

1.开篇前言 关于本人--圣殿骑士刚入住博客园和51CTO写技术博客,目前主要在一家外资企业从事项目管理.技术架构及企业技术培训工作.由于工作和项目需要,所以对一些技术进行了较为深入的研究,之前在整个公司做过一些技术专场的培训,由于每次时间较短且人员较多的关系,没能讲得很透彻,所以挺对不住那些同事的.现在在园子里开一个博客,希望能把所学的微薄知识书写出来,以供大家参考.近期将针对这些培训专场推出"OO到设计模式"."WCF基础到企业应用"."WPF基础到企

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

1.开篇前言 本篇文章取名为WPF千年轮回只因为两个原因: WPF和当年Win32.WinForm等的到来颇为相似,只是在功能和体验上上进行了提高,所以这是微软产品上的一个轮回: WPF的学习过程和其他技术一样,譬如ASP.NET,我们在学习的时候会先要了解Asp.Net构架(Http请求处理流程). Pipeline.HttpHandler 和 HttpModule 等内容,这和WPF的Application生命周期相对应,再如WPF的Window生命周期可以和ASP.NET的页面生命周期相对