WPF Introduction






Windows Presentation Foundation (WPF) is a library to create the UI for smart client applications. This gives you broad information on the important concepts of WPF. It covers a large number of different controls and their categories, including how to arrange the controls with panels, customize the appearance using styles, resources, and templates, add some dynamic behavior with triggers and animations, and create 3-D with WPF. One of the main advantages of WPF is that work can be easily separated between designers and developers. The outcome from the designer’s work can directly be used by the developer. To make this possible, you need to understand eXtensible Application Markup Language, or XAML. Readers unfamiliar with XAML can read Chapter 29, “Core XAML,” for information about its syntax. The first topic of this chapter provides an overview of the class hierarchy and categories of classes that are used with WPF, including additional information to understand the principles of XAML. WPF consists of several assemblies containing thousands of classes. To help you navigate within this vast number of classes and find what you need, this section explains the class hierarchy and namespaces in WPF.

Class Hierarchy
WPF consists of thousands of classes within a deep hierarchy. For an overview of the relationships between the classes, see Figure. Some classes and their functionality are described in the following table.


Core WPF
SHAPES
Shapes are the core elements of WPF. With shapes you can draw two-dimensional graphics using rectangles, lines, ellipses, paths, polygons, and polylines that are represented by classes derived from the abstract base class Shape. Shapes are defined in the namespace System.Windows.Shapes.

The following XAML example (code file ShapesDemo/MainWindow.xaml) draws a yellow face consisting of an ellipse for the face, two ellipses for the eyes, two ellipses for the pupils in the eyes, and a path for the mouth:

xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="300" Width="300">
Stroke="Blue" StrokeThickness="4" Fill="Yellow" />



StrokeThickness="3" Fill="White" />
1054 CHAPTER 35 CORE WPF
StrokeThickness="3" Fill="White" />
Data="M 40,74 Q 57,95 80,74 " />


TRANSFORMATION

Because WPF is vector-based, you can resize every element. In the next example, the vector-based graphics are now scaled, rotated, and skewed. Hit testing (for example, with mouse moves and mouse clicks) still works but without the need for manual position calculation.

Adding the ScaleTransform element to the LayoutTransform property of the Canvas element, as shown here (code fi le TransformationDemo/MainWindow.xaml), resizes the content of the complete canvas by 1.5 in the x and y axes:


Rotation can be done in a similar way as scaling. Using the RotateTransform element you can define the
Angle for the rotation:

For skewing, you can use the SkewTransform element. With skewing you can assign angles for the x and y
axes:

To rotate and skew together, it is possible to defi ne a TransformGroup that contains both
RotateTransform and SkewTransform. You can also defi ne a MatrixTransform whereby the Matrix
element specifi es the properties M11 and M22 for stretch and M12 and M21 for skew:




Figure- shows the result of all these transformations. The figures are placed inside a StackPanel.
Starting from the left, the first image is resized, the second image is rotated, the third image is skewed, and the fourth image uses a matrix for its transformation. To highlight the differences between these four images, the Background property of the Canvas elements is set to different colors.



BRUSHES
This section demonstrates how to use the brushes that WPF offers for drawing backgrounds
and foregrounds. The examples in this section reference Figure, which shows the effects of using various brushes within a Path and the Background of Button elements.

SolidColorBrush
The first button in Figure uses the SolidColorBrush, which, as the name suggests, uses a solid color. The complete area is drawn with the same color. You can define a solid color just by setting the Background attribute to a string that defines a solid color. The string is converted to a SolidColorBrush element with the help of the BrushValueSerializer:


Of course, you will get the same effect by setting the Background child element and adding a
SolidColorBrush element as its content (code fi le BrushesDemo/MainWindow.xaml). The first button in the application is using PapayaWhip as the solid background color:




LinearGradientBrush

For a smooth color change, you can use the LinearGradientBrush, as the second button shows. This
brush defi nes the StartPoint and EndPoint properties. With this, you can assign two-dimensional
coordinates for the linear gradient. The default gradient is diagonal linear from 0,0 to 1,1. By defi ning different values, the gradient can take different directions. For example, with a StartPoint of 0,0 and an EndPoint of 0,1, you get a vertical gradient. The StartPoint and EndPoint value of 1,0 creates a horizontal gradient. With the content of this brush, you can define the color values at the specified offsets with the GradientStop element. Between the stops, the colors are smoothed
 (code file  BrushesDemo/MainWindow.xaml):






RadialGradientBrush
With the RadialGradientBrush you can smooth the color in a radial way. In Figure, the third element is a Path that uses RadialGradientBrush. This brush defines the color start with the GradientOrigin point (code fi le BrushesDemo/MainWindow.xaml):










DrawingBrush
The DrawingBrush enables you to define a drawing that is created with the brush. The drawing that is shown with the brush is defined within a GeometryDrawing element. The GeometryGroup, which you can see within the Geometry property, consists of the Geometry elements discussed earlier (code file BrushesDemo/MainWindow.xaml):

Blue



Point3="150,63" />
Point3="45,91" />












ImageBrush
To load an image into a brush, you can use the ImageBrush element. With this element, the image defi ned by the ImageSource property is displayed. The image can be accessed from the fi le system or from a resource within the assembly. In the example (code file BrushesDemo/MainWindow.xaml), the image is added as a resource to the assembly and referenced with the assembly and resource names:
Foreground="White">



VisualBrush
The VisualBrush enables you to use other WPF elements in a brush. The following example (code fi le BrushesDemo/MainWindow.xaml) adds a WPF element to the Visual property. The sixth element in Figure contains a Rectangle and a Button:






You can add any UIElement to the VisualBrush. For example, you can play a video by using the
MediaElement:
Foreground="White">





You can also use the VisualBrush to create interesting effects such as reflection. The button coded in
the following example contains a StackPanel that itself contains a MediaElement playing a video and a Border. The Border contains a Rectangle that is filled with a VisualBrush. This brush defines an opacity value and a transformation. The Visual property is bound to the Border element. The transformation is achieved by setting the RelativeTransform property of the VisualBrush. This transformation uses relative coordinates. By setting ScaleY to -1, a reflection in the y axis is done. TranslateTransform moves the transformation in the y axis so that the reflection is below the original object. You can see the result in the eighth element in Figure.

Visual="{Binding ElementName=reflected}">









