您的位置:首页 > 其它

深入浅出学WPF窗口- [WPF学习总结]

2014-02-21 21:10 246 查看
深入浅出是个好词,侯大侠的《深入浅出MFC》,最近还在看一本也是以深入浅出为标题的书籍。觉得深入去了解程序实现背后的原理才是真正的透彻掌握一门知识,最近虽然在学习WPF,但感觉没有什么实质性的学习成果,像一些更细节更低层的原理总是迷迷糊糊的,在研究WPF父窗口与子窗口之间的关系时,觉得应该更深入的去了解WPF窗口背后的实现技术,这样才能熟练的浅出使用它。
  首先先通过控制台直接生成一个WPF窗口,从低层去认识WPF窗口的实质。新建一个c#控制台程序,添加引用:Windowsbase,PresentationCore,PresentationFramework以及System.Axml。这样我们才能通过控制台去生成一个WPF应用程序,然后在cs文件里面引入using System.windows; 和using System.windows.Controls;

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.Windows;

using System.Windows.Controls;

namespace ConsoleForWPF

{

class Program

{

[STAThread]

static void Main(string[] args)

{

Window window = new Window();

window.Title = "ConsoleForWPF Application";

window.Show();

}

}

}

  如上面的代码所示,运行该程序,可以看到两个窗口一闪而过,要注意得是在Main方法前面我们要声明[STAThread],因为应用程序的COM线程模型是单线程单元[STA],如果不声明这个你可以看到会发生异常。为了让窗口停留,我们声明一个APP应用程序对象。

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.Windows;

using System.Windows.Controls;

namespace ConsoleForWPF

{

class Program

{

[STAThread]

static void Main(string[] args)

{

Window window = new Window();

window.Title = "ConsoleForWPF Application";

window.Show();

Application app = new Application();

app.Run(window);

}

}

}

  这时,我们可以看到可以通过控制台生成我们需要的WPF程序了。接下来深入的去了解Application这个类以及app.Run函数的执行过程。

  首先来了解下WPF中Application这个类,Application是一个地址空间,在WPF中应用程序就是在System.Windows命名空间下的一个Application实例。一个应用程序只能对应一个Application的实例,而Application的生命周期自然是从应用程序启动到终止的周期。

  Application类的创建有两种方式,一种是显式的,一种是隐式的,像上面的控制台那个是显式的创建,在创建Application对象的时候,会对Application静态属性Current赋予新创建的对象,所以app.run()也可以替换成Application.Current.Run()。而隐式的创建我们早已接触到,就是我们通过VS新建一个WPF项目时,我们可以看到的那个App.xaml,打开App.xaml的后台文件我们就可以看到,在代码中定义一个继承于Application的类,并在类中重写Application启动时(Run方法调用时)出发的OnStartup事件对应的方法,将主窗体的实例化放在该方法中.事实上WPF中会自动创建Main函数,并依照工程文件中指定的相关Application对应所在文件app.xaml,实例化该应用程序类,并调用Run方法.除了在OnStartup时间对应分方法中指定实例化的主窗体,也可以在app.xaml文件中直接指定程序运行时实例化并显示的主窗口。

  然后我们看下那个app.Run函数,函数原型是:

