• WPF窗体研究


    WPF窗体研究(一)

         呵呵,好久没有发过文章了,今天补一篇。最近做WPF,虽然过程中很艰辛,但是自己很快乐,毕竟自己写代码,虽然很乱,但是觉得自己在进步,就不错了,希望所有的程序员都能找到自己的路,不是碌碌无为的为了赚钱。

    好了,开始写自己关于WPF窗体类的想法了。

        WPF(Windows Presentation Foundation)窗体表现基础,是Winform的一个发展。跟Winform一样,也是用来编写桌面程序。所以窗口是少不了的。下面是MSDN里WPF的窗口

    知识点:

    1、一个窗口可以分为工作区和非工作区

    2、要知道的是非工作区是由WPF实现的,在上面的图中包括:

    边框、标题栏、图标、最大化最小化还原按钮、关闭按钮、系统菜单(包括最大化最小化还原移动和关闭按钮)

    3、  工作区如上图也就是非工作区内部的区域。工作区用来添加应用程序特定的内容。

    在WPF中使用Windows类进行封装,可以通过该类进行以下操作

    ①显示窗口。

    ②配置窗口的大小、位置和外观。

    ③承载应用程序特定的内容。

    ④管理窗口的生存期。

    典型的窗口实现既包括外观也包括行为,外观就是用户所看到的窗口的样子,而行为则是用户与窗口交互时的运行方式。在WPF中可以使用代码和XAML标记语言来实现窗口的外观和行为。

    不管是Web还是Winform,页面都要和代码隐藏文件进行关联,协同工作。而WPF中也是一样的,为了XAML和代码隐藏文件进行关联,必须实现以下的条件:

    (1)在标记中,Window 元素必须包含 x:Class 属性。生成应用程序时,标记文件中存在的 x:Class 将使 Microsoft Build Engine (MSBuild) 生成一个从 Window 派生的 partial 类,并且该类的名称是由 x:Class 属性指定的。这要求添加 XAML 架构的 XML 命名空间声明 (xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml")。生成的 partial 类实现了 InitializeComponent 方法,调用此方法可注册事件并设置在标记中实现的属性。

    (2)在代码隐藏中,该类必须是由标记中的 x:Class 属性指定名称的 partial 类,并且它必须从 Window 派生。这样,代码隐藏文件就与应用程序生成时为标记文件生成的 partial 类相关联。

    (3)在代码隐藏中,window 类必须实现调用 InitializeComponent 方法的构造函数。InitializeComponent 是由标记文件的生成的 partial 类实现的,用来注册事件和设置在标记中定义的属性。

        这里我主要说的是窗口的生命周期,就像我们研究WebForm一样要先研究它的生命周期一样。

       打开窗口

        要打开一个窗口,那么必须要创建这个窗口的一个实例。我们新建一个WPF应用程序的时候系统给我们默认添加了下面的构造。在APP.xaml中

    <Application x:Class="WinformExanm.App"

        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

        StartupUri="Window1.xaml">

        <Application.Resources>

        </Application.Resources>

    </Application>

    这里的StartupUri就是我们要启动的窗口。这样就创建了一个实例,或者是在xaml中不声明StartupUri而是在代码隐藏文件中添加Startup事件。如下:

      public partial class App : Application

        {

            public App()

            {

                this.Startup += new StartupEventHandler(Application_Startup);

            }

            private void Application_Startup(object sender, StartupEventArgs e)

            {

                Window1 mywindow = new Window1();

                mywindow.Show();

            }

        }

    这样也可以达到效果,这样在应用程序启动时引发StartUp事件,然后创建Window1的实例并显示。在实例化窗口的时候,指向该窗口的引用会自动添加到Application对象管理的窗口列表(Windows,一个集合属性)中。另外在默认情况下,Application会将实例化的第一个窗口作为主应用程序窗口。

    (一旦窗口在用户界面 (UI) 线程上实例化,一个 Window 引用会自动添加到 Windows;不会添加由辅助线程创建的窗口。Window 引用会在其 Closing 事件已处理之后且其 Closed事件引发之前自动移除。

    默认情况下,添加到 Windows 属性的第一项成为 MainWindow。)对于Closing事件和closed事件后面讲解。

    窗口所属权:

    使用Show创建的窗口跟创建它的窗口没有什么隐式联系。那就是说我们可以对这两个窗口都进行交互。也就是说二者都可以进行以下操作:

    1、  覆盖另一个窗口

    例如:

    我运行程序打开窗口二,然后窗口二覆盖窗口一,但是当我点击窗口一,那么窗口一就覆盖了窗口二。但是,如果有一个窗口的TopMost属性设置为True的话,那么就无法实现了。比如窗口二有此属性,那么就一直在最上方。

    2、在不影响另一个窗口的情况下最大化最小化,当然了这是常识。

    当然,如果希望某个窗口拥有某个窗口,且当关闭了某个窗口其余窗口也关闭,就可以用附加属性Owner来实现。如下代码

    Window2 win2 = new Window2();

                win2.Owner = this;

                win2.Show();

    当建立了这种附属关系后,我们可以在Window1中通过OwnedWindows属性检查它的所有的附属窗口,而window2也可以通过owner检查它的所有者窗口

    Window1代码如下

    Window2 win2 = new Window2();

                string strOwnerWindows="当前窗口的属性窗口有:";

                win2.Owner = this;

                foreach (Window mywindow in this.OwnedWindows)

                {

                    strOwnerWindows += mywindow.Title.ToString();

                }

                this.ownderWindows.Content = strOwnerWindows;

                win2.Show();

    Window2代码如下

    string str = "当前窗口的拥有者是:";

                this.label1.Content = str + this.Owner.Title.ToString();

    关于窗口关闭、激活的原理,大家可以在msdn中查看,而且都讲的很详细

    下面我主要就窗体的生成过程和生命周期做一个总结,因为MSDN上关于这块讲的比较晦涩,而且很笼统。下面是MSDN里关于生命周期中事件的触发顺序:

    WPF <wbr>Window窗体研究(二)

    当我们编译运行Application的Startup事件创立一个窗口的实例的时候,会触发SourceInitiated事件,来获取窗口的HwndSource属性,也就是窗口句柄,这主要是为了可能与Win32交互要用到这个属性。当这个事件触发后,会触发Activiated事件,标明该窗口已被激活,当激活后就要实例化元素。

    此时调用window的构造函数的InitializeComponent()方法,并解析拓展名为.i.cs的文件(这个文件就像是Windows的设计页面的作用是一样的,比如,我们修改页面就能实时反映到这个页面,当然修改这个文件也会实时在我们的页面显示)初始化所有元素。FrameworkElement类实现了ISupportInitialize的接口,而该接口提供了两个用于控制初始化过程的方法。分别是BeginInit和EndInit方法。当执行完EndInit后,通知元素初始化已经完成,并在该方法内触发Initialized事件,此时虽然元素的初始化完成了,但是数据的绑定,或者样式的应用还没有实现,这些工作就要在这个事件中进行。当这些完成后就要触发Loaded事件。

    这里要注明一点就是Initialized事件必须在InitializeComponent()之前注册,这样才能触发,否则是调用事件默认构造。

    当创建窗口的时候,每个元素分支都是从下向上的方式被初始化。这就意味着位于深层的元素在包容器之前被初始化。当引发初始化事件时,可以确保元素树种当前元素以下的子元素已经全部完成了初始化,但是,包含当前的元素可能还没有初始化,并且不能假定任何其他部分已经初始化。

    在所有的元素都初始化完成后,还需要在他们的包容器中进行布局,应用样式,绑定数据源等等,这时候就是Initialized事件的天下了,当这些都完成之后,就要引发Loaded事件了,Loaded事件和Initialized事件的过程也正好是相反的,也就是说包含所有元素的窗口首先触发Loaded事件,然后才是更深层次的元素发生Loaded事件,窗口可见,元素呈现。

    不知道自己说的够不够清楚,可能其中也有说错的地方,如果有的话希望能够给我留言指正。

  • 相关阅读:
    delphi shr和shl的作用
    delphi socket 编程 使用多线程
    mysql 移除服务,并在cmd下切换目录
    delphi 结构体和TList的用法
    delphi 使用工控机控件 iThreadTimes 出现问题, 导致主程序创建页面的时候, 阻塞消息, 不能正常执行。
    Unicode 和 UTF-8 的区别
    Python 模块 re (Regular Expression)
    Python的函数式编程
    反向解析与PTR(Pointer Record)
    simhash算法
  • 原文地址:https://www.cnblogs.com/lteal/p/2826556.html
Copyright © 2020-2023  润新知