CONTROLS
Because you can use hundreds of controls with WPF, they are categorized into the following groups, each of which is described in the following sections.

Decoration
You can add decorations to a single element with the Decorator class. Decorator is a base class that has derivations such as Border, Viewbox, and BulletDecorator. Theme elements such as ButtonChrome and ListBoxChrome are also decorators. The following example (code file DecorationsDemo/MainWindow.xaml) demonstrates a Border, Viewbox, and BulletDecorator, as shown in Figure. The Border class decorates the Children element by adding a border around it. You can define a brush and the thickness of the border, the background, the radius of the corner, and the padding of its children:


FIGURE

LAYOUT
To define the layout of the application, you can use a class that derives from the Panel base class. A layout container needs to do two main tasks: measure and arrange. With measuring, the container asks its children for the preferred sizes. Because the full size requested by the controls might not be available, the container determines the available sizes and arranges the positions of its children accordingly. This section discusses several available layout containers.

StackPanel
The Window can contain just a single element as content, but if you want more than one element inside it, you can use a StackPanel as a child of the Window, and add elements to the content of the StackPanel. The StackPanel is a simple container control that just shows one element after the other. The orientation of the StackPanel can be horizontal or vertical. The class ToolBarPanel is derived from StackPanel (code file LayoutDemo/StackPanelWindow.xaml):

xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="StackPanelWindow" Height="300" Width="300">
TextBox
CheckBox
CheckBox
ListBoxItem One
ListBoxItem Two




WrapPanel
The WrapPanel positions the children from left to right, one after the other, as long as they fit into the
line, and then continues with the next line. The panel’s orientation can be horizontal or vertical (code file LayoutDemo/WrapPanelWindow.xaml):

xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="WrapPanelWindow" Height="300" Width="300">



Figure shows the output of the panel. If you resize the application, the buttons will be rearranged accordingly so that they fit into a line.



Canvas
Canvas is a panel that enables you to explicitly position controls. Canvas defines the attached properties Left, Right, Top, and Bottom that can be used by the children for positioning within the panel (code file LayoutDemo/CanvasWindow.xaml):

xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="CanvasWindow" Height="300" Width="300">





DockPanel

The DockPanel is very similar to the Windows Forms docking functionality. Here, you can specify the area in which child controls should be arranged. DockPanel defines the attached property Dock, which you can set in the children of the controls to the values Left, Right, Top, and Bottom. Figure shows the outcome of text blocks with borders that are arranged in the dock panel. For easier differentiation, different colors are specified for the various areas (code file LayoutDemo/
DockPanelWindow.xaml):

xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="DockPanelWindow" Height="300" Width="300">
Menu

Ribbon

Status

Left Side

Remaining Part




Grid
Using the Grid, you can arrange your controls with rows and columns. For every column, you can specify a ColumnDefinition. For every row, you can specify a RowDefinition. The following example code (code file LayoutDemo/GridWindow.xaml) lists two columns and three rows. With each column and row, you can specify the width or height. ColumnDefinition has a Width dependency property; RowDefinition has a Height dependency property. You can define the height and width in pixels, centimeters, inches, or points, or set it to Auto to determine the size depending on the content. The grid also allows star sizing, whereby the space for the rows and columns is calculated according to the available space and relative to other rows and columns. When providing the available space for a column, you can set the Width property to *. To have the size doubled for another column, you specify 2*. The sample code, which defines two columns and three rows, doesn’t define additional settings with the column and row definitions; the default is the star sizing. The grid contains several Label and TextBox controls. Because the parent of these controls is a grid, you can set the attached properties Column, ColumnSpan, Row, and RowSpan:

xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="GridWindow" Height="300" Width="300">


VerticalAlignment="Center" HorizontalAlignment="Center" Content="Title"
/>
Content="Firstname:" Margin="10" />
Content="Lastname:" Margin="10" />


The outcome of arranging controls in a grid is shown in Figure. For easier viewing of the columns and rows, the property ShowGridLines is set to true.



TRIGGERS

With triggers you can change the look and feel of your controls dynamically based on certain events or property value changes. For example, when the user moves the mouse over a button, the button can change its look. Usually, you need to do this with the C# code. With WPF, you can also do this with XAML, as long as only the UI is influenced.
There are several triggers with XAML. Property triggers are activated as soon as a property value changes. Multi-triggers are based on multiple property values. Event triggers fi re when an event occurs. Data triggers happen when data that is bound is changed. This section discusses property triggers, multi-triggers, and data triggers. Event triggers are explained later with animations.

Property Triggers

The Style class has a Triggers property whereby you can assign property triggers. The following example (code fi le TriggerDemo/PropertyTriggerWindow.xaml) includes a Button element inside a Grid panel. With the Window resources, a default style for Button elements is defined. This style specifies that the Background is set to LightBlue and the FontSize to 17. This is the style of the Button elements when the application is started. Using triggers, the style of the controls change. The triggers are defined within the Style.Triggers element, using the Trigger element. One trigger is assigned to the property IsMouseOver; the other trigger is assigned to the property IsPressed. Both of these properties are defined with the Button class to which the style applies. If IsMouseOver has a value of true, then the trigger fires and sets the Foreground property to Red and the FontSize property to 22. If the Button is pressed, then the property IsPressed is true, and the second trigger fi res and sets the Foreground property of the TextBox to Yellow:

xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="PropertyTriggerWindow" Height="300" Width="300">








You don’t need to reset the property values to the original values when the reason for the trigger is not valid anymore. For example, you don’t need to define a trigger for IsMouseOver=true and IsMouseOver=false. As soon as the reason for the trigger is no longer valid, the changes made by the trigger action are reset to the original values automatically. Figure shows the trigger sample application in which the foreground and font size of the button are changed from their original values when the button has the focus. 

Data Triggers
Data triggers fire if bound data to a control fulfills specific conditions. In the following example (code file TriggerDemo/Book.cs), a Book class is used that has different displays depending on the publisher of the book. The Book class defines the properties Title and Publisher and has an overload of the ToString method:

public class Book
{
public string Title { get; set; }
public string Publisher { get; set; }
public override string ToString()
{
return Title;
}
}
In the XAML code, a style is defined for ListBoxItem elements. The style contains DataTrigger elements that are bound to the Publisher property of the class that is used with the items. If the value of the Publisher property is Wrox Press, the Background is set to Red. With the publishers Dummies and Wiley, the Background is set to Yellow and DarkGray, respectively
(code file TriggerDemo/DataTriggerWindow.xaml):

xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Data Trigger Window" Height="300" Width="300">




In the code-behind (code fi le TriggerDemo/DataTriggerWindow.xaml.cs), the list with the name list1 is initialized to contain several Book objects:

public DataTriggerWindow()
{
InitializeComponent();
list1.Items.Add(new Book
{
Title = "Professional C# 4.0 and .NET 4",
Publisher = "Wrox Press"
});
list1.Items.Add(new Book
{
Title = "C# 2010 for Dummies",
Publisher = "For Dummies"
});
list1.Items.Add(new Book
{
Title = "HTML and CSS: Design and Build Websites",
Publisher = "Wiley"
});
}

Running the application, you can see in Figure the ListBoxItem elements that are formatted according to the publisher value. With DataTrigger, multiple properties must be set for
MultiDataTrigger (similar to Trigger and MultiTrigger).


TEMPLATES
In this, you have already seen that a Button control can contain any content. The content can be simple text, but you can also add a Canvas element, which can contain shapes; a Grid; or a video. In fact, you can do even more than that with a button!

Control Templates
Previously in this chapter you’ve seen how the properties of a control can be styled. If setting simple properties of the controls doesn’t give you the look you want, you can change the Template property. With the Template property, you can customize the complete look of the control. The next example demonstrates customizing buttons; and later in the following sections (“Data Templates,” “Styling a ListBox,” “ItemTemplate,” and “Control Templates for ListBox Elements”), list boxes are customized step by step, so you can see the intermediate results of the changes. You customize the Button type in a separate resource dictionary fi le, Styles.xaml. Here, a style with the key name RoundedGelButton is defi ned. The style GelButton sets the properties Background, Height,
Foreground, and Margin, and the Template. The Template is the most interesting aspect with this style. The Template specifi es a Grid with just one row and one column. Inside this cell, you can find an ellipse with the name GelBackground. This ellipse has a linear gradient brush for the stroke. The stroke that surrounds the rectangle is very thin because the StrokeThickness is set to 0.5.
The second ellipse, GelShine, is a small ellipse whose size is defined by the Margin property and so is visible within the first ellipse. The stroke is transparent, so there is no line surrounding the ellipse. This ellipse uses a linear gradient fill brush, which transitions from a light, partly transparent color to full transparency. This gives the ellipse a shimmering effect (code fi le TemplateDemo/Styles.xaml):

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


From the app.xaml fi le, the resource dictionary is referenced as shown here (code fi le TemplateDemo/App.xaml):

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


Now a Button control can be associated with the style. The new look of the button is shown in Figure and uses code fi le TemplateDemo/StyledButtonWindow.xaml:


The button now has a completely different look. However, the content that is defined with the button itself is missing. The template created previously must be extended to get the content of the Button into the new look. What needs to be added is a ContentPresenter. The ContentPresenter is the placeholder for the control’s content, and it defines the place where the content should be positioned. In the code that follows (code fi le TemplateDemo/StyledButtonWindow.xaml), the content is placed in the first row of the Grid, as are the Ellipse elements. The Content property of the ContentPresenter defines what the content should be. The content is set to a TemplateBinding markup expression. TemplateBinding binds the template parent, which is the Button element in this case.

{TemplateBinding Content} specifies that the value of the Content property of the Button control should be placed inside the placeholder as content. Figure shows the result with the content shown in the here:








VerticalAlignment="Center"
HorizontalAlignment="Center"
Content="{TemplateBinding Content}" />





ANIMATIONS
Using animations you can make a smooth transition between images by using moving elements,
color changes, transforms, and so on. WPF makes it easy to create animations. You can animate the value of any dependency property. Different animation classes exist to animate the values of different properties, depending on their type.

The major elements of animations are as follows:
Timeline — Defines how a value changes over time. Different kinds of timelines are available for
changing different types of values. The base class for all timelines is Timeline. To animate a double,
the class DoubleAnimation can be used. Int32Animation is the animation class for int values.
PointAnimation is used to animate points, and ColorAnimation is used to animate colors.

Storyboard — Used to combine animations. The Storyboard class itself is derived from the base class TimelineGroup, which derives from Timeline. With DoubleAnimation you can animate a double value; with Storyboard you combine all the animations that belong together.

Triggers — Used to start and stop animations. You’ve seen property triggers previously, which fi re when a property value changes. You can also create an event trigger. An event trigger fi res when an event occurs.


Timeline:

A Timeline defines how a value changes over time. The following example animates the size of an ellipse. In the code that follows (code fi le AnimationDemo/EllipseWindow.xaml),
a DoubleAnimation timeline changes to a double value. The Triggers property of the Ellipse class is set to an EventTrigger.

The event trigger is fi red when the ellipse is loaded as defined with the RoutedEvent property of the
EventTrigger. BeginStoryboard is a trigger action that begins the storyboard. With the storyboard, a
DoubleAnimation element is used to animate the Width property of the Ellipse class. The animation
changes the width of the ellipse from 100 to 300 within three seconds, and reverses the animation after three seconds. The animation ColorAnimation animates the color from the ellipseBrush which is used to fill the ellipse:


Duration="0:0:3" AutoReverse="True" FillBehavior="Stop"
RepeatBehavior="Forever" AccelerationRatio="0.9"
DecelerationRatio="0.1" From="100" To="300" />
Storyboard.TargetProperty="(SolidColorBrush.Color)"
Duration="0:0:3" AutoReverse="True"
FillBehavior="Stop" RepeatBehavior="Forever"
From="Yellow" To="Red" />









Nonlinear Animations
One way to define nonlinear animations is by setting the speed of AccelerationRatio and
DecelerationRatio animation at the beginning and at the end. .NET 4.5 has more flexible possibilities than that.


Several animation classes have an EasingFunction property. This property accepts an object that
implements the interface IEasingFunction. With this interface, an easing function object can define
how the value should be animated over time. Several easing functions are available to create a nonlinear animation. Examples include ExponentialEase, which uses an exponential formula for animations; QuadraticEase, CubicEase, QuarticEase, and QuinticEase, with powers of 2, 3, 4, or 5; and PowerEase, with a power level that is configurable. Of special interest are SineEase, which uses a sinusoid curve, BounceEase, which creates a bouncing effect, and ElasticEase, which resembles animation values of a spring oscillating back and forth. Such an ease can be specified in XAML by adding the ease to the EasingFunction property of the animation as shown in the following code (code fi le AnimationDemo/EllipseWindow.xaml). Adding different ease functions results in very interesting animation effects:

