Windows Presentation Foundation

Hello, WPF

WPF from Scratch
Navigation Applications
Content Model
Layout
Controls
Data Binding
Dependency Properties
Resources
Styles and Control Templates
Graphics
Application Deployment
Where Are We?

Layout

Introduction
Layout Basics
DockPanel
StackPanel
Grid
Canvas
Viewbox
Text Layout
Common Layout Properties
When Content Doesn't Fit
Custom Layout
Where Are We?

Controls

Introduction
What Are Controls?
Handling Input
Built-In Controls
Where Are We?

Data Binding

Introduction
Without Data Binding
Data Binding
Binding to List Data
Data Sources
Master-Detail Binding
Where Are We?

Styles and Control Templates

Introduction
Without Styles
Inline Styles
Named Styles
Element-Typed Styles
Data Templates and Styles
Triggers
Control Templates
Where Are We?

Resources

Introduction
Creating and Using Resources
Resources and Styles
Binary Resources
Global Applications
Where Are We?

Graphics

Introduction
Graphics Fundamentals
Shapes
Brushes and Pens
Transformations
Visual-Layer Programming
Video and 3-D
Where Are We?

Animation

Animation Fundamentals
Timelines
Storyboards
Key Frame Animations
Creating Animations Procedurally
Where Are We?

Custom Controls

Introduction
Custom Control Basics
Choosing a Base Class
Custom Functionality
Templates
Default Visuals
Where Are We?

ClickOnce Deployment

A Brief History of Windows Deployment
ClickOnce: Local Install
The Pieces of ClickOnce
Publish Properties
Deploying Updates
ClickOnce: Express Applications
Choosing Local Install versus Express
Signing ClickOnce Applications
Programming for ClickOnce
Security Considerations
Where Are We?

Graphics Fundamentals

7.1. Graphics Fundamentals

WPF makes it easy to use graphics in your application and to exploit the power of your graphics hardware. There are many aspects of the graphics architecture that contribute to this goal. This most important of these is integration.

7.1.1. Integration

Graphical elements can be integrated into any part of your user interface. Many GUI technologies tend to separate graphics off into a separate world. This requires a "gear shift" when moving from a world of buttons, text boxes, and other controls into a world of shapes and images, because in many systems, these two worlds have different programming models.

For example, Windows Forms and Mac OS X's Cocoa both provide the ability to arrange controls within a window and build a program that interacts through those controls. They also both provide APIs offering advanced, fully scalable two-dimensional drawing facilities. (GDI+ in the case of Windows Forms, and Quartz 2D on OS X.) But these drawing APIs are distinct from the control APIs. Drawing primitives are very different from controls in these systemsyou cannot mix the two freely.

WPF, on the other hand, treats shapes as elements in the UI tree like any other. So, we are free to mix them in with any other kind of element. Example 7-1 shows various examples of this.

Example 7-1. Mixing graphics with other elements
<DockPanel>
    <StackPanel DockPanel.Dock="Top" Orientation="Horizontal">
        <TextBlock>Mix</TextBlock>
        <Rectangle Fill="Red" Width="20" />
        <TextBlock>text</TextBlock>

        <Ellipse Fill="Blue" Width="40" />
        <TextBlock>and</TextBlock>
        <Button>Controls</Button>
    </StackPanel>
    <Ellipse DockPanel.Dock="Left" Fill="Green" Width="100" />

    <Button DockPanel.Dock="Left">Foo</Button>
    <TextBlock FontSize="24" TextWrap="Wrap">
        And of course you can put graphics into
        your text: <Ellipse Fill="Cyan" Width="50" Height="20" />
    </TextBlock>
</DockPanel>


As you can see, graphical elements can be added seamlessly into the markup. Layout works with graphics exactly as it does for any other element. The results can be seen in Figure 7-1.

Although this example is in XAML, you can also use code to create elements. Most of the examples in this chapter use XAML because the structure of the markup directly reflects the structure of the objects being created. However, whether you use markup or code will depend on what you are doing. If you are creating drawings, you will most likely use a design program to create the XAML for these drawings, but if you are building graphics from data, it might make more sense to do everything from code.

Most of the techniques shown in this chapter can be used in either code or markup. See Appendix A for more information on the relationship between XAML and code.


Not only can graphics and the other content live side by side in the markup, they can even be intermingled. Notice how in Figure 7-1 the ellipse on the right-hand side has

