1.1 从头认识WPF_1.1 WPF from Scratch

本文介绍如何使用C#和XAML创建基本的WPF应用程序,包括构建过程、使用Application类管理和控制程序生命周期,以及如何分离视图和行为。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.1. WPF from Scratch

Example 1-1 is pretty much the smallest WPF application you can write in C#.

--------------------------------------------------------------

示例1.1几乎是使用C#所能够编写的最小的WPF应用程序了.

 

Example 1-1. Minimal C# WPF application// MyApp.cs using System; using System.Windows; // the root WPF namespace namespace MyFirstAvalonApp { class MyApp { [STAThread] static void Main( ) { // the WPF message box MessageBox.Show(/"Hello, Avalon/"); } } }

 

If you/'re not familiar with the STAThread attribute, it/'s a signal to .NET that when COM is initialized on the application/'s main thread, to make sure it/'s initialized to be compatible with single-threaded UI work , as required by WPF applications.

----------------------------------------------------------------

STAThread特性是当应用程序主线程完成COM组件的初始化时发送给.Net的信号,它用于确保COM组件已经完成初始化,能够胜任单线程用户界面的工作.该特性是WPF应用程序必须的.

 

1.1.1. Building Applications

Building this application is a matter of firing off the C# compiler from a command shell with the appropriate environment variables,[*] as in Example 1-2.

[*] Start --> Programs --> Microsoft WinFX SDK Debug Build Environment or Release Build Environment.

--------------------------------------------------------------

1.1.1.构建应用程序

可以通过在命令行模式中配置使用适当的环境变量并为C#编译器提供足够的信息来构建这个应用程序.

 

Example 1-2. Building a WPF application manuallyC://1st>csc /target:winexe /out:.//1st.exe /r:System.dll /r:c://WINDOWS//Microsoft.NET//Windows//v6.0.4030//WindowsBase .dll /r:c://WINDOWS//Microsoft.NET//Windows//v6.0.4030//PresentationCore .dll /r:c://WINDOWS//Microsoft.NET//Windows//v6.0.4030//PresentationFramework .dll MyApp.cs Microsoft (R) Visual C# 2005 Compiler version 8.00.50215.44 for Microsoft (R) Windows (R) 2005 Framework version 2.0.50215 Copyright (C) Microsoft Corporation 2001-2005. All rights reserved.

Here, we/'re telling the C# compiler that we/'d like to create a Windows application (instead of a Console application, which we get by default), putting the result, 1st.exe, into the current folder, bringing in the three main WPF assemblies (WindowsBase, PresentationCore and PresentationFramework), along with the core .NET System assembly, and compiling the MyApp.cs source file.

Running the resulting 1st.exe produces the world/'s lamest WPF application, as shown in Figure 1-1.

----------------------------------------------------------------

这里,我们介绍了使用C#编译器创建一个Windows应用程序(而不是默认的命令行应用程序),将编译的结果——1st.exe放入到当前目录下,在核心的.Net系统程序集之外加入三个主要的WPF程序集(WindowsBase, PresentationCore以及PresentationFramework),并编译MyApp.cs代码文件.

通过运行该程序,我们将看到这个最为简陋的WPF应用程序,如Figure 1-1所示.

 

Figure 1-1. A lame WPF application

 

In anticipation of less lame WPF applications, refactoring the compilation command line into an msbuild project file is recommended, as in Example 1-3.

-------------------------------------------------

为了减少不可靠的WPF应用程序,可以将编译命令行分解到一个msbuild工程文件中,如例1-3所示.

 

Example 1-3. A minimal msbuild project file<!-- 1st.csproj --> <Project DefaultTargets=/"Build/" xmlns=/"http://schemas.microsoft.com/developer/msbuild /2003/"> <PropertyGroup> <OutputType>winexe</OutputType> <OutputPath>.//</OutputPath> <Assembly>1st.exe</Assembly> </PropertyGroup> <ItemGroup> <Compile Include=/"MyApp.cs/" /> <Reference Include=/"System/" /> <Reference Include=/"WindowsBase/" /> <Reference Include=/"PresentationCore/" /> <Reference Include=/"PresentationFramework/" /> </ItemGroup> <Import Project=/"$(MsbuildBinPath)//Microsoft.CSharp.targets/" /> </Project>