Duration="0:0:3" AutoReverse="True"
FillBehavior=" RepeatBehavior="Forever"
From="100" To="300">




Event Triggers
Instead of having a property trigger, you can define an event trigger to start the animation. The property trigger fi res when a property changes its value; the event trigger fi res when an event occurs. Examples of such events are the Load event from a control, the Click event from a Button, and the MouseMove event. The next example creates an animation for the face that was created earlier with shapes. It is now animated so that the eye moves as soon as a Click event from a button is fi red. Inside the Window element, a DockPanel element is defined to arrange the face and buttons to control the animation. A StackPanel that contains three buttons is docked at the top. The Canvas element that contains the face gets the remaining part of the DockPanel. The first button is used to start the animation of the eye; the second button stops the animation. A third button is used to start another animation to resize the face. The animation is defined within the DockPanel.

Triggers section. Instead of a property trigger, an event trigger is used. The first event trigger is fi red as soon as the Click event occurs with the buttonBeginMove-Eyes button defined by the RoutedEvent and SourceName properties. The trigger action is defined by the BeginStoryboard element that starts the containing Storyboard. BeginStoryboard has a name defined because a name is needed to control the storyboard with pause, continue, and stop actions. The Storyboard element contains four animations. The first two animate the left eye; the last two animate the right eye. The
first and third animation change the Canvas.Left position for the eyes, and the second and fourth
animation change Canvas.Top. The animations in the x and y axes have different time values that make the eye movement very interesting using the defined repeated behavior.

The second event trigger is fi red as soon as the Click event of the buttonStopMoveEyes button occurs. Here, the storyboard is stopped with the StopStoryboard element, which references the started storyboard beginMoveEye. The third event trigger is fi red by clicking the buttonResize button. With this animation, the transformation of the Canvas element is changed. Because this animation doesn’t run endlessly, there’s no stop.
This storyboard also makes use of the EaseFunction explained previously (code file AnimationDemo/EventTriggerWindow.xaml):

xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="EventTriggerWindow" Height="300" Width="300">
AutoReverse="True" By="6" Duration="0:0:1"
Storyboard.TargetName="eyeLeft"
Storyboard.TargetProperty="(Canvas.Left)" />
By="6" Duration="0:0:5"
Storyboard.TargetName="eyeLeft"
Storyboard.TargetProperty="(Canvas.Top)" />
AutoReverse="True" By="-6" Duration="0:0:3"
Storyboard.TargetName="eyeRight"
Storyboard.TargetProperty="(Canvas.Left)" />
By="6" Duration="0:0:6"
Storyboard.TargetName="eyeRight"
Storyboard.TargetProperty="(Canvas.Top)" />




Storyboard.TargetName="scale1"
Storyboard.TargetProperty="(ScaleTransform.ScaleX)"
From="0.1" To="3" Duration="0:0:5">


Storyboard.TargetName="scale1"
Storyboard.TargetProperty="(ScaleTransform.ScaleY)"
From="0.1" To="3" Duration="0:0:5">








Stroke="Blue" StrokeThickness="4" Fill="Yellow" />



Stroke="Blue" StrokeThickness="3" Fill="White" />
Height="5" Fill="Black" />
Stroke="Blue" StrokeThickness="3" Fill="White" />
Height="5" Fill="Black" />
Data="M 40,74 Q 57,95 80,74 " />







Business Applications with WPF

INTRODUCTION
In the previous chapter you read about some of the core functionality of WPF. This chapter continues
the journey through WPF. Here you read about important aspects for creating complete applications,
such as data binding and command handling, and about the DataGrid control. Data binding is an
important concept for bringing data from .NET classes into the user interface, and allowing the user
to change data. WPF not only allows binding to simple entities or lists, but also offers binding of one
UI property to multiple properties of possible different types with multi binding and priority binding that you’ll learn here as well. Along with data binding it is also important to validate data entered by a user. Here, you can read about different ways for validation including the interface INotifyDataErrorInfo that is new with .NET 4.5. Also covered in this chapter is commanding, which enables mapping events from the UI to code. In contrast to the event model, this provides a better separation between XAML and code. You will learn about using predefined commands and creating custom commands. The TreeView and DataGrid controls are UI controls to display bound data. You will see the TreeView control to display data in the tree where data is loaded dynamically depending on the selection of the user. With the DataGrid control you will learn how to using filtering, sorting, and grouping, as well as one new
.NET 4.5 features named live shaping that allows changing sorting or filtering options to change in real time. To begin let’s start with the Menu and the Ribbon controls. The Ribbon control made it into the release of .NET 4.5.

MENU AND RIBBON CONTROLS
Many data-driven applications contain menus and toolbars or ribbon controls to enable users to control actions. With WPF 4.5, ribbon controls are now available as well, so both menu and ribbon controls are covered here. In this section, you create a new WPF application named BooksDemo to use throughout this chapter — not only with menu and ribbon controls but also with commanding and data binding. This application displays a single book, a list of books, and a grid of books. Actions are started from menu or ribbon controls to which commands associated.

Menu Controls
Menus can easily be created with WPF using the Menu and MenuItem elements, as shown in the following code snippet containing two main menu items, File and Edit, and a list of submenu entries. The _ in front of the characters marks the special character that can be used to access the menu item easily without using the mouse. Using the Alt key makes these characters visible and enables access to the menu with this character. Some of these menu items have a command assigned, as discussed in the next section (XAML file BooksDemo/MainWindow.xaml):

xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Wrox.ProCSharp.WPF"
Title="Books Demo App" Height="400" Width="600">












BooksDemo Application Content
In the previous sections, a ribbon and commands have been defined with the BooksDemo application. Now content is added. Change the XAML fi le MainWindow.xaml by adding a ListBox, a Hyperlink, and a TabControl (XAML fi le BooksDemo/MainWindow.xaml):

Show Book



Now add a WPF user control named BookUC. This user control contains a DockPanel, a Grid with several rows and columns, a Label, and TextBox controls (XAML fi le BooksDemo/BookUC.xaml):
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="300">