Figure 7-1. Mixed content


been arranged within the flow of the containing TextBlock. If you want to achieve this sort of effect in Windows Forms, it is not possible with its Label controlyou would have to write a whole new control from scratch that draws both the text and the ellipse. This mixing goes both waysnot only can you mix controls into your graphics, you can also use graphical elements to customize the look of your control, as Chapter 5 showed.

This mixing isn't just about simple elements like text blocksit also works for controls. For example, Figure 7-2 shows a button with mixed text and graphics as its caption.

Figure 7-2. Button with graphical content


Traditionally in Windows, you would get this effect by relying on the button being able to display a bitmap. But bitmaps are pretty inflexiblethey are just a block of fixed graphicsso you can't easily make parts of a bitmap interactive or animate selected pieces in response to user input. So, in WPF, putting graphics in buttons works a little differently. The markup for this button is shown in Example 7-2.

Example 7-2. Adding graphics to a Button
<Button>
    <StackPanel Orientation="Horizontal">

        <Canvas Width="20" Height="18" VerticalAlignment="Center">
            <Ellipse Canvas.Left="1" Canvas.Top="1" Width="16" Height="16"
                        Fill="Yellow" Stroke="Black" />
            <Ellipse Canvas.Left="4.5" Canvas.Top="5" Width="2.5" Height="3"
                        Fill="Black" />
            <Ellipse Canvas.Left="11" Canvas.Top="5" Width="2.5" Height="3"
                        Fill="Black" />
            <Path Data="M 5,10 A 3,3 0 0 0 13,10" Stroke="Black" />
        </Canvas>

        <TextBlock VerticalAlignment="Center">Click!</TextBlock>
    </StackPanel>
</Button>

Of course, buttons with images are not a new idea, but traditionally, buttons enabled this by allowing an image to be set. For example, the Windows Forms Button has an Image property, and in Cocoa, NSButton has a setImage method. This implementation is pretty inflexiblethese controls allow a single caption and a single image to be set. Compare this to Example 7-2, which uses a StackPanel to lay out the interior of the button and just adds the content it requires. You can use any layout panel inside the Button, with any kind of content. Example 7-3 uses a Grid to arrange text and some ellipses within a Button. The results are shown in Figure 7-3.

Example 7-3. Layout within a button
<Button HorizontalAlignment="Center" VerticalAlignment="Center">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition />
            <ColumnDefinition />

            <ColumnDefinition />
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition />
            <RowDefinition />
            <RowDefinition />

        </Grid.RowDefinitions>

        <Ellipse Grid.Column="0" Grid.Row="0" Fill="Blue" Width="10" Height="10" />
        <Ellipse Grid.Column="2" Grid.Row="0" Fill="Blue" Width="10" Height="10" />
        <Ellipse Grid.Column="0" Grid.Row="2" Fill="Blue" Width="10" Height="10" />
        <Ellipse Grid.Column="2" Grid.Row="2" Fill="Blue" Width="10" Height="10" />

        <Ellipse Grid.ColumnSpan="3" Grid.RowSpan="3" Stroke="LightGreen"
 StrokeThickness="3" />

        <TextBlock Grid.Column="1" Grid.Row="1" VerticalAlignment="Center">Click!</
 TextBlock>
    </Grid>
</Button>

Figure 7-3. Button with Grid content (Color Plate 17)


In WPF, there is rarely any need for elements to provide inflexible properties such as Text or Image. If it makes sense for an element to present nested content, then it'll do just thatit will present whatever mixture of elements you choose to provide.

If you are familiar with two-dimensional drawing technologies such as Quartz 2D , GDI+ , or good old GDI32 , another advantage in the way drawing is done may well have struck you. We no longer need to write a function to respond to redraw requestsWPF can keep the screen repainted for us. This is because WPF lets us represent drawings as objects.

7.1.2. Drawing Object Model

With many GUI technologies, applications that want customized visuals are required to be able to re-create their appearance from scratch. The usual technique for showing a custom appearance was to write code that performed a series of drawing operations in order to construct the display. This code would run when the relevant graphics first needed to be displayed. In some systems, the OS did not retain a copy of what the application drew, so this method would end up running any time an area needed repaintinge.g., if a window was obscured and then uncovered.