public int Run(){

EventTrace.NormalTraceEvent(EventTraceGuidId.APPRUNGUID, 0);

return this.Run(null);

}

  在上面的代码中我们可以看到EventTrace.NormalTraceEvent,这里启动了信息路由(路由的详细说明,再次强烈地觉得MSDN是个好东西)。再看下面的代码:

  [SecurityCritical]

  public int Run(Window window)

  {

  base.VerifyAccess();

  if (InBrowserHostedApp())

  {

   throw new InvalidOperationException(SR.Get("CannotCallRunFromBrowserHostedApp"));

  }

  return this.RunInternal(window);

  }

  internal static bool InBrowserHostedApp()

  {

   return (BrowserInteropHelper.IsBrowserHosted && !(Current is XappLauncherApp));

  }

  其中,VerifyAccess判断访问这个WPF的线程是不是主线程,如果不是就抛出异常。通过InBrowserHostedApp函数判断当前程序是否是一个浏览器应用程序而不是一个Xaml应用程序。如果,是一个浏览器应用程序不是一个Xaml应用程序,就抛出一个异常。

  到这里还只是进行到检测运行环境而已,更重要的代码在RunInternal函数中,这个函数为应用程序构造了一个消息循环。对于上面的程序你在运行的时候他会弹出两个窗口,一个是WPF窗口,一个是控制台窗口。这是因为编译器自己为这个程序添加的,我们可以重写代码让程序只弹出一个WPF窗口。

  这时,我想,如果在Main函数里面定义两个Window实例,那么app.Run会执行哪一个window实例呢?事实上当你这样做的时候,两个实例都会被执行,并且,只要你关闭了其中任意一个窗口另外一个窗口也会随之关闭。这时我们需要去了解Application类的关闭模式。Application的关闭模式有三种,分别为OnMainWindowClose、OnLastWindowClose和OnexplictShutDow。通过查看MSDN可以得知,三者的属性如下:

  



  而ShutdownModel默认的枚举类型是OnLastWindowClose,所以当我们定义了两个窗口时,只要关闭其中一个或者在其中一个响应了Shutdown函数,整个应用程序就会关闭,而当我们设定app.ShutdownMode =ShutdownMode.OnMainWindowClose;或者OnExplicitShutdown时,关闭了其中一个窗口另外一个则不受影响。

  以上就是大体上对于一个Application的创建及退出的一些深层次的分析,再接下来分析一下WPF窗口的响应事件。一旦初始化了,窗口做的事情就是响应事件,这些事件通常是一些用户输入、点击之类的。其中,应用程序在UIElement中实现了这些事件的委托,而Windows继承了它的事件。如下面的代码,我们为新建立的window添加一个响应事件:

[STAThread]

static void Main(string[] args)

{

Window window1 = new Window();

window1.Title = "ConsoleForWPF Application 1";

window1.MouseDown +=new MouseButtonEventHandler(win_MouseDown);

window1.Show();

Application app = new Application();

app.ShutdownMode =ShutdownMode.OnMainWindowClose;

app.Run();

}

static void win_MouseDown(object sender, MouseButtonEventArgs e)

{

Window win = sender as Window;

if (win != null)

{

MessageBox.Show("mousebuttonDown", win.Title);

}

}

  我们通过Window win = sender as Window来获取发出这个事件的窗口实例,其实,除了这个方式,我们还可以通过MessageBox.Show("mousebuttonDown", Application.Current.MainWindow.Title);,即Application.Current.MainWindow来获得当前运行程序的窗口实例。

  对于Application类来说,一旦Application的run方法被调用,就会发出一个onStartup事件,一旦run方法返回就会发出一个onExit事件.如果程序需要处理这些事件,可以给它添加对应的响应,或者你直接继承Application,然后再重写这些函数,如下面的代码,我们重要了解onStartup函数,直接在该函数里面生成WPF窗口。

protected override void OnStartup(StartupEventArgs e)

{

base.OnStartup(e);

//定义在初始化的时候调用一下代码

//如果此处初始化了多个窗口,系统会默认第一个调用show方法的窗口时主窗口

Window window = new Window();

window.Title = "ConsoleForWPF Application";

window.Show();

}

/// <summary>

/// 在操作系统选择注销或者关闭的时候会触发应用程序调用这个事件

/// 默认情况下是直接退出应用程序

/// /// </summary>

/// /// <param name="e"></param>

protected override void OnSessionEnding(SessionEndingCancelEventArgs e)

{

base.OnSessionEnding(e);

MessageBoxResult mb = MessageBox.Show("Do you want to save your data?", MainWindow.Title, MessageBoxButton.YesNo, MessageBoxImage.Question);

e.Cancel = (mb == MessageBoxResult.Yes);

}

  正如前面我们演示过那样,通过定义多个Window我们可以获取多个窗口实例,这时我们可以留意下Windows这个属性,Windows属性则是存储了所有的窗体的一个集合,它包含应用程序中所有的窗体资源。如下我们定义多个窗口,则可以通过下面的代码来实现依次访问各个窗口:

void wint_MouseDown(object sender, MouseButtonEventArgs e)

{ foreach (Window win in Windows)

{ MessageBox.Show(String.Format("Sender window is {0},Main window is {1}", win.Title, MainWindow.Title));

}

}
  到这里对WPF窗口的讨论暂告一段落,有新的学习成果再记录下来。

本文来自Ellic的博客,原文地址:http://www.cnblogs.com/libenqing/archive/2011/04/07/2007817.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: