Visual Studio 是微软给开发者提供的IDE开发环境,目前最新版本是VisualStudio2022(以下简称VS2022)。VS2022拥有强大的开发、调试、测试、编译与软件部署功能。例如,使用 IntelliSense 代码建议快速准确地编写代码。 使用启用了机器学习的 IntelliCode 中的建议完成整行代码。 使用提出重命名函数或添加参数等操作建议的灯泡快速改进代码。
使用 Visual Studio 和 .NET,你可以开发适用于桌面、Web、移动设备、游戏和 IoT 的应用程序。你可以使用 C#、F# 或 Visual Basic 语言编写 .NET 应用。
下载VS2022的官方网址:https://visualstudio.microsoft.com/zh-hans/vs/
目前VS2022提供了3个版本,分别是:
第一步,点击上方链接,选择合适的版本,下载VisualStudioSetup.exe并运行。
第二步,接下来,静静地等待Visual Studio Installer 为我们下载安装程序。
第三步,运行安装后,如果只是开发C#+WPF桌面应用程序,只勾选右下角的“.NET桌面开发”选项即可。
第四步,如图所示,勾选.NET桌面开发,点击右下角的安装按钮,全程无须干预,这样VS2022就安装好了。
第五步,待安装完毕,会进到“个性化VisualStudio体验”画面,在"开发设置"中选中"Visual C#",然后选择IDE主题颜色,默认为深色。最后单击“启动Visual Studio”。至此,我们就进入了C#的大门。
——重庆教主 2023年8月9日
按国际惯例,在学习一门新语言时,我们以一个HelloWorld程序来演示如何创建一个WPF应用程序,并就此程序进行代码分析。
第一步,启动VS2022软件。
第二步,在菜单栏中单击文件-新建-项目,或者使用快捷键:Ctrl+Shift+N。
第三步,在右侧列表框中选择WPF应用(.NET Framework)选项,注意图中框红区域的细节:C#、XAML、Windows、桌面。它有4个标签。选择之后点击下一步。
第四步,配置新项目。请在项目名称中输入"HelloWorld“,并在位置中选择一个目录,用于保存项目源代码。这里选择的D:\。然后点击创建。
接下来,请修改下方XAML源码中的第7行,以及在<Grid></Grid>中增加一个<TextBlock>标签,最后的代码如下所示。与此同时,你会观察到界面有所变化。最后请单击上图中框红的”启动“按钮,以Debug的模式调试这个应用程序。
<Window x:Class="HelloWorld.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:HelloWorld"
mc:Ignorable="d"
Title="HelloWorld" Height="350" Width="500">
<Grid>
<TextBlock Text="Hello,World!" FontSize="48"/>
</Grid>
</Window>
这是启动之后的效果,这样,我们的HelloWorld程序就成功创建并运行起来了,这也是我们人生中第一个WPF应用程序。
如何退出调试模式?点击图中框红的停止按钮即可。
最后,我们来熟悉一下当前开发界面中右侧的解决方案资源管理器。它以一棵树的形式呈现在我们面前,位于根部的解决方案显示了当前整个源代码中有多少个项目,此处是1个项目,第二行的”HelloWorld" 表示当前项目的名称。第三行是Properties,它里面有3个子项,用于配置当前程序的一般信息(如版本号、作者)和应用程序设置。第四行是引用,包含了当前程序集所引用的第三方dll库。第五行是App.config,它是一个XML格式的文本文件,作用是保存当前程序的配置信息。第六行是App.xaml,这个非常关键,它就代表当前应用程序本身,一个应用程序可能包含多个窗体或对话框,但是VS2022默认只会为我们创建一个主窗体,也就是第七行的MainWindow.xaml。
下一节,我们将从最重要的App.xaml开始学习。
当前课程源码下载:(注明:本站所有源代码请按标题搜索)
文件名:001-《创建 HelloWorld 程序》-源代码
链接:https://pan.baidu.com/s/1yu-q4tUtl0poLVgmcMfgBA
提取码:wpff
——重庆教主 2023年8月9日
事实上,xaml类型的文件包含两部分,一部分以.xaml扩展名结尾的前端代码,另一部分以.xaml.cs结尾的后端代码,通常我们也把后端代码称为隐藏代码。
我们来看看App.xaml的前端代码和后端代码分别是什么。
前端代码的全称为Extensible Application Markup Language,简称XAML。
//前端代码
<Application x:Class="HelloWorld.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:HelloWorld"
StartupUri="MainWindow.xaml">
<Application.Resources>
</Application.Resources>
</Application>
//后端代码
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
namespace HelloWorld
{
/// <summary>
/// App.xaml 的交互逻辑
/// </summary>
public partial class App : Application
{
}
}
我们先从比较熟悉的后端代码开始分析。WPF的后端代码可以是C#或VB两种语言,本教程一律采用C#语言进行演示。从上面的源代码可以看出,这里定义了一个名叫App的类型,且修饰符为partial关键字,标明App是一个局部类型。最后,App将继承Application父类。
Partial有什么作用?
在C#中,使用 partial 关键字可以将一个类、结构体、接口或者方法分为多个部分进行声明,这些部分可以分布在同一个源文件中,也可以分布在不同的文件中。意思就是说,这里的App类型被分别定义在前端xaml和后端的cs文件中,就好比是一对夫妻,xaml主外,cs主内,两者合二为一,才能形成一个完整的App类型。
那么,前段是如何定义一个类型的呢?
在xaml代码中,我们可以看到有一个<Application>的标签,同时在里面还有一句话:x:Class="HelloWorld.App",它定义一个名叫App的类型,这个类型位于命令空间HelloWorld之中,与后端代码的namespace HelloWorld保持一致。我们可以将x:Class和c#里面的class关键词看成是同一个东西,都表示定义某个类型。
既然前端代码和后端代码都继承了Application父类,那么,这个Application类到底是个什么东西?
namespace System.Windows
{
//
// 摘要:
// 封装 Windows Presentation Foundation (WPF) 应用程序。
public class Application : DispatcherObject, IHaveResources, IQueryAmbient
{
[SecurityCritical]
public Application();
//获取或设置 System.Reflection.Assembly 提供包 统一资源标识符 (URI) 中的资源 WPF 应用程序。
public static Assembly ResourceAssembly { get; set; }
//获取 System.Windows.Application 当前对象 System.AppDomain。
public static Application Current { get; }
//获取应用程序中实例化的窗口。
public WindowCollection Windows { get; }
//获取或设置该应用程序的主窗口。
public Window MainWindow { get; set; }
//获取或设置导致的情况, System.Windows.Application.Shutdown 来调用方法。
public ShutdownMode ShutdownMode { get; set; }
//获取或设置应用程序范围的资源,如样式和画笔的集合。
[Ambient]
public ResourceDictionary Resources { get; set; }
//获取或设置 UI 一个应用程序启动时自动显示。
public Uri StartupUri { get; set; }
//获取应用程序作用域属性的集合。
public IDictionary Properties { get; }
public event EventHandler Deactivated;
public event SessionEndingCancelEventHandler SessionEnding;
public event DispatcherUnhandledExceptionEventHandler DispatcherUnhandledException;
public event NavigatingCancelEventHandler Navigating;
public event NavigatedEventHandler Navigated;
public event NavigationProgressEventHandler NavigationProgress;
public event NavigationFailedEventHandler NavigationFailed;
public event LoadCompletedEventHandler LoadCompleted;
public event EventHandler Activated;
public event NavigationStoppedEventHandler NavigationStopped;
public event FragmentNavigationEventHandler FragmentNavigation;
public static StreamResourceInfo GetContentStream(Uri uriContent);
public static string GetCookie(Uri uri);
public static StreamResourceInfo GetRemoteStream(Uri uriRemote);
public static StreamResourceInfo GetResourceStream(Uri uriResource);
public static object LoadComponent(Uri resourceLocator);
public static void LoadComponent(object component, Uri resourceLocator);
public static void SetCookie(Uri uri, string value);
public object FindResource(object resourceKey);
public int Run(Window window);
public int Run();
public void Shutdown();
public void Shutdown(int exitCode);
public object TryFindResource(object resourceKey);
protected virtual void OnActivated(EventArgs e);
protected virtual void OnDeactivated(EventArgs e);
protected virtual void OnExit(ExitEventArgs e);
protected virtual void OnFragmentNavigation(FragmentNavigationEventArgs e);
protected virtual void OnLoadCompleted(NavigationEventArgs e);
protected virtual void OnNavigated(NavigationEventArgs e);
protected virtual void OnNavigating(NavigatingCancelEventArgs e);
protected virtual void OnNavigationFailed(NavigationFailedEventArgs e);
protected virtual void OnNavigationProgress(NavigationProgressEventArgs e);
protected virtual void OnNavigationStopped(NavigationEventArgs e);
protected virtual void OnSessionEnding(SessionEndingCancelEventArgs e);
protected virtual void OnStartup(StartupEventArgs e);
}
}
Application类继承于DispatcherObject父类,我们曾在《WPF概述》一文中提到,DispatcherObject是WPF的最终抽象基类,下文我们将从这个最终抽象基类说起。在这里我们先讨论一下Application类。
Application类拥有一些属性、事件、和方法,与过去在C#学习中碰到的其它类型从结构上没什么不同。摘要显示,Application类是封装 Windows Presentation Foundation (WPF) 的应用程序。我们开发一款WPF应用程序,本质上是去继承了这个类,并创建了一些窗体和对话框,通过C#语言编写一系列的业务逻辑实现,最终编译成软件交付给用户。
捡几个比较重要的成员了解一下Application类吧!
一个应用程序就好比是一套房子,大多数情况下,这套房子拥有客厅、主卧、客卧、厨房、厕所、阳台等地方,而应用程序会包括多个窗体、对话框等界面,这些窗体和对话框就是这些不同的房间;在众多的窗体中,会有一个主窗体,也就是Application类的MainWindow属性,MainWindow就像是房子的客厅,因为我们推开门进去,通常映入眼帘的就是客厅。但是类似QQ软件呢,打开后并不是立即进入到主窗体,而是登录窗体,只有通过权限验证后才会进入主窗体。
那么,如何在启动程序时决定先显示哪个窗体呢?答案是Application类的StartupUri 属性。StartupUri属性是Uri类型,即统一资源标识符 (URI),它可以指定应用程序第一次启动时显示的用户界面 (UI)。当然,它的功能还远远不止这个,关于URI,我们会专门拿一节内容探讨。
再对比一下QQ和微信两款软件,虽然都有登录窗体和主窗体,都能实现文字和语音信息交换,但是它们的界面截然不同,如何在WPF应用程序中设计出不同风格的界面?
答案是,我们只需要将这些设计要素(资源)放在Application的Resources属性中即可。换句话,我们希望窗体的背景颜色、字体大小、对各种控件所设计的模板样式等内容,全部都只需要放到Resources属性中,这样整个应用都会从Resources中去获取我们提供的资源,并最终呈现出我们想要的样子。
最后我想说的是,有诞生就会有消亡。就像秦始皇的阿房宫,从开始创建、到使用、被销毁,到最后只存在于人们的记忆中,阿房宫走完了它的一生;而一个应用程序,也会有它自己的生命周期,从它在计算机内存中诞生、诞生的过程、用户的使用过程、从内存中消失(退出),同样也会有一个生命过程,那么这个过程是怎么样的呢?
下一节,我们来讨论一下Application的生命周期。
——重庆教主 2023年8月10日
关于软件的生命周期,是指从启动软件到关闭软件的整个过程。
<Application x:Class="HelloWorld.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:HelloWorld"
StartupUri="MainWindow.xaml">
<Application.Resources>
</Application.Resources>
</Application>
我们观察一下HelloWorld应用程序的App.xaml前端代码,StartupUri="MainWindow.xaml"表示本程序第一个启动的窗体是MainWindow,在前端代码中按下F7将进入到App的后端代码界面,在后端代码中,键入下面这些代码。
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
namespace HelloWorld
{
/// <summary>
/// App.xaml 的交互逻辑
/// </summary>
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
Console.WriteLine("1.OnStartup被触发");
}
protected override void OnActivated(EventArgs e)
{
base.OnActivated(e);
Console.WriteLine("2.OnActivated被触发");
}
protected override void OnDeactivated(EventArgs e)
{
base.OnDeactivated(e);
Console.WriteLine("3.OnDeactivated被触发");
}
protected override void OnExit(ExitEventArgs e)
{
base.OnExit(e);
Console.WriteLine("4.OnExit被触发");
}
}
}
然后我们按下F5启动调试流程,最小化窗体,还原窗体,最后关闭窗体。回到开发界面,在菜单栏-视图-输出中找到如下信息(或快捷键Ctro+Alt+Q)
1.OnStartup被触发
“HelloWorld.exe”(CLR v4.0.30319: HelloWorld.exe): 已加载“C:\Windows\Microsoft.Net\assembly\GAC_MSIL\PresentationFramework.Aero2\v4.0_4.0.0.0__31bf3856ad364e35\PresentationFramework.Aero2.dll”。已跳过加载符号。模块进行了优化,并且调试器选项“仅我的代码”已启用。
“HelloWorld.exe”(CLR v4.0.30319: HelloWorld.exe): 已加载“C:\Windows\Microsoft.Net\assembly\GAC_MSIL\PresentationCore.resources\v4.0_4.0.0.0_zh-Hans_31bf3856ad364e35\PresentationCore.resources.dll”。模块已生成,不包含符号。
“HelloWorld.exe”(CLR v4.0.30319: HelloWorld.exe): 已加载“c:\program files\microsoft visual studio\2022\community\common7\ide\commonextensions\microsoft\xamldiagnostics\Framework\x86\Microsoft.VisualStudio.DesignTools.WpfTap.dll”。已跳过加载符号。模块进行了优化,并且调试器选项“仅我的代码”已启用。
“HelloWorld.exe”(CLR v4.0.30319: HelloWorld.exe): 已加载“C:\Windows\Microsoft.Net\assembly\GAC_MSIL\System.Runtime.Serialization\v4.0_4.0.0.0__b77a5c561934e089\System.Runtime.Serialization.dll”。已跳过加载符号。模块进行了优化,并且调试器选项“仅我的代码”已启用。
“HelloWorld.exe”(CLR v4.0.30319: HelloWorld.exe): 已加载“C:\Windows\Microsoft.Net\assembly\GAC_MSIL\SMDiagnostics\v4.0_4.0.0.0__b77a5c561934e089\SMDiagnostics.dll”。已跳过加载符号。模块进行了优化,并且调试器选项“仅我的代码”已启用。
“HelloWorld.exe”(CLR v4.0.30319: HelloWorld.exe): 已加载“C:\Windows\Microsoft.Net\assembly\GAC_MSIL\System.ServiceModel.Internals\v4.0_4.0.0.0__31bf3856ad364e35\System.ServiceModel.Internals.dll”。已跳过加载符号。模块进行了优化,并且调试器选项“仅我的代码”已启用。
2.OnActivated被触发
“HelloWorld.exe”(CLR v4.0.30319: HelloWorld.exe): 已加载“C:\Windows\Microsoft.Net\assembly\GAC_MSIL\System.Runtime.Serialization.resources\v4.0_4.0.0.0_zh-Hans_b77a5c561934e089\System.Runtime.Serialization.resources.dll”。模块已生成,不包含符号。
“HelloWorld.exe”(CLR v4.0.30319: HelloWorld.exe): 已加载“C:\Windows\Microsoft.Net\assembly\GAC_MSIL\UIAutomationTypes\v4.0_4.0.0.0__31bf3856ad364e35\UIAutomationTypes.dll”。已跳过加载符号。模块进行了优化,并且调试器选项“仅我的代码”已启用。
“HelloWorld.exe”(CLR v4.0.30319: HelloWorld.exe): 已加载“C:\Windows\Microsoft.Net\assembly\GAC_MSIL\UIAutomationProvider\v4.0_4.0.0.0__31bf3856ad364e35\UIAutomationProvider.dll”。已跳过加载符号。模块进行了优化,并且调试器选项“仅我的代码”已启用。
3.OnDeactivated被触发
2.OnActivated被触发
3.OnDeactivated被触发
4.OnExit被触发
程序“[10696] HelloWorld.exe”已退出,返回值为 0 (0x0)。
我们可以明确的看到,在输入信息中所打印的信息按如下的顺序显示:
- 1.OnStartup被触发
- 2.OnActivated被触发
- 3.OnDeactivated被触发
- 2.OnActivated被触发
- 3.OnDeactivated被触发
- 4.OnExit被触发
由于我们可以得出结论,当我们启动WPF应用时,首先被执行的是OnStartup方法,其次是OnActivated方法,如果我们把当前应用最小化或切换到其它程序时,这时OnDeactivated会被执行,再切回来时再次执行OnActivated方法,最后,当我们关闭程序时,OnDeactivated会再次被执行,最后执行的是OnExit方法。
Application的生命周期
OnStartup->OnActivated->OnDeactivated->OnExit
- OnStartup:表示启动应用程序时
- OnActivated:表示激活应用程序时
- OnDeactivated:表示由激活状态变为非激活状态时
- OnExit:表示退出应用程序时
这只是Application的生命周期,事实上,由于Application总是会启动一个界面(窗体),而窗体也会有自己的生命周期。下一节,我们将讨论一下窗体(Window)的生命周期。
当前课程源码下载:(注明:本站所有源代码请按标题搜索)
文件名:002-《Application的生命周期》-源代码
链接:https://pan.baidu.com/s/1yu-q4tUtl0poLVgmcMfgBA
提取码:wpff
——重庆教主 2023年8月10日
Window窗体,其实也是一个控件,一个Application应用实例可能会有多个窗体,这些窗体随着用户的操作被创建于内存,最后被销毁于内存。大多数情况下,销毁的请求虽然由用户发起,但最终回收内存则是由GC垃圾回收器在干活儿。
我们以HelloWorld应用为例,打开MainWindowp窗体的源代码,切换至后端代码,可以发现MainWindow继承于Window类。
namespace HelloWorld
{
/// <summary>
/// MainWindow.xaml 的交互逻辑
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
}
}
将鼠标的光标放至“Window”字符串上面,按下F12 ,就可以导航到Window类的定义页面,我们会发现Window类又继承于ContentControl类,下面是MainWindow主窗体的整个继承路线。
MainWindow->Window->ContentControl->Control->FrameworkElement->UIElement->Visual->DependencyObject->DispatcherObject
在这里,我们并不打算将MainWindow的所有知识详尽,因为这必须要将它一路继承下来的所有父亲都要交代清楚,我们只关注MainWinow的生命周期。所以我们在MainWinow的构造函数中写下如下代码:
namespace HelloWorld
{
/// <summary>
/// MainWindow.xaml 的交互逻辑
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.SourceInitialized += (s, e) => Console.WriteLine("1.MainWindow的SourceInitialized被执行");
this.Activated += (s, e) => Console.WriteLine("2.MainWindow的Activated被执行");
this.Loaded += (s, e) => Console.WriteLine("3.MainWindow的Loaded被执行");
this.ContentRendered += (s, e) => Console.WriteLine("4.MainWindow的ContentRendered被执行");
this.Deactivated += (s, e) => Console.WriteLine("5.MainWindow的Deactivated被执行");
this.Closing += (s, e) => Console.WriteLine("6.MainWindow的Closing被执行");
this.Closed += (s, e) => Console.WriteLine("7.MainWindow的Closed被执行");
this.Unloaded += (s, e) => Console.WriteLine("8.MainWindow的Unloaded被执行");
}
}
}
然后我们直接F5调试,待主窗体显示后,直接关闭主窗体,观察输出(Ctrl+Alt+Q)结果。Application的生命周期和主窗体的生命周期是充满交织的,首先是Application的OnStartup,然后是主窗体的SourceInitialized,然后依次执行了Application的OnActivated和MainWindow的Activated,最后直到主窗体Closed,才轮到Application的OnExit。
1.OnStartup被触发
“HelloWorld.exe”(CLR v4.0.30319: HelloWorld.exe): 已加载“C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\PresentationFramework.Aero2\v4.0_4.0.0.0__31bf3856ad364e35\PresentationFramework.Aero2.dll”。已跳过加载符号。模块进行了优化,并且调试器选项“仅我的代码”已启用。
“HelloWorld.exe”(CLR v4.0.30319: HelloWorld.exe): 已加载“C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\PresentationCore.resources\v4.0_4.0.0.0_zh-Hans_31bf3856ad364e35\PresentationCore.resources.dll”。模块已生成,不包含符号。
1.MainWindow的SourceInitialized被执行
“HelloWorld.exe”(CLR v4.0.30319: HelloWorld.exe): 已加载“c:\program files\microsoft visual studio\2022\community\common7\ide\commonextensions\microsoft\xamldiagnostics\Framework\x86\Microsoft.VisualStudio.DesignTools.WpfTap.dll”。已跳过加载符号。模块进行了优化,并且调试器选项“仅我的代码”已启用。
“HelloWorld.exe”(CLR v4.0.30319: HelloWorld.exe): 已加载“C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Runtime.Serialization\v4.0_4.0.0.0__b77a5c561934e089\System.Runtime.Serialization.dll”。已跳过加载符号。模块进行了优化,并且调试器选项“仅我的代码”已启用。
“HelloWorld.exe”(CLR v4.0.30319: HelloWorld.exe): 已加载“C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\SMDiagnostics\v4.0_4.0.0.0__b77a5c561934e089\SMDiagnostics.dll”。已跳过加载符号。模块进行了优化,并且调试器选项“仅我的代码”已启用。
“HelloWorld.exe”(CLR v4.0.30319: HelloWorld.exe): 已加载“C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.ServiceModel.Internals\v4.0_4.0.0.0__31bf3856ad364e35\System.ServiceModel.Internals.dll”。已跳过加载符号。模块进行了优化,并且调试器选项“仅我的代码”已启用。
2.OnActivated被触发
2.MainWindow的Activated被执行
“HelloWorld.exe”(CLR v4.0.30319: HelloWorld.exe): 已加载“C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Runtime.Serialization.resources\v4.0_4.0.0.0_zh-Hans_b77a5c561934e089\System.Runtime.Serialization.resources.dll”。模块已生成,不包含符号。
“HelloWorld.exe”(CLR v4.0.30319: HelloWorld.exe): 已加载“C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\UIAutomationTypes\v4.0_4.0.0.0__31bf3856ad364e35\UIAutomationTypes.dll”。已跳过加载符号。模块进行了优化,并且调试器选项“仅我的代码”已启用。
“HelloWorld.exe”(CLR v4.0.30319: HelloWorld.exe): 已加载“C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\UIAutomationProvider\v4.0_4.0.0.0__31bf3856ad364e35\UIAutomationProvider.dll”。已跳过加载符号。模块进行了优化,并且调试器选项“仅我的代码”已启用。
3.MainWindow的Loaded被执行
4.MainWindow的ContentRendered被执行
5.MainWindow的Closing被执行
6.MainWindow的Deactivated被执行
3.OnDeactivated被触发
7.MainWindow的Closed被执行
4.OnExit被触发
程序“[4808] HelloWorld.exe”已退出,返回值为 0 (0x0)。
我们来单独看看主窗体的生命周期,在上述输出结果中寻找带“MainWindow”的字符串,可以发现如下的输出结果。
- 1.MainWindow的SourceInitialized被执行
- 2.MainWindow的Activated被执行
- 3.MainWindow的Loaded被执行
- 4.MainWindow的ContentRendered被执行
- 5.MainWindow的Closing被执行
- 6.MainWindow的Deactivated被执行
- 7.MainWindow的Closed被执行
观察这些输出结果,与我们订阅事件的代码顺序一致,唯独少了Unloaded的结果输出。因为Unloaded事件没有被触发。下面我们将分析一下这些事件分别代表什么含义。
SourceInitialized | 创建窗体源时引发此事件 |
Activated | 当前窗体成为前台窗体时引发此事件 |
Loaded | 当前窗体内部所有元素完成布局和呈现时引发此事件 |
ContentRendered | 当前窗体的内容呈现之后引发此事件 |
Closing | 当前窗体关闭之前引发此事件 |
Deactivated | 当前窗体成为后台窗体时引发此事件 |
Closed | 当前窗体关闭之后引发此事件 |
Unloaded | 当前窗体从元素树中删除时引发此事件 |
由此我们可以得出结论,Window窗体的生命周期应如下图所示:
在了解窗体的生命周期之后,我们就可以在它不同的生命周期处理一些不同的业务。例如在Application或Window的创建时加载一些本地设置,在窗体关闭或应用程序退出时保存一些本地设置。在了解了Window窗体的生命周期之后,我们再来学习Window窗体本身由哪些构成。
下一节,我们将讨论Window窗体的组成。
当前课程源码下载:(注明:本站所有源代码请按标题搜索)
文件名:003-《Window的生命周期》-源代码
链接:https://pan.baidu.com/s/1yu-q4tUtl0poLVgmcMfgBA
提取码:wpff
——重庆教主 2023年8月11日
Window窗体本质上也是一个控件,只不过它和一般控件有所区别。比如它具有Closing和Closed事件,而一般控件是不可以关闭的;另外,Window窗体可以容纳其它控件,最后,窗体由两部分构成,即工作区和非工作区。
非工作区
非工作区主要包含以下几个要素,它们分别是:图标、标题、窗体菜单、最小化按钮、最大化按钮、关闭按钮、窗体边框、右下角鼠标拖动调整窗体尺寸。
工作区
如图所示,我们在窗体中最中心放置了一个TextBlock文字块控件,说明在这个区域内可以容纳和呈现一般控件。具体情况我们先看一下本例中的前端代码。
<Window x:Class="HelloWorld.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:HelloWorld"
mc:Ignorable="d"
Title="HelloWorld" Height="350" Width="500">
<Grid>
<TextBlock Text="WPF中文网"
Margin="0 50 0 0"
FontSize="48"
HorizontalAlignment="Center"
VerticalAlignment="Center"/>
</Grid>
</Window>
关于Window窗体的的工作区,本质上是指Window类的Content属性。Content属性表示窗体的内容,类型为object,即可以是任意的引用类型 。需要注意的是,Content属性并不在Window类中,而在它的父类ContentControl类中。
技术细节
默认的<Window></Window>之中只能存在一个控件,就是因为Content是object类型,意思是只接受一个对象。那如何向窗体中增加多个控件呢?微软给出了示例,就是先放一个Grid 布局控件,因为Grid控件是一个集合控件,我们可以将多个控件放在Grid控件中,关于这些知识的扩展,我们将在WPF的《可视化树》章节中详细的探讨。
接下来,我们就要学习WPF的控件,并掌握各种控件的用法。但是这些控件都有各自继承的父类,所以在学习控件之前,我们要先学习这些控件父类,它们有的是抽象父类,有的是非抽象父类,不同的父类承担着不同的功能。
下一节,WPF控件的父类们
当前课程源码下载:(注明:本站所有源代码请按标题搜索)
文件名:004-《Window窗体的组成》-源代码
链接:https://pan.baidu.com/s/1yu-q4tUtl0poLVgmcMfgBA
提取码:wpff
——重庆教主 2023年8月11日