HorizontalAlignment="Left" VerticalAlignment="Center" />
Margin="10,0,5,0" HorizontalAlignment="Left"
VerticalAlignment="Center" />
Margin="10,0,5,0" HorizontalAlignment="Left"
VerticalAlignment="Center" />





Within the OnShowBook handler in the MainWindow.xaml.cs, create a new instance of the user control BookUC and add a new TabItem to the TabControl. Then change the SelectedIndex property of the TabControl to open the new tab (code fi le BooksDemo/MainWindow.xaml.cs):

private void OnShowBook(object sender, ExecutedRoutedEventArgs e)
{
var bookUI = new BookUC();
this.tabControl1.SelectedIndex = this.tabControl1.Items.Add(
new TabItem { Header = "Book", Content = bookUI });
}

After building the project you can start the application and open the user control within the TabControl by clicking the hyperlink.

Binding with XAML
In addition to being the target for data binding, a WPF element can also be the source. You can bind the source property of one WPF element to the target of another WPF element.

In the following code example, data binding is used to resize the controls within the user control with a slider. You add a StackPanel control to the user control BookUC, which contains a Label and a Slider BUSINESS APPLICATIONS WITH WPF control. The Slider control defines Minimum and Maximum values that define the scale, and an initial value of 1 is assigned to the Value property

(XAML file BooksDemo/BooksUC.xaml):
HorizontalAlignment="Right">
Width="150" HorizontalAlignment="Right" />


Now you set the LayoutTransform property of the Grid control and add a ScaleTransform element. With the ScaleTransform element, the ScaleX and ScaleY properties are data bound. Both properties are set with the Binding markup extension. In the Binding markup extension, the ElementName is set to slider1 to reference the previously created Slider control. The Path property is set to the Value property to get the value of the slider:

ScaleX="{Binding Path=Value, ElementName=slider1}"
ScaleY="{Binding Path=Value, ElementName=slider1}" />


When running the application, you can move the slider and thus resize the controls within the Grid, as shown in Figures –






Simple Object Binding
To bind to CLR objects, with the .NET classes you just have to define properties, as shown in the Book class example and the properties Title, Publisher, Isbn, and Authors. This class is in the Data folder of the BooksDemo project (code fi le BooksDemo/Data/Book.cs).

using System.Collections.Generic;
namespace Wrox.ProCSharp.WPF.Data
{
public class Book
{
public Book(string title, string publisher, string isbn,
params string[] authors)
{
this.Title = title;
this.Publisher = publisher;
this.Isbn = isbn;
this.authors.AddRange(authors);
}
public Book()
: this("unknown", "unknown", "unknown")
{
}
public string Title { get; set; }
public string Publisher { get; set; }
public string Isbn { get; set; }
private readonly List authors = new List();
public string[] Authors
{
get
{
return authors.ToArray();
}
}
public override string ToString()
{
return Title;
}
}
}
In the XAML code of the user control BookUC, several labels and TextBox controls are defined to
display book information. Using Binding markup extensions, the TextBox controls are bound to the
properties of the Book class. With the Binding markup extension, nothing more than the Path property is defined to bind it to the property of the Book class. There’s no need to define a source because the source is defined by assigning the DataContext, as shown in the code-behind that follows. The mode is defined by its default with the TextBox element, and this is two-way binding (XAML fi le BooksDemo/BookUC.xaml):

Text=
"{Binding Title}" Grid.Row="0" Grid.Column="1" Margin="5" />
Text=
"{Binding Publisher}" Grid.Row="1" Grid.Column="1" Margin="5" />
Text=
"{Binding Isbn}" Grid.Row="2" Grid.Column="1" Margin="5" />

With the code-behind, a new Book object is created, and the book is assigned to the DataContext
property of the user control. DataContext is a dependency property that is defined with the base class
FrameworkElement. Assigning the DataContext with the user control means that every element in the user control has a default binding to the same data context (code file BooksDemo/MainWindow.xaml.cs):

private void OnShowBook(object sender, ExecutedRoutedEventArgs e)
{
var bookUI = new BookUC();
bookUI.DataContext = new Book
{
Title = "Professional C# 4 and .NET 4",
Publisher = "Wrox Press",
Isbn = "978-0-470-50225-9"
};
this.tabControl1.SelectedIndex =
this.tabControl1.Items.Add(
new TabItem { Header = "Book", Content = bookUI });
}



To see two-way binding in action (changes to the input of the WPF element are reflected inside
the CLR object), the Click event handler of the button in the user control, the OnShowBook method,
is implemented. When implemented, a message box pops up to show the current title and ISBN number of the book1 object. Figure 36-8 shows the output from the message box after a change to the input was made during runtime (code fi le BooksDemo/BookUC.xaml.cs):

private void OnShowBook(object sender, RoutedEventArgs e)
{
Book theBook = this.DataContext as Book;
if (theBook != null)
MessageBox.Show(theBook.Title, theBook.Isbn);
}







Creating Documents with WPF

INTRODUCTION
Creating documents is a large part of WPF. The namespace System.Windows.Documents supports
creating both flow documents and fixed documents. This namespace contains elements with which
you can have a rich Word-like experience with flow documents, and create WYSIWYG fixed
documents.

Flow documents are geared toward screen reading; the content of the document is arranged based
on the size of the window and the flow of the document changes if the window is resized. Fixed
documents are mainly used for printing and page-oriented content and the content is always arranged
in the same way. This chapter teaches you how to create and print flow documents and fixed documents, and covers the namespaces System.Windows.Documents, System.Windows.Xps, and System.IO.Packaging.

TEXT ELEMENTS

To build the content of documents, you need document elements. The base class of these elements is
TextElement. This class defines common properties for font settings, foreground and background, and text effects. TextElement is the base class for the classes Block and Inline, whose functionality is explored in the following sections.
Fonts
An important aspect of text is how it looks, and thus the importance of the font. With the TextElement, the font can be specified with the properties FontWeight, FontStyle, FontStretch, FontSize, and FontFamily:

FontWeight — Predefined values are specified by the FontWeights class, which offers values such       as UltraLight, Light, Medium, Normal, Bold, UltraBold, and Heavy.

FontStyle — Values are defined by the FontStyles class, which offers Normal, Italic, and
Oblique.

