Professional VB 2005 with .NET 3.0 (Programmer to Programmer)

When .NET was released, most people realized that in terms of application development a paradigm shift was occurring. The release of WPF is the first step in yet another paradigm shift, this one focused on how user interfaces are designed and implemented. Therefore, it’s appropriate to take a little time to look at not just where the user interface models are coming from, but where they are going. This understanding is part of how WPF fits in and explains not only why you’ll want WPF in the future, but also how you can start leveraging it today.

The original user interfaces were punch cards for input and hard copy text for output. OK, maybe that’s going a little too far back. Instead, let’s jump ahead to the part of the user interface’s resume that applies to where we’re going today. The 1980s through 1990s saw several computer and software manufacturers introduce the graphical user interface (GUI). These GUI environments, while implemented differently on different platforms, became a part of the operating system. For Windows, this is the User32.dll and its companion UI classes. The original Visual Basic 1.0 was designed to enable developers to interact in a simple manner with these files, unlike C++, which referenced the raw User32 interfaces for everything.

Over time, Visual Basic’s simple drag-and-drop approach to creating the forms users would access as part of an application in that GUI environment helped make it the most popular development language.

Over time, however, the paradigm started to shift, and applications began migrating to the Web. The Web introduced its own way of creating forms, one that used HTML. The HTML model is more declarative. For example, the HTML page declares it wants a Textbox, but it’s up to the browser to interpret and provide the code that handles the actual object. This model is also supported on Windows and by Microsoft both in Internet Explorer (the parsing engine) and in ASP.NET.

This brings up the next example, .NET and Windows Forms. When .NET was introduced it shipped with two user-interface implementations, ASP.NET’s HTML-based UI and the desktop-centric Windows Forms. It’s important to realize that Windows Forms isn’t based on the same code that User32 windows are, even though the programming model whereby the designer adds the code to a portion of the application’s source is similar. The managed environment represents both the second and third programming models for developing user interfaces under Windows. Of course, other platforms include still other GUI models, but these three GUI models represent the ones Microsoft must now support.

Thus, Microsoft is left repeating many user-interface controls with three distinct implementations, a pain that Microsoft feels. After all, when .NET 1.0 was released, one of the main complaints about migrating applications from Win32 to Windows Forms was the lack of available controls. Since then, Microsoft continuously finds itself updating a rendering engine used within Internet Explorer (and other tools) for the UI elements of HTML, and a totally unrelated rendering engine used for .NET Windows Forms, keeping the Win32 in place.

However, the real challenge for Microsoft mirrors your challenge. For developers, the pain starts with the fact that a user interface can’t be transported seamlessly between a Web-based version of an application and a local desktop version of the same application, or across platforms. For example, Microsoft can’t design a UI for Outlook and reuse it for Outlook Web Access (OWA). Instead, it needs a different team of developers with different skills to create the OWA interface, and have you seen a remotely downloadable Windows Forms-based OWA application?

Let’s face it, there hasn’t been much economic incentive to create both a Windows Forms-based and ASP.NET-based user interface for the same application. Until doing such a task is almost painless, people will continue to select an environment and build their application targeting that model. Thus, while there are several options for creating a user interface, they all represent “either-or” decisions.

This is where the WPF model comes in. Based more on the HTML model, WPF is a declarative way of designing interfaces. The idea is that you can use a declarative model to design your user interface and then compile or include that definition with either a desktop or Web or even another operating system version of your application. WPF uses XML to declare the user-interface elements, relying on a standard known as the eXtensible Application Markup Language (XAML). This standard is pronounced “zamel” (rhymes with camel). It enables you to layer elements and include things such as colors and nondistinct shapes.

XAML goes well beyond what you normally expect to find in an HTML UI, yet at the same time the format should feel somewhat familiar to those who know HTML and/or XML. As for the implementation of code to interpret XAML UI declarations, Microsoft introduced the components that make up WPF. Having this code ship with Microsoft’s new Vista operating system and available as part of .NET 3.0 for existing operating systems will begin to make XAML interfaces the standard across desktops, the Web, and even handheld devices. It’s also important to note that XAML’s underlying class implementation differs from the model used for previous GUI implementations.

Raster Graphics and Vector Graphics

Currently, when you create a Windows Forms control you decide how large, in pixels, that button should be. A similar action is taken with regard to HTML forms, where you can specify either a size in pixels or a percentage of the screen. In both cases, the computer simply lays out a square or rounded square based on a flat set of pixels. It does the same with other images you use, working with what are known as raster graphics. Raster graphics are a collection of points on the surface of a screen that represent an image.

The alternative form of graphics is known as vector graphics. A vector is a line with a point of origin that continues forward in space from that point of origin. Vector graphics aren’t based on a collection of points, but rather on a series of vectors. A plane representing the surface of your screen is placed in the path of these vectors, which define a set of points, and that is what you see on your screen. Vector graphics provide much better and more realistic image manipulation. Note that while you can incorporate a raster image with vector graphics, since you can place the raster image in your virtual plane, the reverse isn’t feasible.

WPF is the first forms-based engine that relies on this vector-based model. The good news is that you can create user interfaces that truly look fantastic. The bad news is that you need to account for the fact that computing a series of vectors and the plane that intersects those vectors requires more CPU or Graphical Processing Unit (GPU) cycles. Thus, just as you’ve heard with the Vista UI, all WPF user interfaces require a bit more computing horsepower. Another appeal of the WPF model is that the graphical capabilities, which are built around the Direct-X model, provide a much more appealing user interface.

Most of this chapter focuses on the underlying XAML that’s used to define your WPF applications. Fortunately, WPF resolves the pain developers feel, even if it isn’t a near-term solution to the support of multiple UI models that causes Microsoft pain. Microsoft will, of course, need to support all its previous GUI models in addition to WPF for the foreseeable future. However, Microsoft is motivated by the same advantages the rest of us can leverage - better graphics and the idea that a single application can have a UI that runs in multiple environments. This is a major factor in Microsoft’s future as it focuses on things like Office Live. As developers, we can also start to leverage WPF and possibly even start to think about using WPF/E - WPF Everywhere - the standard for supporting handheld devices. We can create application user interfaces that operate both within a Web browser and on the local device - desktop, laptop, handheld, and so on - outside the Web browser.