Msbuild is a .NET 2.0 command-line tool that understands XML files in the form shown in Example 1-3. The file format is shared between msbuild and Visual Studio 2005 so that you can use the same project files for both command-line and IDE builds. In this .csproj file (which stands for /"C# Project/"), we/'re saying the same things that we said to the C# compileri.e., that we/'d like a Windows application, that we/'d like the output to be 1st.exe in the current folder, and that we/'d like to reference the main WPF assemblies while compiling the MyApp.cs file. The actual smarts of how to turn these minimal settings into a compiled WPF application are contained in the .NET 2.0 that imported at the bottom of the file.

 

Executing msbuild.exe on the 1st.csproj file looks like Example 1-4.

------------------------------------------------------------------------

msbuild是.Net 2.0提供的命令行工具.它可以理解例1-3中所示各式的XML文件.这种文件格式是由msbuild和VisualStudio 2005所共享的,因此在使用命令行或IDE构建应用程序的过程中,您可以使用相同的工程文件.通过使用这个.csproj文件(代表了/"C# 工程/"),可以认为我们向C#编译器中输入了相同的东西.比如说,我们需要一个Windows应用程序,要将输出定名为1st.exe并放入当前目录下,而且在编译MyApp.cs文件的过程当中要引用主WPF程序集.事实上将最少的设置传入到编译好的WPF应用程序的方法已经被包含在上述文件末尾应用的.Net 2.0  Microsoft.CSharp.targets file 文件中.

可以向例1-4中那样对1st.csproj文件使用msbuild.exe工具.

 

Example 1-4. Building using msbuildC://1st>msbuild 1st.csproj Microsoft (R) Build Engine Version 2.0.50215.44 [Microsoft .NET Framework, Version 2.0.50215.44] Copyright (C) Microsoft Corporation 2005. All rights reserved. Build started 7/6/2005 8:20:39 PM. ________________________________________________ _ _ Project /"C://1st//1st.csproj/" (default targets): Target PrepareForBuild: Creating directory /"obj//Release///". Target CompileRdlFiles: Skipping target /"CompileRdlFiles/" because it has no inputs. Target CoreCompile: Csc.exe /noconfig /nowarn:/"1701;1702/" /reference:C://WINDOWS//Microsoft.net//Wi ndows//v6.0.4030//PresentationCore.dll /reference:C://WINDOWS//Microsoft.net//Windows //v6.0.4030//PresentationFramework.dll /reference:C://WINDOWS//Microsoft.NET//Framewo rk//v2.0.50215//System.dll /reference:C://WINDOWS//Microsoft.net//Windows//v6.0.4030//W indowsBase.dll /out:obj//Release//1st.exe /target:winexe MyApp.cs Target CopyAppConfigFile: Skipping target /"CopyAppConfigFile/" because it has no outputs. Target CopyFilesToOutputDirectory: Copying file from /"obj//Release//1st.exe/" to /".//1st.exe/". 1st -> C://1st//1st.exe Build succeeded. 0 Warning(s) 0 Error(s) Time Elapsed 00:00:00.98

As I mentioned, msbuild and Visual Studio 2005 share a project file format, so loading the project file into VS is as easy as double-clicking on 1st.csproj, which provides us all of the rights and privileges thereof (as shown in Figure 1-2).

Figure 1-2. Loading the minimal msbuild project file into Visual Studio

Unfortunately, as nice as the project file makes building our WPF application, the application itself is still lame.

----------------------------------------------------------

正如前文所述,msbuild和Visual Studio 2005使用相同的工程文件格式,因此要在VS中加载该工程文件只需要双击1st.csproj即可,并且还提供了和msbuild一样的权限和待遇.

不幸的是,就算我们为了生成我们的WPF应用程序而使用了工程文件,这个应用程序本身还是太蹩脚了.

 

1.1.2. The Application Object