FontStretch — Enables you to specify the degrees to stretch the font compared to the normal aspect ratio. FrontStretch defines predefined stretches that range from 50% (UltraCondensed) to 200% (UltraExpanded). Predefined values in between the range are ExtraCondensed (62.5%), Condensed (75%), SemiCondensed (87.5%), Normal (100%), SemiExpanded (112.5%), Expanded (125%), and ExtraExpanded (150%).

FontSize — This is of type double and enables you to specify the size of the font in device-independent units, inches, centimeters, and points.

FontFamily — Use this to define the name of the preferred font-family, e.g., Arial or Times New
Roman. With this property you can specify a list of font family names so if one font is not available,
the next one in the list is used. (If neither the selected font nor the alternate font are available, a flow
document falls back to the default MessageFontFamily.) You can also reference a font family from a
resource or use a URI to reference a font from a server. With fixed documents there’s no fallback on a font not available because the font is available with the document.
To give you a feel for the look of different fonts, the following sample WPF application includes a ListBox. The ListBox defines an ItemTemplate for every item in the list. This template uses four TextBlock elements whereby the FontFamily is bound to the Source property of a FontFamily object. With different TextBlock elements, FontWeight and FontStyle are set (XAML file ShowFonts/ShowFontsWindow.xaml):

FontSize="18" Text="{Binding Path=Source}" />
FontSize="18" FontStyle="Italic" Text="Italic" />
FontSize="18" FontWeight="UltraBold" Text="UltraBold" />
FontSize="18" FontWeight="UltraLight" Text="UltraLight" />





In the code-behind, the data context is set to the result of the SystemFontFamilies property of
the System.Windows.Media.Font class. This returns all the available fonts (code fi le ShowFonts/
ShowFontsWindow.xaml.cs):



TextEffect
Now let’s have a look into TextEffect, as it is also common to all document elements. TextEffect is defined in the namespace System.Windows.Media and derives from the base class Animatable, which enables the animation of text. TextEffect enables you to animate a clipping region, the foreground brush, and a transformation. With the properties PositionStart and PositionCount you specify the position in the text to which the animation applies.

For applying the text effects, the TextEffects property of a Run element is set. The TextEffect
element specifi ed within the property defines a foreground and a transformation. For the foreground, a SolidColorBrush with the name brush1 is used that is animated with a ColorAnimation element. The transformation makes use of a ScaleTransformation with the name scale1, which is animated from two DoubleAnimation elements (XAML file TextEffectsDemo/MainWindow.xaml):

From="Blue" To="Red" Duration="0:0:16"
Storyboard.TargetName="brush1"
Storyboard.TargetProperty="Color" />
RepeatBehavior="Forever"
From="0.2" To="12" Duration="0:0:16"
Storyboard.TargetName="scale1"
Storyboard.TargetProperty="ScaleX" />
RepeatBehavior="Forever"
From="0.2" To="12" Duration="0:0:16"
Storyboard.TargetName="scale1"
Storyboard.TargetProperty="ScaleY" />




cn|elements








Running the application, you can see the changes in size and color as shown in Figures






FLOW DOCUMENTS
With all the Inline and Block elements, now you know what should be put into a flow document. The
class Flow Document can contain Block elements, and the Block elements can contain Block or Inline elements, depending on the type of the Block. A major functionality of the FlowDocument class is that it is used to break up the flow into multiple pages.

This is done via the IDocumentPaginatorSource interface, which is implemented by FlowDocument.
Other options with a FlowDocument are to set up the default font and foreground and background brushes, and to confi gure the page and column sizes.

The following XAML code for the FlowDocument defines a default font and font size, a column width, and a ruler between columns:

ColumnWidth="300" FontSize="16" FontFamily="Georgia"
ColumnRuleWidth="3" ColumnRuleBrush="Violet">

Now you just need a way to view the documents. The following list describes several viewers:

RichTextBox — A simple viewer that also allows editing (as long as the IsReadOnly property is not set to true). The RichTextBox doesn’t display the document with multiple columns but instead in
scroll mode. This is similar to the Web layout in Microsoft Word. The scrollbar can be enabled by setting the HorizontalScrollbarVisibility to ScrollbarVisibility.Auto.

FlowDocumentScrollViewer — A reader that is meant only to read but not edit documents.
This reader enables zooming into the document. There’s also a toolbar with a slider for zooming
that can be enabled with the property IsToolbarEnabled. Settings such as CanIncreaseZoom,
CanDecreaseZoom, MinZoom, and MaxZoom enable setting the zoom features.

FlowDocumentPageViewer — A viewer that paginates the document. With this viewer you not only have a toolbar to zoom into the document, you can also switch from page to page.

FlowDocumentReader — A viewer that combines the functionality of FlowDocumentScrollViewer and FlowDocumentPageViewer. This viewer supports different viewing modes that can be set from the toolbar or with the property ViewingMode that is of type FlowDocumentReaderViewingMode. This enumeration has the possible values Page, TwoPage, and Scroll. The viewing modes can also be disabled according to your needs. The sample application to demonstrate flow documents defines several readers such that one reader can be chosen dynamically. Within the Grid element you can find the FlowDocumentReader, RichTextBox,
FlowDocumentScrollViewer, and FlowDocumentPageViewer.

With all the readers the Visibility property is set to Collapsed, so on startup none of the readers appear. The ComboBox that is the first child element within the grid enables the user to select the active reader. The ItemsSource property of the ComboBox is bound to the Readers property to display the list of readers. On selection of a reader, the method OnReaderSelectionChanged is invoked (XAML fi le FlowDocumentsDemo/MainWindow.xaml):



ItemsSource=”{Binding Readers}”
Grid.Row="0" Grid.Column="0"
Margin="4" SelectionChanged="OnReaderSelectionChanged"
SelectedIndex="0">




Open Document

Visibility=”Collapsed” Grid.ColumnSpan=”2” />
VerticalScrollBarVisibility=”Auto” Visibility=”Collapsed”
Grid.Row=”1” Grid.ColumnSpan=”2” />
Grid.ColumnSpan=”2” />
Grid.ColumnSpan=”2” />