Installing and Configuring WPF to Build Applications

Regardless of your operating system, you need to install software in order to build a WPF application. As noted, WPF was released with Vista; those intrepid developers, who in the early days were working on a beta and eventually a release version of Vista, had WPF available in their environment. However, if you were (or are) running the Windows XP operating system, then - just as with every other version of .NET - you need to add WPF to your operating system. In the past, with things such as .NET 1.x and 2.0, you usually did this by installing Visual Studio, which included not only the base runtime libraries but also several debug-related libraries.

Unfortunately, there isn’t currently a complete package that includes WPF. Instead, you need to install the new runtime package and then supplement that manually with some low-level tools in order to work with those libraries. The first package you need is the full .NET 3.0 runtime package available on MSDN. To reiterate, if you are running Windows Vista, then the runtime for .NET 3.0 is already installed. Windows 2003 Server and Windows XP developers need to go to MSDN and download the freely available runtime libraries for .NET 3.0, which are available under the .NET Framework Home section of MSDN. As of this writing, you need to install the “release” version of this package and not one of the beta or “release candidate” versions, which are still available.

You can verify your installation by looking in your <System Root>\Microsoft.NET\Framework folder for a folder labeled V3.0. Within this folder you’ll find a WPF directory containing the library files that you will reference in your WPF applications. Of course, your references will be to a set of namespaces contained in those library files, but those are the actual files you will be referencing. Even so, this doesn’t mean you are ready to start building WPF applications.

Regardless of your operating system, you need a second software package in order to build a WPF application. The Windows Vista SDK (Software Development Kit) is a collection of libraries, tools, and upgrades that you need in order to build a WPF application. Similar to the .NET 3.0, this package contains released software. However, while it does make some additional tools available and provide some optional link libraries to Visual Studio 2005, the SDK does not upgrade Visual Studio 2005 to enable you to build WPF and other .NET 3.0 applications. These tools make it possible to build WPF applications from the command line, which is where this chapter starts.

Later in this chapter, the Visual Studio 2005 Extensions for WPF/WCF are covered. However, this is a Community Technology Preview, and will not be updated to a release product. Thus, depending on your work environment, you may find that the corporate or government IT department you work for is not interested in having you install this software. Similarly, Visual Studio “Orcas,” the next version of Visual Studio, is in beta, and will target the next version of .NET, tentatively known as .NET 3.5. It is also covered later in this chapter; however, for now let’s examine the low-level details of WPF.

Creating a WPF Application

This example creates a simple “Hello World” application that will act as the basis for our larger projects. Rather than get too technical about the elements of a WPF application, let’s start with just a simple XAML declaration of a main window titled “Hello World.” The sample application consists of two files: an XAML source file named App.xaml and a Visual Basic project file named provb_net.vbproj.

The XAML shown in the following section declares a new Application and associates the appropriate XML namespace to describe the document contents. It then declares an Application.MainWindow and assigns the string “Hello World” to the Title property of that window. The XAML also declares that the window should be visible. The first thing to notice is that there isn’t a line of code in this XAML file, just a few declarations:

<Application xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"> <Application.MainWindow> <Window Title='Hello World' Visibility='Visible' /> </Application.MainWindow> </Application>

The next step is running this declared application. In this case, you need to compile the application. You could run the Visual Basic compiler from the command line by typing vbc, but that requires a lot of path-dependent statements in order to pull together the libraries. Instead, use the MSBuild engine that ships with Visual Studio 2005. You still need to put together the references, but they go into an MSProject file instead of just being on the command line. Thus, you can invoke MSBuild and have it work out the details and reuse the majority of your typing. You’re still working at the command line, but you aren’t typing complex parameter lists in order to compile.

There isn’t space in this chapter to cover all of the details of the MSBuild engine. For more complete coverage, check on MSDN in the MSBuild Reference section. However, take a minute to review a few of the primary elements that you’ll need in order to create a build. The following table lists a few of the top-level XML nodes, with a brief description of the role each plays in the build process:

Open table as spreadsheet

Project File Nodes

Description

PropertyGroup

This section contains a series of tags that describe what is being compiled, such as whether you are compiling an executable or library, what the name of the compiled file should be, whether it is a debug or release version, etc.

ItemGroup

This section has two roles. The first is to provide a list of references. You’ll recognize these items based on the fact that XML nodes within this section are of type Reference. The Reference nodes name the namespaces that are being referenced by your application.

The second use of the ItemGroup node is to define the list of source files that are part of a project. The section(s) contain the complete list of source files, so if you open an existing project file you’ll find that many files hidden by Visual Studio are listed in the project file.

Import

The Import node is essentially a node that lists other projects to import. To MSBuild, constructs such as C# and VB don’t really exist; instead, MSBuild looks for a project that contains all the standard references for such a language. It then imports this generic project, which provides not only the necessary reference files, but also a reference task to execute the appropriate compiler.

Before you start typing this file, let’s cheat. Take an existing .vbproj file and open it for editing. This is not a completely trivial task, but for this example you’ll mostly be deleting items. Start by reducing the number of PropertyGroups to 1. Then review the items in this property group and keep only the items in the following example. As you can see, this is a much shorter list of items. Then, delete the ItemGroup declarations, keeping one for the references and a second for the project files:

<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <PropertyGroup> <Configuration>Debug</Configuration> <Platform>AnyCPU</Platform> <RootNamespace>ProVB_NET</RootNamespace> <AssemblyName>ProVB_Net</AssemblyName> <OutputType>winexe</OutputType> <OutputPath>.\bin\Debug\</OutputPath> </PropertyGroup> <ItemGroup> <Reference Include="System" /> <Reference Include="WindowsBase" /> <Reference Include="PresentationCore" /> <Reference Include="PresentationFramework" /> </ItemGroup> <ItemGroup> <ApplicationDefinition Include="App.xaml" /> </ItemGroup> <Import Project="$(MSBuildBinPath)\Microsoft.VisualBasic.targets" /> <Import Project="$(MSBuildBinPath)\Microsoft.WinFX.targets" /> </Project>

Continuing to reference the sample MSBuild project file, note that it has only four project references. The first, System, should be familiar to you as a .NET developer. However, the other three all reference WPF namespaces. These files contain the code that implements the elements of the WPF you have declared in your XAML file. You need to add these references to the file you copied.

