Hello World with WPF and IronPython 1
To use WPF, you'll need to install .NET 3.0. If you're running Vista, then you already have .NET 3.0 installed.
Installing .NET 3 installs the following key WPF assemblies:
■ PresentationFramework.dll—Holds the high-level WPF controls and types, such as windows and panels.
■ PresentationCore.dll—Holds base types from which all the controls and display elements derive, including the core classes UIElement and Visual.
■ WindowsBase.dll—Contains lower-level objects that still need to be publicly exposed, such as DispatcherObject and DependencyObject. We use one or two classes, like Point, provided by this assembly.
■ milcore.dll—The WPF rendering engine (and an essential part of Windows Vista).
■ WindowsCodecs.dll—A low-level imaging support library.
We can get a long way using only the first two of these assemblies, PresentationFramework and PresentationCore. These assemblies contain the namespace used by WPF, all of which start with System.Windows.1 (System.Windows.Forms is the only namespace in this hierarchy which isn't part of WPF.) Table 9.2 shows the important WPF namespaces, all of which we use in this chapter.
|
Namespace |
Purpose |
|
System.Windows System.Windows.Controls |
Provides important classes and base element classes used in WPF applications. This includes Application, Clipboard, Window, and UIElement. Provides the standard WPF controls, plus the classes used to create user controls. |
1 The MSDN documentation for this namespace is at http://msdn2.microsoft.com/en-us/library/system.win-dows.aspx.
1 The MSDN documentation for this namespace is at http://msdn2.microsoft.com/en-us/library/system.win-dows.aspx.
|
Namespace |
Purpose | ||
|
System |
Windows |
Documents |
Types for supporting FlowDocument and XPS documents. |
|
System |
Windows |
Media |
For integrating rich media, including drawings, text, and audio and video content in WPF applications. |
|
System |
Windows |
Media.Effects |
Types for applying visual effects to bitmaps. |
|
System |
Windows |
Media.Imaging |
Provides classes for working with images (bitmaps). |
|
System |
Windows |
Shapes |
Various shapes (such as ellipse, line, path, and polygon, plus the base class Shape)—for use in XAML and code. |
|
System |
Windows |
Markup |
Classes for working with XAML, including serialization and extensions to XAML. |
There's an enormous variety of classes in WPF, but the basics are easy to start with. We begin with a traditional Hello World example.
9.1.1 WPF from code
We've talked enough about WPF—it's time to see it in action. Listing 9.12 creates a Window, and an attractive-looking button, contained in a StackPanel. Figure 9.4 is the result of running the code. Figure 9.4 Hello World with WPF and IronPython
Listing 9.1 Hello World with WPF and IronPython import clr clr.AddReference("PresentationFramework") clr.AddReference("PresentationCore")
from System.Windows import ( Application, SizeToContent, Thickness, Window
from System.Windows.Controls import ( Button, Label, StackPanel
from System.Windows.Media.Effects import DropShadowBitmapEffect window = Window()
window.Title = 'Welcome to IronPython' window.SizeToContent = SizeToContent.Height window. Width = 450
2 Adapted from code originally created by Steve Gilham. See http://stevegilham.blogspot.com/2007/07/ hello-wpf-in-ironpython.html.
I Adds references to I WPF assemblies
I Window will update I height to content stack = StackPanel() stack.Margin = Thickness(15) window.Content = stack button = Button()
button.Content = 'Push Me'
button.FontSize = 24
button.BitmapEffect = DropShadowBitmapEffect()
message = Label() message.FontSize = 36
message.Content = 'Welcome to IronPython!'
stack.Children.Add(message)
button.Click += onClick stack.Children.Add(button)
app = Application() app.Run(window)
Most of this listing is simple enough that it should mainly speak for itself. One thing you'll notice straightaway is that the API is different than the one in Windows Forms. This is hardly surprising because WPF is an entirely new library; but, although some patterns are familiar, you need to learn a lot of new details.
One obvious difference is at the end of the code. Instead of starting the event loop with a static method on Application class, you create an instance. Because you can't have multiple event loops within the same AppDomain, Application is a singleton. Instantiating it multiple times is not such a hot idea, but it will always return the same instance. Like Windows Forms, WPF has single thread affinity, so interaction with interface components must be on the same thread that Application is instantiated on—which must be a single-threaded apartment (STA) thread. For single-threaded applications, you don't need to worry about this; for multithreaded applications, you can use the WPF Dispatcher.3
The WPF Button is also similar to its WinForms equivalent. You set up the button click handler using our old friend, the Click event. But instead of setting the Text property, you use Content. For setting the text on a button, you use a string, but you could host any element in it (like an image or a TextBlock for more control over text wrapping and providing an access key).
Setting the Content on UI elements is one of the new patterns that comes with WPF. This is how you set the contained elements in the Window (the new and improved Form of a bygone era).
Setting Content isn't the only way that contained elements are set, though. The star of this example is the StackPanel, one of the new layout classes provided by WPF. Child controls are set on the stack panel by adding them to the Children container. The StackPanel is a great way of stacking elements vertically or horizontally. In this
3 For more details, see the documentation on the WPF threading model at http://msdn2.microsoft.com/en-us/library/ms741870.aspx.
def onClick(sender, event):
example, you add a new message to the panel every time the button is clicked. Other WPF container classes4 include Grid, DockPanel, WrapPanel, and the Canvas.
Most of the online examples for WPF start by showing you XAML, so let's see how our Hello World example looks in XAML.
9.1.2 Hello World from XAML
XAML is a declarative way of representing user interfaces in XML. XAML itself is extensible; following are several XAML variants:
■ WPF XAML—Used to describe WPF content
■ XPS XAML—A subset of WPF XAML for creating formatted electronic documents
■ Silverlight XAML—Another subset of WPF XAML supported by the Silverlight browser plugin
■ WF XAML—Windows Workflow Foundation XAML
■ Binary Application Markup Language (BAML)—A compiled form of XAML
There's a straightforward correspondence between XAML and WPF classes. Any XAML can be replaced by code that does the same job.
When XAML is read in, a type converter turns the XML tree into an object tree using the classes, structures, and enumerations contained in the System.Windows namespaces. XAML is an abstraction; and, before using an abstraction, we like to understand what's going on under the hood.
You can see the correspondence between the classes we've used and XAML in listing 9.2, which is the XAML for Hello World.
Listing 9.2 Hello World user interface in XAML
<Window xmlns="http://schemas.microsoft.com/winfx/2 0 06/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2 0 06/xaml" Title="Welcome to IronPython" Width="450" SizeToContent="Height">
<StackPanel Margin="15" x:Name="stackPanel"> <Button FontSize="24" x:Name="button"> <Button.BitmapEffect>
<DropShadowBitmapEffect /> </Button.BitmapEffect> Push Me </Button> </StackPanel> </Window>
We'd show you a screenshot, but this is identical to the code written in the last section. The top-level element has two XML namespaces, declared as attributes—which is necessary for the text to be valid XML and valid XAML.
4 A useful reference on the WPF layout system can be found at http://msdn2.microsoft.com/en-us/library/ ms745058.aspx.
XML escaping rules for XAML
XAML is XML, so you need to escape some characters using XML character entities. Characters that need escaping are in table 9.3.
|
Character |
XML character entity |
|
Less than (<) |
< |
|
Greater than (>) |
> |
|
Ampersand (&) |
& |
|
Quotation mark (") |
" |
Table 9.3 XML character entities
Table 9.3 XML character entities
The XML tree defines the layout of the user interface. Child elements are nested in their parent elements.
The attributes of the elements set properties. For example, the Title, Width, and SizeToContent properties on the window are set with attributes on the Window tag.
More complex properties (usually objects that have their own properties) are set using property-element syntax. You add a child element that specifies the parent and the property being set. In our example, you set the BitmapEffect property of the button with the following:
<Button.BitmapEffect>
<DropShadowBitmapEffect /> </Button.BitmapEffect>
If you were creating an interface with C# and a WPF designer, then you could also hook up events in the XAML. This creates a partial class in compiled XAML (BAML) that will be associated with a C# class: the code-behind. Because of the differences between IronPython classes and classes created with C#, you can't hook up IronPython event handlers in IronPython.5 Instead, you have to load the XAML and turn it into a WPF object tree using the XamlReader, so you need programmatic access to the elements in the object tree.
In this XAML example, you get access to the Button and the StackPanel by giving them a name using the x:Name attribute. They're then retrieved from the object tree returned by XamlReader.Load using the FindName method. Listing 9.3 is the code that reads in the XAML and sets up the button event handler.
Listing 9.3 Consuming XAML from IronPython import clr clr.AddReference("PresentationFramework") clr.AddReference("PresentationCore")
5 In fact, you can do it with the WPF designer in IronPython Studio. This generates code that uses a technique similar to listing 9.3.
from System.IO import File from System.Windows.Markup import XamlReader from System.Windows import ( Application, Window
from System.Windows.Controls import Label class HelloWorld(object): def _init_(self) :
message = Label() message.FontSize = 36
message.Content = 'Welcome to IronPython!' self.stackPanel.Children.Add(message)
hello = HelloWorld()
app = Application() app.Run(hello.Root)
Because the XAML creates a Window, our HelloWorld class is no longer a window subclass itself. In fact, there's no reason that the top-level element needs to be a window. You could make the StackPanel the top-level element and set the return value from XamlReader.Load as the Content on a Window subclass.
Given that you're probably a programmer, why should you use XAML instead of code? One reason for considering XAML is for dynamic user interface creation. By using the XamlReader, you can read in interface definitions at runtime. Alternative layouts could be loaded in and swapped out in response to user actions, or even created dynamically through text manipulation.
There are other reasons for preferring XAML, as the following fragment illustrates:
<LinearGradientBrush> <LinearGradientBrush.GradientStops > <GradientStop Offset="0.00" Color="Yellow" /> <GradientStop Offset="0.25" Color="Tomato" /> <GradientStop Offset="0.75" Color="DeepSkyBlue" /> <GradientStop Offset="1.00" Color="LimeGreen" /> </LinearGradientBrush.GradientStops> </LinearGradientBrush>
This XAML fragment creates a colored gradient brush used to create colorful backgrounds. The XAML is less verbose than the equivalent in code, even more so if you factor in the imports (and the time it takes to find the right namespaces to import everything from). More importantly, although XAML is quite easy to read and to write by hand, this kind of element is much easier to create with tools like Expression Blend.
Expression Blend allows you (or your designers) to create extremely rich interfaces with transitions, gradients, and animations. If Expression Blend is a designer's stream = File.OpenRead("HelloWorld.xaml")
self.Root = XamlReader.Load(stream)
self.button = self.Root.FindName('button')
self.stackPanel = self.Root.FindName('stackPanel
self.button.Click += self.onClick
def onClick(self, sender, event) :
Sets click handler tool, why is it of interest to developers? As well as designing full interfaces, you can also design individual components. You can copy and paste XAML from an Expression project into your code, and vice versa.
XAML generated by Expression Blend, or by the Visual Studio WPF designer, is intended to be compiled and used with code-behind. It will include an x:Class attribute valid only in compiled XAML. To make the XAML valid for consumption by the XamlReader, you'll need to remove this attribute from the top-level element.
A free trial of Expression Blend is available for download,6 and Mono has a fledgling equivalent tool (free and open source) called Lunar Eclipse.
As well as creating sophisticated interfaces with Expression, WPF comes with a range of standard controls sufficient for creating attractive, modern-looking applications.
Average user rating: 1 stars out of 2 votes
Post a comment