Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Pro CSharp 2008 And The .NET 3.5 Platform [eng]

.pdf
Скачиваний:
78
Добавлен:
16.08.2013
Размер:
22.5 Mб
Скачать

1002 CHAPTER 28 INTRODUCING WINDOWS PRESENTATION FOUNDATION AND XAML

A number of layout managers (far more than Windows Forms) to provide extremely flexible control over placement and reposition of content

Use of an enhanced data-binding engine to bind content to UI elements in a variety of ways

A built-in style engine, which allows you to define “themes” for a WPF application

Use of vector graphics, which allows content to be automatically resized to fit the size and resolution of the screen hosting the application

Support for 2D and 3D graphics, animations, and video and audio playback

A rich typography API, such as support for XML Paper Specification (XPS) documents, fixed documents (WYSIWYG), flow documents, and document annotations (e.g., a Sticky Notes API)

Support for interoperating with legacy GUI models (e.g., Windows Forms, ActiveX, and Win32 HWNDs)

The Various Flavors of WPF Applications

The WPF API can be used to build a variety of GUI-centric applications, which basically differ in their navigational structure and deployment models. The sections that follow present a high-level walk through each option.

Traditional Desktop Applications

The first (and most familiar) option is a traditional executable assembly that runs on a local machine. For example, you could use WPF to build a text editor, painting program, or multimedia program such as a digital music player, photo viewer, and so forth. Like any other desktop applications, these *.exe files can be installed using traditional means (setup programs, Windows Installer packages, etc.) or via ClickOnce technology to allow desktop applications to be distributed and installed via a remote web server.

In this light, WPF is simply a new API to build traditional desktop applications. Programmatically speaking, this type of WPF application will make use (at a minimum) of the Window and Application types, in addition to the expected set of dialog boxes, toolbars, status bars, menu systems, and other UI elements.

Navigation-Based WPF Applications

WPF applications can optionally choose to make use of a navigation-based structure, which makes a traditional desktop application take on the basic behavior of a web browser application. Using this model, you can build a desktop *.exe that provides a “forward” and “back” button that allows the end user to move back and forth between various UI displays called pages. The application itself maintains a list of each page and provides the necessary infrastructure to navigate between them, pass data across pages (similar to a web-based application variable), and maintain a history list. By way of a concrete example, consider Vista’s Windows Explorer (see Figure 28-1), which makes use of such functionality. Notice the navigational buttons (and history list) mounted on the upper-left corner of the window.

Regardless of the fact that a WPF desktop application can take on a weblike navigational scheme, understand that this is simply a UI design issue. The application itself is still little more than a local executable assembly running on a desktop machine, and it has nothing to do with a web application beyond a slightly similar look and feel. Programmatically speaking, this navigational structure is represented using types such as Page, NavigationWindow, and Frame.

CHAPTER 28 INTRODUCING WINDOWS PRESENTATION FOUNDATION AND XAML

1003

Figure 28-1. A navigation-based desktop program

XBAP Applications

WPF also allows you to build applications that can be hosted within a web browser. This flavor of WPF application is termed an XAML browser application, or XBAP. Under this model, the end user navigates to a given URL, at which point the XBAP application (which takes an *.xbap file extension) is transparently downloaded and installed to the local machine. Unlike a traditional ClickOnce installation for an executable application, however, the XBAP program is hosted directly within the browser and adopts the browser’s intrinsic navigational system. Figure 28-2 illustrates an XBAP program in action (specifically, the ExpenseIt WPF sample program that ships with the .NET Framework 3.5 SDK).

Figure 28-2. XBAP programs are downloaded to a local machine and hosted within a web browser.

1004 CHAPTER 28 INTRODUCING WINDOWS PRESENTATION FOUNDATION AND XAML

One possible downside to this flavor of WPF is that XBAPs must be hosted within Microsoft Internet Explorer 6.0 (or higher) or Firefox. If you are deploying such applications across a company intranet, browser compatibility should not be a problem, given that system administrators can play dictator regarding which browser should be installed on users’ machines. However, if you want the outside world to make use of your XBAP, it is not possible to ensure each end user is making use of Internet Explorer/Firefox, and therefore some external users may not be able to view your WPF XBAP application.

Another issue to be aware of is that XBAP applications run within a security sandbox termed the Internet zone. As you may recall from Chapter 20, .NET assemblies that are loaded into this sandbox have limited access to system resources (such as the local file system or system registry) and cannot freely use all aspects of specific .NET APIs that might pose a security threat. Specifically, XBAPs cannot perform the following tasks:

Create and display stand-alone windows

Display application-defined dialog boxes

Display a Save dialog box launched by the XBAP itself

Access the file system (use of isolated storage is permitted)

Make use of legacy UI models (Windows Forms, ActiveX) or call unmanaged code

At first glance, the inability to create secondary windows (or dialog boxes) may seem very limiting. In reality, an XBAP can show users multiple user interfaces by using the page-navigation model mentioned previously.

Silverlight Applications

WPF and XAML also provide the foundation for a cross-platform WPF-centric plug-in termed Silverlight. Using the Silverlight SDK, it is possible to build browser-based applications that can be hosted by Mac OS X as well as Microsoft Windows (additional operating systems are supposedly also in the works).

With Silverlight, you are able to build extremely feature-rich (and interactive) web applications. For example, like WPF, Silverlight has a vector-based graphical system, animation support, a rich text document model, and multimedia support. Furthermore, as of Silverlight 1.1, you are able to incorporate a subset of the .NET base class library into your applications. This subset includes a number of WPF controls, LINQ support, generic collection types, web service support, and a healthy subset of mscorlib.dll (file I/O, XML manipulation, etc.).

Note This edition of the text does not address Silverlight. If you are interested in learning more about this API, check out http://www.microsoft.com/silverlight. Here you can download the free Silverlight SDK (including the Silverlight plug-in itself), view numerous sample projects, and learn more about this intriguing aspect of WPF development.

Investigating the WPF Assemblies

Regardless of which type of WPF application you wish to build, WPF is ultimately little more than a collection of types bundled within .NET assemblies. Table 28-3 describes the core assemblies used to build WPF applications, each of which must be referenced when creating a new project (as you would hope, Visual Studio 2008 WPF projects automatically reference the required assemblies).

CHAPTER 28 INTRODUCING WINDOWS PRESENTATION FOUNDATION AND XAML

1005

Table 28-3. Core WPF Assemblies

 

 

 

 

 

 

 

Assembly

Meaning in Life

 

PresentationCore.dll

This assembly defines numerous types that constitute the

 

 

foundation of the WPF GUI layer. For example, this assembly

 

 

contains support for the WPF Ink API (for programming against

 

 

stylus input for Pocket PCs and Tablet PCs), several animation

 

 

primitives (via the System.Windows.Media.Animation namespace),

 

 

and numerous graphical rendering types (via System.Windows.

 

 

Media).

 

PresentationFoundation.dll

Here you will find the WPF control set, additional animation and

 

 

multimedia types, data binding support, types that allow for

 

 

programmatic access to XAML, and other WPF services.

 

WindowsBase.dll

This assembly defines the core (and in many cases lower-level)

 

 

types that constitute the infrastructure of the WPF API. Here you

 

 

will find types representing WPF threading types, security types,

 

various type converters, and other basic programming primitives (Point, Vector, Rect, etc.).

Collectively, these three assemblies define a number of new namespaces and hundreds of new

.NET classes, interfaces, structures, enumerations, and delegates. While you should consult the

.NET Framework 3.5 SDK documentation for complete details, Table 28-4 documents the role of some (but certainly not all) of the core namespaces you should be aware of.

Table 28-4. Core WPF Namespaces

Namespace

Meaning in Life

System.Windows

This is the root namespace of WPF. Here you will find core types

 

(such as Application and Window) that are required by any WPF

 

desktop project.

System.Windows.Controls

Here you will find all of the expected WPF widgets, including types

 

to build menu systems, tool tips, and numerous layout managers.

System.Windows.Markup

This namespace defines a number of types that allow XAML

 

markup (and the equivalent binary format, BAML) to be parsed

 

and processed programmatically.

System.Windows.Media

This is the root namespace to several media-centric namespaces.

 

Within these namespaces you will find types to work with

 

animations, 3D rendering, text rendering, and other multimedia

 

primitives.

System.Windows.Navigation

This namespace provides types to account for the navigation

 

logic employed by XAML browser applications (XBAPs) as well as

 

standard desktop applications that require a navigational page

 

model.

System.Windows.Shapes

This namespace defines various 2D graphic types (Rectangle,

 

Polygon, etc.) used by various aspects of the WPF framework.

 

 

To begin our journey into the WPF programming model, we’ll examine two members of the System.Windows namespace that are commonplace to any traditional desktop development effort:

Application and Window.

1006 CHAPTER 28 INTRODUCING WINDOWS PRESENTATION FOUNDATION AND XAML

The Role of the Application Class

The System.Windows.Application class type represents a global instance of a running WPF application. Like its Windows Forms counterpart, this type supplies a Run() method (to start the application), a series of events that you are able to handle in order to interact with the application’s lifetime (such as Startup and Exit), and a number of members that are specific to XAML browser applications (such as events that fire as a user navigates between pages). Table 28-5 details some of the key members to be aware of.

Table 28-5. Key Properties of the Application Type

Property

Meaning in Life

Current

This static property allows you to gain access to the running Application object

 

from anywhere in your code. This can be very helpful when a window or dialog box

 

needs to gain access to the Application object that created it.

MainWindow

This property allows you to programmatically get or set the main window of the

 

application.

Properties

This property allows you to establish and obtain data that is accessible throughout

 

all aspects of a WPF application (windows, dialog boxes, etc.). In many ways, this

 

looks and feels very much like establishing application variables for an ASP.NET web

 

application.

StartupUri

This property gets or sets a URI that specifies a window or page to open

 

automatically when the application starts.

Windows

This property returns a WindowCollection type, which provides access to each

 

window created from the thread that created the Application object. This can be

 

very helpful when you wish to iterate over each open window of an application and

 

alter its state (such as minimizing all windows).

 

 

Unlike its Windows Forms counterpart, however, the WPF Application type does not expose its functionality exclusively through static members. Rather, WPF programs define a class that extends this type to represent the entry point to the executable. For example:

//Define the global application object

//for this WPF program.

class MyApp : Application

{

[STAThread] static void Main()

{

//Handle events, run the application,

//launch the main window, etc.

}

}

You’ll build a complete Application-derived type in an upcoming example. Until then, let’s check out the core functionality of the Window type and learn about a number of key WPF base classes in the process.

The Role of the Window Class

The System.Windows.Window type represents a single window owned by the Application-derived type, including any dialog boxes displayed by the main window. As you might expect, the Window type has a series of parent classes, each of which brings more functionality to the table. Consider

CHAPTER 28 INTRODUCING WINDOWS PRESENTATION FOUNDATION AND XAML

1007

Figure 28-3, which shows the inheritance chain (and implemented interfaces) for the System. Windows.Window type as seen through the Visual Studio 2008 object browser.

Figure 28-3. The hierarchy of the Window type

You’ll come to understand the functionality provided by many of these base classes as you progress through this chapter and the chapters to come. However, to whet your appetite, the following sections present a breakdown of the functionality provided by each base class (consult the .NET Framework 3.5 SDK documentation for full details).

The Role of System.Windows.Controls.ContentControl

The direct parent of Window is ContentControl. This base class provides derived types with the ability to host content, which simply put refers to a collection of objects placed within the control’s surface area. Under the WPF content model, a content control has the ability to contain a great number of UI elements beyond simple string data. For example, it is entirely possible to define a Button that contains an embedded ScrollBar as content. The ContentControl base class provides a key property named (not surprisingly) Content for this purpose.

If the value you wish to assign to the Content property can be represented as a simple string literal, you may set the Content property explicitly as an attribute within the element’s opening definition:

<!-- Setting the Content value explicitly -->

<Button Height="80" Width="100" Content="ClickMe"/>

Alternatively, you are able to implicitly set the Context property by specifying a value within the scope of the content control’s element definition. Consider the following functionally equivalent XAML description of the previous button:

<!-- Setting the Content value implicitly -->

<Button Height="80" Width="100"> ClickMe

</Button>

However, if the value you wish to assign to the Content property cannot be represented as a simple array of characters, you are unable to assign the Content property using an attribute in the control’s opening definition. For these cases, you must establish content either implicitly or by using property-element syntax. Consider the following functionally equivalent XAML definition, which sets the Content property to a ScrollBar type (you’ll find more information on XAML later in this chapter, so don’t sweat the details just yet):

1008 CHAPTER 28 INTRODUCING WINDOWS PRESENTATION FOUNDATION AND XAML

<!-- A Button containing a ScrollBar as implicit content -->

<Button Height = "80" Width = "100"> <ScrollBar Width = "75" Height = "40"/>

</Button>

<!-- A Button containing a ScrollBar using property-element syntax -->

<Button Height = "80" Width = "100"> <Button.Content>