The second item group contains the list of files in your application. In this case, there is only a single file named App.xaml, which contains the XAML code shown earlier. This section includes additional file references as you make more complex projects, but for this simple example it only has that one entry.

Finally, there are two Import references in the preceding sample. The first of these should already be present in the file you’ve copied. It’s the reference to the Visual Basic targets project. For now, even though you don’t have any Visual Basic code in your example, leave that, because you will be adding Visual Basic code to the project in future examples. To this you need to add a second target - WPF. WinFx was the working name for .NET 3.0 prior to release. Accordingly, this name is still preserved behind the scenes; the Microsoft.WinFx.targets project contains the logic associated with building WPF, and other .NET 3.0, project types.

The next step is to execute your project. Executing the build engine is done from the command line. Open a command window and modify your directory to be your project directory. At the command prompt, add the following:

msbuild ProVB_Net.vbproj

The preceding triggers Visual Studio’s build engine and builds your executable. If there are any errors, they are displayed within the command window. If you don’t have any errors, then you should be able to navigate down to the subdirectory identified in your project file \bin\debug and find your new executable provb net.exe. Running this executable produces the window shown in Figure 17-1.

Figure 17-1

At this point, you may be wondering why, if the Visual Studio build engine is building your WPF application, was it stated earlier that Visual Studio 2005 isn’t compatible with your WPF project? The answer is that while the Vista SDK updates provide the target files and associated compilers necessary for your WPF application, it doesn’t update Visual Studio 2005 to be able to work with your WPF project within the Visual Studio graphical user environment. Thus, while you can compile .NET 3.0 applications, Visual Studio 2005 doesn’t really contain the logic that would enable you to edit these projects and their associated source files.

XAML

The example doesn’t have much purpose yet, but it makes it easier to keep the discussion of XAML in context. The next step is to take a more detailed look at just what XAML is and how it relates to WPF. XAML is a markup-based protocol. Similar to SOAP and several other XML-based formats, the XAML specification describes a potentially open standard for describing user-interface elements. WPF is Micro soft’s implementation of this standard. Currently, XAML isn’t an open standard and it’s unknown whether XAML will ever be a true open standard. However, the .NET 3.0 implementation, which separates the definition of the XAML elements from the implementation of WPF, means that this is still a possibility for XAML.

Regardless of whether XAML ever actually becomes an open standard, Microsoft has implemented WPF across two XML namespaces. One namespace is focused on the definition of XAML, and the second is focused on WPF’s implementation of the XAML specification. Looking back at the “Hello World” sample presented earlier, the App.xaml file contains the following namespace declaration:

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

The preceding line is similar to an Imports statement for XML in that it indicates a set of nodes and keywords that will be used within the associated XML file. In this case, the winfx/2006/xaml/presenta- tion namespace contains the definition of WPF - not the definition of XAML keywords, but rather the definition of WPF, which is why you see statements such as Application.Window in that XML. The classes contained in the presentation namespace are the .NET implementation of WPF. The XAML file contains declarations referencing these classes, either as part of the XAML standard or as part of WPF. To start working with commands and controls that are part of the XAML standard, a second namespace reference is needed:

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

This second reference is used throughout most XAML files to declare the actual XAML language standard. By convention, it is aliased as x:. For those of you who may not have done much ASP.NET development, this means that within the XAML you’ll see things like x:Class, x:Code, and other similar nodes. These are the actual XAML declarations; and they are, as you might guess, a subset of what is in WPF. What is important to remember is that the XAML namespace can be and is used for things other than just WPF. As you’ll see in Chapter 25, Workflow Foundation (WF) is based on XAML; it has its own /workflows namespace.

XAML Namespace

XAML is defined as a language consisting of a collection of elements, attributes, and related objects. These objects are referenced from the XAML namespace, which by convention precedes each class with an “x:”. WPF extends and maps these declarative structures back into .NET, using specific classes that are part of the .NET 3.0 System.Windows namespace.

Before getting to the syntax, take a look at the three categories of XML statement you will find within the XAML namespace: attribute, markup extension, and XAML directive. Each is a separate category of language element.

Within XML, attributes refer to named properties that are associated with a given XML node. Thus, the XML node “object” might have several attributes such as ID, Name, Text, etc., that are associated with it. These attributes in XML live within the definition of the XML node. They are not contained within the XML node but its definition, as shown below:

<object ></object>

Within XAML, the list of attributes includes those in the following table. Be aware that the term “object” in the following snippets can be replaced with one of several WPF objects, including Application, Window, Button, Brush, and so on:

Open table as spreadsheet

XAML Attributes

Description

x:Class

Used to reference the root class for an XAML document. Each document can be associated with a single root object. An example of using this attribute is

<object x:> </object>

x:ClassModifier

Modifies the class definition for a given XAML document. Specifically, it enables you to indicate that a given class doesn’t provide a public interface. Public is the default.

<object x: x:ClassModifier="Friend"> </object>

x:FieldModifier

Unlike classes, which are by default public, fields within objects are by default assigned with the modifier Friend. If you have added an object within XAML that you want available to other classes (within your code behind), then the FieldModifier needs to declare this field with the modifier of Friend. This property can only be used with objects that also have the x:Name attribute shown here:

<object x:Name="LoginWindow" x:FieldModifier="Public"></object>

x:Key

Some objects, such as the Dictionary object and other collection objects, allow items to be indexed via a key. Such a key must be named, and this attribute is used to provide a unique key name. Note that most XAML applications leverage a resource dictionary, which is a common use of this attribute. Keys need to be unique within the scope of the object to which they are applicable.

<object.Resources> <SolidColorBrush x:Key="string"/> </object.Resources>

x:Name

Similar to a key, but used more for the naming of objects within the scope of an application. Such objects are not public by default, but typically represent the controls and related user-interface objects used by your application.

<object x:Name="LoginWindow"></object>

x:Shared

This actually maps to what Visual Basic users understand the keyword Shared to mean. By default, if your application requests an object from your XAML resources, then you will get the same instance of the requested resource. You can use this property such that each time a given object is requested, a new instance of that object is created.

<ResourceDictionary> <object x:shared="false"/> </ResourceDictionary

x:Subclass