When using this approach, where a rendering function constructs the whole image, updating individual elements is often problematic. Even where the OS does retain a copy of the drawing, it is often retained as a bitmap. This means that if you want to change one part of the drawing, you often need to repaint everything in the area that has changed.

WPF offers a different approach: you can add objects representing graphical shapes to the tree of user-interface elements. Shape elements are objects in the UI tree like any other, so your code can modify them at any time. If you change some property that has a visual impactsuch as the size, location, or colorWPF will automatically update the display.

To illustrate this technique, Example 7-4 shows a simple window containing several ellipses. Each of these is represented by an Ellipse object, which we will use from the code-behind file to update the display.

Example 7-4. Changing graphical elements
<Window x:Class="ChangeItem.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/avalon/2005"
    xmlns:x="http://schemas.microsoft.com/winfx/xaml/2005"
    Text="Change Item"
    >
    <Canvas>
        <Ellipse Canvas.Left="10" Canvas.Top="30" Fill="Indigo"
                   Width="40" Height="20" MouseLeftButtonDown="OnClick" />
        <Ellipse Canvas.Left="20" Canvas.Top="40" Fill="Blue"
                   Width="40" Height="20" MouseLeftButtonDown="OnClick" />
        <Ellipse Canvas.Left="30" Canvas.Top="50" Fill="Cyan"
                   Width="40" Height="20" MouseLeftButtonDown="OnClick" />

        <Ellipse Canvas.Left="40" Canvas.Top="60" Fill="LightGreen"
                   Width="40" Height="20" MouseLeftButtonDown="OnClick" />
        <Ellipse Canvas.Left="50" Canvas.Top="70" Fill="Yellow"
                   Width="40" Height="20" MouseLeftButtonDown="OnClick" />
    </Canvas>
</Window>

Every ellipse's MouseLeftButtonDown event is handled by an OnClick method defined in the code-behind file for this window. As Example 7-5 shows, the OnClick method simply increases the Width property of whichever Ellipse raised the event. The result is that clicking on any ellipse will make it wider.

Example 7-5. Changing a shape at runtime.
using System.Windows;
using System.Windows.Shapes;

namespace ChangeItem
{
    public partial class MainWindow : Window
    {
        public MainWindow( ) : base( )
        {
            InitializeComponent( );
        }


        private void OnClick(object sender, RoutedEventArgs e)
        {
            Ellipse r = (Ellipse) sender;
            r.Width += 10;
        }
    }
}

If we were using the old approach of drawing everything in a single rendering function, this code would not be sufficient to update the display. It would normally be necessary to tell the OS that the screen is no longer valid, causing it to raise a repaint request. But in WPF, this is not necessarywhen you set a property on an Ellipse object, WPF ensures that the screen is updated appropriately. Moreover, WPF is aware that the items all overlap, as shown in Figure 7-4, so it will also redraw the items beneath and above as necessary to get the right results. All you have to do is adjust the properties of the object.

Figure 7-4. Changing overlapping ellipses (Color Plate 18)


Even though computer memory capacities have increased by orders of magnitude since GUIs first started to appear, there are still situations in which this object-model approach for drawing might be too expensive. In particular, for applications dealing with vast data sets, such as maps, having a complete set of objects in the UI tree mirroring the structure of the underlying data could use too much memory. Also, for certain kinds of graphics or data, it may be more convenient to use the old style of rendering code. Because of this, WPF also supports some lighter weight modes of operation. These are described later in this chapter, in the "Visual-Layer Programming" section.


You may have noticed that all of the drawing we've done so far has been with shapes and not bitmaps. WPF supports bitmaps, of course, but there is a good reason to use shapesgeometric shapes can be scaled and rotated without loss of image quality. This high-quality transformability is an important feature of drawing in WPF.

7.1.3. Resolution Independence

Not only have graphics cards improved dramatically since the first GUIs appeared, so have screens. For a long time, the only mainstream display technology was the Cathode Ray Tube (CRT). Color CRTs offered only limited resolutionthey struggled to display images with higher definition than about 100 pixels per inch. However, flat-panel displays, which now outsell CRTs, can exceed this by a large margin.

One of the authors of this book has a two-year-old laptop whose display has a resolution of 150 pixels per inch. At the time of writing, displays are available with over 200 pixels per inch. It is possible to create even higher pixel densities. However, there is a problem with using these screens on current operating systems: everything ends up being so small that it becomes unusable. This is because of a pixel-based development culturethe vast majority of applications measure out their user interfaces in pixels.