The Readers property of the MainWindow class invokes the GetReaders method to return to return the readers to the ComboBox data binding. The GetReaders method returns the list assigned to the variable documentReaders. In case documentReaders was not yet assigned, the LogicalTreeHelper class is used to get all the flow document readers within the grid grid1. As there is not a base class for a flow document reader nor an interface implemented by all readers, the LogialTreeHelper looks for all elements of type FrameworkElement that have a property Document. The Document property is common to all flow document readers. With every reader a new anonymous object is created with the properties Name and Instance. The Name property is used to appear in the ComboBox to enable the user to select the active reader, and the Instance property holds a reference to the reader to show the reader if it should be active (code file FlowDocumentsDemo/MainWindow.xaml.cs):

public IEnumerable Readers
{
get
{
return GetReaders();
}
}
private List documentReaders = null;
private IEnumerable GetReaders()
{
return documentReaders ?? (documentReaders =
LogicalTreeHelper.GetChildren(grid1).OfType()
.Where(el => el.GetType().GetProperties()
.Where(pi => pi.Name == "Document").Count() > 0)
.Select(el => new
{
Name = el.GetType().Name,
Instance = el
}).Cast().ToList());
}

When the user selects a flow document reader, the method OnReaderSelectionChanged is invoked. The XAML code that references this method was shown earlier. Within this method the previously selected flow document reader is made invisible by setting it to collapsed, and the variable activeDocumentReader is set to the selected reader:

private void OnReaderSelectionChanged(object sender,
SelectionChangedEventArgs e)
{
dynamic item = (sender as ComboBox).SelectedItem;
if (activedocumentReader != null)
{
activedocumentReader.Visibility = Visibility.Collapsed;
}
activedocumentReader = item.Instance;
}
private dynamic activedocumentReader = null;

When the user clicks the button to open a document, the method OnOpenDocument is invoked. With this method the XamlReader class is used to load the selected XAML file. If the reader returns a FlowDocument (which is the case when the root element of the XAML is the FlowDocument element), the Document property of the activeDocumentReader is assigned, and the Visibility is set to visible:

private void OnOpenDocument(object sender, RoutedEventArgs e)
{
try
{
var dlg = new OpenFileDialog();
dlg.DefaultExt = "*.xaml";
dlg.InitialDirectory = Environment.CurrentDirectory;
if (dlg.ShowDialog() == true)
{
using (FileStream xamlFile = File.OpenRead(dlg.FileName))
{
var doc = XamlReader.Load(xamlFile) as FlowDocument;
if (doc != null)
{
activedocumentReader.Document = doc;
activedocumentReader.Visibility = Visibility.Visible;
}
}
}
}
catch (XamlParseException ex)
{
MessageBox.Show(string.Format("Check content for a Flow document, {0}",
ex.Message));
}
}




FIXED DOCUMENTS
Fixed documents always define the same look, the same pagination, and use the same fonts — no matter where the document is copied or used. WPF defines the class FixedDocument to create fixed documents, and the class DocumentViewer to view fixed documents. This section uses a sample application to create a fixed document programmatically by requesting user input for a menu plan. The data for the menu plan is the content of the fixed document. Figure shows the main user interface of this application, where the user can select a day with the DatePicker class, enter menus for a week in a DataGrid, and click the Create Doc button to create a new FixedDocument. This application uses Page objects that are navigated within a NavigationWindow. Clicking the Create Doc button navigates to a new page that contains the fixed document.



The event handler for the Create Doc button, OnCreateDoc, navigates to a new page. To do this, the
handler instantiates the new page, DocumentPage. This page includes a handler, NavigationService_
LoadCompleted, that is assigned to the LoadCompleted event of the NavigationService. Within
this handler the new page can access the content that is passed to the page. Then the navigation is
done by invoking the Navigate method to page2. The new page receives the object menus that contains all the menu information needed to build the fixed page. menus is a readonly variable of type ObservableCollection (code fi le CreateXps/MenuPlannerPage.xaml.cs):

private void OnCreateDoc(object sender, RoutedEventArgs e)
{
if (menus.Count == 0)
{
MessageBox.Show("Select a date first", "Menu Planner",
MessageBoxButton.OK);
return;
}
var page2 = new DocumentPage();
NavigationService.LoadCompleted +=
page2.NavigationService_LoadCompleted;
NavigationService.Navigate(page2, menus);
}

Within the DocumentPage, a DocumentViewer is used to provide read access to the fixed document. The fixed document is created in the method NavigationService_LoadCompleted. With the event handler, the data that is passed from the first page is received with the ExtraData property of NavigationEventArgs. The received ObservableCollection is assigned to a menus variable that is used to build the fixed page (code file CreateXps/DocumentPage.xaml.cs):

internal void NavigationService_LoadCompleted(object sender,
NavigationEventArgs e)
{
menus = e.ExtraData as ObservableCollection;
fixedDocument = new FixedDocument();
var pageContent1 = new PageContent();
fixedDocument.Pages.Add(pageContent1);
var page1 = new FixedPage();
pageContent1.Child = page1;
page1.Children.Add(GetHeaderContent());
page1.Children.Add(GetLogoContent());
page1.Children.Add(GetDateContent());
page1.Children.Add(GetMenuContent());
viewer.Document = fixedDocument;
NavigationService.LoadCompleted -= NavigationService_LoadCompleted;
}

Fixed documents are created with the FixedDocument class. The FixedDocument element only contains PageContent elements that are accessible via the Pages property. The PageContent elements must be added to the document in the order in which they should appear on the page. PageContent defines the content of a single page. PageContent has a Child property such that a FixedPage can be associated with it. To the FixedPage you can add elements of type UIElement to the Children collection. This is where you can add all the elements you’ve learned about in the last two chapters, including a TextBlock element that itself can contain Inline and Block elements. In the sample code, the children to the FixedPage are created with helper methods GetHeaderContent, GetLogoContent, GetDateContent, and GetMenuContent. The method GetHeaderContent creates a TextBlock that is returned. The TextBlock has the Inline element Bold added, which in turn has the Run element added. The Run element then contains the header text for the document. With FixedPage.SetLeft and FixedPage.SetTop the position of the TextBox within the fixed page is defined:

private static UIElement GetHeaderContent()
{
var text1 = new TextBlock
{
FontFamily = new FontFamily("Segoe UI"),
FontSize = 34,
HorizontalAlignment = HorizontalAlignment.Center
};
text1.Inlines.Add(new Bold(new Run("cn|elements")));
FixedPage.SetLeft(text1, 170);
FixedPage.SetTop(text1, 40);
return text1;
}

