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

深入浅出是个好词,侯大侠的《深入浅出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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值