A real WPF application is going to need more than a message box. WPF applications have an instance of the Application class from the System.Windows namespace. The Application class provides events like StartingUp and ShuttingDown for tracking lifetime; methods like Run for starting the application; and properties like Current, ShutdownMode, and MainWindow for finding the global application object, choosing when it shuts down, and getting the application/'s main window. Typically, the Application class serves as a base for custom application-wide behavior, as in Example 1-5.

-----------------------------------------------------------

1.1.2 Application对象

一个真正的WPF应用程序应当拥有一个或更多消息框.WPF应用程序拥有一个来自System.Windows命名空间的Application类的实例.这个Application类在其生命周期内提供了诸如StartingUp和ShuttingDown之类的事件;用于启动应用程序的Run方法;以及用于查找全球化应用程序对象的Current属性,用于在应用程序被关闭时选择的ShutdownMode属性,以及用于获得应用程序主窗口的MainWindow属性.一般来说,Application类是应用程序行为的基础,如例子1-5所示.

 

Example 1-5. A less minimal WPF application// MyApp.cs using System; using System.Windows; namespace MyFirstAvalonApp { class MyApp : Application { [STAThread] static void Main(string[] args) { MyApp app = new MyApp( ); app.StartingUp += app.AppStartingUp; app.Run(args); } void AppStartingUp(object sender, StartingUpCancelEventArgs e) { // By default, when all top level windows // are closed, the app shuts down Window window = new Window( ); window.Text = /"Hello, Avalon/"; window.Show( ); } } }-------------Example 1-5的注---------------------------在.Net Framework 3.5 SP1中,这个例子需要修改才能运行,这可能是因为作者使用的WPF版本太旧.如果需要在以上版本Framework下运行这个例子,可以使用下面的代码:

using System;
using System.Windows;

namespace MyFirstAvalonApp
{
    class MyApp : Application
    {
        [STAThread]
        static void Main(string[] args)
        {
            MyApp app = new MyApp();
            app.Startup += app.AppStartingUp;
            app.Run();
        }

        void AppStartingUp(object sender, StartupEventArgs e)
        {
            // By default, when all top level windows
            // are closed, the app shuts down
            Window window = new Window();
            window.Title = /"Hello, Avalon/";
            window.Show();
        }
    }
}

----------------------------------------------------- 