<ScrollBar Width = "75" Height = "40"/> </Button.Content>

</Button>

Do be aware, however, that not every WPF control derives from ContentControl, and therefore only a subset of controls supports this unique content model. Specifically, any class deriving from

Frame, GroupItem, HeaderedContentControl, Label, ListBoxItem, ButtonBase, StatusBarItem,

ScrollViewer, ToolTip, UserControl, or Window can make use of this content model. Any other type attempting to do so results in a markup/compile-time error. For example, consider this malformed

ScrollBar type:

<!-- Error! ScrollBars don't derive from ContentControl! -->

<ScrollBar Height = "80" Width = "100">

<Button Width = "75" Height = "40"/> </ScrollBar >

Another important point regarding this new content model is that controls deriving from ContentControl (including the Window type itself) can assign only a single value to the Content property. Therefore, the following XAML Button definition is also illegal, as the Content property has been implicitly set twice:

<!-- Try to add a TextBox and an Ellipse to a Button? Error! -->

<Button Height = "200" Width = "200">

<Ellipse Fill = "Green" Height = "80" Width = "80"/> <TextBox Width = "50" Height = "40"/>

</Button >

At first glance, this fact might appear to be extremely limiting (imagine how nonfunctional a dialog box would be with only a single button!). Thankfully, it is indeed possible to add numerous elements to a ContentControl-derived type using panels. To do so, each bit of content must first be arranged into one of the WPF panel types, after which point the panel becomes the single value assigned to the Content property. You will learn more about the WPF content model as well as the various panel types (and the controls they contain) in Chapter 29.

Note The System.Windows.ContentControl class also provides the HasContent property to determine if the value of Content is currently null.

The Role of System.Windows.Controls.Control

Unlike ContentControl, all WPF controls share the Control base class as a common parent. This base class provides numerous core members that account for basic UI functionality. For example, Control defines properties to establish the control’s size, opacity, tab order logic, the display cursor, background color, and so forth. Furthermore, this parent class provides support for templating

CHAPTER 28 INTRODUCING WINDOWS PRESENTATION FOUNDATION AND XAML

1009

services. As explained in Chapter 30, WPF controls can dynamically change their appearance using templates, styles, and themes. Table 28-6 documents some key members of the Control type, grouped by related functionality.

Table 28-6. Key Members of the Control Type

Members

Meaning in Life

Background, Foreground, BorderBrush,

These properties allow you to set basic settings

BorderThickness, Padding,

regarding how the control will be rendered and

HorizontalContentAlignment,

positioned.

VerticalContentAlignment

 

FontFamily, FontSize, FontStretch, FontWeight

These properties control various font-centric

 

settings.

IsTabStop, TabIndex

These properties are used to establish tab order

 

among controls on a window.

MouseDoubleClick, PreviewMouseDoubleClick

These events handle the act of double-clicking a

 

widget.

Template

This property allows you to get and set the

 

control’s template, which can be used to change

 

the rendering output of the widget.

 

 

The Role of System.Windows.FrameworkElement

This base class provides a number of lower-level members that are used throughout the WPF framework, such as support for storyboarding (used within animations) and support for data binding, as well as the ability to name a member (via the Name property), obtain any resources defined by the derived type, and establish the overall dimensions of the derived type. Table 28-7 hits the highlights.

Table 28-7. Key Members of the FrameworkElement Type

Members

Meaning in Life

ActualHeight, ActualWidth, MaxHeight, MaxWidth,

Control the size of the derived type (not

MinHeight, MinWidth, Height, Width

surprisingly)

ContextMenu

Gets or sets the pop-up menu associated with

 

the derived type

Cursor

Gets or sets the mouse cursor associated with

 

the derived type

HorizontalAlignment, VerticalAlignment

Control how the type is positioned within a

 

container (such as a panel or list box)

Name

Allows to you assign a name to the type, in

 

order to access its functionality in a code file

Resources

Provides access to any resources defined by

 

the type (see Chapter 30 for an examination of

 

the WPF resource system)

ToolTip

Gets or sets the tool tip associated with the

 

derived type

 

 

1010 CHAPTER 28 INTRODUCING WINDOWS PRESENTATION FOUNDATION AND XAML

The Role of System.Windows.UIElement