The method GetLogoContent adds a logo in the form of an Ellipse with a RadialGradientBrush to the
fixed document:
private static UIElement GetLogoContent()
{
var ellipse = new Ellipse
{
Width = 90,
Height = 40,
Fill = new RadialGradientBrush(Colors.Yellow, Colors.DarkRed)
};
FixedPage.SetLeft(ellipse, 500);
FixedPage.SetTop(ellipse, 50);
return ellipse;
}
The method GetDateContent accesses the menus collection to add a date range to the document:
private UIElement GetDateContent()
{
Contract.Requires(menus != null);
Contract.Requires(menus.Count > 0);
string dateString = String.Format("{0:d} to {1:d}",
menus[0].Day, menus[menus.Count - 1].Day);
var text1 = new TextBlock
{
FontSize = 24,
HorizontalAlignment = HorizontalAlignment.Center
};
text1.Inlines.Add(new Bold(new Run(dateString)));
FixedPage.SetLeft(text1, 130);
FixedPage.SetTop(text1, 90);
return text1;
}

Finally, the method GetMenuContent creates and returns a Grid control. This grid contains columns and rows that contain the date, menu, and price information:

private UIElement GetMenuContent()
{
var grid1 = new Grid { ShowGridLines = true };
grid1.ColumnDefinitions.Add(new ColumnDefinition
{ Width= new GridLength(50)});
grid1.ColumnDefinitions.Add(new ColumnDefinition
{ Width = new GridLength(300)});
grid1.ColumnDefinitions.Add(new ColumnDefinition
{ Width = new GridLength(70) });
for (int i = 0; i < menus.Count; i++)
{
grid1.RowDefinitions.Add(new RowDefinition
{ Height = new GridLength(40) });
var t1 = new TextBlock(new Run(String.Format(
"{0:ddd}", menus[i].Day)));
var t2 = new TextBlock(new Run(menus[i].Menu));
var t3 = new TextBlock(new Run(menus[i].Price.ToString()));
var textBlocks = new TextBlock[] { t1, t2, t3 };
for (int column = 0; column < textBlocks.Length; column++)

{
textBlocks[column].VerticalAlignment = VerticalAlignment.Center;
textBlocks[column].Margin = new Thickness(5, 2, 5, 2);
Grid.SetColumn(textBlocks[column], column);
Grid.SetRow(textBlocks[column], i);
grid1.Children.Add(textBlocks[column]);
}
}
FixedPage.SetLeft(grid1, 100);
FixedPage.SetTop(grid1, 140);
return grid1;
}
Run the application to see the created fixed document shown in Figure-




XPS DOCUMENTS
With Microsoft Word you can save a document as a PDF or a XPS fi le. XPS is the XML Paper Specifi cation, a subset of WPF. Windows includes an XPS reader. .NET includes classes and interfaces to read and write XPS documents with the namespaces System .Windows.Xps, System.Windows.Xps.Packaging, and System.IO.Packaging. XPS is packaged in the zip fi le format, so you can easily analyze an XPS document by renaming a fi le with an .xps extension to .zip and opening the archive.

An XPS fi le requires a specific structure in the zipped document that is defined by the XML Paper
Specifi cations (which you can download from http://www.microsoft.com/whdc/xps/xpsspec.mspx).
The structure is based on the Open Packaging Convention (OPC) that Word documents (OOXML or Office Open XML) are based on as well. Within such a fi le you can find different folders for metadata, resources (such as fonts and pictures), and the document itself. Within the document folder of an XPS document is the XAML code representing the XPS subset of XAML. To create an XPS document, you use the XpsDocument class from the namespace System.Windows.Xps .Packaging. To use this class, you need to reference the assembly ReachFramework as well. With this class
you can add a thumbnail (AddThumbnail) and fixed document sequences  (AddFixedDocumentSequence) to the document, as well as digitally sign the document. A fixed document sequence is written by using the interface IXpsFixedDocumentSequenceWriter, which in turn uses an IXpsFixedDocumentWriter to write the document within the sequence.

If a FixedDocument already exists, there’s an easier way to write the XPS document. Instead of adding every resource and every document page, you can use the class XpsDocumentWriter from the namespace System .Windows.Xps. For this class the assembly System.Printing must be referenced.
With the following code snippet you can see the handler to create the XPS document. First, a fi lename for the menu plan is created that uses a week number in addition to the name menuplan. The week number is calculated with the help of the GregorianCalendar class. Then the SaveFileDialog is opened to enable the user overwrite the created filename and select the directory where the fi le should be stored. The SaveFileDialog class is defined in the namespace Microsoft.Win32 and wraps the native fi le dialog. Then a new XpsDocument is created whose filename is passed to the constructor. Recall that the XPS fi le uses a .zip format to compress the content. With the CompressionOption you can specify whether the compression should be optimized for time or space.
Next, an XpsDocumentWriter is created with the help of the static method XpsDocument
.CreateXpsDocumentWriter. The Write method of the XpsDocumentWriter is overloaded to accept
different content or content parts to write the document. Examples of acceptable options with the Write method are FixedDocumentSequence, FixedDocument, FixedPage, string, and a DocumentPaginator. In the sample code, only the fixedDocument that was created earlier is passed:

private void OnCreateXPS(object sender, RoutedEventArgs e)
{
var c = new GregorianCalendar();
int weekNumber = c.GetWeekOfYear(menus[0].Day,
CalendarWeekRule.FirstFourDayWeek, DayOfWeek.Monday);
string fileName = String.Format("menuplan{0}", weekNumber);
var dlg = new SaveFileDialog
{
FileName = fileName,
DefaultExt = "xps",
Filter = "XPS Documents|*.xps|All Files|*.*",
AddExtension = true
};
if (dlg.ShowDialog() == true)
{



var doc = new XpsDocument(dlg.FileName, FileAccess.Write,
CompressionOption.Fast);
XpsDocumentWriter writer = XpsDocument.CreateXpsDocumentWriter(doc);
writer.Write(fixedDocument);
doc.Close();
}
}
By running the application to store the XPS document, you can view the document with an XPS viewer, as shown in Figure



 Reach us At: - 0120-4029000; 0120-4029024; 0120-4029025, 0120-4029027; 0120-4029029 
Mbl: 9953584548
Write us at: - Smruti@apextgi.com and pratap@apextgi.com


No comments:

Post a Comment