This is not entirely the result of technical limitations. Ever since the first version of Windows NT shipped, it has been possible to draw things in a resolution-independent way, because the drawing API, GDI32, allows you to apply transformations to all of your drawings. GDI+, introduced in 2001, offers the same facility. But just because a feature is available doesn't mean applications will use itmost applications don't exploit this scalability.

Unfortunately, the split between graphics and other UI elements in Win32 means that even if an application does exploit the scalability of the drawing APIs, the rest of the UI won't automatically follow. Figure 7-5 shows a Windows Forms application that uses GDI+ to draw text and graphics scaled to an arbitrary size.

Figure 7-5. Incomplete UI Scaling in Windows Forms


Notice in Figure 7-5 that although the star and the "Hello, World" text have been scaled, the track bar and label controls have not. This is because drawing transformations affect only what you draw with GDI+they do not affect the entire UI. And while Windows Forms offers some features to help with scaling the rest of the UI, it's not automaticyou have to take deliberate and nontrivial steps to build a resolution-independent UI in Windows Forms.

7.1.3.1. Scaling and rotation

WPF solves this problem by supporting transformations at a fundamental level. Instead of just providing scalability at the 2-D drawing level, WPF builds it into the underlying composition engine. The result is that everything in the UI can be transformed, not just the user-drawn graphics. Going back to our smiley-face button in Figure 7-2, we can exploit this scalability by modifying just the first line:

    <Button LayoutTransform="scale 3 3">

The LayoutTransform property is available on all user-interface elements in WPF, so you can scale an entire window just as easily as a single button. Many kinds of transformation are available, and these will be discussed in more detail later. For now, we are simply asking to enlarge the button by a factor of 3 in both x- and y-dimensions. Figure 7-6 shows the enlarged button.

Figure 7-6. Enlarged button with graphics (Color Plate 19)


Comparing the original Figure 7-2 with Figure 7-6, the latter is obviously larger, as you would expect. More significantly, the details have become crisper. The rounded edges of the button are easier to see than they were in the small version. The shapes of the letters are much better defined. And our graphic is clearer. We get this clarity because WPF has rendered the button to look as good as it can at the specified size. Compare Figure 7-6 with the examples in Figure 7-7.

Figure 7-7. Enlarged bitmaps


Figure 7-7 shows what happens if you simply enlarge the original small-button bitmap. There are several different ways of enlarging bitmaps. The example on the left uses the simplest algorithm, known as nearest neighbor, or sometimes pixel doubling. To make the image larger, pixels have simply been repeated. This lends a very square feel to the image. The example on the right uses a more sophisticated interpolation algorithm. It has done a better job of keeping rounded edges looking round, and doesn't suffer from the chunky pixel effect, but it ends up looking very blurred. Clearly, neither of these can hold a candle to Figure 7-6.

7.1.3.2. Resolution, coordinates, and "pixels"

This support for scaling graphics means that there is no fixed relationship between the coordinates your application uses and the pixels onscreen. This is true even if you do not use scaling transforms yourselfa transform may be applied automatically to your whole application if it is running on a high-DPI display.

What are the default units of measurement in a WPF application if not physical pixels? The answer is, somewhat confusingly, pixels! To be more precise, the real answer is device-independent pixels .

The official definition of a device-independent pixel in WPF is 1/96th of an inch. If you specify the width of a shape as 96 pixels, this means that it should be exactly one inch wide. WPF will use as many physical pixels as are required to fill one inch. For example, high-resolution laptop screens have a resolution of 150 pixels per inch. So if you make a shape's width 96 "pixels," WPF will render it 150 physical pixels wide.

WPF relies on the system-wide display settings to determine the physical pixel size. You can adjust them through the Windows Display Properties applet. Right-click on your desktop and select Properties to display the applet and then go to the Settings tab. Click on the Advanced button, and, in the dialog that opens, select the General tab. This lets you tell Windows your screen resolution.


You might be wondering why WPF uses the somewhat curious choice of 1/96 inches, and why it calls this a "pixel." The reason is that 96dpi is the default display DPI in Windows when running with Normal Fonts, so this has long been considered the normal size for a pixel. This means that on screens with a normal pixel density, a device-independent pixel will correspond to a physical pixel, and on screens with a high pixel density, WPF will scale your drawings for you so that they remain at the correct physical size.