Of all the types within a Window’s inheritance chain, the UIElement base class provides the greatest amount of functionality. The key task of UIElement is to provide the derived type with numerous events to allow the derived type to receive focus and process input requests. For example, this class provides numerous events to account for drag-and-drop operations, mouse movement, keyboard input, and stylus input (for Pocket PCs and Tablet PCs).

Chapter 29 digs into the WPF event model in detail; however, many of the core events will look quite familiar (MouseMove, KeyUp, MouseDown, MouseEnter, MouseLeave, etc.). In addition to defining dozens of events, this parent class provides a number of properties to account for control focus, enabled state, visibility, and hit testing logic, as shown in Table 28-8.

Table 28-8. Key Members of the UIElement Type

Members

Meaning in Life

Focusable, IsFocused

These properties allow you to set focus on a given derived

 

type.

IsEnabled

These properties allow you to control whether a given

 

derived type is enabled or disabled.

IsMouseDirectlyOver, IsMouseOver

These properties provide a simple way to perform hit-testing

 

logic.

IsVisible, Visibility, Visible

These properties allow you to work with the visibility setting

 

of a derived type.

RenderTransform

This property allows you to establish a transformation that

 

will be used to render the derived type.

 

 

The Role of System.Windows.Media.Visual

The Visual class type provides core rendering support in WPF, which includes hit testing of rendered data, coordinate transformation, and bounding box calculations. In fact, this type is the connection point between the managed WPF assembly stack and the unmanaged milcore.dll binary that communicates with the DirectX subsystem.

As examined in Chapter 30, WPF provides three possible manners in which you can render graphical data, each of which differs in terms of functionality and performance. Use of the Visual type (and its children, such as DrawingVisual) provides the most lightweight way to render graphical data, but it also entails the greatest amount of manual code to account for all the required services. Again, more details to come in Chapter 30.

The Role of System.Windows.DependencyObject

WPF supports a particular flavor of .NET properties termed dependency properties. Simply put, this approach allows a type to compute the value of a property based on the value of other properties (hence the term “dependency”). In order for a type to participate in this new property scheme, it will need to derive from the DependencyObject base class. In addition, DependencyObject allows derived types to support attached properties, which are a form of dependency property very useful when programming against the WPF data-binding model as well as when laying out UI elements within various WPF panel types.

The DependencyObject base class provides two key methods to all derived types: GetValue() and SetValue(). Using these members, you are able to establish the property itself. Other bits of infrastructure allow you to “register” who can use the dependent/attached property in question.

CHAPTER 28 INTRODUCING WINDOWS PRESENTATION FOUNDATION AND XAML

1011

While dependency properties are a key aspect of WPF development, much of the time their details are hidden from view. Chapter 29 dives further into the details of the “new” property type.

The Role of System.Windows.Threading.DispatcherObject

The final base class of the Window type (beyond System.Object, which I assume needs no further explanation at this point in the text) is DispatcherObject. This type provides one property of interest, Dispatcher, which returns the associated System.Windows.Threading.Dispatcher object. The

Dispatcher type is the entry point to the event queue of the WPF application, and it provides the basic constructs for dealing with concurrency and threading. By and large, this is a lower-level class that can be ignored by the majority of your WPF applications.

Building a (XAML-Free) WPF Application

Given all of the functionality provided by the parent classes of the Window type, it is possible to represent a window in your application by either directly creating a Window type or using this class as the parent to a strongly typed descendent. Let’s examine both approaches in the following code example. Although most WPF applications will make use of XAML, doing so is entirely optional. Anything that can be expressed in XAML can be expressed in code and (for the most part) vice versa. If you wish, it is possible to build a complete WPF project using the underlying object model and procedural code.

To illustrate, let’s create a minimal but complete application without the use of XAML using the Application and Window types directly. Consider the following C# code file (SimpleWPFApp.cs), which creates a main window of modest functionality:

// A simple WPF application, written without XAML. using System;

using System.Windows;

using System.Windows.Controls;

namespace SimpleWPFApp

{

//In this first example, we are defining a single class type to

//represent the application itself and the main window.

class MyWPFApp : Application

{

[STAThread] static void Main()

{

// Handle the Startup and Exit events, and then run the application.

MyWPFApp app = new MyWPFApp(); app.Startup += AppStartUp; app.Exit += AppExit;

app.Run(); // Fires the Startup event.

}

static void AppExit(object sender, ExitEventArgs e)

{

MessageBox.Show("App has exited");

}

static void AppStartUp(object sender, StartupEventArgs e)

{

// Create a Window object and set some basic properties.