Here, our MyApp class derives from the Application base class. In Main, we create an instance of the MyApp class, add a handler to the StartingUp event, and kick things off with a call to the Run method, passing the command-line arguments passed to Main. Those same command-line arguments are available in the StartingUpCancelEventArgs passed to the StartingUp event handler. (The StartingUp event handler will show its value as we move responsibility for the application/'s   entry point to WPF later in this chapter.)

Our StartingUp handler creates our sample/'s top-level window, which is an instance of the built-in WPF Window class, making our sample WPF application more interesting from a developer point of view, although visually less so, as shown in Figure 1-3.

Figure 1-3. A less lame WPF application

While we can create instances of the built-in classes of WPF like Window, populating them and wiring them up from the application, it/'s much more encapsulating (not to mention abstracting) to create custom classes for such things, like the Window1 class in Example 1-6.

-----------------------------------------------------------------

在这里,我们的MyApp类派生自Application基类.在Main方法中,我们创建了一个MyApp的实例,向StartingUp(Startup)中添加了新的方法,并且通过对Run方法的调用中止了原进程的运行,并将命令行参数传递到Main方法.相同的命令行参数可以通过传递给StartingUp事件句柄的StartingUpCancelEventArgs获得(注,在我们的版本中就是传递给Startup事件句柄的StartupEventArgs.Run方法现在已经无法接受string[]类型参数.).(StartingUp事件句柄将会在通过WPF入口点调用应用程序时向应用程序呈现这些值.)

我们的StartingUp句柄创建了这个例子中的顶级窗口,该窗口是一个内建于WPF中的Windows类的实例.从开发者的角度来说,这些工作使WPF做了一些有趣的事情——虽然以平常的眼光来看并非如此.如图表1-3所示.

一旦我们能够创建包括Window在内的WPF内建类的实例并在应用程序中使用它们,则将会封装(更不用说抽象了)多得多的属性用以创建这些东西的类,比如Example 1-6中的Window1类.

 

Example 1-6. Window class declaring its own controls // Window1.cs using System; using System.Windows; using System.Windows.Controls; // Button et al namespace MyFirstAvalonApp { class Window1 : Window { public Window1( ) { this.Text = /"Hello, Avalon/"; //别忘了把Text改成Title~ // Do something interesting (sorta...) Button button = new Button( ); button.Content = /"Click me, baby, one more time!/"; button.Width = 200; button.Height = 25; button.Click += button_Click; this.AddChild(button); } void button_Click(object sender, RoutedEventArgs e) { MessageBox.Show( /"You/'ve done that before, haven/'t you.../", /"Nice!/"); } } }

In addition to setting its caption text, an instance of our Window1 class will include a button with its Content, Width, and Height properties set and its Click event handled. With this initialization handled in the Window1 class itself, our app/'s startup code looks a bit simpler (even though the application itself has gotten /"richer/"), as in Example 1-7.

---------------------------------------------------------------

除了设置标题文本之外,一个Window1类的实例还会包含一个拥有自己的Context,Width,Height属性和Click事件的按钮.初始化工作都将由Window1类自己完成,因此我们的程序的代码看上去有点简单(虽然应用程序自身是变得富态多了),如Example 1-7所示.

 

Example 1-7. Simplified Application instance// MyApp.cs using System; using System.Windows; namespace MyFirstAvalonApp { class MyApp : Application { [STAThread] static void Main(string[] args) { MyApp app = new MyApp( ); app.StartingUp += app.AppStartingUp; app.Run(args); } void AppStartingUp(object sender, StartingUpCancelEventArgs e) { // Let the Window1 initialize itself Window window = new Window1( ); window.Show( ); } } }//如果你用的是和我的一样版本的框架,自己参照上面的例子修改代码吧

The results, shown in Figure 1-4, are unlikely to surprise you much.

Figure 1-4. A slightly more interesting WPF application

As the Window1 class gets more interesting, we/'re mixing two very separate kinds of code: the /"look,/" represented by the initialization code that sets the window and child window properties, and the /"behavior,/" represented by the event-handling code. As the look is something that you/'re likely to want handled by someone with artistic sensibilities (a.k.a. /"turtleneck-wearing designer types/"), whereas the behavior is something you/'ll want to leave to the coders (a.k.a. /"pocket-protector-wearing engineer types/"), separating the former from the latter would be a good idea. Ideally, we/'d like to move the imperative /"look/" code into a declarative format suitable for tools to create with some drag /'n/' drop magic. For WPF, that format is XAML.

-------------------------------------------------------------

结果如Figure 1-4所示,好像并不太出乎我们的意料.

在Window1类变得更有趣的同时,我们混合了两种相当不同类型的代码:/"外观/"由针对窗体和子窗体属性的初始化代码所描述,/"行为/"则由事件响应代码描述.你大概会希望由那些充满艺术细胞的人(就是那些穿着高领绒衣的设计师那样的人)来掌控,而行为代码则留给程序员,这当然是个好主意.最好我们能够拥有一种具有公开的格式且适宜使用工具使用拖拽的方法生成外观代码的语言.对于WPF来说,这种语言就是XAML.

 

1.1.3. XAML

XAML is an XML-based language for creating and initializing .NET objects. It/'s used in WPF as a serialization format for objects from the WPF presentation stack, although it can be used for a much larger range of objects than that. Example 1-8 shows how our Window-derived class is declared using XAML.

---------------------------------------------------------------

1.1.3.XAML

XAML是一种为创建和初始化.Net对象而生的基于XML的语言.尽管它的功能还可以强大得多,XAML在WPF中被用作外观对象序列化的格式.Example 1-8展示了如何使用XAML声明派生自Window类的类型.

 

Example 1-8. Declaring a Window in XAML<!-- Window1.xaml --> <Window x:Class=/"MyFirstAvalonApp.Window1/" xmlns=/"http://schemas.microsoft.com/winfx/avalon/2005/" xmlns:x=/"http://schemas.microsoft.com/winfx/xaml/2005/" Text=/"Hello, Avalon/"> <Button x:Name=/"button/" Width=/"200/" Height=/"25/" Click=/"button_Click/">Click me, baby, one more time!</Button> </Window>

The root element, Window, is used to declare a portion of a class, the name of which is contained in the Class attribute from the XAML XML namespace (declared with a prefix of /"x/" using the /"xmlns/" XML namespace syntax). The two XML namespace declarations pull in two commonly used namespaces for XAML work, the one for XAML itself and the one for WPF. You can think of the XAML in Example 1-8 as creating the partial class definition[*] in Example 1-9.

[*] Partial classes are a new feature in C# 2.0 that allow you to split class definitions between multiple files.

--------------------------------------------------------------

根元素Window被用于声明那个名字被包含在XAML命名空间的Class属性的类的一部分.这两个声明被用于声明两个处理XAML工作的通用XML命名空间,一个是XAML自己使用的,而另一个则提供给WPF使用.Example 1-8中的XAML代码正式为了创建Example 1-9中的分部类声明.

[*]分部类是C#2.0引入的新特性.它允许你将一个类的代码分部写到多个不同的文件中.

 

Example 1-9. C# equivalent of XAML from Example 1-8 namespace MyFirstAvalonApp { partial class Window1 : Window { Button button;   void InitializeComponent( ) { // Initialize Window1 this.Text = /"Hello, Avalon/"; //别忘了已经是Title了哦 // Initialize button button = new Button( ); button.Width = 200; button.Height = 25; button.Click += button_Click; this.AddChild(button); } } }

XAML was built to be as direct a mapping from XML to .NET as possible. Generally, every XAML element is a .NET class name and every XAML attribute is the name of a property or an event on that class. This makes XAML useful for more than just WPF classes; pretty much any old .NET class that exposes a default constructor can be initialized in a XAML file.

Notice that we don/'t have the definition of the click event handler in this generated class. For event handlers and other initialization and helpers, a XAML file is meant to be matched with a corresponding code-behind file , which is a .NET language code file that implements behavior /"behind/" the look defined in the XAML. Traditionally, this file is named with a .xaml.cs extension and contains only the things not defined in the XAML. With the XAML from Example 1-9 in place, our single-buttoned main window code-behind file can be reduced to the code in Example 1-10.

---------------------------------------------------------------

XAML被设计成为一个尽可能直接的由XML到.Net的映射.大体上说,所有XAML元素都是一个.Net类的名称,每一个XAML属性都是类的属性或事件.这使得XAML在WPF之外有了更多的用处.几乎所有的拥有公共构造函数的现有.Net类都可以被初始化为一个XAML文件.

请注意,在这个类中我们没有对于单击事件句柄的描述.对于事件句柄和其他初值,XAML文件需要匹配一个对应的代码隐藏文件,也就是一个包含了XAML文件所描述的外观背后的行为的.Net语言代码文件.根据习惯,这个文件一般会被命名为一个.xaml.cs文件,并且只扩展和包含没有在XAML文件中定义的部分.我们可以使用XAML重新组织Example 1-9中,使得我们那个只有一个按钮的主窗口的代码隐藏文件像Example 1-10一样.

 

Example 1-10. C# code-behind file// Window1 .xaml.cs using System; using System.Windows; using System.Windows.Controls; namespace MyFirstAvalonApp { public partial class Window1 : Window { public Window1( ) { InitializeComponent( ); } void button_Click(object sender, RoutedEventArgs e) { MessageBox.Show(...); } } }

Notice the partial keyword modifying the Window1 class, which signals to the compiler that the XAML-generated class is to be paired with this human-generated class to form one complete class, each depending on the other. The partial Window1 class defined in XAML depends on the code-behind partial class to call the InitializeComponent method and to handle the click event. The code-behind class depends on the partial Window1 class defined in XAML to implement InitializeComponent, thereby providing the look of the main window (and related child controls).

Further, as I mentioned, XAML is not just for visuals. For example, there/'s nothing stopping us from moving most of the definition of our custom MyApp class into a XAML file, as in Example 1-11.

------------------------------------------------------------------

注意用于修饰Window1类的partial关键字.这个关键字通知编译器一个由XAML描述的类需要和这个代码描述的类相互匹配,互相依赖来形成一个完整的类.在XAML文件中定义的Window1分部类依靠后台代码的分部类调用InitializeComponent方法和响应单击事件.后台代码中的类则依赖XAML文件中定义的类贯彻InitializeComponent方法的内容,由此产生出主窗口的外观(并绘制子控件).

况且,正如我所提到的,XAML的能力并不仅仅局限于视觉效果上.举例来说,我们完全可以在XAML文件中完成我们的MyApp类的大部分声明工作.

 

Example 1-11. Declaring an Application in XAML<!-- MyApp.xaml --> <Application x:Class=/"MyFirstAvalonApp.MyApp/" xmlns=/"http://schemas.microsoft.com/winfx/avalon/2005/" xmlns:x=/"http://schemas.microsoft.com/winfx/xaml/2005/" StartingUp=/"AppStartingUp/"> </Application>

This reduces the MyApp code-behind file to the event handler in Example 1-12.

Example 1-12. Application code-behind file// MyApp.xaml.cs using System; using System.Windows; namespace MyFirstAvalonApp { public partial class MyApp : Application { void AppStartingUp(object sender, StartingUpCancelEventArgs e) { Window window = new Window1( ); window.Show( ); } } }

You may have noticed that we no longer have a Main enTRy point to create the instance of the application-derived class and call its Run method. That/'s because WPF has a special project setting to specify the XAML file that defines the application class, which appears in the msbuild project file, as in Example 1-13.

------------------------------------------------------------------

你也许注意到了,我们没有使用Main入口点来创建继承自application的类的实例并调用它的run方法.这是因为WPF有着特殊的工程设定来指定定义application类的XAML文件,而这些设置都在msbuild工程文件里面,如Example 1-13所示.

 

Example 1-13. Specifying the application/'s XAML in the project file<!-- MyFirstAvalonApp.csproj --> <Project ...> <PropertyGroup> <OutputType>winexe</OutputType> <OutputPath>.//</OutputPath> <Assembly>1st.exe</Assembly> </PropertyGroup> <ItemGroup> <ApplicationDefinition Include=/"MyApp.xaml/" /> <Compile Include=/"Window1.xaml.cs/" /> <Compile Include=/"MyApp.xaml.cs/" /> <Reference Include=/"System/" /> <Reference Include=/"WindowsBase/" /> <Reference Include=/"PresentationCore/" /> <Reference Include=/"PresentationFramework/" /> <Page Include=/"Window1.xaml/" /> <Page Include=/"MyApp.xaml/" /> </ItemGroup> <Import Project=/"$(MsbuildBinPath)//Microsoft.CSharp.targets/" /> <Import Project=/"$(MSBuildBinPath)//Microsoft.WinFX.targets/" /> </Project>

The combination of the ApplicationDefinition element and the WinFX-specific Microsoft.WinFX.targets file produces an application entry point that will create our application for us. Also notice in Example 1-13 that we/'ve replaced the MyApp.cs file with the MyApp.xaml.cs file, added the Window1.xaml.c file, and included the two corresponding XAML files as Page elements. The XAML files will be compiled into partial class definitions using the instructions in the Microsoft.WinFX.targets file.

------------------------------------------------------------------------

对ApplicationDefinition元素和专用于WinFX的Microsoft.WinFX.targets文件的包含为我们运行应用程序提供了应用程序入口点.注意一下Example 1-13,我们用MyApp.xaml.cs文件替换了MyApp.cs文件,添加了Window1.xaml.c文件并以Page元素的形式包含了两个对应的XAML文件.这个XAML文件将会通过Microsoft.WinFX.target文件中的描述被编译到分部类定义当中.

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值