WPF's ability to optimize its rendering of graphical features for any scale means it is ideally placed to take advantage of increasing screen resolutions. For the first time, onscreen text and graphics will be able to compete with the crisp clarity we have come to expect from laser printers. Of course, for all of this to work in practice, we need a comprehensive suite of drawing primitives.

7.1.4. Shapes, Brushes and Pens

Most of the classes in WPF's drawing toolkit fall into one of three categories: shapes, brushes , and pens . There are many variations on these themes, and we will examine them in detail later. However, to get anywhere at all with graphics, a basic understanding of these classes is mandatory.

Shapes are objects in the user-interface tree that provide the basic building blocks for drawing. The Ellipse, Path, and Rectangle elements we have seen already are all examples of shape objects. There is also support for lines, both simple and multi-segment, using Line and Polyline, respectively. Arbitrary filled shapes can also be created. Polygon enables shapes whose edges are all straight, but if you want curved edges, the Path class supports filled shapes as well as curved lines. Figure 7-8 shows each of these shapes in action.

Figure 7-8. Rectangle, Ellipse, Line, Polyline, Polygon, and Path


Regardless of which shape you choose, you'll need to decide how it should be colored in. For this, you use a brush. There are many brush types available. The simplest is the single-color SolidColorBrush . You can achieve more interesting visual effects using the LinearGradientBrush or RadialGradientBrush . These allow the color to change over the surface of a shape, which can be a great way of providing an impression of depth. You can also create brushes based on imagesthe ImageBrush uses a bitmap, and the DrawingBrush uses a scalable drawing. Finally, the VisualBrush lets you take any visual treeany chunk of user interface you likeand use that as a brush to paint some other shape. This makes it easy to achieve effects like reflections of whole sections of your user interface.

Finally, pens are used to draw the outline of a shape. A pen is really just an augmented brush. When you create a Pen object, you give it a Brush to tell it how it should paint onto the screen. The Pen class just adds information such as line thickness, dash patterns, and end-cap details. Figure 7-9 shows a few of the effects available using brushes and pens.

Figure 7-9. Brushes and pens (Color Plate 20)


7.1.5. Composition

The final key feature of the graphics architecture is composition. In computer graphics, the term composition refers to the process of combining multiple shapes or images together to form the final output. WPF's composition model is very different from the model on which Windows has traditionally worked, and it is crucial to enabling the creation of high-quality visuals.

In the classic Win32 model, each user-interface element (each HWND) has exclusive ownership of some region of the application's window. Within each top-level window, any given pixel in that window is controlled completely by exactly one element. This prevents elements from being partially transparent. It also precludes the use of anti-aliasing around the edges of elements, a technique that is particularly important when combining non-rectangular elements. Although various hacks have been devised to provide the illusion of transparency in Win32, they all have limitations and can be somewhat inconvenient to work with.

WPF's composition model supports elements of any shape and allows them to overlap. It also allows elements to have any mixture of partially and completely transparent areas. This means that any given pixel onscreen may have multiple contributing visible elements. Moreover, WPF uses anti-aliasing around the edges of all shapes. This reduces the jagged appearance that simpler drawing techniques can produce onscreen, resulting in a smooth-looking image. Finally, the composition engine allows any element to have a transformation applied before composition.

WPF's composition engine makes use of the capabilities of modern graphics cards to accelerate the drawing process. Internally, it is implemented on top of the Direct3D model. This may seem odd since the majority of WPF's drawing functionality is two-dimensional, but most of the 3-D-oriented functionality on a modern graphics card can also be used to draw 2-D shapes. For example, pixel shaders can be used to implement the advanced ClearType mechanism when composing text into a UI. WPF exploits the same ultra-fast polygon drawing facilities used by 3-D games to render primitive shapes. Moreover WPF caches portions of the visual tree on the graphics card, significantly improving the repainting performance when changing small details onscreen.

Now that we've seen the core concepts underpinning the WPF graphics system, let's take a closer look at the details.


©2008 FAQ - WPF Labs - Discuss - Terms of Use - Privacy Policy - About WPF
- Interview Questions - Sharepoint Articles - Interview Questions Resource Library - All about LINQ - MS Knowledgebase Articles - Electronics and Hardware discussions