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

来源:互联网 发布:西科大网络教育平台 编辑:程序博客网 时间:2024/05/03 02:53

2010-07-14 03:46 by圣殿骑士

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.本文总结

3.Application

一.介绍

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

二.创建WPF应用程序

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

1、Visual Studio和ExpressionBlend默认的方式,使用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{    /// <summary>    /// Interaction logic for App.xaml    /// </summary>    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 app = new Application();          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 {        /// <summary>    /// InitializeComponent    /// </summary>    [System.Diagnostics.DebuggerNonUserCodeAttribute()]    public void InitializeComponent() {                #line 4 "..\..\App.xaml"        this.StartupUri = new System.Uri("Window5.xaml", System.UriKind.Relative);                #line default        #line hidden    }        /// <summary>    /// Application Entry Point.    /// </summary>    [System.STAThreadAttribute()]    [System.Diagnostics.DebuggerNonUserCodeAttribute()]    public static void Main() {        WPFApplications.App app = new WPFApplications.App();        app.InitializeComponent();        app.Run();    }}

五、WPF应用程序生存周期

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

4.Window

一、窗体类基本概念

对于WPF应用程序,自定义的窗体均继承System.Windows.Window类.

大家都可能听说过或者看过Applications = Code + Markup: A Guide to the Microsoft WindowsPresentation Foundation这本书,它里面就是用XAML和后台代码两种形式来实现同一个功能,那么我们这里定义的窗体也由两部分组成:

1、 XAML文件,在这里面通常全部写UI的东西

2、后台代码文件

二、窗体的生命周期

1、显示窗体

·        构造函数

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

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

2、关闭窗体

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

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

3、窗体的激活

·        Activate()方法:激活窗体

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

4、窗体的生命周期

5.Dispatcher及多线程

提到这个UI和后台线程交互这个问题,大家都可能在WinForm中遇到过,记得几年前我参加一个外资企业的面试,公司的其中一道题就是说在WinForm中如何使用后台线程来操作UI,所以对这个问题比较记忆犹新。

WPF线程分配系统提供一个Dispatcher属性、VerifyAccess 和 CheckAccess 方法来操作线程。线程分配系统位于所有 WPF 类中基类,大部分WPF 元素都派生于此类,如下图的Dispatcher类:

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

  1. 一个是用来处理UI呈现(处理UI的请求,比如输入和展现等操作)。
  2. 一个用来管理 UI的 (对UI元素及整个UI进行管理)。

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

更详细参考:WPF入门教程系列四——Dispatcher介绍

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 Presentation Foundation (WPF) 元素和基本表示特征基础上。

·        System.Windows.FrameworkElement类:为 Windows Presentation Foundation (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 元素在 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代码的例子:

<Window x: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是一个根结点,它有一个子结点StackPanelStackPanel有一个子结点Label。注意Label下还有一个子结点stringLabelText),它同时也是一个叶子结点。这就构成了窗口的一个逻辑树。逻辑树始终存在于WPFUI中,不管UI是用XAML编写还是用代码编写。WPF的每个方面(属性、事件、资源等等)都是依赖于逻辑树的。

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


当然并不是所有的逻辑树结点都可以扩展为视觉树结点。

只有从System.Windows.Media.Visual或者System.Windows.Media.Visual3D继承的元素才能被视觉树所包含。

其他的元素不能包含是因为它们本身没有自己的提交(Rendering)行为。

8.本文总结

本篇主要对Application、window、多线程、类继承结构、逻辑树与可视树等的理论和实际Demo进行了探讨,通过这一篇文章,我们可以大概了解WPF在这些元素上的处理,同时也给我后面的内容奠定了基础,后面会逐渐牵涉到实际的一些案例和新的概念,所以如果有不熟悉且对这个专题感兴趣的朋友可以仔细看一下这篇文章,在文章后面也会把本文用到的代码附加上去,大家可以下载下来进行测试。

       最后圣殿骑士 会尽心尽力写好这个系列,同时由于是自己对这些技术的使用总结和心得体会,错误之处在所难免,怀着技术交流的心态,在博客园51CTO发表出来,所以希望大家能够多多指点,这样在使一部分人受益的同时也能纠正我的错误观点,以便和各位共同提高,后续文章敬请关注!

0 0
原创粉丝点击