This attribute can be used in conjunction with an x:Class declaration. It essentially allows your XAML to inherit from another class; however, as a Visual Basic user you won’t use this attribute because you can do this in a much more natural manner in the code-behind-source file associated with your class.

<object x: x:Sub></object>

x:TypeArguments

This attribute enables you to create a collection of x:Type markup extensions. This collection acts as the parameters to the constructor for a generic class to ensure that the associated types are defined with the constructor. This attribute must be used with a class declaration, and the associated class must be a generic.

<object x: x:TypeArguments="{x:Type=type1}"> </object>

Notice that none of the preceding attributes are actually referenced as a node within XML. Instead, they modify the properties associated with a node. Thus, the attributes are modifiers, as opposed to the next category of elements: markup extensions. As implied by the word extensible in the name Extensible XML, one of the features of this model is that the format allows for the definition of extensions. These extensions expand on the base elements associated with that markup definition. XAML includes a limited number of such extensions. Unlike an attribute, a markup extension can be used to create an XML node or a collection of XML attributes. When used to create a node, the markup extension allows for the definition of property values within this node. When used to allow for the creation of a collection of attributes, it can be recognized by the surrounding curly braces, as shown in the preceding TypeArguments definition. Markup extensions for XAML are shown in the following table:

Open table as spreadsheet

XAML Markup Extension

Description

x:Array

Used to provide support for arrays. The array declaration allows for the assignment of a datatype, to support strong typing and the inclusion of a series of elements. An example of this code is:

<x:Array Type="object"> <myObject1/> <myObject2/> </x:Array>

x:Null

Nothing in Visual Basic, but the extension is implemented based on the C#/C++ keyword of null. Will set an object property to null, which may or may not be the default state when that object property is created. x:Null has no additional modifiers and is typically implemented as a node, as opposed to an attribute, as it references the value of its parent node.

<object><object.property> <x:Null/> </object.property></object>

x:Static

Supports the reference of constant values, shared properties of objects, and enumeration values. Similar to an attribute, it is most commonly used as an attribute with the format: X:static "{namespace.class"} This extension is used to gain access to common values that are defaults for your application - for example, to the system colors used by the operating system.

<object Background="{x:Static SystemColors.ControlBrush}"></object>

x:Type

As previously introduced with the x:Typename attribute, the x:Type extension allows for the specification of a type when creating an object that is a generic. However, it has a second use: the specification of a property type. Thus, if you create an object that has properties, the x:Type extension is used to specify the type associated with that property.

<object><object.property> <x:Type TypeName="namespace.class"/> </object.property></object>

Don’t let that last extension confuse you; there are two ways that markup extensions are used - either as attributes contained within curly braces or as nodes that may contain their own attributes and properties. Some, such as x:Static, always appear as attributes; others, such as x:Null and x:Array, always appear as nodes; and of course x:Type can be found in either location. Up until now, all the XAML language elements have been used to operate within the definition of XML. That is, they define attributes and nodes, and as long as you understand the definition of the keyword you can understand the data it references.

Open table as spreadsheet

XAML Directive

Description

x:Code

Enables you to embed Visual Basic code directly into your XAML file. However, although you can do it, you shouldn’t: It’s considered a very poor coding practice not only because it isolates code outside of a code-behind file, but also because such code makes the XAML dependent on a language for compilation, and is isolated and more difficult to debug and maintain. However, you may come across such an element. In general, it is considered best to further nest any embedded code within an x:Code block within a CDATA block as shown in the following sample, so that the XAML parsing engine doesn’t attempt to parse the code. Thus, a code block will look similar to this:

<object><x:Code> <![CDATA[ // code instructions, usually enclosed by CDATA... Sub MyMethod() End Sub ]]> </x:Code></object>

x:XData

The second item you might want to embed within your XAML document that isn’t standard XAML is another XML document. You might want to display an e-mail message or a Word document that has been converted to XML, and of course you might want this data to be within your XAML document. The key point is that you don’t want this additional XML to accidentally use the same tag information associated with your XAML. Thus, you need to provide an x:XData directive containing your root data node, which contains your custom data. Note that in most cases the “object’” node in this sample will be a System.Windows.Data.XMLDataProvider as opposed to a Window or some other object. A sample of this is shown here:

<object><x:XData> <dataItems xmlns="yourNamespace"> ... </dataItems> <elementDataRoot> </x:XData></object>

As you can see, the scope of the XML definition for what you’re going to see within a XAML file is not that complex. You’re probably wondering where all the controls, windows, and even the application object that we’ve already seen in action are. These items, while defined as part of the WPF implementation, are not part of the core XAML language definition. They are the WPF extensions, and the reason why you added a second namespace reference to the Presentation folder. Everything else you see in XAML that falls into this second category is also available for reference from your .NET application, so let’s take a look at the integration of XAML and Visual Basic.

Application Code

In the first “Hello World,” you’ll recall that the XAML file began by creating an instance of an Application object. In this instance, the Application object was declared in the XAML file. The Application object is required for all WPF applications. Just as with your Visual Basic Windows Forms code, the Application object represents the application to the CLR. It is this object that represents the base reference for things such as garbage collection, and it is registered as the primary process. Because the Application object is implemented as an object in the System.Windows namespace, it supports properties, methods, and events just like any other class.

In fact, it is possible to do much of your WPF programming from within accompanying class files that may or may not contain the references to the Application class, but let’s start by defining how XAML can be integrated with code. After all, at some point you probably expect to start seeing some Visual Basic code again. The “Hello World” sample was a pure XAML application. In most cases, as a Visual Basic developer, you’ll start with a Visual Basic application. Because Visual Basic provides the underlying application object, you won’t define this object. Instead, you’ll create Window and Page objects that are referenced by your built-in Visual Basic Application object.

Even in XAML it is best practice to separate the Window object(s) that will be in your application from the Application object, so let’s quickly take care of that: Instead of having an empty window, let’s create a window with a button in it, and a code-behind file with an event handler for the click button. Begin with the updated application file:

<Application xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" StartupUri="MainWindow.xaml"> </Application>

As you can see, this file has gotten much smaller. In fact, in most cases when you create a new WPF application from one of the tools, you’ll find that this file isn’t used at all; instead, you can leverage the Application object created within your .NET code. Moving on, let’s look at the new MainWindow.xaml source file that defines the main window with a single button:

<Window x: xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Hello from VB" Height="233" Width="308"> <Grid> <Button Margin="100,85,100,85" Name="ButtonHello" Click="VBButtonClick"> Say Hello?</Button> </Grid> </Window>

The preceding code should look similar to what you saw in the original application code, but several items have been added. For starters, this XAML references the XAML namespace in addition to the WPF namespace. It also defines a default size for the application window of 233 × 308 pixels, and the very first line where the window is declared includes an x:Class reference (discussed later in relation to the code-behind). As for a height of 233, as you’ll see when we discuss the Cider designer, the border on the top and bottom of a typical window takes up 33 pixels, thus leaving 200 for the display area. Similarly, the left and right borders take up an additional eight pixels from the width, leaving a 300-pixel display area.

Within the window the first control is a grid. Why the grid? By default, if that grid weren’t present, there would be no way to reference specific points within the window. The window just defines a blank area, and if you placed a button within the window the button would fill the entire window. That isn’t the desired behavior, so a grid is layered on top of the window. Then, notice that the button defines an attribute of Margin. Margin is a property on the WPF class, and the points it defines are used to specify where the borders for the button should be displayed. In this case, the button should start 100 pixels from the left and 100 pixels from the right margin, respectively. In addition, the top of the button should start 85 pixels from the top and 85 pixels from the bottom of the display area.

This isn’t the only way to manage the display of elements within a window, but it is one of the more precise ways. Conversely, the parameters included act more like a button that is bound to the four borders of the window; resize this sample window and watch the button’s size change. It’s far better to use only a distance from the top and a distance from the bottom in the Margin property and provide a height and width for the button. You’ll look at control layout again later in this chapter.

In addition to the Margin property, note that the button has a Name property that defines an event handler for the Click event. The name of the event handler’s procedure is VBButtonClick, and that procedure will be found in the remainder of the partial class definition for the main window. As noted earlier, the XAML file’s first line includes a class attribute. The class attribute is actually a reference to a partial class. The remainder of the class definition should be defined in your new code-behind file (named MainWindow.xaml.vb by convention). The Partial Public Class MainWindow should be defined as shown in the following code block:

Partial Public Class MainWindow Inherits System.Windows.Window Public Sub New() InitializeComponent() End Sub Public Sub VBButtonClick(ByVal sender As Object, _ ByVal e As System.Windows.RoutedEventArgs) ButtonHello.Content = "Hello VB'er" System.Windows.MessageBox.Show("Hello VB'er") End Sub End Class

You may recall from Chapter 15 that the libraries for Windows Forms are located in the System .Windows.Forms namespace. This namespace is implemented in System.Windows.Forms.dll and is the parent namespace for things such as Forms controls. The WPF, on the other hand, is located in the System.Windows namespace directly. It is implemented in the System.PresentationUI.dll and contains the implementation for the WPF classes. Most of these classes mirror what you’ll find for Windows Forms, but they have been implemented based on WPF requirements.

In the preceding code snippet the MainWindow class is inheriting from the Window object defined in the System.Windows namespace. It then implements a default constructor, which includes a call to InitializeComponent(). This is a required call when creating a code-behind file for a WPF XAML. The second method is the event handler added to handle the click event for the button on the form. Note that by default this click event does not need a Handles clause as part of its declaration.

After that, you’ll note that it relies on a System.Windows parameter for routed events. However, it is the code within the handler that is of interest. Notice that the code references the ButtonHello declared in the XAML for this window. There is no other file where this button is defined or instantiated. The integration of XAML and Visual Basic allows you to reference this declared object from the XAML file. Of more concern to most developers, however, is the property being referenced, Content. One of the changes that occurred with Windows Forms was that controls such as buttons had Text properties that were consistently used for the display text. However, under WPF, the Text property no longer exists - as shown momentarily - and instead you reference the Content property.

Why the change in properties? Under Windows Forms, if you wanted to display an image on a button, then you needed either a special property or a special button. WPF recognized that there wasn’t any reason the button’s display couldn’t adapt to handle an Object. This had two implications, especially given how this was implemented. The first implication was that an object would potentially come with its own style of output. While some of these, such as text or images, could be coded, in reality there’s no way to know all the possible objects.

This leads to the second implication, related to the fact that the object assigned as “content” for the button might completely obscure the button. This resulted in the idea that controls could be layered. The content of one control would sit “on top” of the control that hosted it. This is the behavior you already see implemented in our example. At the back of the display are the window and its frame; layered atop the window is the display grid; atop the grid is the button - and we could continue from there.

In short, an object such as a button doesn’t need to know how the content of custom objects will be displayed; it simply has to notify the object that it needs to be displayed. The preceding is a simplification, but it does reflect the core idea of how the WPF interacts between various controls. The resulting interaction model is much more robust because controls are not “contained,” but rather are layered.

Getting back to the preceding code-behind logic, the second statement in the example makes a call to a WPF message box to display a simple message. The goal here is twofold: to demonstrate that the WPF is a complete departure from Windows Forms and to illustrate that, where possible, you’ll find familiar structures. In general, this handler is a simple example of how, from Visual Basic, you can still interact with the XAML form and the user interface. It’s just that under WPF you now reference a different set of .NET libraries. At this point, you should compile and run this simple example.

However, before attempting to compile the new application using MSBuild, a few updates to the project file are in order. The good news is that these updates are limited to a few lines. Essentially, you simply need to include the new MainWindow files into the list of files associated with the project. This change is shown in the following XML snippet:

<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> ... <ItemGroup> <Page Include="MainWindow.xaml" /> <Compile Include="MainWindow.xaml.vb" /> <ApplicationDefinition Include="App.xaml" /> </ItemGroup> ... </Project>

Notice that the order of the files in the include file does not matter, but what type of action is associated with each does. The new XAML file is listed as a Page, while the Visual Basic source file associated with it is tagged as needing compilation. The MSBuild engine simply passes this list of files to the targets defined in the project file, and the targets each recognize the appropriate file types. The Visual Basic target recognizes the Compile tag and builds the MSIL from the .vb file(s). Then the WinFx engine recognizes the Page and ApplicationDefinition tags and builds the remainder of the executable, combining the MSIL from the Visual Basic compilation with the window definitions to create the .exe file, which is your application. Figure 17-2 illustrates a test run of the application, showing the main window before the button is pressed and the message box after the button is pressed. The dotted line within the button indicates that this is the default button, a visual indicator that appears in the main window after the button is pressed.

Figure 17-2

The preceding example provides a more reasonable base framework from which to work as we move forward. As some of the key elements of WPF are described, this basic application framework will act as a basis. Let’s start by discussing what controls are available and how they can be manipulated.

Controls

As noted under WPF, there is an entirely different set of libraries for developing applications. However, aside from the fact that these controls exist in a different library, to a large extent how you interact with them from Visual Basic is the same. The control has a set of properties. The XAML file may assign these values in the declarative format of XML, but you can still reference the same properties on the instances of the objects that the framework creates.

Thus, in the preceding example you saw that just as with existing .NET applications, controls are assigned a name, also known as an id, and that from the application code it is possible to address a control based on this name. Once the control is referenced, it is possible to update properties and retrieve values. Additionally, it is possible to create controls programmatically and add them to an existing window’s control set. Thus, everything you’ve done in the past both with Windows Forms and ASP.NET is still possible. On the surface, the WPF controls have more in common with existing programming models than might at first seem apparent.

Having uttered that heresy against this new paradigm, it’s time to examine what is meant by a paradigm shift with the XAML model. As noted, it starts with the fact that your controls are no longer “contained,” but rather are layered. This may sound like a minor change, but the implications are significant. The button class provides an excellent example of how this change impacts how controls are approached in WPF. Under previous UI models, an object had a container of some sort. For example, a panel could contain other controls of certain types, but not necessarily all types. A button’s content was generally text unless you had a button configured for images, but you couldn’t really find a button configured to contain, say, a drop-down list box, unless you wrote custom display implementation.

By moving to a more layered approach, you can create a single control that handles text, images, and other controls. The technique chosen to accomplish that was for those controls that support layering to include a content presenter control. Thus, the button has a layer of generic button code - background color, border, size, and so on - which is managed within the display for the button. Then the button has a content presenter, which takes whatever was placed into the button’s content property and calls the presentation logic for that control. This enables most controls to contain other controls.

As for the specific controls available in WPF, let’s start with a couple of the bottom-layer elements. The one seen thus far in this chapter is the Window control. As demonstrated in the first “Hello World” example, a window includes a frame and a title in addition to its content area. You might want to then apply a layout grid or a panel - for example a stack panel - within this content area. However, you could also take a different tack and place a Page control within this window. A Page control is, of course, the base UI element for a Web application, so it becomes easy to see how this paradigm of the content area can support the layering of two different user-interface implementations. When the application is running on a local computer, it can encapsulate the pages in a Window control; and when hosted in a browser, it can simply use those same pages without the window layer.

However, the preceding paragraph also mentions other controls called panels. Panel is an abstract base class, and there are different types of panels. Stack panels are a form of panel control. The purpose of a stack panel is to align a set of controls into a single column (horizontal or vertical). There are several panel implementations, including the canvas and grid. When you start working with WPF, among the items you’ll need to understand are the different types of panels, as you will want to leverage the unique characteristics of different panels for consistent behavior.

Aside from standard user interface controls, the WPF contains all of the controls such as Tabs, Toolbars, ToolTips, TextBoxes, Drop Downs, Validation, and so on that you can find in every other Windows-based user-interface model. Going through a list of all the possible controls would take up too much space and not provide all that much value. Instead, let’s take a quick look outside the controls. It should also be noted that the WPF namespace consists of several graphics, ink, and even data and data-bound controls.

Resources

For most applications there comes a point where you want to include one or more resources with your application. A resource can be anything, including a static string, an image, and a graphics element. Because the final example in this chapter looks at an image resource, this section uses a graphical element as a resource.

As noted earlier in the introduction to XAML syntax, the definition for x:Key included the label object.Resources. The implication is that objects of different types can include resources. The scope of a resource, then, is defined by the scope of the object with which it is associated. For a resource that will span your application, you can in fact define that resource within your application XAML. Resources that are to be available within a given window are defined in the XAML file for that window. The following XAML demonstrates adding a resource to the application file of the sample application created earlier:

<Application xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" StartupUri="MainWindow.xaml"> <Application.Resources> <SolidColorBrush x:Key='greenbrush' Color='Green' /> </Application.Resources> </Application>

A SolidColorBrush is one of several graphical devices that can be used to fill in an area with color or create graphics within your application display. In this case, the resource in question is creating a green brush. Notice that it is assigned an x:Key value that, as far as XAML is concerned, is the identity and name of this resource. Once this has been assigned, objects within your XAML can reference this resource and apply it to an object or property. To illustrate this, the following XAML code shows a reference of this brush to change the background color of the button Hello, which is part of the MainWindow.xaml file:

<Grid> <Button Margin="100,85,100,85" Background="{StaticResource greenbrush}" Name="ButtonHello" Click="VBButtonClick"> Say Hello?</Button> </Grid>

This resource could, of course, be referenced by several different resources, but if you want to slightly adjust the background color of dozens of resources, having to place that resource reference into each of your objects isn’t the most efficient way of implementing that solution. After all, each time someone edited your source file in any way, that person would need to be aware of the requirement to add this resource as the background color. Fortunately, one of the other concepts XAML borrows from the Web-based idea of style sheets enables you to define a style statement, which is then applied to all objects of the same type.

Styles

Styles essentially leverage the concept of resources but instead of having a resource that objects reference with a style, you are referencing objects of a common type and assigning a property to those objects. In short, styles provide a mechanism for you to apply a theme across an application. If another developer later adds new elements to your application, the styles are automatically applied.

Styles are defined like resources; in fact, they are defined within the same XML area in which resources are defined. By defining a style at the application level, the style can be applied across all of the windows in the application. For example, take the resource defined earlier and use it instead to help create a style. Note that in this case the XAML references the resource previously defined. This is a best practice, as in most cases you will be defining a different style resource for each object type.

<Application xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" StartupUri="MainWindow.xaml"> <Application.Resources> <SolidColorBrush x:Key='greenbrush' Color='Green' /> <Style x:Key='{x:Type Button}' TargetType='{x:Type Button}'> <Setter Property='Background' Value='{StaticResource greenbrush}' /> </Style> </Application.Resources> </Application>

To have these styles use the same value, you would define a common resource and apply it to each of your styles, which are then applied to all of the objects of the appropriate type. When handling resources, it was necessary to apply the resource to the object property you wanted to target. The style encapsulates this same behavior in a single location. Notice that the key is assigned to all objects of the type Button. The preceding example works great if you want all the buttons to have this style, but what if you only want certain buttons to have this theme?

In that case, you can revert to something closer to the resource model. Instead of using a key based on the object type, you would use a custom key and then apply that key to certain objects of the associated type. The associated type for a style is defined in the TargetType attribute of your style definition.

Layout

Recall how the button you defined was essentially bound to the four borders of your application window. This was because of the limited amount of layout information available at that point. WPF supports a very robust model for control layout, which it achieves by leveraging the ability to layer controls, and by providing a set of controls directly related to layout. Combined with the ability to define a reasonable set of layout information for each control, what you wind up with is an adaptable environment that can at the extreme provide unique behavior.

So how does the process work? Within each control are the basic elements associated with the sizing of that control. As with past versions of Windows Forms, you have the concept of height and width and the four limitations: MaxHeight, MaxWidth, MinHeight, and MinWidth. Most developers tend to rely on setting the height and width of controls and forget about the rest.

However, that’s more of a style behavior and isn’t really our focus. More important is the concept of layered controls. What happens when you layer an image on top of the button? The first thing to recognize is that if a control is layered on top of another control, then its display boundaries are limited by the underlying control boundaries. However, depending on the control, the control that is layered on top of it may change the boundaries of the underlying control. The example shown in the next section makes use of this layout behavior using an image.

Finally, as noted in our example earlier, the layering of images allows for the definition of different panels. WPF provides a series of different “panel” controls that are used to provide a framework for control layout. The grid is probably the one most familiar to .NET Windows Forms developers because it maps most closely to the default behavior of Windows Forms - in fact, the Visual Studio 2005 Windows Forms Designer actually displays a grid on your form if you request it.

However, the grid isn’t the only control that inherits from panel. Others include StackPanel, Canvas, DockPanel, ToolBar and Tab-related controls. Each of these provides unique layout behavior. Because these are available as controls and because you can nest controls, you can combine these different layout paradigms within different sections of a single form, which enables you to group controls and achieve common layout behavior of related controls.

Visual Studio Integration

The first reaction of most developers is, “How do I know what controls are available from this library, and, more importantly, how do I know what properties are the correct properties on these controls?” Because the majority of WPF applications show all the controls in XAML files, it almost seems like magic. However, in reality you can use Visual Basic to expose all of those classes from within Visual Studio. As part of the process for beta testing .NET Framework 3.0, Microsoft released a series of Visual Studio 2005 packages to extend the reach of Visual Studio 2005. The last of these packages was released in November 2006; and while it is not release quality, it provides an environment in which you can better visualize and test your XAML applications.

Keep in mind that if you are in an environment where you cannot use pre-release software, then you may have a challenge getting permission to install this package. However, available on MSDN are the “Visual Studio 2005 extensions for .NET Framework 3.0 (WCF & WPF), November 2006 CTP.” This package provides support for WPF and for the Windows Communication Format. Omitted from this package is the Windows Workflow Foundation. The extensions for Workflow are in a separate package, which you can choose to install or not. The extension packages for Visual Studio all require that you have already installed .NET 3.0 and the Vista SDK described earlier in this chapter.

Finally, a quick note on why we are demonstrating with this set of tools on Windows XP. Currently, Visual Studio 2005 on Vista is at times inconsistent. The Service Pack 1 package for Visual Studio 2005 that has been released targets Windows XP, not Vista. Therefore, to reduce inconsistencies, Windows XP was chosen for these examples. Also note that “Orcas,” the code name for the next version of Visual Studio - which will provide native support for WPF and all of the .NET 3.0 Framework features - is in beta. Orcas will provide a complete implementation of the WPF designer code named “Cider.” Trying to leverage that environment would have introduced a consistency problem. Between now and the release of Visual Studio Orcas, the extensions to Visual Studio 2005 are not going to change. Visual Studio Orcas, however, will go through several different Community Technology Preview releases and is likely to undergo significant changes. Using Visual Studio 2005 provides a more consistent interface that will remain consistent throughout the Visual Studio Orcas development cycle.

Shown in Figure 17-3 is the new project screen from Visual Studio 2005 after the Visual Studio Extensions for .NET Framework 3.0 have been installed. It contains a new category for .NET Framework 3.0. Within this category are four standard project types, three of which are related to WPF. The first is for creating a Windows application, which is the one you can choose to follow in this simple example; it shows some of the current tool capabilities and limitations.

Figure 17-3

Just as with other Visual Studio 2005 templates, after you have selected the template and had Visual Studio create your template, you are presented with your project in Visual Studio 2005. Unlike a pure Visual Basic project, however, in place of the default forms you’ll find an App.xaml and Window1.xaml as your project contents. Additionally, this is a good time to compare the project settings that are created by Visual Studio 2005 with those of the manually created sample .vbproj file used earlier in this chapter. The easiest way to do this is to right-click your project name in Visual Studio 2005 Solution Explorer and then choose to “unload” the project. The project contents will collapse and the project names will be grayed out and labeled as unavailable, as shown in Figure 17-4.

Figure 17-4

Once you have unloaded the project, right-click it; now the option to load the project file into the Visual Studio editor is available. Loading the project file into the editor provides a display similar to the one shown in Figure 17-4, and allows you to directly edit and access the project file’s settings.

Now that you’ve had a chance to review the contents of the project file, go ahead and reload the contents of the project. Figure 17-5 shows one default solution in the Solution Explorer. It indicates that Window1.xaml has been opened for editing and displays the graphical UI editing surface. There is quite a bit to discuss in this image, so let’s work from left to right, top to bottom.

Figure 17-5

On the far left side is the Toolbox. The XAML Toolbox looks and behaves similarly to the standard Windows Forms Toolbox discussed in Chapter 13. However, this Toolbox contains the controls supported by XAML. The controls displayed in the figure do not constitute the complete list of available controls, but it does illustrate the fact that the WPF offers a feature-rich set of controls. The controls can be dragged and dropped onto the design surface; in fact, drag and drop a button onto the form and resize it as shown in the display. The caption of the button will be Show Image. You’ll find that the design surface has new guides to help position your control; pay attention to these lines, as they also imply that a control is bound to that border of the window.

Take the time to set the Margin property for your new button to bind it to the top and left borders at a space of around 18 pixels. Next, set the button to a height of 30 and a width of 100. Don’t declare an event handler for the click event. This example uses an alternative method for defining the event handler that bypasses the XAML declaration file.

As for the design surface, you probably recognize at once the ProVB_Net graphical portion of that design, and on first glance you might think that the tab labeled XAML is actually a separate window. However, it is in fact part of the design surface even though it contains the XAML declarations that describe your window. As you move your cursor’s focus from the upper half of the design window to the lower half, Visual Studio automatically reinterprets and updates your display. If you type in some invalid design information, then the graphical portion of the display will stop displaying your user interface until you correct your syntax in the XAML.

More interesting is the small square between the Design and XAML tabs in Figure 17-5. This small square currently highlighted enables you to swap the display of these two panels so that when you are focused on working with the XAML statements you can do so in a larger display area. Then, when you are focused on using the Windows designer, you can have that portion of the display dominate your working area.

Note that the system requirements of the Extension for .NET Framework 2.0 indicate that it is compatible with Visual Basic 2005 Express Edition. However, be forewarned - what the system requirements allow, the release notes take away. While the package can be installed with Visual Basic Express Edition, there is a key exclusion to the supported capabilities - namely, the Visual Designer. Installing it with one of the Express Editions is not a recommended configuration. Note that this specifically refers to the designer support shown in Figure 17-5.

Getting back to our example: In addition to adding the button, let’s also add a simple image control to the display. You’ll position the control-display area to be located below the button and allow it to stretch with the other three borders. The result is some XAML code that looks similar to the following:

<Image Name="Image1" Margin="0,60,0,0" Source="BillyForTheWeb.jpg" Visible="Collapsed"> </Image>

The preceding line references a local copy of an image file. This file needs to be added to the project and reside in the project directory, as it’s referenced locally. The expectation is that, when the application is compiled, the executable will contain an embedded version of this image resource. If you compile this same statement using MSBuild, then everything builds as expected, and the resulting application works almost as expected. You may find that the image doesn’t appear as expected when the application is run and the event handler described below is called. The challenge is that the image file (which is part of the application) needs to have the property to include this as a resource set. The build action must not be Embedded Resource; it must be resource. If it isn’t, then the image won’t be compiled correctly for the XAML reference.

The next step is to go to the code view for your application. This brings up a second minor issue. If you right-click within the designer, you aren’t offered the opportunity to switch to code view for your XAML file. Fortunately, you can move to the Solution Explorer on the far right side and go to the code view by right-clicking on the file there. Note that if you can’t find that context menu option for some reason (beta software has its moments), then use the button at the top of the Solution Explorer to display all files, after which you’ll see the window1.xaml.vb file and can double-click on it to open it for editing.

At this point, build your application. The reason for this is that once you’ve compiled your XAML code, Visual Studio can then provide IntelliSense capabilities on the properties of your XAML objects. This limitation is likely to disappear in the next version of Visual Studio, but for now it is just a minor annoyance. Once you have built the XAML, you can reference Image1 and Button1 and get a better sense of what properties are available on each.

For now, add the code shown here to your source file:

Public Sub VBButtonClick(ByVal sender As Object, _ ByVal e As System.Windows.RoutedEventArgs) _ Handles Button1.Click Button1.Content = "Hello Billy" Image1.Visibility = Windows.Visibility.Visible End Sub

This code adds a handler for Button1 (since you haven’t renamed the default control names in this example). However, in this case you can leverage the traditional Visual Basic-style Handles keyword. Thus, instead of having a handler in the XAML, the handler is placed in Visual Basic. From an existing application this model probably most closely matches the way you could move an existing handler to the XAML model. It also enables you to see within your application code which methods are in fact event handlers.

At this point, go ahead and compile and run your application. First, check whether the button handler is working. When the button is pressed, the text on the button should change and the image that was collapsed (and essentially invisible) should become visible. If only the button text changes, then the odds are good that there is a problem with the image resource you attempted to include. Otherwise, if you are using the sample code available from the Wrox website, then you should see the screen and image shown in Figure 17-6.

Figure 17-6

As you move the borders on this display, the image maintains the proper aspect. Behind the scenes, even though the Image control has been defined to have a margin of zero from each of the window borders, the image still wants to maintain its aspect. WPF allows this behavior to controls, as discussed earlier in the layout discussion. The image is given the borders of the control on which it is layered, and then the image determines its display size within those limitations. Finally, both the image and the control on which it is layered are displayed.

While WPF is still not fully supported by the tool suite, it does provide a quick and easy way for you to start becoming familiar with this new application development paradigm for user interfaces. You can start designing and planning the next versions of your applications to use these new controls, and by the time the tools release, you will be ready to leverage them for the production version of your application.

Keep in mind that this single chapter hasn’t covered all the new features you can potentially leverage with WPF - that would require an entire book. Instead, you should now have an understanding of the base principals of the WPF programming model and how it integrates with Visual Basic.

Orcas

Orcas is the code name for the version of Visual Studio that will follow Visual Studio 2005. This development environment will include the new WPF designer environment code-named Cider as well as several other enhancements. This tool is currently available in beta, and if you are planning to work with .NET 3.0 features, it is recommended that you participate in this beta program. Visual Studio Orcas will incorporate changes beyond those discussed in this chapter, including new data access strategies and other new features such as Language-Integrated Query (LINQ). LINQ is a new way of accessing both SQL and XML data without needing specialized query languages such as T-SQL and XQuery. This well-publicized feature is outside the scope of this book, but be aware that it will change how you work with much of your application data.

Expression Suite of Tools

In addition to a new version of Visual Studio, with an updated forms designer, Microsoft is also working on a new set of design tools. Called the Expression Suite, it consists of four different graphic design tools. These tools focus on creating vector-based graphics (vs. raster), which can then be incorporated into your WPF display. The tools are not in any way integrated with development languages such as C# or Visual Basic, and as such aren’t a focus of this book. If you want to create a hyped-up graphical interface, then you’ll want to take a look at these tools for help in designing the complex graphics that you can then use within your WPF display.

Категории