Rad Controls Silver Light Course Ware
Rad Controls Silver Light Course Ware
by Telerik Inc.
All rights reserved. No parts of this work may be reproduced in any form or by any means - graphic, electronic, or
mechanical, including photocopying, recording, taping, or information storage and retrieval systems - without the
written permission of the publisher.
Products that are referred to in this document may be either trademarks and/or registered trademarks of the
respective owners. The publisher and the author make no claim to these trademarks.
While every precaution has been taken in the preparation of this document, the publisher and the author assume no
responsibility for errors or omissions, or for damages resulting from the use of information contained in this
document or from the use of programs and source code that may accompany it. In no event shall the publisher and
the author be liable for any loss of profit or any other commercial damage caused or alleged to have been caused
directly or indirectly by this document.
Last but not least, thank you to all our families for their support and
patience while we wrote the book .
4 RadControls for Silverlight
Table of Contents
Foreword 0
Part I Introduction 15
1 Who Should
...................................................................................................................................
Read This Courseware 15
2 What ...................................................................................................................................
Do You Need to Have Before Reading This Courseware 15
3 How This
...................................................................................................................................
Courseware is Organized 16
4 About...................................................................................................................................
Telerik 16
5 About...................................................................................................................................
Falafel 16
6 Introducing
...................................................................................................................................
RadControls for Silverlight 17
5
6 RadControls for Silverlight
1 Objectives
................................................................................................................................... 173
2 Overview
................................................................................................................................... 173
3 Getting
...................................................................................................................................
Started 174
4 Applying
...................................................................................................................................
Themes to RadControls 177
5 Creating
...................................................................................................................................
a Custom Theme 180
6 Modifying
...................................................................................................................................
themes in Expression Blend 182
Overview .......................................................................................................................................................... 182
Modifying the
..........................................................................................................................................................
Them e Brushes 183
Testing the Modified
..........................................................................................................................................................
Them e 186
Modifying Them
..........................................................................................................................................................
e Styles 188
7 Wrap
...................................................................................................................................
Up 191
1 Objectives
................................................................................................................................... 283
2 Overview
................................................................................................................................... 283
3 Getting
...................................................................................................................................
Started 284
4 Control
...................................................................................................................................
Details 290
RadMenu .......................................................................................................................................................... 290
Item s .......................................................................................................................................................... 292
Walk Through:
..........................................................................................................................................................
Creating Menu Item s in Code 294
RadContextMenu
.......................................................................................................................................................... 297
5 Binding
................................................................................................................................... 303
6 Customization
................................................................................................................................... 311
7 Wrap
...................................................................................................................................
Up 331
7
8 RadControls for Silverlight
1 Objectives
................................................................................................................................... 444
2 Overview
................................................................................................................................... 444
3 Getting
...................................................................................................................................
Started 445
4 Control
...................................................................................................................................
Details 452
Overview . .......................................................................................................................................................... 452
Make a Control
..........................................................................................................................................................
Draggable 453
Accept Dropped
..........................................................................................................................................................
Controls 454
RadDragAndDropManager
.......................................................................................................................................................... 455
Events. .......................................................................................................................................................... 456
Visual Cues .......................................................................................................................................................... 462
5 Binding
................................................................................................................................... 465
6 Wrap
...................................................................................................................................
Up 466
Removing.........................................................................................................................................................
Nodes 569
Node Images
......................................................................................................................................................... 570
Selections .......................................................................................................................................................... 572
Node Expansion
.......................................................................................................................................................... 576
Checkboxes..........................................................................................................................................................
and Radiobuttons 577
Drag-and-Drop.......................................................................................................................................................... 581
Editing .......................................................................................................................................................... 587
Keyboard Support
.......................................................................................................................................................... 590
Perform ance.......................................................................................................................................................... 591
5 Binding
................................................................................................................................... 592
Basic Binding.......................................................................................................................................................... 592
Hierarchical Tem
..........................................................................................................................................................
plates 595
Tem plate Selectors
.......................................................................................................................................................... 613
Load-On-Dem ..........................................................................................................................................................
and 617
6 Customization
................................................................................................................................... 621
7 Wrap
...................................................................................................................................
Up 632
9
10 RadControls for Silverlight
6 Customization
................................................................................................................................... 727
7 Wrap
...................................................................................................................................
Up 731
3 Getting
...................................................................................................................................
Started 861
4 Control
...................................................................................................................................
Details 871
Chart Series..........................................................................................................................................................
Types 871
Chart Elem ents
.......................................................................................................................................................... 877
Series and.........................................................................................................................................................
DataPoints 877
Axis Elements
......................................................................................................................................................... 880
Anim ations .......................................................................................................................................................... 881
Integration w..........................................................................................................................................................
ith ASP.NET AJAX 883
5 Binding
................................................................................................................................... 889
Binding Basics
.......................................................................................................................................................... 889
Binding Axis ..........................................................................................................................................................
Labels 897
Tooltips .......................................................................................................................................................... 898
Form at Expressions
.......................................................................................................................................................... 902
6 Customization
................................................................................................................................... 908
Coloring Chart
..........................................................................................................................................................
Elem ents 908
Styling the Chart
.......................................................................................................................................................... 917
7 Wrap
...................................................................................................................................
Up 919
11
12 RadControls for Silverlight
Index 1100
13
Part
I
Introduction
Introduction 15
1 Introduction
Computer Setup
The courseware assumes you are running Windows X86 or x64 500-megahertz (MHz) or higher processor
with 128-megabytes (MB) of RAM.
Silverlight Setup
For information on system requirements for Silverlight, see "Microsoft Silverlight System Requirements" at
http://www.microsoft.com/silverlight/get-started/install/default.aspx. This will show you the combinations of
operating system and browser that will support Silverlight.
Development Tools
This courseware assumes that you have installed:
Visual Studio 2008 SP1 or better
Expression Blend 3
Silverlight 3 SDK
Silverlight Toolkit
Services
See the Silverlight Getting Started site at http://silverlight.net/getstarted/ for links to these resources.
Comprehensive Toolset from the Masters of Rich Data Visualization Capabilities: Featuring
Web UI: An established leader in web interface everything from a super powerful Silverlight grid, to
technologies, Telerik now offers you RadControls for animated, fully customizable Silverlight charts and
Silverlight 3 – a comprehensive suite of controls that gauges, Telerik Silverlight 3 controls enable
bring style and interactivity to your LOB developers to transform data into interactive,
applications. animated visuals that empower end-users to
analyze complex business scenarios.
Engineered for Great Performance: Telerik Full Interoperability with ASP.NET AJAX:
Silverlight 3 controls are engineered for outstanding RadControls for Silverlight 3 are a perfect addition
performance through native UI virtualization, an for existing ASP.NET AJAX applications and work
innovative LINQ-based data engine, asynchronous nicely with Telerik RadControls for ASP.NET AJAX.
data binding, RadCompression module and other This enables you to add islands of rich functionality
techniques that help reduce page loading time and to standards-based websites when needed, without
speed up data operations. rewriting your working applications for scratch.
WCF RIA Services support: All data-bound Telerik Ready-to-Use Themes: Styling is made easy
Silverlight 3 controls support binding to WCF RIA thanks to the integrated theme support. Telerik
Services. This enables all data operations to Silverlight controls ship with 5 major themes: Vista,
execute on the server, which results in speedier Summer, Office Blue, Black and Silver. These
sorting, filtering and paging for RadGridView. With themes help you deliver a consistent look-and-feel
completely codeless binding, it takes almost no throughout your application.
extra effort to bind RadControls for Silverlight 3 to
WCF RIA Services.
Validation support: All Telerik Silverlight 3 input 3D Charts for Silverlight: Pushing the envelope of
controls support metadata-driven validation via data rich data presentation, Telerik offers the first
annotations. commercial 3D chart control for Silverlight.
Integration with Leading-Edge Technologies: Support for Expression Blend: RadControls for
RadControls for Silverlight 3 are designed to work Silverlight are styleable through Microsoft
effortlessly with cutting-edge Silverlight technologies Expression Blend. As a result, you can unleash
like MVVM and Microsoft Composite Application your imagination and redefine how elements look
Guidance (Prism). and behave.
Code Re-Use with RadControls for WPF: Enhanced Routed Events Framework: To help
RadControls for Silverlight 3 and RadControls for your code become even more elegant and concise,
WPF suites are derived from the same codebase we implemented an Enhanced Routed Events
and share the same API. They represent two almost Framework for RadControls for Silverlight 3. This
mirror toolsets for building rich line-of-business web allows you to handle "bubbling" and "tunneling"
and desktop applications, allowing for substantial events from other elements in the visual tree.
code and skills reuse between Silverlight and WPF
development.
II
Silverlight Introduction
20 RadControls for Silverlight
2 Silverlight Introduction
2.1 Objectives
This chapter will give you a feel for how Silverlight fits relative to other web technologies. You will get a
rundown on the parts that make up a Silverlight application and how those parts fits in a Visual Studio
project. You will get an overview of the Silverlight Life Cycle. Finally, you will learn about the development
tools available for Silverlight development and where each tool is used.
\Courseware\Projects\<CS|VB>\SilverlightIntroduction\SilverlightIntroduction.sln.
2.2 Overview
Silverlight is a web application framework that runs on the client and delivers Rich Internet Applications
(RIA) capability. Here's a blurb from the MSDN Silverlight Architecture page that summarizes:
"Silverlight is not only an appealing canvas for displaying rich and interactive Web and media
content to end users. It is also a powerful yet lightweight platform for developing portable, cross-
platform, networked applications that integrate data and services from many sources.
Furthermore, Silverlight enables you to build user interfaces that will significantly enhance the
typical end user experience compared with traditional Web applications."
Silverlight started out life as a video streaming plug-in. As the platform has matured, capabilities have
expanded to more closely rival those of Adobe Flash: interactive graphics, multi-media and animations in a
single runtime environment.
Silverlight is routinely compared to Flash or AJAX and there is frequent speculation on what technology
might prevail. Silverlight actually fits within a spectrum of existing technologies with "simple" HTML and
ASP.NET web pages on one end and desk top applications on the other. Silverlight uses a small plug-in
that can be hosted within traditional web pages either as an "island" or constituting the entire browser
experience. Even when used as an island, you can communicate between the Silverlight application and the
HTML page through Javascript.
Like AJAX, the Silverlight display is updated without refreshing the entire page. But where AJAX
development has a patchwork feel, Silverlight lets you write managed code using your favorite .NET
language. Silverlight user interfaces are defined in XAML (Extensible Application Markup Language) and can
be built using Visual Studio or Expression Blend. The isolation between UI and functionality allows web
designers to build high-end graphic interfaces that really shine without colliding with development efforts.
Silverlight is supported by a lightweight class library. This slim subset of the .NET framework Base Class
Library handles collections, reflection, regular expressions, threading, web services, rich media, vector
graphics and user interface tasks. Parts of the framework that are not compatible with security or that would
make the plugin too heavy have been omitted, e.g. System.IO is not present in the Silverlight class library.
Don't expect to find all the classes, methods or properties of the original framework, even in familiar
namespaces. For example, the Enum class exists in Silverlight and has GetName() and Parse() methods,
but no GetValues() method. Likewise, WebClient running in Silverlight does not allow synchronous
communication. Even with these changes, Silverlight has significant functionality for such a small footprint.
Silverlight runs in a "Sandbox" and can't invoke the platform API. Files are accessed through the native
"Save" and "Open" file dialogs and files can also be persisted in local "Isolated Storage". To connect to
databases or access server-side classes, you need to go through an intermediary, typically a web service.
Silverlight can talk with WCF, RIA, ADO.NET Data Services and REST based services. Silverlight can
converse using JSON and XML formats, including RSS and ATOM.
At a high level we can think of a Silverlight application as assembly that includes two sets of files:
App.xaml & App.xaml.cs: This class is the entry point for the application as a whole. The class handles
application startup and has several events for handling application wide events such as startup, exit and
unhandled exceptions.
InitializeComponent()
End Sub
public App()
{
this.Startup += this.Application_Startup;
this.Exit += this.Application_Exit;
this.UnhandledException += this.Application_UnhandledException;
InitializeComponent();
}
MainPage.xaml & MainPage.xaml.cs: The initial main page is created automatically and contains a
XAML (Extensible Application Markup Language) file to describe the user interface and managed code-
behind to define the client logic. The screenshot below shows a minimal "Hello World" example. Most of
your typical development effort will center around these files.
When the application is compiled, the assembly DLL is placed into a "*.xap" file along with a manifest. The
"xap" file is simply a compressed file, like a "*.zip". When a browser requests a Silverlight application to
run, the xap file is downloaded on demand and executed by the Silverlight plugin. To view the "xap" file,
change the extension to "zip" and open it. The screenshot below shows the xap contents for a minimal
"Hello World" application named "SilverlightIntroduction".
If we reference assemblies that are not part of the base Silverlight class library, these DLL's will also be
included in the xap. Adding a RadCalendar to the "Hello World" application requires references to the
Telerik.Windows.Controls and Telerik.Windows.Controls.Input assemblies. Once referenced, these
assemblies are automatically included in the xap:
The application manifest describes the contents of the xap file and tells Silverlight how to run a particular
application. Looking inside the AppManifest.xaml you can see that the Deployment.Parts element lists
and names each assemblies in the xap. The Deployment RuntimeVersion is the version of Silverlight
required to run the application. The Deployment EntryPointAssembly and EntryPointType are the names
of the assembly and the class that will be instantiated to start the Silverlight application.
<Deployment
xmlns="http://schemas.microsoft.com/client/2007/deployment"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
EntryPointAssembly="SilverlightIntroduction"
EntryPointType="SilverlightIntroduction.App"
RuntimeVersion="3.0.40624.0">
<Deployment.Parts>
<AssemblyPart x:Name="SilverlightIntroduction" Source="SilverlightIntroduction.dll" />
<AssemblyPart x:Name="Telerik.Windows.Controls" Source="Telerik.Windows.Controls.dll" />
<AssemblyPart x:Name="Telerik.Windows.Controls.Input"
Source="Telerik.Windows.Controls.Input.dll" />
</Deployment.Parts>
</Deployment>
When you create a new Silverlight application in Visual Studio, two projects are created, the Silverlight
Application project and a Host Web Application project. The screenshot below shows the key elements:
1. "App" represents the application class. This class handles instantiation and other application level
tasks.
2. "MainPage" has xaml to describe the user interface for your application and code-behind to define the
logic.
3. After the Silverlight application is compiled, the "xap" is placed in the host web application \ClientBin
folder.
4. Standard ASP.NET or HTML test pages are automatically added to the host web application. These
pages contain "<object>" tags that reference the Silverlight plugin. The two pages are substantially the
same. Looking at the "*.html" file we can see that it contains boilerplate Javascript code for handling
Silverlight errors. In the HTML portion of the file below the Javascript, there is an "<object>" element. The
object element represents the Silverlight plugin. Notice that it has a series of parameter tags that tell it
where the "xap" file can be found and what Javascript should be run if there's an error.
"minRuntimeVersion" and "autoUpgrade" are configured to automatically update the plugin if it is out of
date.
<body>
<form id="form1" runat="server" style="height:100%">
<div id="silverlightControlHost">
<object data="data:application/x-silverlight-2," type="application/x-silverlight-2"
width="100%" height="100%">
<param name="source" value="ClientBin/SilverlightIntroduction.xap"/>
<param name="onError" value="onSilverlightError" />
<param name="background" value="white" />
<param name="minRuntimeVersion" value="3.0.40624.0" />
<param name="autoUpgrade" value="true" />
<a href="http://go.microsoft.com/fwlink/?LinkID=149156&v=3.0.40624.0"
style="text-decoration:none">
<img src="http://go.microsoft.com/fwlink/?LinkId=108181"
alt="Get Microsoft Silverlight" style="border-style:none"/>
</a>
</object>
<iframe id="_sl_historyFrame" style="visibility:hidden;height:0px;width:0px;border:0px">
</iframe></div>
</form>
</body>
Notes
Expect the dichotomy between Visual Studio and Expression Blend to be a temporary state of
affairs. Future versions of both environments are likely to overlap to a greater degree as the
toolset progresses.
Links to Silverlight development tools and other resources can be found at http://silverlight.net/getstarted/.
Silverlight SDK
The SDK includes the necessary Silverlight assemblies, documentation, Visual Studio templates and
examples. This download is required for developing Silverlight applications. The install currently includes the
following DLL's:
System.ComponentModel.DataAnnotations.dll
System.Data.Services.Client.dll
System.Json.dll
System.Runtime.Serialization.Json.dll
System.ServiceModel.PollingDuplex.dll
System.ServiceModel.Syndication.dll
System.Windows.Controls.Data.dll
System.Windows.Controls.Data.Input.dll
System.Windows.Controls.dll
System.Windows.Controls.Input.dll
System.Windows.Controls.Navigation.dll
System.Windows.Data.dll
System.Windows.VisualStudio.Design.dll
System.Xml.Linq.dll
System.Xml.Serialization.dll
System.Xml.Utils.dll
"Microsoft WCF RIA Services simplifies the traditional n-tier application pattern by bringing together the
ASP.NET and Silverlight platforms. RIA Services provides a pattern to write application logic that runs on
the mid-tier and controls access to data for queries, changes and custom operations."
Using WCF RIA services is demonstrated in this Courseware as part of the "Data Binding" chapter.
Silverlight Toolkit
The toolkit has a collection of useful controls and classes. While its not required to build Silverlight
applications, some of the resources in this download are used in this Courseware. The toolkit is a
community project at located at http://silverlight.codeplex.com.
2.5 Wrap Up
In this chapter you got a feel for where Silverlight fits relative to other web technologies. You were given a
rundown on the elements that make up a Silverlight application and how those parts fits in a Visual Studio
project. You learned about the basic Silverlight Life Cycle. Finally, you learned about the development tools
available for Silverlight development and where each tool is used.
III
Working with Silverlight
30 RadControls for Silverlight
3.1 Objectives
This chapter is intended to get you started working with Silverlight. This is not intended to be a
comprehensive coverage of all things Silverlight, but rather a quick guide to basic "up-and-running"
information. In this chapter you will build simple "Hello World" Silverlight applications using both Visual
Studio and Expression Blend. You will also learn how to hand off projects back and forth between the two
environments and in the process learn the basic working styles of both. In this chapter you will learn the
basics about XAML, including XML namespaces, defining objects, properties and collections in XAML,
attached properties, markup extensions, how to define and apply styles to Silverlight elements, how to
define and use resources and how RadControls are defined and themed in XAML.
You will learn how templates are used to create free form arrangements of Silverlight elements without
affecting underlying functionality. You will learn the basics on how to bind data to Silverlight elements.
You will learn about Silverlight best practices including MVVM and Prism. Finally, you will learn the settings
for debugging a Silverlight application.
\Courseware\Projects\<CS|VB>\WorkingWithSilverlight\WorkingWithSilverlight.sln.
This walk through will take you step-by-step building a classic "Hello World" Silverlight application.
Project Setup
1) From the Visual Studio menu choose File > New > Project..., select the Silverlight project type, then
select the Silverlight Application template. Provide a unique name for the project and click the OK
button.
2) In the "New Silverlight Application" dialog make sure that the "Host the Silverlight application in a new
Web site" option is checked, give the project a unique name and verify that the "ASP.NET Web
Application Project" New Web Project Type option is selected. Click OK to close the dialog and create
the project.
3) Review the contents of the solution in the Solution Explorer. There are two projects. The Silverlight
Application project contains "MainPage" where MainPage.xaml contains the markup for the user
interface and "MainPage.xaml.cs" is the code-behind that defines the client-side logic. The host project
contains two test pages: an "aspx" test page and a "html" test page.
4) In the Solution Explorer, right-click the host project and select "Set as Startup Project" from the
context menu.
5) In the Solution Explorer, right-click the host project "aspx" test page and select "Set as Start Page".
6) Build the solution. In the Solution Explorer, find the "ClientBin" folder and notice that a "xap" file has
been created
7) Navigate to the Silverlight project and open MainPage.xaml for editing. It should contain markup similar
to the example shown below.
<UserControl x:Class="_01_GettingStarted.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="640" d:DesignHeight="480">
<Grid x:Name="LayoutRoot">
</Grid>
</UserControl>
8) Drag a Button control from the "Silverlight XAML Controls" tab of the Toolbox to a point between the
main Grid element tags.
10)The completed XAML should now look like the example below:
<UserControl x:Class="_01_GettingStarted.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="640" d:DesignHeight="480">
<Grid x:Name="LayoutRoot">
<Button Content="Click Me" Click="Button_Click"></Button>
</Grid>
</UserControl>
Press F5 to re-run the application which should now look like this:
This walk through will take you step-by-step building a classic "Hello World" Silverlight application in
Expression Blend. You will also get a chance to contrast the working styles of Expression Blend and Visual
Studio from the previous example.
Project Setup
1) Run Expression Blend.
2) From the File menu select New Project. If Expression Blend is configured to show the startup dialog,
click the New Project... option.
3) In the "New Project" dialog select the "Silverlight" project type. In the right hand list select the
"Silverlight 3 Application + Website". Provide a unique name for the application and click OK to close
the dialog.
4) When Expression Blend displays there are quite a number of features available at once. The key
features we will use right away are the Assets pane, the Projects pane, the Objects and Timeline pane,
the Artboard and the Properties pane.
The Assets pane can be thought of as a smart Toolbox that makes it easy to search for controls but
also contains effects, styles, behaviors and media. The Projects pane is analogous to the Solution
Explorer and contains the projects, files and other resources for a solution. The Objects and Timeline
pane displays the visual tree of Silverlight elements in outline form and helps orient you even when the
page is very complex. The Artboard is the design surface for the page where controls are added and
modified. The Properties pane is much like the Visual Studio Properties window but includes
extensive tools for visual properties such as alignment or color gradients.
7) In the Assets pane, just above the tree view is the Assets Find entry text box. Enter "button" and
notice that the list of controls narrows as you type.
8) From the Assets pane, drag the Button control onto the Artboard.
9) Looking at the Button in the Artboard you can see that it does not stretch to take up the entire page
and remains at the location on the page where you dropped it.
10)Look at the Properties pane, locate the Layout tab and notice that the Width, Height, Horizontal
Alignment, Vertical Alignment and Margin have all been set. Locate the Common Properties tab and
set the Content property to "Click Me". Note: You can also set the text on the control directly in the
Artboard by double-click ing or selecting the control and click ing F2.
11)Locate the Events button in the Properties pane and click it.
12)Double-click the "Click" event to create an event handler. This will navigate you to the Expression
Blend code editor.
13)In the code editor, add a call to MessageBox.Show() inside the Click event handler.
The process of building the same application in two environments provides a glimpse of the stylistic
differences between the two environments. This is a glimpse only. As we use Expression Blend more
extensively you will see how it excels when handling design tasks, animation and rich media. Visual Studio
and Expression Blend are designed to work together, handing off the project to the best suited environment
for a given task.
Let's walk through how the hand off occurs, starting with the previous Expression Blend project.
1) Start with the previous Expression Blend project.
2) In the Projects pane, locate MainPage.xaml, right-click and select Edit in Visual Studio from the
context menu. Do NOT close Expression Blend. We will want to test how the two environments are
notified of changes by the other.
3) Visual Studio will start and automatically open the entire Silverlight project, including the host web
application.
4) In Visual Studio, drag an image file "Folder_open.png" to the Silverlight project. You can find this file in
the "\courseware\images" directory.
5) Open MainPage.xaml for editing. Replace the Button with the XAML below.
This version of the Button replaces the Content attribute with Content as a full sub-element. Inside the
Content element, a Stack Panel contains an Image and a TextBlock .
6) Select File > Save All from the Visual Studio menu.
7) Press F5 to run the application. The Button in the Silverlight application running from Visual Studio now
looks like the screenshot below.
9) In the Visual Studio Solution Explorer, right-click MainPage.xaml and select Open in Expression
Blend... from the context menu.
10)Expression Blend will come to the front and display a dialog "This Solution has been modified outside
of Expression Blend. Do you want to reload it?". Click the Yes button to close the dialog and reload the
project.
11)Notice how the Objects and Timeline pane reflects the new Button content.
12)In the Assets pane, select the "Effects" node in the tree on the left. Locate the "Drop Shadow Effect"
effect, drag it to Objects and Timeline pane and drop it on the StackPanel.
Notes
Keep your eye on the tooltip as you drag assets to the Objects and Timeline pane. The tooltip will
change to let you know what container it will be dropped into.
13)Press F5 to run the application. The shadow effect added by Expression Blend shows on the Image
and Text elements.
From this walk through you have seen how the project is passed back and forth between Visual Studio
and Expression Blend.
In this section we will look at how to think about XAML syntax, how to reference assemblies, use
IntelliSense, create styles, build templates and refactor your XAML with resources.
Gotcha!
Before working with XAML in Visual Studio you can tweak the environment to make editing a little
faster. Go to Tools > Options... > Text Editor > XAML > Miscellaneous. Set the "Always open
documents in full XAML view" option on. Now when you open a XAML file the read-only Visual
Designer view will not load. It is substantially quicker to edit XAML in this manner for Visual Studio
2008. Use Expression Blend when you need to visually edit your pages.
XAML is a declarative markup language that simplifies creating user interfaces. XAML elements directly
represent instantiation of objects. The visible elements are defined in XAML, then matching code-behind is
used to manipulate those objects. This separation of concerns allows a work flow where separate parties
can work on UI and logic at the same time, using different tools.
MainPage.xaml
Your typical starting point for a XAML file is the either the App.xaml or MainPage.xaml. In App.xaml you
can define resources for the entire application. We will discuss resources a little later but for now take a
look at MainPage.xaml. The page starts out looking like the example below:
<UserControl x:Class="_02_WorkingWithXAML.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="640" d:DesignHeight="480">
<Grid x:Name="LayoutRoot">
</Grid>
</UserControl>
The root element is "UserControl". The "x:Class" attribute lets us know the name of the UserControl class
in the code-behind, i.e. "MainPage".
XML Namespaces
Following the class declaration is a series of "xmlns" or XML namespaces. The syntax is "xmlns" followed
by a name, an equals sign, then the namespace itself. The first "xmlns" has no name and is the default
namespace.
What happens if we want to include references to assemblies, similar to "Imports" (VB) or "using" (C#)
statements? For that we can type in "xmlns:" plus a name and an equals sign. IntelliSense will display a
list of assemblies in scope for the project. The screenshot below shows a namespace called "thisProject".
The list drop down to reveal all the possible namespaces in scope.
If you have a class called "MyElement" defined in the code-behind, you can reference it in XAML by starting
your tag and adding "thisProject:" namespace. Once you type a colon, IntelliSense will display a list of
objects in the namespace as shown in the screenshot below.
XAML elements are used to declare instances of a classes. Elements can be expressed as starting and
ending XML tags or can use the shorter "self closing" syntax. Here is an example that shows both
syntaxes:
Properties can also be assign with "property element", "content element" and "collection" syntax. Property
element syntax takes the form of "<typeName.PropertyName>". The Width and Height example below
makes the XAML more verbose in this case, but if properties are complex or can't be expressed in a single
string value, this syntax becomes useful.
<Button >
<Button.Width>100</Button.Width>
<Button.Height>100</Button.Height>
</Button>
Another example shows property element syntax where the Button's BorderBrush contains another object
called a SolidColorBrush.
<Button>
<Button.BorderBrush>
<SolidColorBrush Color="Red"/>
</Button.BorderBrush>
</Button>
For objects that have Content, the syntax can be simplified to place the content between the tags as a
default.
<Button>Hello World</Button>
And lastly, collection syntax allows you to list a number of elements and have them automatically added to
a collection. The XAML below shows a LinearGradientBrush object that has a GradientStop collection.
<Button>
<Button.Background>
<LinearGradientBrush>
<LinearGradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Offset="0.0" Color="Red" />
<GradientStop Offset="1.0" Color="Blue" />
</GradientStopCollection>
</LinearGradientBrush.GradientStops>
</LinearGradientBrush>
</Button.Background>
</Button>
The parser knows how to locate and create a GradientStopCollection, so we can leave the
GradientStopCollection element out for a shorter syntax:
<Button>
<Button.Background>
<LinearGradientBrush>
<LinearGradientBrush.GradientStops>
<GradientStop Offset="0.0" Color="Red" />
<GradientStop Offset="1.0" Color="Blue" />
</LinearGradientBrush.GradientStops>
</LinearGradientBrush>
</Button.Background>
</Button>
It turns out that GradientStops is also the content property for the class and so we can simplify even further:
<Button>
<Button.Background>
<LinearGradientBrush>
<GradientStop Offset="0.0" Color="Red" />
<GradientStop Offset="1.0" Color="Blue" />
</LinearGradientBrush>
</Button.Background>
</Button>
A XAML language feature allows assigning a property or event from any owning element, even if the element
that's being assigned doesn't have that property or event. You can think of an attached property as being
essentially a global property. The syntax is "ownerType.propertyName". The example below demonstrates
the utility of attached properties. The Button class has no ToolTip property. Button doesn't need one
because we can attach a ToolTip property from the ToolTipService class.
"Markup extensions" is an XAML concept where the curly braces "{}" are used as escape characters for
syntax that differs from XML standard notation. Usually these refer to resources or binding (resources and
binding are discussed in upcoming sections). Here's a brief example of what markup extensions look like in
the wild:
<TextBlock
Text="{Binding ProductNumber}"
Style="{StaticResource MyTextStyle}">
</TextBlock>
Resources are an extremely powerful feature of XAML that let us define styles, templates, brushes,
classes, properties and events in a separate location from their use. This allows resources to be reusable.
As a side effect, you can refactor your XAML so that as a page becomes burdened with larger amounts of
markup, chunks of the markup can be named and moved to separate files for reuse.
Resources are kept in ResourceDictionary objects and are properties of the Application and any
FrameworkElement (any element participating in Silverlight layout). What do you store in a resource?
Brushes for setting element colors, templates for customizing controls and data source objects can all be
kept in resources. One common use for resource dictionaries is to store Styles, i.e. named groups of
properties and values. Lets start with a Button that has several font related properties. Styles aren't used In
the example below where the attributes are described directly on the button element.
You can convert the font related properties into a single Style. For the moment, this is more verbose and
isn't more useful than the previous example. Wait for it...
If we make the Style into a Resource, the stye can be used over and over. The button markup gets cleaned
up in the process. The Resources element in the example below is a sub property of the Grid, but could
have been placed in UserControl.Resources or App.Resources. Notice that the Style has two new attributes
that are very important, x:Key and TargetType. "x:Key" identifies the style so that we can refer to it later.
TargetType specifies the type that the style should be applied to. Also notice how the Style is applied to
each of the buttons. The curly braces enclose "StaticResource" followed by the style key.
In the running Silverlight example, the styled buttons look like the screenshot below:
Gotcha!
Its very easy to forget the TargetType attribute for a Style. The error can be hard to debug
because the error message does not point straight back to the cause. If we forget TargetType in
the above example, an XMLParseException is generated with message "Invalid attribute value
FontFamily for property Property".
Inheriting Styles
If several styles have some common attributes, why repeat these attributes in every Style definition? Styles
can become large, complex and difficult to manage. At some point you will want to use an existing style as
a base for a new style. In the example below, the "BigButtonStyle" uses the BasedOn attribute to point
back to "ButtonStyle". In the example we overwrite the FontSize to "16", and all the other "ButtonStyle"
properties come along for the ride. The new "BigButtonStyle" is applied to the third button.
<StackPanel>
<StackPanel.Resources>
<Style x:Key="ButtonStyle" TargetType="Button">
<Setter Property="FontFamily" Value="Verdana" />
<Setter Property="FontSize" Value="12" />
<Setter Property="Foreground" Value="Green" />
</Style>
<Style x:Key="BigButtonStyle" TargetType="Button"
BasedOn="{StaticResource ButtonStyle}">
<Setter Property="FontSize" Value="16" />
</Style>
</StackPanel.Resources>
<Button Content="Button 1" Style="{StaticResource ButtonStyle}" />
<Button Content="Button 2" Style="{StaticResource ButtonStyle}" />
<Button Content="Button 3" Style="{StaticResource BigButtonStyle}" />
</StackPanel>
In the running Silverlight example, the styled buttons look like the screenshot below.
Resource Scope
Where resources are placed in the XAML determine scope. The example below fails with an
XMLParseException, "Cannot find a Resource with the Name/Key ButtonStyle", because the last button
can't find "ButtonStyle". "ButtonStyle" is a resource declared inside the StackPanel resources and is out of
scope for the last button.
<StackPanel>
<StackPanel.Resources>
<Style x:Key="ButtonStyle" TargetType="Button">
<Setter Property="FontFamily" Value="Verdana" />
<Setter Property="FontSize" Value="12" />
<Setter Property="Foreground" Value="Green" />
</Style>
</StackPanel.Resources>
<Button Content="Button 1" Style="{StaticResource ButtonStyle}" />
<Button Content="Button 2" Style="{StaticResource ButtonStyle}" />
</StackPanel>
You can also move your resources out to the App.xaml file where they can be reused by the entire
application. All you are doing is moving the resource to a wider scope. After moving "ButtonStyle" to App.
xaml, App.xaml looks something like the example below:
<Application
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="_02_WorkingWithXAML.App">
<Application.Resources>
<Style x:Key="ButtonStyle" TargetType="Button">
<Setter Property="FontFamily" Value="Verdana" />
<Setter Property="FontSize" Value="12" />
<Setter Property="Foreground" Value="Green" />
</Style>
</Application.Resources>
</Application>
Merging Resources
Silverlight 3 introduced a feature called "Merged Resources" where resources can be broken out into several
files. This feature allows you to better organize your project as it becomes larger. For the sake of
discussion, lets place all our button related styles into their own resource dictionary.
To add a new resource dictionary using the Solution Explorer, right-click the project and select Add > New
Item... from the context menu. You can find the "Silverlight Resource Dictionary" template in the
"Silverlight" category. Provide a unique name for the resource dictionary and click Add to create the file.
You can create as many resource files as you require to organize your application. The styles we have
defined previously can be moved to this file where the contents now look like the example below.
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
</ResourceDictionary>
We can use the MergedDictionaries collection to pull in the separate XAML files to any Resources element.
The example below merges two dictionaries to the Application Resources element where they will be
available application wide.
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="MyButtonStyles.xaml" />
<ResourceDictionary Source="MyTextStyles.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
Once installed to the Toolbox, RadControls can be dragged into the XAML just as you would drag a
standard Silverlight control. The screenshot below shows a RadCalendar being dragged inside the main
"LayoutRoot" Grid element.
When the drag is complete, an XML namespace is automatically added to the UserControl, and the control
is added, along with its corresponding namespace, to the grid.
To theme a RadControl, first right-click the References node in the Solution Explorer and select Add
Reference... Browse to the Telerik assemblies located in the Telerik RadControls for Silverlight installation
and select Telerik.Windows.Controls. Also add a reference to one of the theme assemblies. These
assemblies all start with "Telerik.Windows.Themes" and the name of the theme. The example below adds
the Telerik.Windows.Themes.Summer assembly.
Once assembly DLL's are referenced in the project you can add XML namespace references in the markup.
The screenshot below shows the reference being added with the name of "telerik".
That will provide access to the StyleManager class. From there you can set the theme using the
StyleManager Theme attached property. The example below sets the Summer theme to a RadCalendar
control. The exact same steps are used to theme all RadControls.
3.3.6 Templates
Templates are XAML patterns used visualize Silverlight elements. You can think of them as recipes for
rendering element trees. Templates descend from the abstract FrameworkTemplate where there are a few
commonly used descendants:
ControlTemplate creates a visual representation of a UI control, such as a button. With
ControlTemplate you can completely replace the visual tree of a control with any set of elements you
care to use without altering the control's behavior. The ControlTemplate "Template" property is available
to every descendant of "Control". This makes Template very general purpose and is useful when you
need to "rip up the planks" to create a new visual representation.
DataTemplate describes visual structure of a data object, such as a business object.
ItemsControl uses ItemTemplate to visualize child elements. ListBox would be an example of an
ItemsControl descendant that can be customized using ItemTemplate.
HierarchicalDataTemplate is used to display child objects of a data object, for example master/detail
grids or tree views.
Templates can also be surfaced for particular areas of a control that can be customized. This strategy is
used by RadControls to allow customization of controls that can be displayed in different orientations or
that have different states. For example, the RadPane element used in docking has a BottomTemplate,
LeftTemplate, RightTemplate and TopTemplate where the position of the pane determines which template
is being rendered. RadPane also has a TitleTemplate. All these templates allow you to customize the
parts of the control that are of interest to you without altering the underlying functionality of the control.
ControlTemplate
Let's take our simple Button example and add a ControlTemplate definition. The example below shows
that the Button has a Template property. The Template property is of type ControlTemplate. Inside the
ControlTemplate we place a simple TextBlock.
When you run this, the text "Print" shows, not the content "Button 1" and the Click event still fires. The
usage you see in the example above is not usual for multiple reasons. First, templates are typically defined
in resources:
<Grid x:Name="LayoutRoot">
<Grid.Resources>
<ControlTemplate x:Key="MyButtonTemplate">
<TextBlock Text="Print" />
</ControlTemplate>
</Grid.Resources>
The TextBlock in the template has hard coded text. What if we want the Content for the button to be used
instead. Here the markup syntax "{TemplateBinding}" can be used to pass a property value through to the
template. In this example, Content is used to populate the TextBlock Text property so that "Button 1"
displays.
<Grid x:Name="LayoutRoot">
<Grid.Resources>
<ControlTemplate x:Key="MyButtonTemplate">
<TextBlock Text="{TemplateBinding Content}" />
</ControlTemplate>
</Grid.Resources>
Routed events allow you to hook up disparate elements that perhaps aren't designed to be used together.
Say you have a combo box in a stack panel. The stack panel can be notified that the combo box selection
has changed. Events are no longer constrained to the element where they are defined.
Silverlight has been a little weak on some features that are standard with WPF.
Routed events haven't been widely defined.
Silverlight doesn't support custom routed events as of this writing.
Telerik RadControls for Silverlight provides WPF-like support for custom routed events and also allows
expanded support for varying "routing strategies" (Bubbling, Tunneling and Direct). Telerik RadControls
routinely implement their events as routed events so you can use them right "out of the box".
For more information on Silverlight routed events, download the Telerik Trainer application and the
"RadControls for Silverlight - Overview of Routed Events" interactive video at:
http://www.telerik.com/support/trainer/silverlight.aspx
End Class
To bind the particular properties of an element, use the markup extension syntax where curly braces
surround "Binding" and specify a "Path" to a particular property of your bound object. In the example below,
Button Content is bound to the "Title" property of the Report object and the ToolTip is bound to the
"Description".
<Button x:Name="btnPrint"
Content="{Binding Path=Title}"
ToolTipService.ToolTip="{Binding Path=Description}"
HorizontalAlignment="Left" VerticalAlignment="Top"
Margin="20"></Button>
The running Silverlight application shows the Button with bound properties:
Notes
To instantiate objects for binding in XAML, first create an XML namespace for the project that contains the
object you want to bind to (see Working with XAML, XAML Basics for directions on how to do this). Then, in
a Resources element add a reference to your object. The XML namespace for the example below is
"thisProject" and the object is "Report". The two properties for the Report object, "Title" and "Description",
are represented by attributes of the same name. You must set the "x:Key" to some unique name so you
can refer to it during binding.
To bind the Report object instantiated in XAML, bind the DataContext to the resource key name.
<Grid.Resources>
<thisProject:Report x:Key="Report"
Title="Product Listing"
Description="Lists all active products in stock" />
</Grid.Resources>
<Button x:Name="btnPrint"
DataContext="{StaticResource Report}"
Content="{Binding Title}"
ToolTipService.ToolTip="{Binding Description}"
HorizontalAlignment="Left" VerticalAlignment="Top"
Margin="20" />
MVVM
When integration between the user interface and the data is too tight, testing, refactoring and maintenance
become more difficult. Changes in the data schema or the querying logic can require changes throughout
the application. Separating the application into clear separation of concerns makes the application easier to
maintain and test as changes are introduced.
There are well defined architectural patterns that provide a map of where functional parts of the application
should be placed. The MVC pattern, Model - View - Controller, can work well for applications that don't
have declarative, i.e. XAML, interfaces. In this pattern, Model is the data, View is the user interface and
Controller is the programmatic interface between model and view. But XAML can declare data binding,
blurring the line between data and the view.
The MVVM pattern, Model - View - ViewModel, is a pattern more suited to declarative interfaces. In this
pattern, the ViewModel sits between the Model and View, providing an abstraction that can be used both
programmatically and declaratively by the view. The View can be programmatically or declaratively bound to
the ViewModel without the View having direct knowledge of the Model.
Prism
When you start building Silverlight applications to test various Silverlight and RadControls, your applications
will be simple and small. As your applications become larger they need to allow separate testing and
maintenance. But separation of concerns is only one of the requirements of a full scale production
application including logging, load-on-demand and event publishing between services, to name a few.
Prism is a collection of resources for implementing best practices in Silverlight. You can pick and choose
the pieces you need from the Prism libraries, source code, examples, quick starts and documentation.
For more information see "Composite Apps from Prism" at http://msdn.microsoft.com/en-us/magazine/
dd943055.aspx. You can find the download for Prism at http://www.microsoft.com/downloads/details.aspx?
FamilyID=fa07e1ce-ca3f-4b9b-a21b-e3fa10d013dd&DisplayLang=en.
3.8 Debugging
You can debug a Silverlight application in much the same way as you would for a WinForms application.
Just set your breakpoints and press F5 to debug the application. Be aware that you cannot debug
Javascript and Silverlight at the same time. To enable debugging when you first create the Silverlight
application, leave the "Enable Silverlight debugging" option checked.
Later, you can go back to the Solution Explorer, right-click the host project and select Properties from the
context menu. Select the Web tab, then locate the Silverlight checkbox.
Gotcha!
Question: When trying to debug I receive the error message "Unable to start debugging. The
silverlight managed debugging package isn't installed.".
Answer: You may have installed the end-user runtime, not the developer runtime. See the "What
Do You Need to Have Before Reading This Courseware" chapter and make sure you have
everything installed correctly.
3.9 Wrap Up
This chapter provided a quick guide to basic "up-and-running" information. In this chapter you built simple
"Hello World" Silverlight applications using both Visual Studio and Expression Blend. You also learned how
to hand off projects back and forth between the two environments and in the process learned the basic
working styles of both. In this chapter you learned the basics about XAML, including how XML namespaces
are defined, defining objects, properties and collections in XAML, attached properties, markup extensions,
how to define and apply styles to Silverlight elements, how to define and use resources and how
RadControls are defined and themed in XAML.
You learned how templates are used to create free form arrangements of Silverlight elements without
affecting underlying functionality. You learned the basics on how to bind data to Silverlight elements.
You learned about Silverlight best practices including MVVM and Prism. Finally, you learned the settings
that control debugging of a Silverlight application.
IV
Data Binding
70 RadControls for Silverlight
4 Data Binding
4.1 Objectives
In this chapter you will learn the basics of binding objects and collections of objects to Silverlight elements.
You will learn syntax variations used to bind declaratively and how to bind elements using code alone. You
will also learn how to detect changes in property data and changes to collections.
You will learn how to bind elements in two common templates: DataTemplate and
HierarchicalDataTemplate.
You will learn how to bind getting data from a variety of popular data sources including straight XML and
variations of XML, i.e. ATOM and RSS. In the process you will use a combination of XDocument and LINQ
to read, parse and convert the data to a form suitable for binding. Building off the XML techniques we will
discuss REST services, build WCF RIA services, WCF and ADO.NET Data Services.
\Courseware\Projects\<CS|VB>\Binding\Binding.sln.
\Courseware\Projects\<CS|VB>\Binding_ORM\Binding_ORM.sln.
\Courseware\Projects\<CS|VB>\Binding_Ria\Binding_Ria.sln.
tbTitle.DataContext = category
tbTitle.DataContext = category;
Lets look at an example where we have a standard CLR object that we will call "Category". Category has
two properties, "Title" and "Description".
Because we're working with a dependency property, we can perform the binding right in the XAML. Take a
look at the example below. The UserControl.Resources defines a single instance of Category. In this
example, the FrameworkElement is a TextBox and the dependency property is the Text property. The
Binding object is declared using "{Binding ...} syntax. The critical Binding properties are:
Path: The name of the source property to get the data from. The example below omits the "Path" attribute
in favor of the shorter syntax where the property following "Binding" is the Path, i.e. "Title".
Source: The standard CLR object that contains the data. This object is assigned to the
FrameworkElement DataContext.
Mode: The data's direction of travel. This can be OneTime where the data is set one time and is not
updated after that, OneWay where the data flows one way from the CLR object to the Silverlight element
and TwoWay where the source object updates the target element and the target element can also update
the source object.
<UserControl.Resources>
<local:Category x:Key="Category" Title="Fiction"
Description="A wide range of fiction from the worlds best authors." />
</UserControl.Resources>
<Grid x:Name="LayoutRoot">
<TextBox x:Name="tbTitle"
Text="{Binding Title, Source={StaticResource Category}, Mode=OneTime}"
HorizontalAlignment="Left" VerticalAlignment="Top" MinWidth="100"
></TextBox>
</Grid>
The bound TextBox in the running Silverlight application shows the title of the Category object.
What if you want to bind more than one Category property? The example below shows how you can assign
the DataContext of the element one time and then set the Binding for each property that needs data. This
example binds the Text property to the Category "Title" and also adds an attached ToolTip property and
binds it to the Category "Description".
<UserControl.Resources>
<local:Category x:Key="Category" Title="Fiction"
Description="A wide range of fiction from the worlds best authors." />
</UserControl.Resources>
<Grid x:Name="LayoutRoot">
<TextBox x:Name="tbTitle"
DataContext="{StaticResource Category}"
Text="{Binding Title, Mode=OneTime}"
ToolTipService.ToolTip="{Binding Description, Mode=OneTime}"
HorizontalAlignment="Left" VerticalAlignment="Top"
MinWidth="100" Margin="20"></TextBox>
</Grid>
The running application now shows that both the Text and ToolTip properties are bound.
DataContext is available to all children of an element. If we assign the Category object to a StackPanel
containing two TextBox elements, we can set the Binding for each TextBox using the Category as the
source.
The running application below shows that both TextBox elements contain data from the Category that was
assigned to the StackPanel's DataContext.
Binding can also be performed programmatically. The example below creates a new instance of a Category,
creates a new Binding object and finally, calls the SetBinding() method. SetBinding() is a
FrameworkElement method that takes a DependencyProperty and a Binding object. The Binding object is
assigned the Source, Path and Mode, just as in the XAML examples.
tbTitle.SetBinding(TextBox.TextProperty, binding)
tbTitle.SetBinding(TextBox.TextProperty, binding);
<Grid x:Name="LayoutRoot">
<Grid.Resources>
<local:Categories x:Key="Categories">
<local:Category Title="Fiction"
Description="A wide range of fiction from the worlds best authors." />
<local:Category Title="Business and Investing" Description="Timely strategies and advice." />
<local:Category Title="Cooking, Food and Wine"
Description="Cook books for all budgets and palates." />
</local:Categories>
</Grid.Resources>
<ListBox ItemsSource="{StaticResource Categories}" DisplayMemberPath="Description" />
</Grid>
The running Silverlight project displays the ListBox populated with category descriptions.
INotifyPropertyChanged
To allow changes in business object properties to be reflected automatically by Silverlight elements, add a
reference to System.ComponentModel and implement the INotifyPropertyChanged interface.
INotifyPropertyChanged has a single method PropertyChanged(). When setting a property value, you need
to call PropertyChanged if its been assigned and pass the name of the property that's being changed.
set
{
if (title != value)
{
title = value;
OnPropertyChanged("Title");
}
}
}
set
{
if (description != value)
{
description = value;
OnPropertyChanged("Description");
}
}
}
ObservableCollection
To provide notifications of collection changes, inherit from ObservableCollection<T>. Add a reference to
System.Collections.ObjectModel before using ObservableCollection. The example below adds several
sample Category objects to the collection.
The example below assigns a Categories collection to the ItemsSource of a ListBox and sets the
DisplayMemberPath to the Category "Description" property. The example also has a Delete button that
removes the top element in the collection so we can see ObservableCollection notification in action.
<UserControl.Resources>
<local:Categories x:Key="Categories" />
</UserControl.Resources>
<Grid x:Name="LayoutRoot">
<ListBox x:Name="lbCategories" ItemsSource="{StaticResource Categories}"
DisplayMemberPath="Description" HorizontalAlignment="Left" VerticalAlignment="Top"
Margin="10" />
<Button x:Name="btnDelete" Content="Delete" Click="btnDelete_Click"></Button>
</Grid>
When elements are removed from the collection, the ListBox reflects the change.
Updating Data
This next example shows several of the previous techniques together. When the user changes the
description in the text box, then tabs off the text box, the Category business object is updated. Because
Category implements INotifyPropertyChanged, the currently selected list box item displays the matching.
text. Notice the new syntax in the Text Binding expression for the TextBox. It uses a new property
ElementName that points to the list box. The Binding Path points to the SelectedItem property of the list
box. The SelectedItem is actual a Category, so we can drill down to the "Description" property. Also notice
that the Binding Mode is TwoWay, allowing updates made in the Silverlight element to propagate back to
the business object.
<TextBox x:Name="tbDescription"
Text="{Binding Path=SelectedItem.Description, ElementName=lbCategories, Mode=TwoWay}"
HorizontalAlignment="Left" VerticalAlignment="Top" Margin="10" />
When the user selects items in the list box, the TextBox displays the corresponding Category Description.
When the TextBox Text is changed by the user and they tab off, the text is updated in the list box.
Using templates you can bind your data but arrange the visual layout in any configuration that suits your
purpose. You're not limited to just using the DisplayMemberPath because you can aggregate any number of
elements in the template and bind each of them. The ListBox ItemTemplate is a DataTemplate type. Inside
the ListBox element tag use the syntax "ListBox.ItemTemplate", i.e. "objectName.propertyName". Inside
that element place the "<DataTemplate>". The DataTemplate can contain only a single item, but you can
make that a StackPanel, Grid or other container item. From there you can populate the container with any
elements that work for your requirements.
<UserControl.Resources>
<local:Categories x:Key="Categories" />
</UserControl.Resources>
<Grid x:Name="LayoutRoot">
<ListBox ItemsSource="{StaticResource Categories}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel
ToolTipService.ToolTip="{Binding Description}" Margin="10" Orientation="Horizontal">
<Image Source="journal.png" Width="48" Height="48" Margin="5" />
<TextBlock Text="Title: " VerticalAlignment="Center" />
<TextBlock Text="{Binding Title}" VerticalAlignment="Center" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
In the running Silverlight application we can see that using templates lets you achieve a free form layout
with data bound elements.
4.5.2 HierarchicalDataTemplate
Each level of the hierarchy has its own template. The RadTreeView ItemTemplate points at the
"PresidentTemplate", the "PresidentTemplate" ItemTemplate points to the "VPTemplate" and so on until the
last "ManagerTemplate".
<UserControl.Resources>. . .
<DataTemplate x:Key="ManagerTemplate">. . . content
</DataTemplate>
<telerik:HierarchicalDataTemplate x:Key="DirectorTemplate"
ItemTemplate="{StaticResource ManagerTemplate}">. . . content
</telerik:HierarchicalDataTemplate>
<telerik:HierarchicalDataTemplate x:Key="VPTemplate"
ItemTemplate="{StaticResource DirectorTemplate}">. . . content
</telerik:HierarchicalDataTemplate>
<telerik:HierarchicalDataTemplate x:Key="PresidentTemplate"
ItemTemplate="{StaticResource VPTemplate}">. . . content
</telerik:HierarchicalDataTemplate>
</UserControl.Resources>. . .
<telerikNavigation:RadTreeView x:Name="tvMain" ItemTemplate="{StaticResource PresidentTemplate}">
</telerikNavigation:RadTreeView>
Here's another example that shows a two level hierarchy with Categories/Products. Look at the relationship
of the templates in the XAML below. The RadMenu ItemTemplate points to a HierarchicalDataTemplate
"CategoryTemplate". "CategoryTemplate" also has an ItemTemplate that points to a DataTemplate called
"ProductTemplate". There can be additional levels of templates. The rule is to use a
HierarchicalDataTemplate where there are more child items, then use a standard DataTemplate when no
more child levels remain.
Let's take another look at that same XAML to see how the data is being hooked up. The ItemsSource
attribute works in concert with the ItemTemplate. In the RadMenu XAML you can see that the
"CategoryTemplate" is supplied by data from the "Categories" object. In the "CategoryTemplate", the
"ProductTemplate" is supplied by data from the "Products" object.
See the "Menu Controls" and "TreeView" chapters "Binding" section for full walk throughs of these
examples.
If you need to bind to an XML file, consider using a combination of the XDocument class to read and parse
the XML, and LINQ to transform the parsed XML into a collection suitable for binding.
XDocument is found in the System.XML.Linq namespace and has a static Load() method that reads an
entire XML file and return an XDocument. Or you can use the static Parse() method that takes a string and
also returns an XDocument. Using the same Category data from previous examples, we can create XML
with the same data and structure.
This XML can be copied to a "Categories.xml" file and added to the project. In the constructor of the
UserControl, call the XDocument.Load() method and pass the path of the file, i.e. "Categories.xml".
XDocument has Element() and Elements() methods that return XElement and collections of XElement,
respectively. The example below first returns a collection of "Category" elements. A LINQ statement is used
to drill down to each Category "Title" and "Description" attribute and create new Category objects. This
collection of categories is assigned to the ItemsSource property and the DisplayMemberPath points to
"Description".
Imports System.Linq
Imports System.Windows.Controls
Imports System.Xml.Linq
Namespace _06_XML
Partial Public Class MainPage
Inherits UserControl
Public Sub New()
InitializeComponent()
Dim document As XDocument = XDocument.Load("Categories.xml")
Dim categoryElements = document.Element("Categories").Elements("Category")
Dim categories = _
From c In categoryElements _
Select New Category()
c.Attribute("Title").Value, Description = c.Attribute("Description").Value
Title = c.Attribute("Title").Value, Description
lbCategories.ItemsSource = categories
lbCategories.DisplayMemberPath = "Description"
End Sub
End Class
End Namespace
using System.Linq;
using System.Windows.Controls;
using System.Xml.Linq;
namespace _06_XML
{
public partial class MainPage : UserControl
{
public MainPage()
{
InitializeComponent();
XDocument document = XDocument.Load("Categories.xml");
var categoryElements = document.Element("Categories").Elements("Category");
var categories = from c in categoryElements
select new Category()
{
Title = c.Attribute("Title").Value,
Description = c.Attribute("Description").Value
};
lbCategories.ItemsSource = categories;
lbCategories.DisplayMemberPath = "Description";
}
}
}
The running Silverlight application results in the same display as when we bound the collection of Category
through other means.
4.6.2 REST
You can find a number of RESTful web services from popular web sites like YouTube, Flicker and Twitter.
These services are especially attractive because you don't need to deal with SOAP protocol, but instead,
you can simply use a WebClient object to download a resource from a standard URL. The returned string is
likely to be in XML, RSS or ATOM format. RSS and ATOM are XML variants and so the XDocument and
LINQ techniques explained in the previous XML section work well with REST service results.
For numerous example walk through using REST services, see the Binding sections of the "GridView",
"Docking", "ComboBox", "CoverFlow", "MediaPlayer" and "Charting" chapters.
The following explains the basic techniques of using REST services and a discussion of issues involved with
downloading images in Silverlight.
if (!webClient.IsBusy)
webClient.DownloadStringAsync(
new Uri("http://search.twitter.com/search.atom?q=telerik"));
Inside the DownloadStringCompleted event handler you have access to the argument's Result property.
Result contains XML that looks something like this abbreviated version:
<feed
<entry>
<id>tag:search.twitter.com,2005:4175721855</id>
<published>2009-09-22T16:37:21Z</published>
<link type="text/html"
href="http://twitter.com/timheuer/statuses/4175721855"
rel="alternate" />
<title>RT @nikiatanasov: Telerik announces new Book control for
Silverlight/WPF for Q3 http://tinyurl.com/maguwd</title>
<content type="html">RT <a href="http://twitter.com/nikiatanasov">
@nikiatanasov</a>: <b>Telerik</b> announces new Book
control for Silverlight/WPF for Q3 <a href="http://tinyurl.com/maguwd"
>http://tinyurl.com/maguwd</a></content>
<updated>2009-09-22T16:37:21Z</updated>
<link type="image/png"
href="http://a1.twimg.com/profile_images/426936590/tim-twitter_normal.png"
rel="image" />
<twitter:source><a href="http://www.seesmic.com/"
rel="nofollow">Seesmic</a></twitter:source>
<twitter:lang>en</twitter:lang>
<author>
<name>timheuer (Tim Heuer)</name>
<uri>http://twitter.com/timheuer</uri>
</author>
</entry>
</feed>
You can use the static XDocument Parse() method and pass the result XML to create an XDocument
instance. LINQ works nicely against XDocument so that you can select data from the document and
create a collection suitable for binding. The example below is primarily conceptual, so don't expect to
copy and paste it.
gvMain.ItemsSource = _
From item In xDocument.Descendants("entry") _
Select
End Sub
The WebClient OpenReadAsync() method and OpenReadCompleted event have a similar pattern to their
DownloadStringAsync siblings. In this case we take advantage of a second parameter called "userToken"
that can be any object you care to send along. We can send a reference to an Image object that's already
on the page.
The Result parameter for the OpenReadCompleted event handler is a stream that contains our Image data.
Create a BitmapImage and set its source to be the Result stream. Finally, assign the BitmapImage as the
source of the Image control on the page.
Image Issues
The image downloading code above is actually incomplete. There are two common problems that occur
when grabbing images from uncontrolled sources:
As of this writing, Silverlight supports only ".png" and ".jpg" images. For the Twitter service, you can
check the file extension when you parse the initial document. If the image is not a supported extension
you can either skip loading the image altogether or load a default image of some sort.
Silverlight restricts "Cross Domain" access, i.e., where the image data is retrieved from an internet
domain that is different from the requesting application. This may be allowed if the server where the
image resides has a file called ClientAccessPolicy.xml and is configured to permit the download. In
sites that permit access to both Silverlight and Flash, the server may have a "Crossdomain.xml" file
instead (as is currently the case with Twitter ). System.Reflection.TargetInvocationException is
raised when one of these files doesn't exist or is not configured to allow the download. You should test
that the inner exception is System.Security.SecurityException.
4.6.3 RIA
"Microsoft WCF RIA Services simplifies the traditional n-tier application pattern by bringing together the
ASP.NET and Silverlight platforms. RIA Services provides a pattern to write application logic that runs
on the mid-tier and controls access to data for queries, changes and custom operations. It also
provides end-to-end support for common tasks such as data validation, authentication and roles by
integrating with Silverlight components on the client and ASP.NET on the mid-tier."
The very short story is that WCF RIA Services allows you can write code describing your entities once and
to share that data with the client. You first create a "domain service" in the ASP.NET application that is
flagged to "EnableClientAccess". You describe your entities there (typically using LINQ to Entities, but this
is not required). The Silverlight client application must be in the same solution with the service and linked to
the service. Visual Studio takes care of generating client proxy code. The client code is termed the
"Context" object.
1) In Visual Studio, create a new Silverlight Application. This will display the New Silverlight Application
dialog. Check the "Enable .NET RIA Services" option. Leave the other defaults and click OK to close the
dialog and create the projects. The RIA service layer will be created in the ASP.NET host application, in
this case, the WcfRiaServicesProject.Web project.
2) Take a look at the Solution Explorer and notice that you now have two projects, the ASP.NET host
project ("WcfRiaServicesProject.Web in the screenshot below) that contains the RIA service and the
Silverlight client application ("WcfRiaServicesProject").
1) Add a "ADO.NET Entity Data Model" item to the ASP.NET project. Name the data model "Northwind.
edmx" and click the Add button. This will display the Entity Data Model Wizard dialog.
2) In the first page of "Entity Data Model Wizard", select the "Generate from Database" icon and click the
Next button.
3) Create a connection to the Northwind database file that ships with RadControls for Silverlight.
a) In the "Choose your Data Connection" page of the wizard, click the New Connection button. This will
display the Connection Properties dialog.
b) Click the Change button to show the "Change Data Source" dialog, select the "Microsoft SQL Server
Database File" option and click the OK button to return to the "Change Data Source" dialog.
c) Click the Browse button, locate the file "Northwind.mdf" file in the RadControls for Silverlight
installation directory under \Demos\DataSources.
d) Click OK to create the connection and return to the "Entity Data Model Wizard" "Choose Your Data
Connection" page.
4) In the "Choose Your Data Connection" page of the wizard, enter "NorthwindEntities" in the "Save entity
connection settings in Web.Config" text box and click the Next button.
5) In the "Choose Your Database Objects" page of the wizard, expand the "Tables" node in the tree view
and select the "Employees" table check box.
9) In the "Add New Domain Service Class" dialog, name the Domain Service Class "NorthwindService",
make sure that the "Enable client access" option is checked (this allows the client proxy code to be
generated), select "NorthwindEntities" from the drop down list of available context objects and check the
"Employees" entity check box. Click the OK button to create the domain service class and close the
dialog.
2) In the Solution Explorer, right-click the References node and select Add References... from the context
menu. Add references to these assemblies:
a) Telerik.Windows.Controls
b) Telerik.Windows.Data
c) Telerik.Windows.Controls.GridView
d) Telerik.Windows.Controls.Input
3) Open "MainPage.xaml" for editing.
4) Drag a RadGridView from the Toolbox into the main "LayoutRoot" grid element of the page. Set the "x:
Name" attribute to "gvMain" so that we can refer to it later in code.
5) Open "MainPage.xaml.cs" for editing.
6) Add a namespace reference to the ASP.NET service project "Imports" (VB) or "using" (C#) portion of
code.
7) In the constructor for the UserControl, get the data from the domain service by way of the context object
and bind it to the grid view using the code below:
a) Create the "context" object (the generated client counterpart to the domain service).
b) Assign the context "Employees" property to the grid view ItemsSource property.
c) Call the context object Load() method and pass the context "GetEmployeesQuery()" method.
public MainPage()
{
InitializeComponent();
Notes
If you place your cursor on the Load() method and press F12 to see its definition, the Load()
method is of type LoadOperation<TEntity> and represents an asynchronous load operation. You
can get the return value from the Load() call and use it to attach a Completed event handler.
From there you can respond to errors or fine tune the data before assigning to the grid. Here's an
example that checks for the Error exception object and uses LINQ to only show employees with
first names starting with "A".
public MainPage()
{
InitializeComponent();
NorthwindContext context = new NorthwindContext();
LoadOperation operation = context.Load(context.GetEmployeesQuery());
operation.Completed += new EventHandler(operation_Completed);
}
Press F5 to run the application. The web page should look something like the screenshot below.
4.6.4 WCF
Windows Communication Foundation (WCF) is an industrial strength infrastructure for moving data around
on the network. WCF separates the communication plumbing from the code that actually works with the
data. Silverlight applications can call WCF services provided we include a few tweaks that make WCF and
Silverlight happy with our security arrangements.
If the WCF service resides in a different domain than the client, i.e. a different port number for example, the
call is considered to be cross domain. To provide "cross domain access" to resources from a Silverlight
application, you must include a properly configured "ClientAccessPolicy.xml" file in the root where the
service lives. ClientAccessPolicy.xml defines the resources that can be retrieved and where they can be
retrieved from. The walk through following will show you how to do this using a generic ClientAccessPolicy.
xml that allows all traffic. You can "batten down the hatches" to restrict traffic later by making the policy file
entries more specific.
See the articles "Network Security Access Restrictions in Silverlight" (http://msdn.microsoft.com/en-us/
library/cc645032(VS.95).aspx and Http Communication with Silverlight (http://msdn.microsoft.com/en-us/
library/cc838250(VS.95).aspx) for more information.
The service for this walk through will bring back a simple list of categories. The actual logic or data access
in the service is not critical. In the background you could be using a SQL command, creating objects on the
fly, using Entities, WebClient, reading XML files, reading from isolated storage or any combination. This all
takes place in a server environment, so you have a lot of latitude.
1) Create a new ASP.NET Web Application project. This application will host the WCF service.
2) In the Solution Explorer, right-click the web application project and select Add > New Item... from the
context menu.
3) Select the "Silverlight-enabled WCF Service" template, name it "CategoriesService.svc" and click the
Add button to create the service and close the dialog.
Gotcha!
The Silverlight-enabled WCF Service template type is available for Visual Studio, SP1. Be aware
that it's not a project type, but rather a new item type that you add to an existing project, i.e. a
web application. If you have Visual Studio SP1, are adding an item to an existing ASP.NET Web
Application project and still don't see the "Silverlight-enabled WCF Service" template type, you
may have an issue with the path that defines the template location.
Using the Visual Studio menu, select the Tools > Options item and navigate to "Projects and
Solutions" in the tree view. Locate the "User item templates location:" entry on the right and
verify that it points to this path:
The screenshot below shows the options dialog with the default User Item Templates location
filled in.
4) Add a new class file "Category.cs" and replace the class with the code below. The code defines a
simple Category class and a generic list of Category with sample categories defined in the constructor.
5) Replace the service DoWork() method with a new GetCategories() method that returns a "Categories"
collection.
<OperationContract> _
Public Function GetCategories() As Categories
Return New Categories()
End Function
[OperationContract]
public Categories GetCategories()
{
return new Categories();
}
6) In the Solution Explorer, right-click the project and select Add > New Item... from the context menu.
Name the file "ClientAccessPolicy.xml", then click the Add button to create the file and close the
dialog.
7) Replace the contents of "ClientAccessPolicy.xml" with the following XML. This XML allows requests
from all domains to get resources from all locations on the server.
<?xml version="1.0" encoding="utf-8"?>
<access-policy>
<cross-domain-access>
<policy>
<allow-from http-request-headers="*">
<domain uri="*"/>
</allow-from>
<grant-to>
<resource path="/" include-subpaths="true"/>
</grant-to>
</policy>
</cross-domain-access>
</access-policy>
8) In the Solution Explorer, right-click the "ClientAccessPolicy.xml" file and select "Properties" from the
context menu. Set the "Copy to Output Directory" property to "Copy if Newer". This step will mak e
sure that policy file ends up in the \bin directory, i.e. the root directory for the service. When Silverlight
tries to access the service, it will find the policy file there and can continue interacting with the service.
Project Setup
1) From the Visual Studio menu choose File > New > Project..., select the Silverlight project type, then
select the Silverlight Application template. Provide a unique name for the project and click the OK
button.
2) In the "New Silverlight Application" dialog make sure that the "Host the Silverlight application in a new
Web site" option is checked, give the project a unique name and verify that the "ASP.NET Web
Application Project" New Web Project Type option is selected. Click OK to close the dialog and create
the project.
3) In the Solution Explorer, right-click the References node and select Add References... from the
context menu. Add Assembly references:
a) Telerik.Windows.Controls
b) Telerik.Windows.Controls.GridView
c) Telerik.Windows.Data
XAML Editing
1) Drag a RadGridView from the Toolbox to a point between the main "LayoutRoot" grid. Set the "x:Name"
attribute to "gvMain" so we can reference it later in code.
2) In the "Add Service Reference" dialog, click the Discover button. The CategoriesService.svc server will
display. Enter "CategoriesServiceReference" as the Namespace and click OK to create the client
proxy.
Code Behind
1) Add a namespace reference for the proxy in the "Imports" (VB) or "using" (C#) section of code. This will
be the name of your project + "." + "CategoriesServiceReference", i.e. "MyProject.
CategoriesServiceReference".
2) Add the code below to the page constructor to create the proxy and call its methods. . Be sure that
you leave the call to InitializeComponent().
The client proxy methods are asynchronous. The proxy will have a "Completed" event, i.e.
"GetCategoriesCompleted" and an "Async" method, i.e. "GetCategoriesAsync()". Create an instance of
the client proxy object, hook up the "GetcategoriesCompleted" event, then call the
"GetCategoriesAsync()" method.
client.GetCategoriesAsync()
End Sub
public MainPage()
{
InitializeComponent();
client.GetCategoriesAsync();
}
3) Handle the GetCategoriesCompleted event. The result of the method is passed back in e.Result.
Assign the result to the grid view ItemsSource property. Note: The result, even though it left the
service as a List<Customer>, ends up in Silverlight as ObservableCollection<Customer>.
You may not get the error to fire right away. Try renaming or removing the ClientAccessPolicy.xml
altogether. Make sure the service is stopped and rebuild it. Then, on the client, right-click the service
reference node in the Solution Explorer and choose "Update Service Reference" from the context menu.
Its a good idea to get familiar with how these errors occur while you have control in this lab setting.
ADO.NET Data Services expose data models as a set of REST Uri's that map to HTTP verbs POST
(Create), GET (Read), PUT (Update) and DELETE (Delete). The following walk through demonstrates
creating an ADO.NET Entity Data Model of the Northwind database. The example returns a simple address
table and displays it in a RadGridView control.
1) Create a new ASP.NET Web Application. Give it a unique name, click the OK button to create it and
close the dialog.
2) Add a "ADO.NET Entity Data Model" item to the ASP.NET project. Name the data model "Northwind.
edmx" and click the Add button. This will display the Entity Data Model Wizard dialog.
3) In the Entity Data Model Wizard dialog, select "Generate from database" and click the Next button to
continue.
4) Create a connection to the Northwind database file that ships with RadControls for Silverlight.
a) In the "Choose your Data Connection" page of the wizard, click the New Connection button. This will
display the "Connection Properties" dialog.
b) Click the Change button to show the "Change Data Source" dialog, select the "Microsoft SQL Server
Database File" option and click the OK button to return to the "Change Data Source" dialog.
c) Click the Browse button, locate the file "Northwind.mdf" file in the RadControls for Silverlight
installation directory under \Demos\DataSources.
d) Click OK to create the connection and return to the "Entity Data Model Wizard" "Choose Your Data
Connection" page.
5) In the "Choose Your Database Objects" page of the wizard, click the "AddressBook" table. Click the
Finish button to close the dialog.
Create Service
1) From the Solution Explorer, right-click the project and select Add > New Item... from the context
menu. Select the ADO.NET Data Service template, name it "NorthwindDataService.svc" and click the
Add button to create the service. This step will open "NorthwindDataService.svc.cs" for editing.
2) Change the NorthwindDataService class to the match the code below. "NorthwindDataService.svc.cs"
has a "TODO" comment marked in the source to insert a type into the DataService<T> class
declaration and another reminder to set access rules in the InitializeService() method. .
Notes
The XML/ATOM above may display as a news feed. You can shut this off temporarily in Internet
Explorer from Tools > Internet Options > Content > Settings and unselecting the "Turn on feed
reading view" checkbox. Reopen the browser to display the XML.
Project Setup
1) From the Visual Studio menu choose File > New > Project..., select the Silverlight project type, then
select the Silverlight Application template. Provide a unique name for the project and click the OK
button.
2) In the "New Silverlight Application" dialog make sure that the "Host the Silverlight application in a new
Web site" option is checked. Important note: Select the data service ASP.NET application as the
host. Currently the client and service must have the same domain and using the service application as
the host accomplishes this. Click OK to close the dialog and create the project.
3) In the Solution Explorer, right-click the References node and select Add References... from the
context menu. Add Assembly references:
a) Telerik.Windows.Controls
b) Telerik.Windows.Controls.GridView
c) Telerik.Windows.Data
XAML Editing
1) Drag a RadGridView from the Toolbox to a point between the main "LayoutRoot" grid. Replace the
RadGridView tag with the XAML below.
The XAML will define the name of the grid as "gvAddressBook " so we can refer to it later in code
behind. The columns are configured to fill the available width and column generation is turned off.
Instead, "FirstName", "LastName" and "Phone" columns of the table are defined.
<telerikGridView:RadGridView x:Name="gvAddressBook"
ColumnWidth="SizeToHeader" AutoGenerateColumns="False">
<telerikGridView:RadGridView.Columns>
<telerikGridView:GridViewDataColumn Header="First"
DataMemberPath="FirstName" />
<telerikGridView:GridViewDataColumn Header="Last"
DataMemberPath="LastName" />
<telerikGridView:GridViewDataColumn Header="Phone"
DataMemberPath="Phone" />
</telerikGridView:RadGridView.Columns>
</telerikGridView:RadGridView>
2) In the "Add Service Reference" dialog, click the Discover button. The NorthwindDataService.svc server
will display. Expand the node and select "AddressBook". Enter "NorthwindServiceReference" as the
Namespace and click OK to create the client proxy.
Gotcha!
An exception that contains the phrase "Pattern constraint failed", can be caused by a leading
underscore "_" character in the project name. Also know that projects beginning with numeric
characters will also be automatically modified to include a leading underscore in the namespace.
The short story is to avoid leading underscores because the parser will generate an exception.
Code Behind
1) Add a namespace reference for the proxy in the "Imports" (VB) or "using" (C#) section of code. This will
be the name of your project + "." + "NorthwindServiceReference", i.e. "MyProject.
NorthwindServiceReference".
2) Add the code below to the page constructor to query the data service. Be sure that you leave the call to
InitializeComponent().
First create a Uri that points at the service. You can use the HtmlPage.Document.DocumentUri to get
the base address and then add the name of the service. Create an instance of NorthwindEntities,
passing the Uri in the constructor. The NorthwindEntities instance will be our "context". The context
contains "AddressBook " in the form of a DataServiceQuery that can be executed asynchronously to
return data. Call the DataServiceQuery BeginExecute() method and pass the name of a handler (to be
defined next) and the DataServiceQuery instance itself.
public MainPage()
{
InitializeComponent();
Gotcha!
3) Handle the DataServiceQuery execution result event. The signature of the event handler expects an
IAsyncResult "result" parameter. The AsyncState property of the result is the DataServiceQuery
instance you passed into BeginExecute(). Cast the AsyncState back to its original DataServiceQuery
type so that you can call the EndExecute() method, passing the IAsyncResult as the parameter.
EndExecute() returns an IEnumerable of AddressBook . Call the ToList() function on the collection and
assign it to the ItemsSource of the grid.
Gotcha!
If you don't include the call ToList(), you may get the error "Only a single enumeration is
supported by this IEnumerable". A Microsoft engineer's take on this from their forums:
"In the current implementation, the result from a query can only be enumerated once. This is
because the underlying feed is not saved locally, and once we have gone through it once, we
save all entities in the context . . ."
4.6.6 OpenAccess
OpenAccess is a Object Relational Mapping technology from Telerik. OpenAccess is used to generate
objects that map to database tables and have data access tasks taken care of for you automatically. We
can use OpenAccess against databases such as SQL Server or together with other sources of data such
as REST, WCF and ADO.NET Data Services. These latter services can be code intensive and prone to error
if coded by hand. At the time of this writing, Telerik Labs has introduced the Data Services Wizard to handle
creating OpenAccess infrastructure for REST, WCF and ADO.NET Data Services. At this time, the
download is located at:
http://www.telerik.com/community/labs/telerik-data-services-wizard.aspx
This walk through requires OpenAccess to be installed.
1) In Visual Studio, create a new Class Library project to contain the Data Access Layer (DAL).
2) In Visual Studio, Right-click the class library project and select OpenAccess > Enable Project from
the context menu.
3) Take the defaults and on the "Does your project contain the following" page of the OpenAccess Wizard,
uncheck "Persistent Classes" and check the "Data Access code (DAL)" options.
5) When you have finished ORM enabling the project using the wizard, open the Visual Studio menu and
select Telerik > Open Access > Reverse Mapping. Select only the Employees table and ignore the
other tables. Generate and save the configuration.
6) Build the DAL class library project.
1) Add a Silverlight Application to the solution and select the option to create a new web application to
host the Silverlight client.
2) Using the Telerik > Enable Project option against the ASP.NET host application. Uncheck both
"Persistent Classes" and "Data Access Code" options. In the connection portion of the wizard you can
select "None" from the drop down list.
3) In the Solution Explorer, make sure there are references to the following:
a) The DAL class library project.
b) Telerik.OpenAccess
c) Telerik.OpenAccess.Query
d) System.Data.Services
e) System.Data.Services.Client
f) System.ServiceModel
4) Run the Telerik Data Services Wizard and generate your data context and data service classes:
a) Click the Browse button for the "OpenAccess DAL". Locate and select the DAL assembly DLL.
b) Enter a new Namespace "Northwind" and Service Name "NorthwindService"
c) The server type should be "ADO.NET Data Service".
d) Click the Save button to generate the files. When the "Choose a Folder" dialog appears, locate the
folder in the host web application.
The sample project in the Solution Explorer looks like the screenshot below. Notice the generated
"Northwind" folder and that it contains "WebDataService.svc". We will refer to this file later in code.
1) In the Solution Explorer, create a reference to the service (See the ADO.NET Data Services walk
through for more information on how to do this).
2) In the Solution Explorer, add references to the following assemblies:
a) Telerik.Windows.Controls
b) Telerik.Windows.Controls.GridView
c) Telerik.Windows.Data
d) Telerik.Windows.Themes.Windows7 (optional)
3) Open MainPage.xaml for editing.
4) Drag a RadGridView from the Toolbox to the a point inside the main "LayoutRoot" grid element.
Replace the RadGridView element with the XAML below. The "x:Name" is defined so we can refer to
the grid in code for binding. The other top level properties are optional and deal with the appearance of
the grid. AutoGenerateColumns is set to false and two columns are defined for the first and last names
in the Employee table.
<telerikGridView:RadGridView x:Name="gridEmployees"
telerik:StyleManager.Theme="Windows7"
ShowGroupPanel="False" AutoGenerateColumns="False"
GridLinesVisibility="None">
<telerikGridView:RadGridView.Columns>
<telerikGridView:GridViewDataColumn Header="First"
DataMemberBinding="{Binding FirstName}" />
<telerikGridView:GridViewDataColumn Header="Last"
DataMemberBinding="{Binding LastName}" />
</telerikGridView:RadGridView.Columns>
</telerikGridView:RadGridView>
5) In the code behind, use the code below to replace the constructor and add a handler for the result of the
data service query.
public MainPage()
{
// leave this method call in place!
InitializeComponent();
4.7 Wrap Up
In this chapter you learned the basics of binding objects and collections of objects to Silverlight elements.
You learned some of the syntax variations used to bind declaratively and how to bind elements using code
alone. You also learned how to detect changes in property data and changes to collections.
We looked at how elements are bound in two common templates: DataTemplate and
HierarchicalDataTemplate.
You learned how to bind data from a variety of popular data sources including straight XML and variations of
XML, i.e. ATOM and RSS. In the process you used a combination of XDocument and LINQ to read, parse
and convert the data to a form suitable for binding. Building off the XML techniques we discussed REST
services. Then you worked through examples of WCF RIA services, WCF and ADO.NET Data Services.
V
Expression Blend
Expression Blend 131
5 Expression Blend
5.1 Objectives
In this chapter you will apply your general Silverlight knowledge to the Expression Blend environment. First
we will tour project types available in Expression Blend, then delve deeper into elements of the Expression
Blend environment, becoming familiar with the major functional areas of the application. You will learn how
Expression Blend organizes and manipulates resources. You will use Expression Blend to create and edit
styles and also restyle a RadControl. You will use Expression Blend to edit a control template. You will see
how Expression Blend creates animation and how animation can be triggered in code and through state
changes. Finally, you will learn how to interactively bind data to elements.
\Courseware\Projects\<CS|VB>\Blend\Animation
\Courseware\Projects\<CS|VB>\Blend\Binding
\Courseware\Projects\<CS|VB>\Blend\Resources
5.2 Overview
Expression Blend is an interactive, WYSIWYG design tool that helps you create compelling graphical web
and desktop applications. Expression Blend is designed to work well with Visual Studio to allow smooth
coordination between designers and developers. Both environments use the same design file format (XAML),
solution and project format. Expression Blend is part of the Microsoft Expression suite of tools that also
includes a website designer, a website compatibility preview, a vector graphics editor and applications to
manage and encode digital media.
Currently in Expression Blend you can create the following project types:
In the "Working with Silverlight" chapter you briefly explored the Expression Blend environment and became
acquainted with some of the key areas such as the Artboard, Properties pane and the Objects and Timeline
pane. This section takes a deeper dive into the Expression Blend environment.
Projects pane
Assets pane
Parts pane
States pane
The Objects and Timeline pane displays the visual tree in the
left side of the pane. You can drag controls, effects, behaviors
and other assets to locations within the tree. This can be
especially helpful if the Artboard has become two
complicated to navigate easily. Element visibility can be
toggled and elements can also be locked in place to prevent
accidental rearranging. "StoryBoards" can be created to
animate element properties.
Properties pane
Data pane
The Data pane lets you define data sources and drag data to
the design surface. Use the List button (upper left) to
determine that dragged items will be interpreted as
collections and will create a bound ListBox when dropped.
Use the Details button when you want to drop individual items
as a bound TextBlock.
The Sample Data drop down lets you define or import
"sample" data. The Data Source drop down lets you choose
an existing object as the data source.
Data sources are automatically defined as resources for the
project or the document (UserControl.Resources) only.
Resources pane
Results pane
Tools pane
The Tools pane provides quick access to the key tools you'll
need for working in the Artboard without having to dig too
deep into a menu. Look for the small arrow in the lower right
of some icons that can be right-clicked to show a sub-menu.
The screenshot shows all the available layout panels.
If one of the panes is not visible, use the main Expression Blend menu "Window" item to make the pane
visible again.
Artboard
The application's visual interface is described using the Artboard. One or more files can be edited and these
files show as tabs across the top of the Artboard. Below these tabs is a breadcrumb bar that is
synchronized with the selected element in the Objects and Timeline pane. The selected element may have
a drop down list to enable template editing.
To the right of the file tabs, a drop down arrow displays a list of all files being edited currently. Below that
are three buttons that determine the active document. By default you first see a pure design view. You can
view the document as XAML only or split the view between design and XAML.
Below the design surface are controls that fine tune the interface. The Zoom control lets you enter a zoom
percentage directly, select from a list of values or choose "Fit to Selection". The set of buttons next to
zoom let you toggle rendering of effects, grid visibility, snap-to-grid, snap-to-snapline and annotations.
5.3 Resources
Resources are populated automatically by Expression Blend when you create new styles, templates and
data sources. The Resources pane lets you drag a resource between locations: the App.xaml file, the
UserControl.Resources element of MainPage.xaml or any other resource dictionaries that you define.
You can also use the Properties pane Advanced Property Options button to move properties between
locations, for example from "local", i.e. inside the element definition, to UserControl.Resources. Advanced
Property Options button also lets you take a property and Convert to New Resource. This places the
property into the resource element of the Application, the User Control or into a new resource dictionary.
The screenshot below is the Advanced Property Options for a Background property.
Notes
Notice the color key in the advanced properties menu. When you
select an option such as a local resource, the Advanced Property
Options button displays in the corresponding color. Here the
Background has been assigned a resource and displays in green.
Once a resource has been created, it can be applied to other elements by using the Advanced Property
Options button and selecting from the Local Resources list:
For larger production applications you will want to divide your resources into separate resource dictionaries.
You can do this in the Resources pane using the "Create new resource dictionary" button. Resource
dictionaries created this way are automatically linked into the App.xaml file and therefore available to your
entire application. Right-click the App.xaml node to Link or Unlink a resource dictionary.
<UserControl.Resources>
<Style x:Key="MyButtonStyle" TargetType="Button"/>
</UserControl.Resources>
The Objects and Timeline pane shows that we're currently editing "MyButtonStyle". Note: The Scope
Up button lets you navigate back to editing the entire user control.
The Resources pane lists the new "MyButtonStyle" and has a button to edit the resource.
Once we're editing the resource, you simply work directly in the Artboard and the changes become part of
the style. For example, you could change the brush colors for the Button Background, BorderBrush and
Foreground to some red, orange and yellow colors. Now the style reflects the changes made in the Artboard
and can be seen in the XAML below.
If there are other buttons on the page you can right-click them and choose Edit Template > Apply
Resource > "MyButtonStyle" from the context menu. All the buttons will be styled exactly the same and
take up very little room in the generated XAML.
Styling a RadControl
Lets use a RadButton and select the Edit a Copy context menu option. Where before the XAML was easily
viewed, the updated XAML will require scrolling through several screenfuls of styles, templates and brushes
and storyboard animations. This doesn't present a problem because Expression Blend lets us work in a
visual environment and forget about the XAML resource definitions. Looking at the Resources pane we can
see that RadButton has a number brushes already defined. Each brush colors part of the control for a given
state, e.g. "CoreButtonOuterBorder_Pressed". So we can edit colors right from the Resources pane without
having to pinpoint specific parts or states of the control first.
Now the edited style for the button in the designer looks like the screenshot below.
Project Setup
1) Run Expression Blend.
2) From the File menu select New Project. Note: If you have an existing solution open, right-click the
solution and select Add New Project... from the context menu instead.
3) In the New Project dialog, select "Silverlight" from "Project types" and "Silverlight 3" Application from
the right-most list. Enter a unique name for the project and click OK.
2) Right-click the grid and select "Rename" from the context menu. Name the grid "GridContent".
3) From the Assets pane > Media, drag "search.png" inside the "GridContent". The tool tip will read
"Create in GridContent". This should insert an Image control just before "Content". The screenshot
below shows the state of the template at this point.
c) Right-click, select Rename from the context menu and set the name to "Content_Shadow". The
Objects and Timeline pane should look like the screenshot below.
6) Select the "Content_Shadow" element in the Objects and Timeline pane and set properties:
a) Appearance > Opacity = "30%"
b) Transform > Scale > Y = "-0.5"
c) Transform > Skew > X = "30"
d) Transform > Center Point > Y = "0.8"
7) The template in the Artboard looks like this screenshot:
8) Press the "Up Scope" button until you reach the User Control.
The screenshot shows the button in its normal state and with mouse over. The button has all of its previous
capability and also includes the new "shadow" and image elements.
Overview
Animation lets you control the transition between two property values. The property could be an element's
location, size, brush color, nearly any property. Animations can be contained in a "storyboard" and
triggered programmatically or can be triggered by a change in "state", e.g. the mouse passes over an
element.
You work with animation in the Timeline portion of the Objects and Timeline pane. The main function of the
Timeline is to record "Keyframe" objects that contain one or more properties for an element at a particular
point in time. Each row in the Timeline corresponds to an element and each column is a "frame", i.e. a
location in time. The Timeline marker indicates the frame that we're working with currently.
Clicking the Play Animation button snaps the Timeline marker to the start of the Timeline, where it begins to
move steadily to the right, encountering Keyframes and changing properties along the way. When the
animation is playing, you can watch the dynamically changing properties in the Artboard.
Let's look at the steps to creating a simple animation using the Expression Blend designer that rotates a
RadButton in place in response to the button's Click event. After the RadButton has been added to the
Artboard we can create a new Storyboard in the Objects and Timeline pane:
Once the storyboard is created, two things happen in the Expression Blend environment. The Artboard
displays a message that indicates that property changes are being recorded:
...and a Timeline has opened up in the Objects and Timeline pane. The Timeline will allow us to set property
changes at specific times and to play those changes as if playing a video.
Now we can change property values of the RadButton and the changes will be recorded in the Keyframe for
the current location in the Timeline. This example uses the Properties pane to change the Transform >
Rotate > Angle property to "180".
Looking back to the Objects and Timeline pane we can see a new Keyframe is created for the button at the
Timeline marker location.
Tip!
If you don't want to change a property but instead want to use the current value of a property,
use the Properties pane Advanced Property Options button and select the "Record Current
Value" option if its enabled. If the button is not enabled, the property may not be supported for
animation.
If you double-click the Keyframe, its properties will display in the Properties pane along with the Keyframes
"Easing" settings. "Easing" is a path that the transition takes between two property values. For example,
you may wish for the change to take place evenly as the timeline progresses or you might want the change
to be nearly complete early on. You can use an EasingFunctions to establish a predefined easing profile
such as "Sine" or "Elastic". The "Hold In" tab simply waits until the animation completes before changing
the property value.
Clicking the Play button causes the RadButton to rotate at an even pace,
ending up at 180 degrees.
You can initiate the animation in code using the Storyboard Begin() method.
Tip!
Click F6 in Expression Blend to switch between the "Design" and "Animation" workspaces.
"Animation" arranges the workspace with maximum space available for the Timeline.
State Changes
Animations are often triggered by changes in control state such as a "mouse over" or "focused".
RadControls have a great deal of animation built-in for state changes, right out-of-the-box. For example,
RadMenu animates menu expansion and RadButton animates the MouseOver state. You can alter the
existing animations or add your own. The following walk through demonstrates enlarging the content of a
button when the mouse passes over.
1) Create a new Silverlight Application in Expression Blend.
2) MainPage.xaml should already be open for editing. If not, locate MainPage.xaml in the Projects pane
and double-click to open the page.
3) In the Projects pane, right-click the References node and select Add Reference... from the context
menu.
4) Add a reference to the Telerik.Windows.Controls.dll assembly.
5) From the Project menu select Build Project.
6) Add the RadButton to the page.
a) Open the Assets pane.
b) On the left side of the Assets pane is a tree view. Locate and select the "Controls" node.
c) In the Assets pane, just above the tree view is the Assets Find entry text box.
d) Type the first few characters of RadButton into the Assets Find entry text box. A list of all matching
controls will show to the right of the tree view.
e) Locate the RadButton control and drag it onto the MainPage.xaml Artboard.
7) Set properties using the Properties pane:
a) Common Properties > Content = "Click Me!"
8) Right-click the RadButton and select Edit Template > Edit a Copy from the context menu. In the
"Create Style Resource" dialog, set the Name (Key) to "MyButtonStyle". Click OK to create the style
resource and close the dialog.
9) In the Objects and Timeline pane, expand the tree view and locate and select the "Content" node.
10)In the States pane, click the MouseOver state. This will turn on recording of properties. This should be
reflected in the Artboard as shown in the screenshot below:
11)The Objects and Timeline pane should now display the animation Timeline. If the Timeline does not
display, click the "camera" button (circled in red below) to open it. Notice the yellow Timeline marker at
the "0" position in the Timeline. There are elliptical marks next to two border elements that indicate that
animations have already been defined. These animations are responsible for changing brushes as the
mouse passes over the button.
12)Move the Timeline marker to the next closest tick on the right of its current position. You can use the
"Go to next frame" button to move the marker or simply drag it.
13)In the Properties pane, set Transform > Scale > X = "1.5" and Transform > Scale > Y = "1.5"
14)Click the "Go to next frame" button.
15)In the Properties pane, set Transform > Scale > X = "1" and Transform > Scale > Y = "1"
16)Click the Play button to preview the animation.
Press F5 to run the application. The web page should look something like the screenshot below.
As the mouse passes over the button, the content expands briefly. Also notice that the existing animations
that change brush color also occur at the same time.
5.7 Binding
5.7.1 Overview
Using Expression Blend you can interactively assign a DataContext and bind specific properties to any data
in the project, including other elements in the page. The Data pane lets you drag-and-drop data onto
elements in the Artboard to automatically assign the DataContext and begin configuring the ItemsSource.
Data can come from business objects or you can use "sample" data where Expression Blend assigns
mock data while the interface is still being developed.
Expression Blend introduces the notion of "Sample" data where you create data sources intended for use
during prototyping and development. You can create objects or collections right in the designer and
Expression Blend will populate with mock data. You can also import XML to populate your sample objects.
Sample data can be switched off in the production compile to significantly reduce the applications footprint.
See the upcoming "Drag and Drop Binding" on how to bind sample and object data sources to elements in
the page.
You can create your own objects on the fly using the "Define New Sample Data" option.
This will display the "Define New Sample Data" dialog. Enter a new Data source name and click the OK
button to create the data source and collection.
The data source starts out as a collection with sample properties. Each property can be renamed by
double-clicking. The drop down to the right of each property allows you to change the property types and
configure options for each property type.
For example, we can rename the first property to "RoomNumber". By dropping down the Property Options,
we can configure the property by choosing the type (String, Number, Boolean, Image), the Format (Name,
URL, Date, Phone number, etc), maximum number of words and the maximum word length.
Selecting the "Edit Sample Values" button displays the "Edit Sample Values" dialog. Rows are pre-
populated by default with random data that fits the property options rules. In the dialog you can access the
property options in the header of each column, edit the data directly in the grid or change the number of
records.
When the dialog is closed, the data refreshes the sample data XAML file maintained by Expression Blend.
The generated XAML is shown below:
<!--
********* DO NOT MODIFY THIS FILE *********
This file is regenerated by a design tool. Making
changes to this file can cause errors.
-->
<SampleData:ReservationsDataSource
xmlns:SampleData="clr-namespace:Expression.Blend.SampleData.ReservationsDataSource"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<SampleData:ReservationsDataSource.Collection>
<SampleData:Item RoomNumber="A3443" Vacant="True" />
<SampleData:Item RoomNumber="A3445" Vacant="False" />
<SampleData:Item RoomNumber="A3447" Vacant="True" />
<SampleData:Item RoomNumber="A3450" Vacant="False" />
<SampleData:Item RoomNumber="B1220" Vacant="True" />
</SampleData:ReservationsDataSource.Collection>
</SampleData:ReservationsDataSource>
To create sample data from existing XML, use the Data pane "Add sample data source" drop down and
select "Import Sample Data from XML...".
Shown below is a very small bit of XML that we can import as an example. The XML is stored in a file called
"Flights.xml". You can also use the XML files that ship in the RadControls installation under the \Examples
directory.
<Flights>
<Flight Airline="United" FlightNumber="123" />
<Flight Airline="Southwest" FlightNumber="3423" />
<Flight Airline="American" FlightNumber="566" />
</Flights>
Clicking the "Import Sample Data from XML..." button displays the "Import Sample Data from XML" dialog.
Use the Browse... button to locate an XML file for importing. You can store the resource that points to this
data in either the user control or the application XAML. The "Enable sample data when application is
running" checkbox can be shut off later when you don't need the overhead of the sample data. Click the OK
to create the sample data objects in your project.
So what happened when we imported the sample data? A "SampleData" directory was created in our
project and populated with a second directory containing all the materials needed for our data source.
The flightsSampleDataSource.xaml contains the imported data in XML form. The flightsSampleDataSource.
xaml.cs wraps the data in class form complete with INotifyPropertyChanged and ObservableCollection<>
support. These files are all generated and shouldn't be edited by hand. See an example of the generated
code below:
// To significantly reduce the sample data footprint in your production application, you can set
// the DISABLE_SAMPLE_DATA conditional compilation constant and disab le sample data at runtime.
#if DISABLE_SAMPLE_DATA
internal class Flights { }
#else
In the App.xaml file, a resource has been added to allow easy declarative access when we bind the data
source to an element in the page.
<Application . . .
xmlns:SampleData1="clr-namespace:Expression.Blend.SampleData.flightsSampleDataSource" >
<Application.Resources>
<SampleData1:Flights x:Key="flightsSampleDataSource" d:IsDataSource="True"/>
</Application.Resources>
</Application>
The sample data is handy for prototyping but limited in terms of the types of properties available and how
they are implemented. Expression Blend lets you hook up to any object in an available namespace. Lets
start with a "Flights" collection that defines instance of "Flight" in the constructor. This class could as
easily be a web service client or some other business object.
The first step to accessing a class for binding is to select "Define New Object Data Source..." from the Data
pane.
This action displays the "Define New Object Data Source" dialog. The dialog has a tree view of classes
available to the Silverlight application. Use the entry above the tree view to filter the list. In the screenshot
below, the "Flights" class is selected.
The result of selecting OK in this dialog is to add a reference to the assembly that contains the data source
class. In the example below, the assembly is called "Binding" and the XML namespace alias is "local". A
resource is added automatically that references the "Flights" class called "FlightsDataSource". Now we can
use this data source in binding expressions. See the next section "Drag and Drop Binding" on how to bind
your data sources to elements in the page.
<UserControl.Resources>
<local:Flights x:Key="FlightsDataSource" d:IsDataSource="True"/>
</UserControl.Resources>
How a drag operation plays out between the Data pane and the Artboard depends on three things: the node
being dragged, where the node is being dragged to and the "Mode" button that is currently pressed. Either
the "List" or "Details" Mode button will be depressed at any one time. The node being dragged can be a
namespace, a data source class, a collection within the class or a field within a collection.
In List Mode, if you drag fields to the Artboard, a ListBox is created and the ItemsSource is bound to the
collection containing the fields.
In XAML, the result of the drag operation in the screenshot above is:
A DataTemplate is created to contain TextBlock elements bound to the Fields that were dragged
The DataContext of the parent, i.e. the "LayoutRoot" Grid, is bound to the data source class
The ListBox ItemsSource is bound to the collection.
The XAML below shows the placement of DataTemplate, DataContext and ItemsSource after the drag in
List Mode.
<UserControl.Resources>
<DataTemplate x:Key="EmployeeTemplate">
<StackPanel>
<TextBlock Text="{Binding FirstName}"/>
<TextBlock Text="{Binding HomePhone}"/>
<TextBlock Text="{Binding LastName}"/>
</StackPanel>
</DataTemplate>
</UserControl.Resources>
In Details mode, fields dragged onto the Artboard are rendered as a set of TextBlocks wrapped in a bound
grid. If we already have a bound ListBox on the page, Expression Blend automatically binds to the ListBox
element SelectedItem property, so that the ListBox and the details are in sync.
Here's an abbreviated look at the XAML produced by a drag of fields in Details mode. Attributes not directly
related to this binding scenario have been removed. Notice that the innermost grid containing the detail
items is bound to the "listBox" element, SelectedItem property. .
<UserControl.Resources>
<DataTemplate x:Key="EmployeeTemplate">
<StackPanel>
<TextBlock Text="{Binding FirstName}"/>
<TextBlock Text="{Binding HomePhone}"/>
<TextBlock Text="{Binding LastName}"/>
</StackPanel>
</DataTemplate>
</UserControl.Resources>
<Grid x:Name="LayoutRoot"
DataContext="{Binding Source={StaticResource EmployeesSampleDataSource}}" >
<ListBox x:Name="listBox" ItemTemplate="{StaticResource EmployeeTemplate}"
ItemsSource="{Binding EmployeeCollection}" />
<Grid DataContext="{Binding SelectedItem, ElementName=listBox}"
d:DataContext="{Binding EmployeeCollection[0]}">
<TextBlock Text="FirstName"/>
<TextBlock Text="{Binding FirstName}" />
<TextBlock Text="HomePhone"/>
<TextBlock Text="{Binding HomePhone}" />
<TextBlock Text="LastName"/>
<TextBlock Text="{Binding LastName}" />
</Grid>
</Grid>
Notes
Don't be confused by the "d:DataContext" tag in the XAML. This is for design time only and binds to
the first element in the collection at design time just so you have some data to look at. You can
remove this tag without impact to the running application
When you run the application, the fields you selected in the Data pane are displayed in the ListBox. When
a list box item is selected, the corresponding data shows up in the detail TextBlocks.
Project Setup
1) Run Expression Blend.
2) From the File menu select New Project. Note: If you have an existing solution open, right-click the
solution and select Add New Project... from the context menu instead.
3) In the New Project dialog, select "Silverlight" from "Project types" and "Silverlight 3" Application from
the right-most list. Enter a unique name for the project and click OK.
6) Click the Browse... button, locate and select the "Employees.xml" file. "Employees.xml" can be found
in the RadControls installation directory under the \Examples\Datasources directory. Click OK to create
the data source.
7) Drag the "EmployeeCollection" from the Data pane onto the RadGridView in the Artboard. Notice the
tooltip that appears "Choose a property of [RadGridView] to bind to EmployeeCollection[0]". The
"Create Data Binding" dialog will appear in response to the drop.
Notes
Be sure that the "Details Mode" button is down before you drag the collection. The
other button "List Mode" will create a bound ListBox.
8) In the "Create Data Binding" dialog, select the DataContext property and click OK to close the dialog.
9) In the Properties pane > Miscellaneous > ItemsSource, select Data Binding... from the Advanced
Property Options button. This action will display the Create Data Binding dialog.
10)In the Create Data Binding dialog, select the Data Field tab, select "EmployeesSampleDataSource"
from the Data sources list and "EmployeeCollection" in the Fields list. Click OK to create the binding
and close the dialog.
Notes
If you review the XAML at this point, you can see that the DataContext is bound to the data
source through the "EmployeesSampleDataSource" resource. The ItemsSource is bound to the
EmployeeCollection within the data source.
This can be done through the Properties pane > Common Properties > Text, clicking the Advanced
Property Options button and selecting Data Binding... from the menu. In the Create Data Binding
dialog, select the Element property tab. Then select the RadGridView from the "Scene Elements" list
on the left and the CurrentCell > Content from the "Properties" list on the right.
Clicking OK on the dialog creates the following binding in the XAML where the Text property of the
TextBlock is bound to the RadGridView CurrentCell.Content property.
When you run the modified application, the contents of the selected cell show up in the text block.
5.8 Wrap Up
In this chapter you learned how to apply your general Silverlight knowledge to the Expression Blend
environment. First we took a tour of project types available in Expression Blend. Then you delved deeper
into elements of the Expression Blend environment, becoming familiar with the major functional areas of the
application. You learned how Expression Blend organizes and manipulates resources. You used
Expression Blend to create and edit styles and you also restyled a RadControl. You used Expression Blend
to edit a control template. You saw how Expression Blend creates animation and how animation can be
triggered in code and through state changes. Finally, you learned how to interactively bind data to elements.
VI
Theming and Skinning
Theming and Skinning 173
6.1 Objectives
In this chapter you will learn how Telerik themes are used with RadControls and other Silverlight elements to
create a uniform look-and-feel for your application. First you will apply predefined themes to RadControls
and observe how themes can be changed without otherwise altering target elements. You will learn how
themes are distributed and applied to elements in XAML and in the code behind. You will apply themes to
an entire application and to individual elements in the application. You will also create a custom theme and
learn the syntax for applying the theme.
In the "Modifying Themes" section of the chapter you will learn about the "Themes" solution and its
structure. You will modify theme brushes to skin the theme with a new color set. You will apply the modified
theme to the UI in a Visual Studio project. Finally, you will expand this example to add effects and
animation to a RadButton control.
\Courseware\Projects\<CS|VB>\Theming\Theming.sln
6.2 Overview
A theme is a mechanism to organize multiple sets of styles and a apply them all at one time to a control or
an entire application. The theme engine of RadControls for Silverlight is quite powerful and provides the
developers with the ability to apply an application-wide theme on all RadControls, as well as specific
themes on a single control. If the theme does not contain the needed styles, the controls fallback to their
default styles. You can create an application theme for just a couple of controls and the rest will display
with their default styles. RadControls for Silverlight comes with a set of predefined themes, that can be used
right out of the box, modified for your specific application requirements or new themes can be created from
scratch.
Project Setup
1) From the Visual Studio menu choose File > New > Project..., select the Silverlight project type, then
select the Silverlight Application template. Provide a unique name for the project and click the OK
button.
2) In the "New Silverlight Application" dialog make sure that the "Host the Silverlight application in a new
Web site" option is checked, give the project a unique name and verify that the "ASP.NET Web
Application Project" New Web Project Type option is selected. Click OK to close the dialog and create
the project.
3) In the Solution Explorer, right-click the References node and select Add References... from the
context menu. Add Assembly references:
a) Telerik.Windows.Controls
b) Telerik.Windows.Controls.Input
c) Telerik.Windows.Controls.Navigation
d) Telerik.Windows.Themes.Summer
e) Telerik.Windows.Themes.Windows7
XAML Editing
1) Open MainPage.xaml for editing.
2) Add XML namespaces to the UserControl element:
<UserControl
xmlns:telerikNavigation=
"clr-namespace:Telerik.Windows.Controls;assembly=Telerik.Windows.Controls.Navigation"
xmlns:telerikInput=
"clr-namespace:Telerik.Windows.Controls;assembly=Telerik.Windows.Controls.Input"
xmlns:telerik=
"clr-namespace:Telerik.Windows.Controls;assembly=Telerik.Windows.Controls" . . .>
3) Add the XAML below to the main "LayoutRoot" Grid element. The XAML should be added between the
<Grid> and </Grid> tags. This will define a number of RadControls and one standard Silverlight
CheckBox control. No specific theme has been applied to any of the controls.
<Grid x:Name="LayoutRoot">
<StackPanel>
<telerikNavigation:RadToolBarTray Margin="10" HorizontalAlignment="Left" >
<telerikNavigation:RadToolBar >
<telerikNavigation:RadToolBar.Items>
<telerik:RadButton Content="Click Me!" Margin="10" />
<telerik:RadRadioButton Content="Red" IsChecked="True" Margin="10" />
<telerik:RadRadioButton Content="Green" Margin="10" />
<telerik:RadRadioButton Content="Blue" Margin="10" />
<telerikInput:RadNumericUpDown Margin="10" Value="23" />
<telerikInput:RadComboBox Margin="10">
<telerikInput:RadComboBoxItem Content="Large" />
<telerikInput:RadComboBoxItem Content="Medium" />
<telerikInput:RadComboBoxItem Content="Small" />
</telerikInput:RadComboBox>
</telerikNavigation:RadToolBar.Items>
</telerikNavigation:RadToolBar>
</telerikNavigation:RadToolBarTray>
<telerikInput:RadCalendar Margin="10" HorizontalAlignment="Left" />
<StackPanel Orientation="Horizontal">
<CheckBox Content="Print Double Sided" IsChecked="True" Margin="10" />
</StackPanel>
</StackPanel>
</Grid>
4) Press F5 to run the application. The controls display their default appearance.
6) Press F5 to run the application. Now all the RadControls inside the tool bar tray, the RadCalendar and
the standard Silverlight CheckBox control are all styled in the "Summer" theme.
7) Edit StyleManager.Theme tags in the XAML and assign the "Windows7" theme.
8) Press F5 to run the application. Now all elements on the page display the "Windows7" theme.
To use a theme in XAML, first declare an XML namespace alias to Telerik.Windows.Controls so that you
have access to the StyleManager.Theme attached property.
<UserControl
xmlns:telerik=
"clr-namespace:Telerik.Windows.Controls;assembly=Telerik.Windows.Controls" . . .>
To apply a theme to a RadControl, add a StyleManager.Theme attached property. The XAML below
applies the "Windows7" theme to a RadButton.
You can also apply Telerik themes to standard controls such as Button, Check, RadioButton, ScrollViewer,
ListBox, etc. The example below sets themes to ListBoxItem elements, a different them per item. Note:
You typically use a single theme against all your elements to achieve a common look-and-feel, but this
demonstrates that themes can be applied individually.
<ListBox >
<ListBoxItem Content="Windows7" telerik:StyleManager.Theme="Windows7" />
<ListBoxItem Content="Summer" telerik:StyleManager.Theme="Summer" />
<ListBoxItem Content="Vista" telerik:StyleManager.Theme="Vista" />
<ListBoxItem Content="Office_Black" telerik:StyleManager.Theme="Office_Black" />
<ListBoxItem Content="Office_Blue" telerik:StyleManager.Theme="Office_Blue" />
<ListBoxItem Content="Office_Silver" telerik:StyleManager.Theme="Office_Silver" />
</ListBox>
Here is a screenshot of the list box with themed items in the running Silverlight application.
You can also set a theme for the entire application by adding a resource to the App.xaml file. The resource
points to one of the theme objects and sets the IsApplicationTheme property to "True".
<Application.Resources>
<telerik:SummerTheme x:Key="theme" IsApplicationTheme="True" />
</Application.Resources>
Notes
At the time of this writing, application themes only work against RadControls. To apply themes
to standard Silverlight controls they need to be applied individually.
You can apply a theme to the entire application in code, but do this as early as possible. Use the
Application.Startup event or the page constructor. The example below sets the application theme to "Vista"
in the constructor before calling InitializeComponent().
public MainPage()
{
StyleManager.ApplicationTheme = new VistaTheme();
// or ...
//new VistaTheme().IsApplicationTheme = true;
InitializeComponent();
}
Gotcha!
Be sure to use a Silverlight class library, so that the proper Silverlight versions of the referenced
DLL's are included.
The resource dictionary has one or more styles. The example below has a single Style element where the
target type is Button and the Key is the full class name of the button. This style only sets the Background
property to "Red".
<ResourceDictionary . . .>
<Style TargetType="Button" x:Key="System.Windows.Controls.Button">
<Setter Property="Background" Value="Red" />
</Style>
</ResourceDictionary>
To use the custom theme, create a resource that points to the theme, then assign the resource to the
StyleManager.Theme property using a "StaticResource" binding expression. Notice the syntax used in the
resource below for the Source attribute follows the pattern required to access resource URIs located in other
assemblies. The general syntax is:
"/assemblyShortName;component/resourceLocation".
... where the leading slash and the component keyword followed by a slash are both required.
<UserControl . . .
xmlns:telerik="clr-namespace:Telerik.Windows.Controls;assembly=Telerik.Windows.Controls">
<UserControl.Resources>
<telerik:Theme x:Key="myTheme" Source="/MyTheme;component/Themes/Common.xaml" />
</UserControl.Resources>
Notes
Running in the browser, the button has a red background. The pattern used to create this simple theme can
be used against any of the RadControls.
In this example we will modify the Windows7 theme in Expression Blend, changing the brushes for some of
the button related controls including RadButton, RadRadioButton and RadToggleButton and
RadDropDownButton.
3) In the Resources pane, the brushes for all of the buttons are listed. Use the drop down arrow for each
brush and open the color editing popup. In the editor tab, make each of the colors some variant of a red
shade by dragging the marker in the rainbow colored bar.
4) When you're done editing brushes, they should look something like the example below. Note: the
colors you choose here aren't significant, except that they should be very different from the shades of
blue that we're starting with. We need to see that all of the colors have changed in the themed control.
5) Save the project, then press Ctrl-Shift-B to build the project (or select Build from the Project menu).
Project Setup
1) From the Visual Studio menu choose File > New > Project..., select the Silverlight project type, then
select the Silverlight Application template. Provide a unique name for the project and click the OK
button.
2) In the "New Silverlight Application" dialog make sure that the "Host the Silverlight application in a new
Web site" option is checked, give the project a unique name and verify that the "ASP.NET Web
Application Project" New Web Project Type option is selected. Click OK to close the dialog and create
the project.
3) In the Solution Explorer, right-click the References node and select Add References... from the
context menu. Add Assembly references:
a) Telerik.Windows.Controls
b) Telerik.Windows.Themes.Windows7
Important note! Be sure to choose your custom version of this assembly, not the version that ships
with RadControls for Silverlight. You should be able to find this assembly in the \bin\debug directory of
the Windows7 themes project.
XAML Editing
1) Open MainPage.xaml for editing.
2) Replace the main "LayoutRoot" Grid element with the XAML below.
<Grid x:Name="LayoutRoot">
<StackPanel>
<!--RadButton-->
<!--RadToggleButton-->
<!--RadRadioButton-->
<!--RadDropDownButton-->
</StackPanel>
</Grid>
3) Drag a RadButton from the Toolbox to a point just under the "<!--RadButton-->" comment.
4) Drag a RadToggleButton, RadRadioButton and RadDropDownButton to a point underneath their
respective comments.
5) Set the attributes of all four buttons as follows:
a) Margin = "10"
b) HorizontalAlignment = "Left"
c) VerticalAlignment = "Top"
6) Set the Content attribute of each button to any text you choose.
7) Above the main "LayoutRoot" grid, add a UserControl.Resources element. Inside the resources, add a
resource for the Windows7Theme, set the "x:Key" to "Windows7" and the IsApplicationTheme
property to "True".
<UserControl.Resources>
<telerik:Windows7Theme IsApplicationTheme="True"
x:Key="Windows7" />
</UserControl.Resources>
Often, modifying theme brushes can make the color scheme of the controls compatible with application
look-and-feel. But you don't need to stop there, you can also modify the entire style of a control, including
changes to layout, adding effects and animation. For example, you can add a shadow effect to the
RadButton Content and animate it so that it looks as if a light passed over the content text.
1) From the Expression Blend Resources pane, double-click the edit button next to the RadButton default
style.
2) In the Objects and Timeline pane, right-click the "Style<>" item and select Edit Template > Edit
Current. From this point on you can simply edit the control by selecting elements of the control in the
Objects and Timeline pane and Artboard.
3) In the Objects and Timeline pane, expand the outline and locate the "Content" node.
4) Open the Assets pane and drag a DropShadowEffect to the "Content" node in the Objects and Timeline
pane. The content in the button should now display a shadow as shown in the screenshot below.
The Objects and Timeline pane will look something like the screenshot below.
6) In the Properties pane, set both the Direction and Opacity properties to zero.
7) In the Objects and Timeline pane, click the play ( ) button to test the animation.
The animation shows the shadow moving evenly under the content. Next, to mak e the animation less
mechanical, we can use an easing function to mak e the animation start gradually, then pick up speed.
8) In the Objects and Timeline pane, select the Direction property of the effect.
10)Select the Opacity property and set the EasingFunction to "Power In".
11)Build the theme assembly in Expression Blend using the menu Project > Build Project or press Ctrl-
Shift-B.
12)Run the Visual Studio project that applies the theme and observe the new animated effects when the
mouse passes over the button.
6.7 Wrap Up
In this chapter you learned how Telerik themes are used with RadControls and other Silverlight elements to
create a uniform look-and-feel for your application. First you applied predefined themes to RadControls and
observed how themes can be changed without otherwise altering target elements. You learned how themes
are distributed and applied to elements in XAML and in the code behind. You applied themes to an entire
application and to individual elements in the application. You will also created a custom theme and learned
the syntax for applying the theme.
In the "Modifying Themes" section of the chapter you learned about the "Themes" solution and its structure.
You modified theme brushes to skin the theme with a new color set. You applied the modified theme to the
UI in a Visual Studio project. Finally, you expanded this example to add effects and animation to a
RadButton control.
VII
Localization
Localization 193
7 Localization
7.1 Objectives
In this chapter you will learn how to localize your applications using the Telerik LocalizationManager class.
You will learn how to retrieve translations from resource files and from custom storage. You will assign keys
to translations in both XAML and in code.
\Courseware\Projects\<CS|VB>\Localization\Localization.sln
7.2 Overview
You may want your application to reflect some particular language, dialect or professional terminology.
Localization is a way to present your software product in a given language or culture that doesn't require
changes in the application itself. The translations are kept separate from application, allowing new
translations to be added. The language of the application can be changed on-the-fly so that buttons, titles,
column headings and other content will display in the appropriate language. The screenshot below shows
RadScheduler localized for Spanish.
Project Setup
1) From the Visual Studio menu choose File > New > Project..., select the Silverlight project type, then
select the Silverlight Application template. Provide a unique name for the project and click the OK
button.
2) In the "New Silverlight Application" dialog make sure that the "Host the Silverlight application in a new
Web site" option is checked, give the project a unique name and verify that the "ASP.NET Web
Application Project" New Web Project Type option is selected. Click OK to close the dialog and create
the project.
3) In the Solution Explorer, right-click the References node and select Add References... from the
context menu. Add Assembly references:
a) Telerik.Windows.Controls
b) Telerik.Windows.Controls.Input
c) Telerik.Windows.Themes.Windows7
4) In the Solution Explorer, right-click the Silverlight project and select Add > New Item... from the
context menu. Select the "Resources File" template, name it "Resources.resx" and click the OK
button.
7) In the Solution Explorer, double-click Resources.resx and edit the file to include the following keys and
values.
8) In the Solution Explorer, double-click Resources.fr.resx and edit the file to include the following keys
and values.
9) Open the project file for the Silverlight project (i.e. "myProject.csproj") in Notepad. Locate the
SupportedCultures element and include a semi-colon delimited list of culture codes that you intend to
localize for. In this walk through the languages are English and French i.e. "en;fr".
<SupportedCultures>en;fr</SupportedCultures>
XAML Editing
1) Open MainPage.xaml for editing.
2) Add the XAML below to the main "LayoutRoot" Grid element. The XAML should be added between the
<Grid> and </Grid> tags.
<StackPanel Orientation="Horizontal">
<!--Print Button-->
<!--Reports List-->
</StackPanel>
3) Drag a RadButton from the Toolbox to a point just below the "<!--Print Button-->" comment. Add a
LocalizationManager.ResourceKey attached property and assign it "ButtonPrintContent" (matching
the resource key of the same name). Set the VerticalAlignment to "Top", Margin to "10" and
Padding to "5".
<!--Print Button-->
<telerik:RadButton
telerik:LocalizationManager.ResourceKey="ButtonPrintContent"
VerticalAlignment="Top" Margin="10" Padding="5"></telerik:RadButton>
4) Drag a RadComboBox from the Toolbox to a point just below the "<!--Reports List-->" comment.
Replace the element with the XAML below. Notice that each of the RadComboBoxItem elements has
its own LocalizationManager.ResourceKey property defined. Each of the ResourceKey assignments
exactly matches keys in the two resource files.
<!--Reports List-->
<telerikInput:RadComboBox VerticalAlignment="Top" Margin="10">
<telerikInput:RadComboBoxItem
telerik:LocalizationManager.ResourceKey="ReportProductList" />
<telerikInput:RadComboBoxItem
telerik:LocalizationManager.ResourceKey="ReportProductNotInStock" />
<telerikInput:RadComboBoxItem
telerik:LocalizationManager.ResourceKey="ReportProductTop100" />
</telerikInput:RadComboBox>
Code Behind
1) In the code-behind for the page, add references to the "Imports" (VB) or "using" (C#) section of the code
for these namespaces:
a) Telerik.Windows.Controls
2) Replace the constructor with the code below. Notice that the DefaultResourceManager points to the
Resources.ResourceManager. The file name "Resources" is arbitrary and can be whatever you lik e.
The DefaultCulture is set to the French culture. All of this code needs to happen before the call to
InitializeComponent().
InitializeComponent()
End Sub
public MainPage()
{
new Windows7Theme().IsApplicationTheme = true;
LocalizationManager.DefaultResourceManager =
Properties.Resources.ResourceManager;
LocalizationManager.DefaultCulture =
new CultureInfo("fr");
InitializeComponent();
}
Telerik handles localization through the LocalizationManager class from the Telerik.Windows.Controls
assembly. LocalizationManager can work with almost any storage and retrieval scenario and apply it to any
Silverlight element. Currently, the LocalizationManager is designed to assign translations to Silverlight
elements only, not to HTML that may be on the host page.
You can store your localized content in either resource files or in any custom storage (database, web
service, isolated storage, etc). Translations are attached to Silverlight elements through keys that can be
assigned in XAML or in code behind.
Notes
You can skip assigning the Culture or DefaultCulture properties and the current UI culture will be
used.
If you use culture settings to determine the correct resource file, you must also modify a
SupportedCultures element in your project file. To do this, open your project "*.csproj" file as a text file,
locate the SupportedCultures element and include a semi-colon delimited list of culture codes that you
intend to localize for. In the example below, languages are English, German and French.
<SupportedCultures>en;de;fr</SupportedCultures>
Resource files are effective for many uses, but you may need to store localization content in another
medium such as a database or from a web service. For example you might want to leverage an existing
database already containing a large number of translations. To introduce your own custom logic, descend
from LocalizationManager and override the GetStringOverride() method. Use the "key" parameter passed
in to determine the item being localized and return your translated value using any mechanism that suits
your purpose. The example below simply returns Swedish strings for the given key, but you can use any
custom storage and retrieval inside this method.
The running Silverlight application is identical to the example in "Getting Started" but with Swedish content.
Notes
You can call the GetStringOverride() method directly if you need a translated string for some
other purpose than assigning to an element.
Use the attached ResourceKey property to identify the specific resource that should be applied to an
element. The example below shows the XAML syntax where "telerik" is an XML namespace that points to
Telerik.Windows.Controls.
In code, use the SetResourceKey() method and pass the element to be localized and the key name. The
example below is identical to the previous XAML-only example.
public MainPage()
{
InitializeComponent();
LocalizationManager.SetResourceKey(btnPrint, "ButtonPrintContent");
}
The mechanism works against any Silverlight element, not just RadControls. For example, you could
localize a window title, a standard button or a tool tip.
<Button>
<ToolTipService.ToolTip>
<ToolTip telerik:LocalizationManager.ResourceKey="MyKey" />
</ToolTipService.ToolTip>
</Button>
Several of the RadControls are localized, right out of the box including RadUpload, RadTreeView,
RadMediaPlayer, RadColorSelector, RadColorPicker, RadScheduler and RadGridView.
7.5 Wrap Up
In this chapter you learned how to localize your applications using the Telerik LocalizationManager class.
You learned how to retrieve translations from resource files and using any custom storage mechanism. You
also learned how to assign translation keys in both XAML and in code.
VIII
UI Automation Support
UI Automation Support 203
8 UI Automation Support
8.1 Objectives
In this chapter you will learn how RadControls for Silverlight provides UI Automation Support. First you will
see how the Microsoft UI Automation Verify tool is used to locate RadControls elements and spelunk the
element's properties and events. You will use AutomationProperties to support accessible applications in a
Silverlight application. You will also learn how to use AutomationPeer descendents to automate RadControl
elements.
\Courseware\Projects\<CS|VB>\UI_Automation\UI_Automation.sln
8.2 Overview
Applications that speak to the widest possible audience need to allow their user interface (UI) elements to
be automated. Making your applications accessible means thinking outside the visible rectangle to include
other means of communication. Your application may be read and interacted with using alternatives such as
Braille output, screen readers, head tracking or sip & puff devices to name a few.
Microsoft UI Automation (UIA) allows programmatic access to user interface elements that enable
accessibility tools, such as screen readers, to provide information to end users and manipulate the UI
without requiring the use of standard input devices. UIA masks differences between frameworks, exposing
every piece of UI to client applications. UIA also allows automated test scripts to interact with the UI.
RadControls for Silverlight has excellent UI Automation Support that enables deep UI testing and control
from accessibility tools. We can see the support in action using the UI Automation Verify tool from
Microsoft. The tool and associated resources can be downloaded at:
http://www.codeplex.com/UIAutomationVerify
Lets use the tool to tour a simple application that contains a single RadNumericUpDown control. The XAML
below shows that the current Value property is "10" that the Minimum and Maximum are "1" and "100".
<Grid x:Name="LayoutRoot">
<telerikInput:RadNumericUpDown x:Name="myUpdownControl" Value="10"
Minimum="1" Maximum="100" HorizontalAlignment="Left"
VerticalAlignment="Top" Margin="20" ></telerikInput:RadNumericUpDown>
</Grid>
When you first run the tool, you can set the Mode > Always On Top menu option to make it easier to view
the browser and the tool at the same time. Also turn on Mode > Focus Tracking so that the tool will
automatically find elements as they get focus. After you run the Silverlight application, simply click on it.
The focus rectangle generated by the tool will let you know what element is being examined. When you
click on the edit box of the RadNumericUpDown, the "Automation Elements Tree" navigates to the edit
control.
The "Properties" will display useful information about the selected element including General Accessibility
(accelerator keys, help text, etc), Identification properties (hWnd, AutomationId, ClassName and
ControlType), Patterns to expose control functionality independent of the control type or appearance
(TextPattern, RangeValuePattern, ValuePattern, etc.), State (HasKeyBoardFocus, IsEnabled) and
Visibility (BoundingRectangle, IsOffScreen).
You can directly interact with elements using patterns. If you edit the SetValue of the ValuePattern, the
value in the Silverlight application is modified to match.
The UI Automation tool also includes a set of generic tests that can be run against the selected element or
against an element and all of its children. These are fairly basic tests and aren't designed to test the
functionality of your application, but instead check on low level element functionality, e.g. can we set focus
to the control.
The Test Results pane publishes a summary of the results and allows you to output the log of results as
XML.
If you follow the links and drill into the test detail you can see what classes and methods were used to run
the tests.
Project Setup
1) From the Visual Studio menu choose File > New > Project..., select the Silverlight project type, then
select the Silverlight Application template. Provide a unique name for the project and click the OK
button.
2) In the "New Silverlight Application" dialog make sure that the "Host the Silverlight application in a new
Web site" option is checked, give the project a unique name and verify that the "ASP.NET Web
Application Project" New Web Project Type option is selected. Click OK to close the dialog and create
the project.
3) In the Solution Explorer, right-click the References node and select Add References... from the
context menu. Add Assembly references:
a) Telerik.Windows.Controls
b) Telerik.Windows.Controls.Input
XAML Editing
1) Open MainPage.xaml for editing.
2) Add the XAML below to the main "LayoutRoot" Grid element. The XAML should be added between the
<Grid> and </Grid> tags.
<StackPanel>
<!--TextBlock "label"-->
<!--RadComboBox with list of print choices-->
</StackPanel>
3) Replace the comment "<!--TextBlock 'label'-->" with the XAML below. The TextBlock has an added
AutomationProperties.Name attached property.
<!--TextBlock "label"-->
<TextBlock AutomationProperties.Name="tbPrintComboBox" Text="Print Choices:" />
4) Drag a RadComboBox from the Toolbox to a point underneath the "<!--RadComboBox with list of print
choices-->" comment. Replace the RadComboBox element with the XAML below. The RadComboBox
has AutomationProperties defined for the Name and HelpText. Also notice that a SelectionChanged
event handler is included that we will code later.
</telerikInput:RadComboBox>
5) Add three RadComboBoxItem elements inside the RadComboBox element begin and end tags. Each
item will contain the AutomationProperties HelpText and AcceleratorKey.
<telerikInput:RadComboBoxItem Content="Fast"
AutomationProperties.HelpText="Fast Economical Printing"
AutomationProperties.AcceleratorKey="F" />
<telerikInput:RadComboBoxItem Content="Green"
AutomationProperties.HelpText="Paper Saving Printing"
AutomationProperties.AcceleratorKey="G" />
<telerikInput:RadComboBoxItem Content="Photo"
AutomationProperties.HelpText="Photo Quality Printing"
AutomationProperties.AcceleratorKey="P" />
Code Behind
1) In the code-behind for the page, add references to the "Imports" (VB) or "using" (C#) section of the code
for these namespaces:
a) Telerik.Windows.Controls
b) System.Windows.Automation
2) In the constructor for the UserControl, add a line of code below the InitializeComponent() method call to
set the "LabeledBy" element. The first parameter is the element being labeled and the second, the
label element.
AutomationProperties.SetLabeledBy(Me.cbPrintChoices, Me.tbPrintComboBox)
End Sub
public MainPage()
{
InitializeComponent();
AutomationProperties.SetLabeledBy(this.cbPrintChoices,
this.tbPrintComboBox);
}
If you navigate to one of the RadComboBoxItem elements, the UI Automation Verify Tool displays the
AcceleratorKey and HelpText.
8.4 Automating
In addition to using the AutomationProperties, you can use the descendents of the abstract
AutomationPeer class to actually automate Silverlight elements. AutomationPeer has a number of
properties and methods of interest, such as how to focus on an element, how to get AutomationProperties,
how to raise automation events, execute patterns and get references to parent and child elements. Note:
This would not be your first stop for things like setting values or opening a combo box. You would use the
control's API for that. These methods are intended for applications that require generalized automation
functionality such as accessibility and testing applications.
The example below builds off the Getting Started example and adds a single button that automates the
RadComboBox. To do this, you can use the RadComboBoxAutomationPeer CreatePeerForElement()
method and pass the RadComboBox element you wish to automate. As you might guess, there are
AutomationPeer descendents implemented for other Silverlight elements and for RadControls elements.
This particular peer has Expand() and Collapse() methods that can be called.
8.5 Wrap Up
In this chapter you learned how RadControls for Silverlight provides UI Automation Support. First you saw
how the Microsoft UI Automation Verify tool is used to locate RadControls elements and spelunk the
automation information. In a standard Windows application you used AutomationProperties to support
accessible applications. You will also learned how to use AutomationPeer descendents to automate
RadControl elements.
IX
Input Controls
214 RadControls for Silverlight
9 Input Controls
9.1 Objectives
This chapter introduces controls for gathering user input, including a masked text box for restricting entry to
predefined patterns, a set of "UpDown" controls for entering numeric or other input through repeater buttons,
a set of color picking controls for selecting from a predefined palette of colors and a slider control for
choosing values within a range.
You'll learn how the masked text box handles numeric, date/time and developer-defined formats using
predefined masks or custom masks. We'll look at the important events for retrieving the user entered value
and how to retrieve the value combined together with the mask. You will also see how to localize the
masked text box.
In the section on "UpDown" controls you'll see how the basic up-down control and numeric up-down control
both inherit from the RangeBase class. You'll learn the key common properties and events for both controls.
We will discuss the key numeric up-down control ValueFormat property and some special case issues
such as scrolling through integers. You will learn how to format numeric entry for data that uses custom
units.
We will review three different types of color pickers along with their common properties and events. We will
make use of the Silverlight Toolkit and the ObjectCollection class to define a custom palette of colors in
XAML and will also define a custom palette in code. You'll learn how to customize text labels for elements
of a color choosing control and how to hide specific elements of these controls.
The last part of this chapter will deal with how the slider control is used to pick a value within a range or a
range within a range. We will talk about the basic structure and parts of a slider, the slider's default
behavior, how to retrieve new and previous values and how to control selection ranges. We will pay particular
attention to how tick marks are placed along the slider and how to control tick mark frequency and visibility.
We will delve a little deeper into how templates allow fine-tune control over each tick mark, how to use
binding in templates to show data in a tick mark and finally how IValueConverter is used for highly
customized binding scenarios.
\Courseware\Projects\<CS|VB>\Input\Input.sln.
9.2 Overview
Input controls make it easier for your end user to provide the right information to your application.
RadMaskedTextBox restricts entry to date time, numeric and customized patterns. RadSlider allows
selection from a defined range using an intuitive interface with one or two slider buttons. The up-down
controls facilities paging through numbers or any custom series of values. Using a color picker the user can
select from a configurable palette of colors.
Project Setup
1) From the Visual Studio menu choose File > New > Project..., select the Silverlight project type,
then select the Silverlight Application template. Provide a unique name for the project and click the
OK button.
Figure 1
2) In the "New Silverlight Application" dialog select "Host the Silverlight application in a new Web site",
give the project a unique name and select the "ASP.NET Web Application Project" New Web Project
Type. Click OK to close the dialog and create the project.
Figure 2
XAML Editing
1) Use the Solution Explorer to open the MainPage.xaml file for editing.
2) Replace the <Grid> tag named "LayoutRoot" with the XAML markup below. This will setup a basic grid
with five rows and three columns. XML comments such as "<!--Title-->" will mark where you can paste
additional XAML mark up in later steps.
<Grid
x:Name="LayoutRoot"
Background="White"
Margin="20">
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
</Grid.ColumnDefinitions>
<!--Title-->
<!--Numeric Up Down-->
<!--Color Picker-->
<!--Slider-->
</Grid>
3) Replace the XAML comment "<!--Title-->" with the markup below. This step will add a TextBlock control
that displays the title "Preferences" at the top of the page. Notice the Grid.Row and Grid.Column that
define where the TextBlock should be displayed within the grid.
<!--Title-->
<TextBlock
Grid.Row="0"
Grid.Column="0"
HorizontalAlignment="Left"
VerticalAlignment="Top"
FontWeight="Bold"
Margin="5"
MinHeight="25">
Preferences
</TextBlock>
4) Replace the XAML comment "<!--Masked Edit Box-->" with the markup below. This step will add a
TextBlock with text "IP Address".
<TextBlock
Grid.Row="1"
Grid.Column="0"
HorizontalAlignment="Right"
VerticalAlignment="Top"
Padding="0,0,10,0">
IP Address:
</TextBlock>
5) Drag a RadMaskedTextBox control from the Toolbox to the XAML, just below the <TextBlock> tag
defined in the previous step. This step will include a xml name space reference to the assembly
containing RadMask edTextBox.
Figure 3
6) Replace the RadMaskedTextBox tag with the XAML markup below. This step defines properties for the
RadMask edTextBox. The most important properties here are "Mask " that determines what k ind of input
will be accepted and "Mask Type" that defines how the mask is interpreted. This particular mask will
allow twelve numeric characters separated by literal "." characters, e.g. "1234.5678.9012.1234". Also
notice that we've supplied a "Name" attribute so we can refer to the RadMask edTextBox in code.
<telerikInput:RadMaskedTextBox
Grid.Row="1"
Grid.Column="1"
HorizontalAlignment="Left"
VerticalAlignment="Top"
Name="tbIP"
Mask="####.####.####.####"
MaskType="Standard">
</telerikInput:RadMaskedTextBox>
7) Below the RadMaskedTextBox add the TextBlock tag below. This TextBlock will be used later to
display new values as the user types into the RadMask edTextBox.
<TextBlock
Name="tbNewIP"
Margin="10,0,0,0"
Grid.Row="1"
Grid.Column="2"
HorizontalAlignment="Left"
VerticalAlignment="Top">
</TextBlock>
8) Replace the comment "<!--Numeric Up Down-->" with the XAML below. This step will add a TextBlock
that displays a title "Back up Disk Percentage" and will appear just above a RadNumericUpDown
control.
<!--Numeric Up Down-->
<TextBlock
Grid.Row="2"
Grid.Column="0"
HorizontalAlignment="Right"
VerticalAlignment="Top"
Padding="0,0,10,0">
Backup Disk Percentage:
</TextBlock>
9) Drag a RadNumericUpDown control from the Toolbox to the XAML, just below the TextBlock defined
in the previous step. Set the Name attribute to "nupBackupDiskPercentage", Grid.Row ="2", Grid.
Column="1", HorizontalAlignment="Left" and VerticalAlignment="Top". Add a ValueFormat
attribute and notice the IntelliSense list of valid values when you first type the equal sign ("="). Select
"Percentage" from the list.
Figure 4
10)Below the RadNumericUpDown, add the TextBlock tag shown below. The TextBlock will be used to
display value changes in the RadNumericUpDown control.
<TextBlock
Name="tbNewPercentage"
Margin="10,0,0,0"
Grid.Row="2"
Grid.Column="2"
HorizontalAlignment="Left"
VerticalAlignment="Top">
</TextBlock>
11)Replace the "<!--Color Picker-->" comment with the XAML shown below. This step creates a
TextBlock with text "Font Color:" that will appear above a RadColorPick er control.
<!--Color Picker-->
<TextBlock
Grid.Row="3"
Grid.Column="0"
HorizontalAlignment="Right"
VerticalAlignment="Top"
Padding="0,0,10,0">
Font Color:
</TextBlock>
12)Drag a RadColorPicker control from the Toolbox to the XAML, just below the TextBlock defined in the
previous step. Set the Name attribute to "cpFont", Grid.Row = "3", Grid.Column = "1" and
HorizontalAlignment = "Left".
13)Add an <Ellipse> tag below the RadColorPicker. The ellipse will display the color selected from the
RadColorPick er.
<Ellipse
Name="elNewColor"
Margin="10,0,0,0"
Grid.Row="3"
Grid.Column="2"
HorizontalAlignment="Left"
VerticalAlignment="Top"
Width="20"
Height="20">
</Ellipse>
14)Replace the comment "<!--Slider-->" with the TextBlock defined below. The TextBlock will add a label
"Volume Range:" above a RadSlider control.
<!--Slider-->
<TextBlock
Grid.Row="4"
Grid.Column="0"
HorizontalAlignment="Right"
VerticalAlignment="Top"
Padding="0,0,10,0">
Volume Range:
</TextBlock>
15)Drag a RadSlider control from the Toolbox to the XAML, just below the TextBlock defined in the
previous step. Set the Name attribute to "slVolume", Grid.Row="4" and Grid.Column="1". Also set
the following RadSlider-specific attributes: SelectionStart="2", SelectionEnd="8", EnableSideTicks
="True", TickFrequency="1", TickPlacement="BottomRight", IsSelectionRangeEnabled="True"
and HandlesVisibility="Visible".
16)Below the RadSlider add a TextBlock as defined below. This TextBlock will be used to display the
SelectionStart and SelectionEnd values as the slider is moved by the user.
<TextBlock
Name="tbNewVolume"
Margin="10,0,0,0"
Grid.Row="4"
Grid.Column="3"
HorizontalAlignment="Left"
VerticalAlignment="Top">
</TextBlock>
Figure 5
Use the RadSlider control by way of clicking the +/- handles at the extreme ends of the slider using the
mouse, holding down the mouse outside the thumbs, dragging the thumbs, dragging the area between
the two thumbs and by using the mouse wheel.
Code Behind
The next step is to attach event handlers to the input controls and react to this feedback within the code
behind.
1) Navigate back to the RadMaskedTextBox in MainPage.xaml. Enter a new line inside the
RadMaskedTextBox tag and click Ctrl-Spacebar to activate IntelliSense. Locate the ValueChanged
event and press Enter to add it to the XAML.
Figure 6
2) IntelliSense will display an option to add "<New Event Handler>". Press Enter to create the new
event handler.
Figure 7
3) Right-click the new event handler and select "Navigate to Event Handler" from the context menu.
This step will tak e you to the code-behind for the event handler.
Figure 8
4) Replace the code-behind for the event handler with the code below. This event handling code
corresponds to the ValueChanged event you defined in the XAML for the previous step. In this code we
cast "sender" to be the RadMask edTextBox, get its value property and display it in the TextBlock of
the right-most column. Whenever the user types a new character, the ValueChanged event fires and
this code re-displays the value.
5) The reference to RadMaskedTextBox in the code-behind will be underlined in red, signalling an error.
This is because there is no namespace in scope that defines RadMaskedTextBox. To fix this, left-click
the red underline and look for a dark underline indicator at the end of "RadMaskedTextBox".
6) Move your mouse over the indicator until an icon with a drop-down arrow appears, click the arrow and
select "using Telerik.Windows.Controls;". This step will automatically add a reference to the "Telerik .
Windows.Controls" namespace.
Figure 9
7) Navigate back to MainPage.xaml and locate the RadNumericUpDown tag. Enter a new ValueChanged
attribute. Create a new event handler so that the XAML looks like the example below.
<telerikInput:RadNumericUpDown
Name="nupBackupDiskPercentage"
Grid.Row="2"
Grid.Column="1"
HorizontalAlignment="Left"
VerticalAlignment="Top"
ValueFormat="Percentage"
ValueChanged="RadNumericUpDown_ValueChanged"
>
</telerikInput:RadNumericUpDown>
8) Navigate to the code-behind for the new ValueChanged event handler and replace the code with the
code below. The event arguments for this event handler include OldValue and NewValue properties.
Both properties are double types. In this event handler we are displaying the new value in a TextBlock .
9) Navigate back to MainPage.xaml and locate the RadColorPicker tag. Enter a new
SelectedColorChanged attribute. Create a new event handler so that the XAML looks like the
example below.
<telerikInput:RadColorPicker
Name="cpFont"
Grid.Row="3"
Grid.Column="1"
HorizontalAlignment="Left"
SelectedColorChanged="RadColorPicker_SelectedColorChanged"
>
</telerikInput:RadColorPicker>
10)Navigate to the code-behind for the new SelectedColorChanged event handler and replace the code
with the code below. This step will create a new SolidColorBrush using the RadColorPick er's currently
selected color. The new brush is assigned to an Ellipse shape displayed in the right-most column of
the grid.
11)Verify that you have a reference to the System.Windows.Media namespace in the "Imports" (VB) or
"using" (C#) section of code.
12)Navigate back to MainPage.xaml and locate the RadSlider tag. Enter a new
SelectionRangeChanged attribute. Create a new event handler so that the XAML looks like the
example below.
<telerik:RadSlider
Name="slVolume"
Grid.Row="4"
Grid.Column="1"
Minimum="1"
Maximum="10"
SelectionStart="2"
SelectionEnd="8"
EnableSideTicks="True"
TickFrequency="1"
TickPlacement="BottomRight"
IsSelectionRangeEnabled="True"
HandlesVisibility="Visible"
SelectionRangeChanged="RadSlider_SelectionRangeChanged">
</telerik:RadSlider>
13)Navigate to the code-behind for the new SelectionRangeChanged event handler and replace the code
with the code below. This step displays the upper and lower value of the two slider thumbs using the
event arguments NewValue.SelectionStart and NewValue.SelectionEnd properties.
Figure 10
Theming
The last step in this exercise is to apply a theme to each of the input controls.
1) Navigate to the Solution Explorer in Visual Studio. Right-click the References node in the project
containing your MainPage.xaml file and select Add Reference.... In the "Add Reference" dialog, select
the "Browse" tag, locate the RadControls for Silverlight installation directory and select the Telerik.
Windows.Themes.Vista.dll assembly in the \Binaries\Silverlight directory. Press the OK button to
add the assembly.
Figure 11
<telerikInput:RadMaskedTextBox
Grid.Row="1"
Grid.Column="1"
HorizontalAlignment="Left"
VerticalAlignment="Top"
Name="tbIP"
Mask="####.####.####.####"
MaskType="Standard"
ValueChanged="RadMaskedTextBox_ValueChanged"
telerik:StyleManager.Theme="Vista"
>
</telerikInput:RadMaskedTextBox>
3) Press Ctrl-F5 to run the application. The changes to the control can be very subtle depending on the
particular style and the version of the software. A text input may have slightly rounded corners with
subtle shading instead of an abrupt square of a solid color. More pronounced styles may include
complete changes in graphics for buttons and other visual elements of a given control.
Use RadMaskedTextBox to validate user entry "up front". This control not only restricts entry to certain
predefined patterns but makes it easier for the user to figure out what data the application is asking for. This
help is provided courtesy of the Mask property that contains a set of characters to define the order and
content of allowed input. For example, a mask of "#####-####" could define a United States postal code
and looks something like this in the browser:
The user can enter numbers only and automatically skips over the literal dash "-" symbol. The other key
property that makes RadMaskedTextBox so flexible is the MaskType property. MaskType determines how
the mask will be interpreted. MaskType can be one of four possible values:
MaskType Description
None If MaskType is set to "None", the RadMaskedTextBox does not validate and acts as a
standard text box for free-form entry.
Standard Use the Standard MaskType to define custom masks as in the previous "US Postal Code"
example.
Numeric Use predefined format codes to only allow currency, fixed point, decimal, percentages or
integers.
DateTime Use predefined format codes for a wide range of date and time layouts. You can also use
format codes to create custom date formats.
Standard Masks
Numeric Masks
c,C Currency
n,N Decimal
p,P Percent
You can also add a length specifier to any of the above masks. For example. "d5" accepts a five digit
integer.
Date/Time Masks
d Short date
D Long date
r,R RFC1123
t Short time
T Long time
To completely customize the date mask, use a combination of the codes in the table below.
Mask Description
m, mm The minute.
s, ss The second.
Here is an example of using date/time formatting codes in combination to create a custom mask: "ddd -
MMMM - yyy H:m:s tt". The result in the browser might look like this:
The user is able to use the mouse to put focus on one of the codes, e.g. the "MMMM" portion that shows
as "July", and then use the arrow keys to page through the months.
To localize the RadMaskedTextBox to a particular language and culture, set the Culture property to a
culture code at design time:
<telerikInput:RadMaskedTextBox
Culture="fr-FR"
MaskType="DateTime"
Mask="D" >
</telerikInput:RadMaskedTextBox>
Both the XAML markup and code-behind examples above show a long Date/Time in French:
Walk Through
In this lab you will use a sampling of each mask type with some basic settings, then react to changing
values using the primary events for RadMaskedTextBox.
Project Setup
1) From the Visual Studio menu choose File > New > Project..., select the Silverlight project type, then
select the Silverlight Application template. Provide a unique name for the project and click the OK
button.
2) In the "New Silverlight Application" dialog make sure that the "Host the Silverlight application in a new
Web site" option is checked, give the project a unique name and verify that the "ASP.NET Web
Application Project" New Web Project Type option is selected. Click OK to close the dialog and create
the project.
3) In the Solution Explorer, right-click the References node and select Add References... from the
context menu. Add Assembly references:
XAML Editing
1) Open the MainPage.xaml file for editing.
2) Replace the <Grid> tag named "LayoutRoot" with the XAML markup below. This will setup two nested
Stack Panel controls inside the grid. The outermost stack panel will contain three RadMask edTextBox
controls and the innermost stack panel will contain some TextBlock controls to display new values as
the user edits.
<Grid
x:Name="LayoutRoot"
Background="White">
<StackPanel>
<!--MaskType Standard-->
<!--MaskType Numeric-->
<!--MaskType DateTime-->
<StackPanel
Orientation="Horizontal">
<!--Value-->
<!--MaskedText-->
</StackPanel>
</StackPanel>
</Grid>
3) Locate the comment "<!--MaskType Standard-->" and drop a RadMaskedTextBox just below it. This
step will add the correct XAML name space assembly reference for RadMask edTextBox.
4) Replace the RadMaskedTextBox with the XAML shown below. This step will add a TextBlock to act as
a label and a RadMask edTextBox with Mask Type = "Standard". The Mask will display an escaped
literal "#", followed by three alpha characters, a dash "-" and finally three more alpha characters. The
ValueChanged event handler will be common among all three RadMask edTextBoxes and will be coded
in a later step.
<!--MaskType Standard-->
<TextBlock
Text="Standard:">
</TextBlock>
<telerikInput:RadMaskedTextBox
Margin="20"
HorizontalAlignment="Left"
MaskType="Standard"
Mask="\#LLL-LLL"
ValueChanged="ValueChangedHandler">
</telerikInput:RadMaskedTextBox>
5) Locate the comment "<!--MaskType Numeric-->" and add the XAML shown below. The
RadMask edTextBox here has the Mask Type set to "Numeric" with a mask of "p2", a percentage with
two places.
<!--MaskType Numeric-->
<TextBlock
Text="Numeric Percentage:">
</TextBlock>
<telerikInput:RadMaskedTextBox
Margin="20"
HorizontalAlignment="Left"
MaskType="Numeric"
Mask="p2"
ValueChanged="ValueChangedHandler">
</telerikInput:RadMaskedTextBox>
6) Locate the comment "<!--MaskType DateTime -->" and add the XAML shown below. The
RadMask edTextBox here has the Mask Type set to "DateTime" with a mask of "F" to designate a "Full"
date and time.
<!--MaskType DateTime-->
<TextBlock
Text="DateTime: Full date and time">
</TextBlock>
<telerikInput:RadMaskedTextBox
Margin="20"
HorizontalAlignment="Left"
MaskType="DateTime"
Mask="F"
ValueChanged="ValueChangedHandler">
</telerikInput:RadMaskedTextBox>
7) Locate the comment "<!--Value-->" and add the XAML shown below. The first TextBlock defined here
acts as a simple label. The second is named so we can update it with the latest value from the code-
behind.
<!--Value-->
<TextBlock
Margin="10,0,0,0"
Text="Value:">
</TextBlock>
<TextBlock
Name="tbValue"
Margin="10,0,0,0">
</TextBlock>
8) Locate the comment "<!--MaskedText-->" and add the XAML shown below.
<!--MaskedText-->
<TextBlock
Text="MaskedText:"
Margin="10,0,0,0">
</TextBlock>
<TextBlock
Name="tbMaskedText"
Margin="10,0,0,0">
</TextBlock>
9) Locate one of the RadMaskedTextBox controls (any of them will do), find the ValueChanged property,
right-click the "ValueChangedHandler" and select "Navigate to Event Handler" from the context menu.
Replace the event handler with the code below. The code casts "sender" as the RadMask edTextBox
that has triggered this particular event, then assigns the Value and Mask edText to TextBlock controls
in the inner Stack Panel. Notice that Value is an object type: depending on the Mask Type, value may
be a string, double, or DateTime.
10)Add a Telerik.Windows.Controls namespace reference to your "Imports" (VB) or "uses" (C#) section of
code.
Gotcha!
At the time of this writing, setting focus in Silverlight applications requires a work-around. First,
you need to set focus to the Silverlight Plugin, then you need to set focus to control itself. Set
focus using Dispatcher.BeginInvoke() to queue your code so that it occurs after the plugin has
loaded.
"UpDown" input controls consist of the RadUpDown and RadNumericUpDown controls. RadUpDown has a
paired set of "repeat" buttons and can be used to increment and decrement values, but has no other input
control of its own. The RadNumericUpDown control has both the repeat buttons and a text input element
as well.
Figure 12
The story begins with the Silverlight RangeBase abstract class. RangeBase represents an element that
has a value within a specific range such as a progress bar or slider control. RadUpDown,
RadNumericUpDown and RadSlider controls all ultimately descend from RangeBase. RangeBase
introduces LargeChange, SmallChange, Maximum, Minimum and Value properties and the
ValueChanged event. LargeChange determines the value change when the page up and down keys are
used and SmallChange is the value change when the arrow up and down keys are used.
Gotcha!
The RangeBase class may be replaced in the future by a custom Telerik class to work around
limitations in the RangeBase class.
RadUpDown Properties
RadUpDown adds properties that relate to the two repeat buttons:
Delay: The number of milliseconds after pressing one of the repeat buttons before a value change occurs.
RepeatInterval: The number of milliseconds between value changes as a repeat button is held down.
AutoReverse: If true, the value will be reset to the minimum value when the maximum is reached and
visa versa.
ChangeAcceleration: When the PageUp, PageDown, Up, or Down buttons are held for more than 20
clicks, the repeat interval automatically decreases.
Walk Through
To use RadUpDown you typically hook up the ValueChanged event and use the event argument NewValue
property in some kind of assignment to another control. In this example the RadUpDown controls the
dimensions of an Ellipse control.
Project Setup
1) From the Visual Studio menu choose File > New > Project..., select the Silverlight project type, then
select the Silverlight Application template. Provide a unique name for the project and click the OK
button.
2) In the "New Silverlight Application" dialog make sure that the "Host the Silverlight application in a new
Web site" option is checked, give the project a unique name and verify that the "ASP.NET Web
Application Project" New Web Project Type option is selected. Click OK to close the dialog and create
the project.
3) In the Solution Explorer, right-click the References node and select Add References... from the
context menu. Add Assembly references:
XAML Editing
1) Open the MainPage.xaml file for editing.
2) Add a StackPanel from the Toolbox "Silverlight XAML Controls" tab to the XAML just inside the <Grid>
tag.
3) Inside the StackPanel add a RadUpDown control from the RadControls tab of the Toolbox.
4) Replace the RadUpDown tag with the XAML below. This will define an Ellipse element and a
RadUpDown with a ValueChanged event handler. Notice that the AutoReverse and ChangeAcceleration
properties are set to true. Because the Minimum property is set to "20", the ellipse will never be sized
less than 20 pixels. Lik ewise, due to the Maximum property setting, the ellipse will never be sized more
than 100 pixels.
<telerikInput:RadUpDown
Name="udSizer"
Margin="10"
HorizontalAlignment="Left"
MaxWidth="50"
Minimum="20"
Maximum="100"
AutoReverse="True"
ChangeAcceleration="True"
ValueChanged="RadUpDown_ValueChanged"></telerikInput:RadUpDown>
<Ellipse
Name="elUpDown"
Width="20"
Height="20"
Margin="20,0,0,0"
HorizontalAlignment="Left">
<Ellipse.Fill>
<LinearGradientBrush>
<GradientStop
Color="Pink"
Offset="0" />
<GradientStop
Color="Red"
Offset="0.2" />
<GradientStop
Color="Crimson"
Offset="0.87" />
<GradientStop
Color="DarkRed"
Offset="1" />
</LinearGradientBrush>
</Ellipse.Fill>
</Ellipse>
RadNumericUpDown Properties
RadNumericUpDown adds the ability to display and format a value. The key properties added by
RadNumericUpDown are ValueFormat and NumberFormatInfo. ValueFormat is an enumeration that can be
Numeric, Currency or Percentage. NumberFormatInfo is a type from the System.Globalization
namespace that lets you fine-tune formatting and display depending on the culture. NumberFormatInfo
properties are clumped into prefixes that correspond to the ValueFormat setting, e.g. the number of decimal
digits is controlled by NumberDecimalDigits, CurrencyDecimalDigits and PercentageDecimalDigits.
If these formatting options aren't sufficient, you can assign a string to the CustomUnit property and the
string will be appended to the value automatically. For example, if you assigned Value = "3" and
CustomUnit = "Cases", the control will display "3 Cases". If you want to retrieve the formatted value, read
the ContentText property. To make the numeric up down control read-only set the IsEditable property to
false. You can set the ShowButtons property to false as well.
Walk Through
In this walk-through you will create a set of entries for price, quantity, discount and extended price. As the
values change, the extended price is calculated and placed in a read-only RadNumericUpDown.
1) Start with the previous RadUpDown project or a copy.
2) Open the MainPage.xaml file for editing.
3) Add a new StackPanel inside the StackPanel that already exists in this project. Notice that the
Orientation property is set to "Horizontal" so all the controls we add next will be arranged from left to
right in a line. The comments inside the Stack Panel indicate where we will drop in groups of controls.
<StackPanel
Orientation="Horizontal">
<!--Price-->
<!--Quantity-->
<!--Discount-->
<!--Extended Price-->
</StackPanel>
4) Locate the "<!--Price-->" comment and replace it with the XAML below. Notice that the ValueFormat is
"Currency". The NumberFormatInfo will be set later in the Loaded event of the user control.
<!--Price-->
<TextBlock
Margin="5">Price:</TextBlock>
<telerikInput:RadNumericUpDown
Name="tbPrice"
Margin="5"
HorizontalAlignment="Left"
MinWidth="75"
ValueFormat="Currency"
Minimum="0"
ValueChanged="RadNumericUpDown_ValueChanged">
</telerikInput:RadNumericUpDown>
Gotcha!
As of this writing, the NumberFormatInfo properties can't be set properly in XAML. In this
exercise we will set the NumberFormatInfo properties in code.
5) Locate the "<!--Quantity-->" comment and replace it with the XAML below. Notice that the
ValueFormat is "Numeric", SmallChange is "1" and CustomUnit is "Items".
<!--Quantity-->
<TextBlock
Margin="5">Quantity:</TextBlock>
<telerikInput:RadNumericUpDown
Name="tbQuantity"
Margin="5"
HorizontalAlignment="Left"
MinWidth="75"
ValueFormat="Numeric"
CustomUnit="Items"
SmallChange="1"
Minimum="0"
Maximum="1000"
ValueChanged="RadNumericUpDown_ValueChanged">
</telerikInput:RadNumericUpDown>
Tip!
6) Locate the "<!--Discount-->" comment and replace it with the XAML below. Notice that the ValueFormat
is "Percentage".
<!--Discount-->
<TextBlock
Margin="5">Discount:</TextBlock>
<telerikInput:RadNumericUpDown
Name="tbDiscount"
Margin="5"
HorizontalAlignment="Left"
MinWidth="75"
ValueFormat="Percentage"
ValueChanged="RadNumericUpDown_ValueChanged">
</telerikInput:RadNumericUpDown>
7) Locate the "<!--Extended Price-->" comment and replace it with the XAML below. Notice that the
ValueFormat is "Currency". Also notice that the IsEditable and ShowButtons properties are false to
mak e this a read-only control.
<!--Extended Price-->
<TextBlock
Margin="5">Extended Price:</TextBlock>
<telerikInput:RadNumericUpDown
Name="tbExtendedPrice"
Margin="5"
HorizontalAlignment="Left"
MinWidth="75"
IsEditable="False"
ShowButtons="False"
ValueFormat="Currency">
</telerikInput:RadNumericUpDown>
8) Locate the outermost <UserControl> tag and add a Loaded event and set the value to
"UserControl_Loaded". The XAML markup will look something like the example below.
<UserControl
...
Loaded="UserControl_Loaded">
9) Right-click the "UserControl_Loaded" event and select "Navigate to Event Handler" from the context
menu. Replace the event handler with the code below. Here we set the number of decimal digits for the
quantity to zero so that the control will be formatted as an integer. Also notice that we're setting the
price and extended price Maximum properties to the maximum allowed by the Double type.
10)Go back to the XAML and locate one of the ValueChanged properties, right-click and select "Navigate
to Event Handler" from the context menu. Replace the event handler with the code below. Here we
calculate an extended price, and subtract a discount if the user has entered a discount percentage.
There are three color choosing controls currently in the Toolbox: RadColorPaletteView,
RadColorSelector and RadColorPicker. Since RadColorPicker has the superset of functionality in all the
color choosing controls, lets take a quick look at its structure:
Notes
Each of the controls above builds on the functionality of the previous control, but there is no direct
inheritance between these controls as of this writing.
Using ObjectCollection
Silverlight, unlike WPF, does not support ArrayList or any type that will easily let you define a list of objects
directly within XAML. To assign a list of any object in XAML you can use ObjectCollection which is included
in the "Toolkit" at http://silverlight.net/getstarted/. The Toolkit contains transitional controls, samples,
utilities and various goodies that haven't been incorporated to the base Silverlight product. Be aware that the
"Toolkit" is not the same as the "Developer Tools for Silverlight", where the latter contains the SDK,
Developer Runtime and the Visual Studio Project Templates.
Once you have the Tookit installed, you can find the binaries under:
\Program Files\Microsoft SDKs\Silverlight\v3.0\Toolkit\<version>\Bin
ObjectCollection is found in the System.Windows.Controls.Toolkit assembly. To use ObjectCollection, first
include a reference to this assembly in the Solution Explorer References node of your project. Then, in the
App.xaml, add a reference similar to the one below:
xmlns:controls=
"clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Toolkit"
Then in your App.xaml markup, within the Application.Resources tag, add any number of custom
collections that can be referenced in your other XAML files. The collection below is named "FoodGroups"
and contains three String objects. The "sys" below stands for the System namespace that contains basic
CLR types and is found in the mscorlib.dll assembly.
<Application.Resources>
<controls:ObjectCollection
x:Key="FoodGroups">
<sys:String>Burgers</sys:String>
<sys:String>Fries</sys:String>
<sys:String>Soft Drinks</sys:String>
</controls:ObjectCollection>
</Application.Resources>
You can reference the ObjectCollection using the name specified in the "x:Key" attribute using the binding
syntax below.
<telerikInput:RadComboBox
ItemsSource="{Binding Source={StaticResource FoodGroups}}">
Gotcha!
Be aware, due to the transitional nature of the toolkit, that this information is volatile and that the
naming, location and makeup of the toolkit may change.
Walk Through
This walk-through demonstrates how to respond to user selections for each color choosing control. The
exercise shows how to define a list of colors as a resource and assign in the XAML to a palette. You will
also see how a list of colors can be assigned programmatically to a palette.
Project Setup
1) From the Visual Studio menu choose File > New > Project..., select the Silverlight project type, then
select the Silverlight Application template. Provide a unique name for the project and click the OK
button.
2) In the "New Silverlight Application" dialog make sure that the "Host the Silverlight application in a new
Web site" option is checked, give the project a unique name and verify that the "ASP.NET Web
Application Project" New Web Project Type option is selected. Click OK to close the dialog and create
the project.
3) In the Solution Explorer, right-click the References node and select Add References... from the
context menu. Add Assembly references:
XAML Editing
1) From the Solution Explorer, open the App.xaml file for editing.
2) Add XML namespace references shown below to the App.xaml file. The Toolk it namespace location
may change in the future. See "Using ObjectCollection" above for more information.
xmlns:controls="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Toolkit"
xmlns:media="clr-namespace:System.Windows.Media;assembly=System.Windows"
<Application.Resources>
<controls:ObjectCollection
x:Key="SmokyMountain">
<media:Color>#FFDDDDDD</media:Color>
<media:Color>#FFCCCCFF</media:Color>
<media:Color>#FFAAAAFF</media:Color>
<media:Color>WhiteSmoke</media:Color>
<media:Color>LightGray</media:Color>
<media:Color>Gray</media:Color>
<media:Color>#FFFFBBBB</media:Color>
<media:Color>#FFEEAAAA</media:Color>
<media:Color>#FFDDAAAA</media:Color>
</controls:ObjectCollection>
</Application.Resources>
4) From the Solution Explorer, open the MainPage.xaml file for editing.
5) Insert the XAML below inside the Grid tag named "LayoutRoot". This step will setup the basic user
interface structure using Stack Panels.
<StackPanel>
<TextBlock
Margin="5"
FontWeight="Bold">Wall Color</TextBlock>
<StackPanel
Orientation="Horizontal">
<!--RadColorPaletteView-->
<Rectangle
Name="rectWallColor"
Width="50"
Height="50">
</Rectangle>
</StackPanel>
<TextBlock
Margin="5"
FontWeight="Bold">Cabinet Colors</TextBlock>
<StackPanel
Orientation="Horizontal">
<!--RadColorSelector-->
<Rectangle
Name="rectCabinets"
Width="50"
Height="50"
HorizontalAlignment="Left">
</Rectangle>
</StackPanel>
<StackPanel
Orientation="Horizontal">
<TextBlock
Margin="5"
FontWeight="Bold">Appliance Color</TextBlock>
<!--RadColorPicker-->
<Rectangle
Name="rectAppliance"
Margin="5"
Width="50"
Height="50"
HorizontalAlignment="Left">
</Rectangle>
</StackPanel>
<!--Combined Colors-->
<TextBlock
Margin="5"
FontWeight="Bold">Combined Colors</TextBlock>
<StackPanel
Orientation="Horizontal">
<Rectangle
Name="rectCombined"
Margin="20"
Width="150"
Height="150"
HorizontalAlignment="Left">
</Rectangle>
</StackPanel>
</StackPanel>
If you run the application now the web page looks something like this:
6) Drag a RadColorPaletteView control from the Toolbox to a place just under the comment "<!--
RadColorPaletteView-->". Set the following properties:
a) Name="cpvWallColor"
b) HorizontalAlignment="Left"
c) MaxWidth="200"
d) Margin="20, 5, 20, 5"
e) ItemsSource="{Binding Source={StaticResource SmokyMountain}}"
f) PaletteOrientation="Vertical"
g) PaletteColumnsCount="3"
h) SelectionChanged="RadColorPaletteView_SelectionChanged"
Notes
In particular you should notice the ItemsSource property binding to the resource named
"SmokyMountain". This refers to the ObjectCollection you defined earlier in the App.xaml file.
Also notice the SelectionChanged event handler that we will define in a later step.
7) Drag a RadColorSelector control from the Toolbox to a place just under the comment "<!--
RadColorSelector-->". Set the following properties:
a) Name="csCabinets"
b) HorizontalAlignment="Left"
c) MaxWidth="200"
d) Margin="20, 5, 20, 5"
e) MainPalette="Concourse"
f) MainPaletteHeaderText="Cabinets"
g) HeaderPaletteVisibility="Collapsed"
h) StandardPalette="Concourse"
i) StandardPaletteHeaderText="Base Colors"
j) NoColorVisibility="Collapsed"
k) SelectedColorChanged="RadColorSelector_SelectedColorChanged"
Notes
Notice that the "No Color" button and the HeaderPalette visibility are set to "Collapsed" and will
not be visible in the browser. We have also customized the MainPaletteText and
StandardPaletteHeaderText. The MainPalette is set to the ColorPreset enumeration value
"Concourse". Finally, we have a SelectedColorChanged event handler that we will define in a later
step.
8) Drag a RadColorPicker control from the Toolbox to a place just under the comment "<!--
RadColorPicker-->". Set the following properties:
a) Name="cpAppliance"
b) HorizontalAlignment="Left"
c) MaxWidth="200"
d) Margin="20, 5, 20, 5"
e) SelectedColorChanged="RadColorPicker_SelectedColorChanged"
f) Click="RadColorPicker_Click"
Notes
Notice that there are event handlers for when the color first changes, or for when you click the
Color button after that.
9) Add a "Loaded" event handler to the UserControl tag. Later we will use this event handler to add a
custom set of colors to the RadColorPick er main palette.
<UserControl
...
Loaded="UserControl_Loaded">
10)Right-click the "Loaded" event and select "Navigate to Event Handler" from the context menu. Add the
code below to the Loaded event handler. This code demonstrates how to create a custom palette of
colors in code. A generic list of Color is created and populated, then assigned to the
MainPaletteItemsSource property. The color pick er control is configured in code to display only the
main palette.
11)Create a private method to update the user interface using the code below. This method extracts the
currently selected color for each color choosing control, then assigns those colors to the fill of several
Rectangle controls. The end of the method creates a RadialGradientBrush using a combination of all
the colors and assigns it to a rectangle.
' show the selected appliance color, b lended with the wall and cab inet color
Dim stopCollection As New GradientStopCollection()
// show the selected appliance color, b lended with the wall and cab inet color
GradientStopCollection stopCollection = new GradientStopCollection();
if (cpvWallColor.SelectedValue != null)
{
GradientStop stop1 = new GradientStop();
stop1.Color = (Color)cpvWallColor.SelectedValue;
stop1.Offset = 1;
stopCollection.Add(stop1);
}
The RadSlider control lets users select a value from a defined range. The slider user interface makes the
process easy, quick and intuitive. The control is completely customizable in terms of appearance and offers
numerous configuration options like orientation, small change, mouse wheel support, snap to tick and tick
placement. Tick Templates allow you to tailor the Tick appearance and to customize through binding.
This enhanced slider control can be configured to allow a selection range. The structure diagram below
shows two "thumbs" that slide along a "track". The two thumbs delineate the start and end of the selection
range.
To use the slider defaults without the range selection feature enabled, handle the ValueChanged event.
You can read the slider Value property from any location in code or use the event arguments NewValue
and OldValue properties from within the ValueChanged event handler.
<StackPanel Orientation="Horizontal">
<telerik:RadSlider
MinWidth="200"
ValueChanged="RadSlider_ValueChanged"></telerik:RadSlider>
<TextBlock Name="tbSliderFeedback"></TextBlock>
</StackPanel>
<telerik:RadSlider
MinWidth="200"
IsSelectionRangeEnabled="True"
SelectionRangeChanged="RadSlider_SelectionRangeChanged">
</telerik:RadSlider>
End Sub
Now the control displays thumbs for both selection start and end. By default the start value is "0.2" and the
end is "0.8".
Ticks
Default Behavior
To have tick marks placed along the track automatically, set the EnableSideTicks property to "true". By
default the TickPlacement property is "None" so you won't see the ticks even with the EnableSideTicks
property turned on. To show the ticks set TickPlacement to "TopLeft", "BottomRight" or "Both". To control
the interval between ticks set the TickFrequency property to some value between the Minimum and
Maximum property values that will display well. By default the thumb slides without interruption between
tick marks but you can set the IsSnapToTickEnabled property so that the thumb jumps from one tick to
the next.
<telerik:RadSlider
MinWidth="200"
EnableSideTicks="True"
TickFrequency="0.1"
TickPlacement="BottomRight"
IsSnapToTickEnabled="True"
>
</telerik:RadSlider>
Templates
If the default tick display doesn't work for you, a TickTemplate defines what a single tick looks like. Inside
the TickTemplate tag add a DataTemplate tag. Within the DataTemplate you can add whatever markup you
want to represent the tick mark. The example markup below adds a 10 x 10 pixel red ellipse to represent a
tick mark. Pay particular attention to the TickTemplate tag and syntax.
<telerik:RadSlider
MinWidth="200"
EnableSideTicks="True"
TickFrequency="0.1"
TickPlacement="BottomRight"
IsSnapToTickEnabled="True">
<telerik:RadSlider.TickTemplate>
<DataTemplate>
<Ellipse
Width="10"
Height="10"
Fill="Red"></Ellipse>
</DataTemplate>
</telerik:RadSlider.TickTemplate>
</telerik:RadSlider>
<telerik:RadSlider
MinWidth="200"
EnableSideTicks="True"
Minimum="0"
Maximum="10"
TickFrequency="1"
TickPlacement="BottomRight"
IsSnapToTickEnabled="True">
<telerik:RadSlider.TickTemplate>
<DataTemplate>
<Grid>
<TextBlock
Text="{Binding}"
FontSize="11" />
</Grid>
</DataTemplate>
</telerik:RadSlider.TickTemplate>
</telerik:RadSlider>
If you leave the default "0.1" to "1", the intervening double values are very long and don't display well. Setting
TickFrequency to "1" outputs an array of integers:
Walk Through
This walk-through demonstrates how IValueConverter can accept a value and output a string.
Project Setup
1) From the Visual Studio menu choose File > New > Project..., select the Silverlight project type, then
select the Silverlight Application template. Provide a unique name for the project and click the OK
button.
2) In the "New Silverlight Application" dialog make sure that the "Host the Silverlight application in a new
Web site" option is checked, give the project a unique name and verify that the "ASP.NET Web
Application Project" New Web Project Type option is selected. Click OK to close the dialog and create
the project.
3) In the Solution Explorer, right-click the References node and select Add References... from the
context menu. Add Assembly references:
3) Modify the Convert() method to use the code below. This code creates a generic list of strings with the
proper number names and indexes into this list using the "value" parameter passed in. "value" is the
value for the tick mark being represented.
Return numbers(CInt(Fix(CDbl(value))))
End Function
#End Region
End Class
return numbers[(int)(double)value];
}
#endregion
}
5) At the top of the file in the UserControl tag, add a reference to your project. To do this, type in
"xmlns" (XML name space) and a colon. After the colon put in an alias for the namespace; in this case
the alias will be called "local". Once you type in the equal sign ("=") a list of assemblies in scope will
drop down. Select the project that contains the IValueConverter implementation class. In the
screenshot below the project is "06_Slider".
6) Inside the UserControl element, add a resource that references your new MyValueConverter class. Use
the "x:Key" attribute to identify the class for later use in the XAML.
<UserControl.Resources>
<local:MyValueConverter
x:Key="MyValueConverter" />
</UserControl.Resources>
7) In the DataTemplate add the XAML below to define the RadSlider and the binding using the converter.
Notice the binding expression "{Binding Converter={StaticResource MyValueConverter}}" that
references the MyValueConverter through the resource.
<telerik:RadSlider
Margin="30"
Minimum="1"
Maximum="10"
TickFrequency="1"
TickPlacement="BottomRight"
IsSnapToTickEnabled="True">
<telerik:RadSlider.TickTemplate>
<DataTemplate>
<TextBlock
Text="{Binding Converter={StaticResource MyValueConverter}}"
FontSize="10">
</TextBlock>
</DataTemplate>
</telerik:RadSlider.TickTemplate>
</telerik:RadSlider>
Use layout panels to make use of multiple elements for each tick
Add an Ellipse above the text
Rotate text items using a RenderTransform tag.
9.5 Customization
Walk Through
Customizing controls by editing styles and control templates involves a fair amount of XAML, but you still
make these changes easily using Expression Blend. In this example we will customize the RadSlider
control background and thumb.
"Scoville" Styles
We will use a set of colors that include black, red, yellow and orange in many of the style related
topics and prefix the style names with "Scoville". The "Scoville scale" measures the spiciness of
peppers and other culinary irritants.
Project Setup
1) From the Visual Studio menu choose File > New > Project..., select the Silverlight project type, then
select the Silverlight Application template. Provide a unique name for the project and click the OK
button.
2) In the "New Silverlight Application" dialog make sure that the "Host the Silverlight application in a new
Web site" option is checked, give the project a unique name and verify that the "ASP.NET Web
Application Project" New Web Project Type option is selected. Click OK to close the dialog and create
the project.
3) In the Solution Explorer, right-click the References node and select Add References... from the
context menu. Add Assembly references:
3) Save and close the solution.
8) Right-click the slider and select Edit Template > Edit a Copy from the context menu. In the "Create
Style Resource" dialog, set the Name (Key) to "ScovilleSlider" and click the "Define in Application"
radio button. Click OK to create the stye resource and close the dialog.
Notes
1) This last step opens just the RadSlider template for editing, not the RadSlider instance on the
MainPage.xaml. The MainPage.xaml RadSlider has a reference to the "ScovilleSlider" style that
looks something like the XAML below. Notice the "Style" attribute points at "StaticResource
ScovilleSlider".
In the App.xaml file, the complete definition of the "ScovilleSlider" style has been copied from
the RadSlider defaults and looks something like the small excerpt below:
9) In the Objects and Timeline tab, select the LayoutRoot from the tree view.
10)In the Properties tab, locate the Brushes group and select the "Gradient" brush.
11)Find the right-most gradient stop indicator and click it with the mouse.
12)Grab the eye dropper tool and select a red color for the gradient stop.
13)In the Objects and Timeline tab, select the "HorizontalSingleThumb" item.
14)In the Expression Blend design surface, right-click the slider thumb and select Edit Template > Edit
Current from the context menu. As a result, only the slider thumb will be shown in the designer.
15)In the Objects and Timeline tab, select "Path" from the tree view.
16)In the Properties tab, locate the Brushes section and again select the "Gradient" brush. Click the
right-most gradient stop indicator and use the eye dropper tool to select a red color.
17)In the Projects tab, locate MainPage.xaml and double-click to display the RadSlider with the styling in
play.
Add some logic to the application to verify that the slider events and behavior work as before the styling
was applied.
Add a RadMaskedTextBox to project and create a matching style for it in Expression Blend.
9.6 Wrap Up
This chapter introduced several controls used for gathering user input, including a masked text box for
restricting entry to predefined patterns, a set of "UpDown" controls for entering numeric or other input
through repeater buttons, a set of color picking controls for selecting from a predefined palette of colors and
a slider control for choosing values within a range.
In this chapter you learned how the masked text box handles numeric, date/time and developer-defined
formats using predefined masks or custom masks. We looked at important events for retrieving values and
also learned how to retrieve the value combined together with the mask. You also saw how masked text
boxes can be localized.
In the section on "UpDown" controls you saw how both the basic up-down control and numeric up-down
control both inherit from the RangeBase class. You learned the key common properties and events for both
controls. We discussed the key numeric up-down control ValueFormat property and some special case
issues such as scrolling through integers. You learned how to format numeric entry for data that uses
custom unit types.
We reviewed three different types of color pickers along with their common properties and events. We made
use of the Silverlight Toolkit and the ObjectCollection class to define a custom palette of colors in XAML
and also defined a custom palette in code. You learned how to customize text labels for elements of a color
choosing control and how to hide specific elements of these controls.
The last part of this chapter dealt with how the slider control is used to pick a value within a range or a
range within a range. We talked about the basic structure and parts of a slider, the slider's default behavior,
how to retrieve new and previous values and how to control selection ranges. We paid particular attention to
how tick marks are placed along the slider, how to control tick mark frequency and how to control visibility.
We delved a little more deeply into how templates allow fine-tune control over each tick mark, how to use
binding in templates to show data in a tick mark and finally how IValueConverter is used for highly
customized binding scenarios.
X
Menu Controls
Menu Controls 283
10 Menu Controls
10.1 Objectives
In this chapter you will use RadMenu, RadContextMenu and RadMenuItem to supply drop-down and pop-up
lists of user selections. You will see how MenuBase supplies properties common to both RadMenu and
RadContextMenu. You will build standard menus using RadMenu and use properties to control item opening
behavior and check/uncheck support. You will learn how to respond to user selections, create menus
dynamically in code and add images to menu items. Then you will apply these fundamentals to
RadContextMenu and explore context menu specific issues such as initiating the popup, placement, sizing
and right-click support. Finally, you will use Expression Blend to customize the appearance of a RadMenu
and its items.
10.2 Overview
Telerik Menu for Silverlight lets you build powerful, complex menu systems that are easy for your customer
to use. The control set includes a "main menu" style drop-down control and a context menu that can be
associated with a control or popped up from any location.
The menu controls are fully customizable and have advanced functionality including:
Hierarchical Data Binding: The menu controls bind to objects created in code-behind or from a web
service.
Boundary Detection: The menu controls detect the Silverlight plugin boundaries and open child items
in the opposite direction to avoid crossing screen boundaries. When there is not enough space in all
directions, the control adjusts its items' positions to make them visible whenever possible.
Checkable Items: Menu items can have check marks that can be toggled. Companion properties like
ClickToOpen and StaysOpenOnClick enhance usability.
Project Setup
1) From the Visual Studio menu choose File > New > Project..., select the Silverlight project type, then
select the Silverlight Application template. Provide a unique name for the project and click the OK
button.
2) In the "New Silverlight Application" dialog make sure that the "Host the Silverlight application in a new
Web site" option is checked, give the project a unique name and verify that the "ASP.NET Web
Application Project" New Web Project Type option is selected. Click OK to close the dialog and create
the project.
3) In the Solution Explorer, right-click the References node and select Add References... from the
context menu. Add Assembly references:
a) Telerik.Windows.Controls
b) Telerik.Windows.Controls.Navigation
XAML Editing
1) Open MainPage.xaml for editing.
2) Add the XAML below to the main "LayoutRoot" Grid element. The XAML should be added between the
<Grid> and </Grid> tags.
<StackPanel>
<!--Menu-->
</StackPanel>
3) Drag a RadMenu control from the Toolbox to a point just under the "<!--Menu-->" comment. Set the
following RadMenu properties:
a) x:Name = "mnuMain"
b) ClickToOpen = "True"
With the Click ToOpen property set true, the menu does not open on mouse over, but rather when the
mouse button is click ed.
4) Inside the RadMenu tag, add a RadMenu.Items tag. As of this writing, the namespace for this tag is
"telerik Navigation".
6) Set the RadMenuItem Header property to "Reports". The markup should now look something like the
figure below:
The StaysOpenOnClick property set to true mak es the "Print Two Side" menu item stay visible while
IsCheck able allows the user to toggle the check mark back and forth. These two properties work well
together and also combine nicely with the RadMenu Click ToOpen property.
<!--Menu-->
<telerikNavigation:RadMenu
x:Name="mnuMain"
ClickToOpen="True">
<telerikNavigation:RadMenu.Items>
<telerikNavigation:RadMenuItem
Header="Reports">
<telerikNavigation:RadMenuItem
Header="Product Listing"
Click="Reports_Click">
</telerikNavigation:RadMenuItem>
<telerikNavigation:RadMenuItem
Header="Products by Category"
Click="Reports_Click">
</telerikNavigation:RadMenuItem>
</telerikNavigation:RadMenuItem>
<telerikNavigation:RadMenuItem
Header="Options">
<telerikNavigation:RadMenuItem
Header="Print Two Sided"
Click="OptionsMenuItem_Click"
StaysOpenOnClick="True"
IsCheckable="True">
</telerikNavigation:RadMenuItem>
</telerikNavigation:RadMenuItem>
</telerikNavigation:RadMenu.Items>
</telerikNavigation:RadMenu>
Code Behind
1) In the code-behind, verify that references to the Telerik.Windows.Controls and Telerik.Windows
namespaces are included in the "Imports" (VB) or "using" (C#) section of code. Add these references if
they do not exist.
2) In the code-behind, add the "Reports_Click" and "OptionsMenuItem_Click" event handling code. The
code here simply shows the object that represents the click ed item. We could have used the System
MessageBox.Show() method to display the class name, but instead we're using the RadWindow.Alert()
function.
Notes
The RoutedEventArgs are seen everywhere in Silverlight code. In the example above these
arguments are cast as RadRoutedEventArgs classes that retain more information than their
RoutedEventArgs ancestors.
Try extracting information from the RadMenuItem available in the Click event and displaying that
information in the alert.
Both RadMenu and RadContextMenu descend from MenuBase and ultimately, ItemsControl. ItemsControl
is the base class for objects that have multiple items.
MenuBase introduces basic menu functionality where users can choose from a drop down list of items.
MenuBase includes common events and properties:
ItemClick: This event fires when any item in the menu is clicked.
ClickToOpen: We saw in the "Getting Started" project how the ClickToOpen property, when true,
prevents the menu from opening merely from mouse-over, but instead requires a mouse click to open
the menu.
ShowDelay, HideDelay: These are both Duration properties that define a number of milliseconds
before the menu is shown or hidden.
NotifyOnHeaderClick when true causes the ItemClick event to fire even when the menu header is
clicked.
or Vertical
10.4.2 Items
Overview
Both RadMenu and RadContextMenu have an Items collection that contain a series of RadMenuItem
objects. RadMenuItem descends from HeaderedItemsControl (representing a control that has multiple
items and a header).
RadMenuItem has routed events for Checked, Click, SubmenuClosed, SubmenuOpened and
Unchecked.
To display an image in the menu item, assign a BitmapImage to the Icon property:
myMenu.Items.Add(dayItem)
myMenu.Items.Add(dayItem);
Notes
This walk through will give you some practice at creating menu items with text and icons in code.
Project Setup
1) From the Visual Studio menu choose File > New > Project..., select the Silverlight project type, then
select the Silverlight Application template. Provide a unique name for the project and click the OK
button.
2) In the "New Silverlight Application" dialog make sure that the "Host the Silverlight application in a new
Web site" option is checked, give the project a unique name and verify that the "ASP.NET Web
Application Project" New Web Project Type option is selected. Click OK to close the dialog and create
the project.
3) In the Solution Explorer, right-click the References node and select Add References... from the
context menu. Add Assembly references:
a) Telerik.Windows.Controls
4) Drag the image file "Calendar.png" from the Windows Explorer to the project in Solution Explorer. You
can find "Calendar.png" in the "\courseware\images" directory.
XAML Editing
1) Open MainPage.xaml for editing.
2) Add a Loaded event handler to the UserControl element.
<UserControl
...
Loaded="UserControl_Loaded">
3) Add the XAML below to the main "LayoutRoot" Grid element. The XAML should be added between the
<Grid> and </Grid> tags. The "<!--menu-->" comment will be replaced with the actual RadMenu tag in
the next step.
<StackPanel>
<!--menu-->
</StackPanel>
4) Drag a RadMenu from the Toolbox to a point just below the "<!--menu-->". Set the following properties:
a) x:Name = "mnuMain"
b) ItemClick = "mnuMain_ItemClick"
Code Behind
1) In the code-behind, add the Loaded event handler as shown below. The handler first defines a single
top level RadMenuItem with Header "Days" and adds it to the RadMenu Items collection. Then the
handler defines an array of short day names, creates RadMenuItem instances, assigns Icon and
Header for each before adding the instances to the "Days" menu item.
Dim days() As String = { "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun" }
daysHeaderItem.Items.Add(dayItem)
Next day
End Sub
daysHeaderItem.Items.Add(dayItem);
}
}
2) In the code-behind, add the handler for the menu's ItemClick event.
10.4.4 RadContextMenu
Using RadContextMenu
<Button
Content="Submit Product Request"
MaxWidth="200"
HorizontalAlignment="Left"
Margin="20"
>
<telerikNavigation:RadContextMenu.ContextMenu>
<telerikNavigation:RadContextMenu ItemClick="RadContextMenu_ItemClick">
<telerikNavigation:RadMenuItem
Header="Print Product List"></telerikNavigation:RadMenuItem>
<telerikNavigation:RadMenuItem
Header="Add Product to Palette"></telerikNavigation:RadMenuItem>
<telerikNavigation:RadMenuItem
Header="Reassign Product Group"></telerikNavigation:RadMenuItem>
</telerikNavigation:RadContextMenu>
</telerikNavigation:RadContextMenu.ContextMenu>
</Button>
The context menu shows when the button is right-clicked by the mouse and looks something like the
screenshot below:
The context menu can display in response to any event by specifying the EventName property. In this
Button example, we can use the MouseEnter event so that when the mouse passes over the button, the
menu displays.
<Button>
<telerikNavigation:RadContextMenu.ContextMenu>
<telerikNavigation:RadContextMenu
EventName="MouseEnter"
...
For additional specificity, you can add the ModifierKey property in combination with the EventName. If we
set the ModifierKey property to "Control"...
...now the menu will only show when the control key is held down and the mouse passes over the button.
contextMenu.Items.Add(topLevelItem)
contextMenu.AddHandler(RadMenuItem.ClickEvent, _
New RoutedEventHandler(AddressOf OnMenuItemClick), False)
RadContextMenu.SetContextMenu(MyButton, contextMenu)
End Sub
topLevelItem.Items.Add(new RadMenuItem()
{
Header = "Product Listing"
});
topLevelItem.Items.Add(new RadMenuItem()
{
Header = "Products by Category"
});
contextMenu.Items.Add(topLevelItem);
contextMenu.AddHandler(
RadMenuItem.ClickEvent, new RoutedEventHandler(OnMenuItemClick), false);
RadContextMenu.SetContextMenu(MyButton, contextMenu);
}
<div id="silverlightControlHost">
<object data="data:application/x-silverlight-2," type="application/x-silverlight-2"
width="100%" height="100%">
...
<param name="windowless" value="true" />
...
</object>
...
</div>
"Why doesn't my context menu display" is a frequently asked question in the Telerik forums. As
of this writing, you must set the "windowless" parameter if you want a context menu in response
to a mouse right-click. Also be aware that setting the windowless parameter may have an adverse
effect on performance.
If you forget to set the parameter you will get the default Silverlight context menu:
10.5 Binding
In this walk through, you will populate a menu with hierarchical data.
Project Setup
1) From the Visual Studio menu choose File > New > Project..., select the Silverlight project type, then
select the Silverlight Application template. Provide a unique name for the project and click the OK
button.
2) In the "New Silverlight Application" dialog make sure that the "Host the Silverlight application in a new
Web site" option is checked, give the project a unique name and verify that the "ASP.NET Web
Application Project" New Web Project Type option is selected. Click OK to close the dialog and create
the project.
3) In the Solution Explorer, right-click the References node and select Add References... from the
context menu. Add Assembly references:
a) Telerik.Windows.Controls
b) Telerik.Windows.Controls.Navigation
The Categories class will initialize a list of Categories and Products to assign to the ItemsSource for
the menu. This will be a read-only list where the data is not expected to change. You would use
ObservableCollection<> and implement if the data was dynamic. See the Data Binding chapter for
more information.
Imports System.Collections.Generic
Imports System.Windows.Controls
Namespace _05_Databinding
Public Class Product
Private privateProductName As String
Public Property ProductName() As String
Get
Return privateProductName
End Get
Set(ByVal value As String)
privateProductName = value
End Set
End Property
Private privateDescription As String
Public Property Description() As String
Get
Return privateDescription
End Get
Set(ByVal value As String)
privateDescription = value
End Set
End Property
End Class
End Get
Set(ByVal value As List(Of Product))
privateProducts = value
End Set
End Property
End Class
using System.Collections.Generic;
using System.Windows.Controls;
namespace _05_Databinding
{
public class Product
{
public string ProductName { get; set; }
public string Description { get; set; }
}
ProductName = "Guatemala",
Description = "Grown above 4,000 feet"
},
new Product()
{
ProductName = "Kenya",
Description = "Excellent body"
}
};
this[1].Products = new List<Product>() {
new Product()
{
ProductName = "Bittersweet Chocolate",
Description = "Sweetened dark chocolate without milk"
},
new Product()
{
ProductName = "White Chocolate",
Description = "Sugar, cocoa butter, milk solids"
}
};
}
}
}
XAML Editing
1) Open MainPage.xaml for editing.
2) Verify that the XML namespaces for Telerik.Windows.Controls and Telerik.Windows.Controls.
Navigation exist in the UserControl element. Add them if they do not exist. Also, add a reference to
your project and name it "local".
<UserControl
xmlns:telerikNavigation="clr-namespace:Telerik.Windows.Controls;
assembly=Telerik.Windows.Controls.Navigation"
xmlns:telerik="clr-namespace:Telerik.Windows.Controls;
assembly=Telerik.Windows.Controls"
xmlns:local="clr-namespace:_05_Databinding"
. . . >
3) Add a Resources sub-element to the UserControl element. Copy the XAML below into the Resources
element.
This step creates a reference to the "Categories" object that contains the data we want to display in the
menu. The two data templates defined here, "CategoryTemplate" and "ProductTemplate" supply the
root node items in the menu and the children under those root nodes, respectively. Also notice that
we're binding a product description to the ToolTip of the child menu items.
<UserControl.Resources>
<DataTemplate x:Key="ProductTemplate">
<TextBlock Text="{Binding ProductName}"
controls:ToolTipService.ToolTip="{Binding Description}" />
</DataTemplate>
<telerik:HierarchicalDataTemplate x:Key="CategoryTemplate"
ItemsSource="{Binding Products}"
ItemTemplate="{StaticResource ProductTemplate}">
<TextBlock Text="{Binding CategoryName}" />
</telerik:HierarchicalDataTemplate>
</UserControl.Resources>
4) Drag a RadMenu from the Toolbox to the main "LayoutRoot" Grid element. Set the VerticalAlignment
attribute to "Top", the ItemsSource attribute to "{StaticResource Categories}" and the ItemTemplate
to "{StaticResource CategoryTemplate}".
<Grid x:Name="LayoutRoot">
<telerikNavigation:RadMenu VerticalAlignment="Top"
ItemsSource="{StaticResource Categories}"
ItemTemplate="{StaticResource CategoryTemplate}">
</telerikNavigation:RadMenu>
</Grid>
Notes
Look at the relationship of the templates in the XAML below. The RadMenu ItemTemplate points
to a HierarchicalDataTemplate "CategoryTemplate". "CategoryTemplate" also has an
ItemTemplate that points to a DataTemplate called "ProductTemplate". There can be additional
levels of templates. The rule is to use a HierarchicalDataTemplate when there are child items,
otherwise use a standard DataTemplate.
Let's take another look at that same XAML to see how the data is being hooked up. The
ItemsSource attribute works in concert with the ItemTemplate. In the RadMenu XAML you can
see that the "CategoryTemplate" is supplied by data from the "Categories" object. In the
"CategoryTemplate", the "ProductTemplate" is supplied by data from the "Products" object.
10.6 Customization
Walk Through
In this example we will customize the RadMenu control background and border. We will also add an
animated effect that takes place when the menu becomes disabled.
"Scoville" Styles
We will use a set of colors that include black, red, yellow and orange in many of the style related
topics and prefix the style names with "Scoville". The "Scoville scale" measures the spiciness of
peppers and other culinary irritants.
Project Setup
1) Run Expression Blend.
2) From the File menu select New Project. Note: If you have an existing solution open, right-click the
solution and select Add New Project... from the context menu instead.
3) In the New Project dialog, select "Silverlight" from "Project types" and "Silverlight 3" Application from
the right-most list. Enter a unique name for the project and click OK.
1) In the Projects pane, locate MainPage.xaml and double-click to open the project in Expression Blend.
2) In the Projects pane, right-click the References node and select Add Reference... from the context
menu.
3) Add a reference to the Telerik.Windows.Controls.dll assembly.
4) Add a reference to the Telerik.Windows.Controls.Navigation.dll assembly.
5) From the Project menu select Build Project.
6) Open the Assets pane. On the left side of the Assets pane is a tree view. Locate and open the
"Controls" node, then click the "All" node. A list of all available controls will show to the right of the tree
view. Locate the RadMenu control and drag it onto the MainPage.xaml Artboard.
7) In the Objects and Timeline pane, double-click "[RadMenu]" in the tree view. Enter a new name
"mnuMain".
8) Right-click the menu and select Edit Template > Edit a Copy from the context menu. In the "Create
Style Resource" dialog, set the Name (Key) to "ScovilleMenu" and click the "Define in Application"
radio button. Click OK to create the stye resource and close the dialog.
9) In the Objects and Timeline pane select "[border]" from the tree view.
10)Select the Gradient tool from the Tool Bar. Drag the tool from the top of the menu to the bottom.
This step will reset the fill style for the RadMenu border object and provide it with a gradient fill.
11)Find the right-most gradient stop indicator and click it with the mouse.
12)Grab the and select a shade of black for the gradient stop.
13)Find the left-most gradient stop indicator and click it with the mouse.
14)Grab the eye dropper tool and select a shade of red for the gradient stop.
15)Click the Gradient Bar somewhere in the middle to create a new gradient stop.
16)In the States pane, click the "Disabled" state. This will cause a "Disabled" item to appear at the top of
the Objects and Timeline pane. In the Objects and Timeline pane, click the "Show Timeline" button.
17)In the Timeline, drag the Timeline marker to the 1 second mark.
18)Back on the Properties pane, slide the middle gradient stop to the far left. This step creates an
animation of the middle gradient stop that plays automatically when the menu becomes disabled.
19)In the Objects and Timeline pane, click the Play button to see the animation in action. During the one
second animation, the dark er shades should roll up to cover most of the menu.
20)In the States pane, click the "Normal" state. This will cause a "Normal" item to appear at the top of the
Objects and Timeline pane.
21)In the Timeline, drag the Timeline marker to the 1 second mark.
22)Back on the Properties pane, click the middle gradient stop. Type in "50" to the gradient step offset
text box. This step will create an animation that ends at one second leaving the "normal" property
settings, i.e. with the middle gradient stop midway between the red and black .
23)In the Projects pane, double-click MainPage.xaml to open it for editing in the Artboard.
24)Drag a CheckBox control from the Assets pane to a point just below the RadMenu.
Notes
The "Select" tool must be active to allow you to double-click and rename the CheckBox control.
26)In the Properties pane make sure the Properties button is toggled down, then enter a new name
"cbDisable" for the CheckBox. Note: To see the properties, be sure you have the Properties button
toggled down, not the Events button.
27)In the Properties pane, toggle the "Events" button down. In the "Checked" event, enter an event handler
name "OnDisableChecked". The event handler will be created automatically by Expression Blend and
the code-behind for MainPage.xaml will display Also note that the event handler name is arbitrary and
can be whatever you choose to assign.
28)In the code-behind for MainPage.xaml, code the Checked event handler to set the menu's IsEnabled
property to false as shown below. Note that the code editing happens right in Expression Blend and
includes IntelliSense support. You can also switch to Visual Studio and edit the same project if you
prefer.
29)Navigate back to editing MainPage.xaml in the Artboard. In the Properties pane select the Events
button and add an event handler for the Unchecked event.
30)In the code-behind for MainPage.xaml, code the Unchecked event handler to set the menu's IsEnabled
property to true as shown below.
Toggle the disable check box and observe the effect on the menu. When you check "Disable", the
animation of the center gradient stop should darken the menu. Unchecking "Disable" should animate
the center gradient stop back to its lighter "Normal" state.
Add menu items to the menu using Expression Blend. This is done by navigating to MainPage.xaml
and editing the menu in the Artboard. Right-click the RadMenu control and select Add RadMenuItem
from the context menu.
You can click on the RadMenuItems in the menu and set properties. Find the Header property in the
"Miscellaneous" properties group.
Customize the template for the RadMenuItem, not just the RadMenu. You might do this so that menu
items react as the mouse passes over or to make the visual style of the menu items agree with the
overall menu. For example, the disabled state of the menu looks fine until you put items into it, but the
disabled state of the menu items includes a partially transparent rectangle over the item. This works fine
for the default menu, but is horrible for our new "Scoville" menu shown in the screenshot below.
This situation is fixed in the same manner as when creating the new RadMenu "ScovilleMenu" style, i.e.
right-clicking a RadMenuItem and selecting Edit Template > Edit a Copy from the context menu. We
might call this new template "ScovilleMenuItem" and edit it in the Artboard. The menu item includes a
"DisabledBox" that you can find in the Objects and Timeline pane.
If you select "DisabledBox" and navigate to the Properties pane, you can locate the Fill brush. At this
point the brush is set to "DisabledBrush" which happens to represent that unpleasant transparent white
background. If you click the Fill "Advanced property options" button, you can click the "Reset" option
from the drop down menu. This will set the brush fill to "Transparent", allowing the menu background to
show through without interference.
Now the menu item with the new "ScovilleMenuItem" template applied displays correctly when shown in
a menu with the "ScovilleMenu" template applied.
To fix the other menu items, right-click each item and select Edit Template > Apply Resource > "<your
menu item style>", i.e. "ScovilleMenuItem".
By clicking the "XAML" button we can see that the markup for the menu is still clean, with the complexity
of these changes being stored in the resources for "ScovilleMenu" and "ScovilleMenuItem". In this case
we have the resources tucked away in the App.xaml file where they can be used by other pages in the
application, but they could also be stored in the same "MainPage.xaml" page or in another assembly
altogether.
Try running the application from Visual Studio. Start from the Expression Blend Projects pane, right-
click the solution or project and select Edit in Visual Studio from the context menu.
10.7 Wrap Up
In this chapter you used RadMenu, RadContextMenu and RadMenuItem to supply drop-down and pop-up
lists of user selections. You saw how MenuBase supplies properties common to both RadMenu and
RadContextMenu. You built standard menus using RadMenu and used properties to control item opening
behavior and check/uncheck support. You learned how to respond to user selections, created menus
dynamically in code and added images to menu items. You applied these fundamentals to
RadContextMenu and explored context menu specific issues such as initiating the popup, placement,
sizing and right-click support. Finally, you used Expression Blend to customize the appearance of a
RadMenu and its items.
XI
Tabbed Interfaces
Tabbed Interfaces 333
11 Tabbed Interfaces
11.1 Objectives
In this chapter you will learn how to create tabbed navigation systems and interfaces using RadTabControl
and RadPanelBar controls. You will see the commonality and differences between the two controls. You will
build tab controls and panel bars directly in the XAML and programmatically. You will assign simple text to
the header and content areas of each control and also learn how to stuff the header and content with filling
of any amount and complexity. You will embed controls into the RadTabControl header. You will also handle
the significant events of each control. Finally, you will learn how to completely customize a RadTabItem by
overriding the ControlTemplate.
11.2 Overview
Use RadTabControl to create tabbed navigation systems and interfaces.
RadPanelBar is a versatile component allowing you to build intuitive navigation systems such as left/right
side menus and Outlook style panels.
Notes
You may have noticed a control similar to RadPanelBar in the Toolbox called RadExpander.
RadExpander is a very simple HeaderedContent control. It has only header and content and a single
action for toggling content visibility. When you insert several expanders into a single container, the
expanders don't communicate with one another. By contrast, RadPanelBar is an ItemsControl
descendant and supports hierarchy, data binding and a more complete set of functionality.
Project Setup
1) From the Visual Studio menu choose File > New > Project..., select the Silverlight project type, then
select the Silverlight Application template. Provide a unique name for the project and click the OK
button.
2) In the "New Silverlight Application" dialog make sure that the "Host the Silverlight application in a new
Web site" option is checked, give the project a unique name and verify that the "ASP.NET Web
Application Project" New Web Project Type option is selected. Click OK to close the dialog and create
the project.
3) In the Solution Explorer, right-click the References node and select Add References... from the
context menu. Add Assembly references:
a) Telerik.Windows.Controls
b) Telerik.Windows.Controls.Navigation
c) Telerik.Windows.Themes.Summer
4) Add an "Images" directory to the project. Copy the following images from the "\courseware\images"
directory to the new images directory
d) Calculator.png
e) Camera.png
f) Favorites.png
g) gamecontroller.png
The Images directory should look something like the screenshot below.
XAML Editing
1) Open MainPage.xaml for editing.
2) Add a XML namespace,"telerik", that references the Telerik.Windows.Controls assembly.
xmlns:telerik=
"clr-namespace:Telerik.Windows.Controls;assembly=Telerik.Windows.Controls"
3) Inside the UserControl element, add the XAML below. This XAML defines resources we will use later.
<UserControl.Resources>
<Style x:Key="MyContentStyle" TargetType="FrameworkElement">
<Setter Property="HorizontalAlignment" Value="Left"></Setter>
<Setter Property="Margin" Value="10"></Setter>
<Setter Property="MaxWidth" Value="200"></Setter>
<Setter Property="MaxHeight" Value="200"></Setter>
</Style>
<Style
x:Key="ImageStyle"
TargetType="Image"
BasedOn="{StaticResource MyContentStyle}">
<Setter Property="Stretch" Value="Uniform"></Setter>
</Style>
4) Drag a RadTabControl from the Toolbox to a point within the main Grid tag.
5) Add three RadTabItem controls between the RadTabControl tags. Note: the XAML in the screenshot
below was massaged slightly to place the tab items on two lines each and to label each tab item with
comments for easier reference later in this tutorial.
6) Add a StyleManager.Theme property to the RadTabControl and set it equal to the "Summer" theme.
<telerikNavigation:RadTabControl
telerik:StyleManager.Theme="Summer">
</telerikNavigation:RadTabControl>
7) Add Header and Content attributes to the first RadTabItem tag and set these attributes equal to
"Simple text in header" and "Simple text can go in the content", respectively.
<telerikNavigation:RadTabControl
telerik:StyleManager.Theme="Summer">
...
</telerikNavigation:RadTabControl>
8) Inside the second tag add a RadTabItem.Header sub-element. Below the RadTabItem.Header
element, add a RadTabItem.Content sub-element.
<telerikNavigation:RadTabControl
telerik:StyleManager.Theme="Summer">
...
</telerikNavigation:RadTabItem.Header>
<telerikNavigation:RadTabItem.Content>
</telerikNavigation:RadTabItem.Content>
</telerikNavigation:RadTabItem>
...
</telerikNavigation:RadTabControl>
Tip!
You can populate the Header and Content attribute when you only need a single assignment. For
more complex arrangements with multiple items and arbitrary layout within the tab item, use
Header and Content sub-elements as shown in this last step. In following steps we will add
controls to the Header and Content sub-elements.
9) Inside the second tab item Header element, add a TextBlock. You can use the text as shown below or
add your own text
Notes
.The <Run> tags within the TextBlock element allow you to assign style to only parts of the
TextBlock contents.
<telerikNavigation:RadTabControl
telerik:StyleManager.Theme="Summer">
...
</telerikNavigation:RadTabItem.Content>
</telerikNavigation:RadTabItem>
...
</telerikNavigation:RadTabControl>
10)Inside the second tab item's Content tags add a StackPanel tag. Within the StackPanel add
TextBlock, a Telerik RadCalendar and an Image control from the Toolbox. Add text to the TextBlock
either using the XAML below or any arbitrary text you care to add. To set the RadCalendar properties all
at one time, assign "{StaticResource MyContentStyle}" to the RadCalendar Style attribute. Set the
Image Style to "{StaticResource ImageStyle}". Point the Image Source property to the "Blue Hills.jpg"
image.
<telerikNavigation:RadTabControl
telerik:StyleManager.Theme="Summer">
...
...
</telerikNavigation:RadTabControl>
Gotcha!
Does the editor flag an error "The type 'telerikInput:RadCalendar' was not found"? Make sure that
you actually drag the RadCalendar from the Toolbox rather than just copying the XAML here.
Dragging from the toolbox also adds an XML namespace that references the correct assembly.
Gotcha!
11)In the third RadTabItem, set the Header value to "RadPanelBar". Add a RadTabItem.Content element.
Drag a RadPanelBar from the Toolbox to a point within the RadTabItem.Content beginning and ending
tags. Set the RadPanelBar attributes HorizontalAlignment = "Left" and add a StyleManager.Theme
attribute set to "Summer".
12)Add three RadPanelBarItem tags to the RadPanelBar from the Toolbox and assign Header attributes
"Favorites", "Reports" and "Options". The Result should now look like the XAML below.
<telerikNavigation:RadTabControl
telerik:StyleManager.Theme="Summer">
...
<telerikNavigation:RadTabItem.Content>
<!--panel bar-->
<telerikNavigation:RadPanelBar
HorizontalAlignment="Left"
telerik:StyleManager.Theme="Summer">
</telerikNavigation:RadPanelBar>
</telerikNavigation:RadTabItem.Content>
</telerikNavigation:RadTabItem>
</telerikNavigation:RadTabControl>
13)Locate the first RadPanelBarItem and remove the 'Header="Favorites"' attribute. Add a
RadPanelBarItem.Header element inside the RadPanelBarItem. Inside the RadPanelBarItem.Header,
add an Image control. Set the Image Style equal to "{StaticResource IconStyle}", and point the
Source to "../Images/Favorites.png".
<telerikNavigation:RadTabControl
telerik:StyleManager.Theme="Summer">
...
<telerikNavigation:RadTabItem.Content>
<!--panel bar-->
<telerikNavigation:RadPanelBar
HorizontalAlignment="Left"
telerik:StyleManager.Theme="Summer">
...
</telerikNavigation:RadPanelBarItem>
</telerikNavigation:RadPanelBar>
</telerikNavigation:RadTabItem.Content>
</telerikNavigation:RadTabItem>
</telerikNavigation:RadTabControl>
14)Below the RadPanelBarItem.Header, add a RadPanelBarItem.Items tag and three Image controls
within the Items tag. Again, set the Style for each Image to "{StaticResource IconStyle}" and the image
Source properties to "Calculator.png", "Camera.png" and "gamecontroller.png" (all image paths within
the \Images folder as shown below). Now the relevant portion of XAML for that first RadPanelBarItem
looks like the example below.
<telerikNavigation:RadTabControl
telerik:StyleManager.Theme="Summer">
...
<telerikNavigation:RadTabItem.Content>
<!--panel bar-->
<telerikNavigation:RadPanelBar
HorizontalAlignment="Left"
telerik:StyleManager.Theme="Summer">
...
</telerikNavigation:RadPanelBar>
</telerikNavigation:RadTabItem.Content>
</telerikNavigation:RadTabItem>
</telerikNavigation:RadTabControl>
15)Add two more RadPanelBarItem tags, each with a set of TextBlocks within the RadPanelBarItem.Items
tag. You can use any arbitrary text to fill the TextBlock controls or borrow from the XAML below.
<telerikNavigation:RadTabControl
telerik:StyleManager.Theme="Summer">
...
<telerikNavigation:RadTabItem.Content>
<!--panel bar-->
<telerikNavigation:RadPanelBar
HorizontalAlignment="Left"
telerik:StyleManager.Theme="Summer">
</telerikNavigation:RadPanelBar>
</telerikNavigation:RadTabItem.Content>
</telerikNavigation:RadTabItem>
</telerikNavigation:RadTabControl>
<telerikNavigation:RadTabControl x:Name="tcDogBreeds">
<telerikNavigation:RadTabItem Header="Toy" />
<telerikNavigation:RadTabItem Header="Small" />
<telerikNavigation:RadTabItem Header="Medium" />
<telerikNavigation:RadTabItem Header="Large" />
</telerikNavigation:RadTabControl>
The example running in the browser looks like the screenshot below:
The Header can simply be text as in the example above, or can have a more complex arrangement of any
elements. This is done by enclosing content markup within a RadTabItem.Header tag and filling it with any
Silverlight markup that suits your purpose. For example, the snippet below removes the "Header='Some
text'" property and adds the RadTabItem.Header tag with a StackPanel inside to handle layout.The
StackPanel contains a TextBlock and an Image control. Note: Resources not shown in this example are
defined in the <UserControl.Resources> tag simply to move non-relevant visual property settings out of the
way. If you're interested you can review these resources in the example solution for this chapter.
<telerikNavigation:RadTabControl
x:Name="tcDogBreeds">
<telerikNavigation:RadTabItem>
<telerikNavigation:RadTabItem.Header>
<StackPanel>
<TextBlock Text="Toy"
Style="{StaticResource CaptionStyle}" />
<Image Style="{StaticResource ImageStyle}"
Source="../images/ToyDog.png" />
</StackPanel>
</telerikNavigation:RadTabItem.Header>
</telerikNavigation:RadTabItem>
...
</telerikNavigation:RadTabControl>
The RadTabItem Content works in a similar manner to the Header. You could define "Content" just using
strings...
<telerikNavigation:RadTabControl
x:Name="tcDogBreeds" telerik:StyleManager.Theme="Summer">
<telerikNavigation:RadTabItem
Header="Toy"
Content="The Chihuahua is a brave and good natured dog." />
<telerikNavigation:RadTabItem
Header="Small"
Content="The Pug is gentle and affectionate." />
<telerikNavigation:RadTabItem
Header="Medium"
Content="The Irish Setter is active and affectionate." />
<telerikNavigation:RadTabItem
Header="Large"
Content="The Great Pyrenees is loyal and protective." />
</telerikNavigation:RadTabControl>
More commonly you want to define a RadTabItem.Content tag and add other markup for a richer display
as demonstrated in this snippet:
<telerikNavigation:RadTabControl x:Name="tcDogBreeds">
<telerikNavigation:RadTabItem Header="Toy">
<telerikNavigation:RadTabItem.Content>
<Border Style="{StaticResource ItemBorderStyle}">
<Image Style="{StaticResource ImageStyle}"
Source="../images/ToyDog.png" />
</Border>
</telerikNavigation:RadTabItem.Content>
</telerikNavigation:RadTabItem>
...
The end result has simple text in the Header with Border and Image controls inside the RadTabItem.Content
tags.
The tab displays after tabs already defined in the XAML, as shown in the screenshot below.
Notes
If you need only the content for the current selection then read the RadTabControl.SelectedContent
property. You can also get the position of the current selection using the SelectedIndex property that
indexes into the RadTabControl Items[] collection.
Tip!
The Telerik.Controls.Windows namespace defines a UIElementExtensions class that has two handy
extension methods. ChildrenOfType<T>() takes a UIElement and returns a IList<T> of all children in
the visual tree for a given type. ParentOfType<T>() takes a UIElement and returns a parent element.
The example running in the browser looks like the screenshot below. For the full source, see the solution
for this chapter.
<telerikNavigation:RadTabControl x:Name="tcDogBreeds"
SelectionChanged="tcDogBreeds_SelectionChanged">
<telerikNavigation:RadTabItem Style="{StaticResource TabItemStyle}">
<telerikNavigation:RadTabItem.Header>
<StackPanel Style="{StaticResource HorizontalPanelStyle}">
<TextBlock Text="Toy" Style="{StaticResource CaptionStyle}" />
<Button Click="Button_Click">
<Button.Content>
<Image Style="{StaticResource ButtonImageStyle}"
Source="../images/Close.png" />
</Button.Content>
</Button>
</StackPanel>
</telerikNavigation:RadTabItem.Header>
The remaining RadTabItem objects in the example XAML have a simple text header and an Image in the
content. How could you populate the remaining tabs to replace the simple text header with the same items
used in the XAML above? The code below runs in the UserControl Loaded event handler. First it gets all the
styles it will need from UserControl.Resources. The remaining code iterates all the RadTabItem instances in
the RadTabControl Items collection. If the RadTabItem Header does not have a StackPanel we assume it
needs the entire StackPanel-TextBlock-Button-Image combination. The StackPanel is then built, replicating
the settings of the previous XAML example.
' add close b uttons to any tab s that don't have them
For Each tab As RadTabItem In tcDogBreeds.Items
' this item doesn't have a stack panel with a close b utton
If Not(TypeOf tab.Header Is StackPanel) Then
' get the header text
Dim caption As String = tab.Header.ToString()
' create the stack panel, text b lock, b utton and image
Dim stackPanel As New StackPanel()
stackPanel.Style = HorizontalPanelStyle
stackPanel.Children.Add(textBlock)
stackPanel.Children.Add(button)
tab.Header = stackPanel
tab.Style = TabItemStyle
End If
Next tab
End Sub
The Button Click event handler gets the RadTabItem that the button is sitting in, using the ParentOfType<>
() extension method. The RadTabItem is then removed from RadTabControl Items collection.
Running in the browser, the tabs look like the screenshot below where some of the tabs have already been
closed.
The tab control in the example screenshot below has its TabOrientation property set to Vertical so that
the header text runs from top-to-bottom, not left-to-right. The Align property is "Justify", causing the tabs
to take the entire length next to the content instead of being bunched on the left, right or center.
TabStripPlacement is set to "Right".
11.4.2 RadPanelBar
Minimally defining a RadPanelBar and its items in XAML follows much the same pattern as RadTabControl.
You only need a RadPanelBar outer tag containing a series of RadPanelBarItem tags, each with a
Header property defined. Be aware that RadPanelBarItem is available in the Toolbox and can be dragged to
your XAML from there.
<telerikNavigation:RadPanelBar
x:Name="pbDogBreeds"
HorizontalAlignment="Left"
MaxWidth="300"
Margin="20">
<telerikNavigation:RadPanelBarItem Header="Small Dogs" />
<telerikNavigation:RadPanelBarItem Header="Medium Dogs" />
<telerikNavigation:RadPanelBarItem Header="Large Dogs" />
</telerikNavigation:RadPanelBar>
The example running in the browser looks like the screenshot below:
Creating panel bar item headers with more complex arrangements of contents also follows the same pattern
as RadTabControl. Inside each RadPanelBarItem tag add a RadPanelBarItem.Header, and within the header
add any contents that suit your purpose. Typically you will find a layout panel of some sort inside the
RadPanelBarItem.Header tag, such as a StackPanel or Grid.
<telerikNavigation:RadPanelBar x:Name="pbDogBreeds"
HorizontalAlignment="Left"
MaxWidth="300" Margin="20">
<telerikNavigation:RadPanelBarItem>
<telerikNavigation:RadPanelBarItem.Header>
<StackPanel>
<TextBlock Text="Toy"></TextBlock>
<Border Style="{StaticResource ItemBorderStyle}">
<Image Style="{StaticResource ImageStyle}"
Source="../images/ToyDog.png" />
</Border>
</StackPanel>
</telerikNavigation:RadPanelBarItem.Header>
</telerikNavigation:RadPanelBarItem>
...
</telerikNavigation:RadPanelBar>
The panel bar running in the browser looks like the screenshot below.
Adding content to the items area below the header in the panel bar again follows the same pattern as
RadTabControl, but instead of a Content tag we use RadPanelBarItem.Items. The example below adds a
single item inside each header.
<telerikNavigation:RadPanelBar x:Name="pbDogBreeds"
Margin="20" telerik:StyleManager.Theme="Vista">
<telerikNavigation:RadPanelBarItem Header="Toy">
<telerikNavigation:RadPanelBarItem.Items>
<StackPanel Style="{StaticResource StackPanelStyle}">
<TextBlock
Text="Toy"
Style="{StaticResource CaptionStyle}">
</TextBlock>
<Image
Style="{StaticResource ImageStyle}"
Source="../images/ToyDog.png" />
</StackPanel>
</telerikNavigation:RadPanelBarItem.Items>
</telerikNavigation:RadPanelBarItem>
...
</telerikNavigation:RadPanelBar>
The results in the browser look something like the screenshot below.
You can define multiple items as shown in the XAML below. Notice that RadPanelBarItem.Items tag is the
default and can be left out.
<telerikNavigation:RadPanelBar
x:Name="pbDogBreeds"
Margin="20"
telerik:StyleManager.Theme="Vista">
<telerikNavigation:RadPanelBarItem
Header="Toy">
<TextBlock
Text="Chihuahua"
Style="{StaticResource CaptionStyle}"></TextBlock>
<TextBlock
Text="English Toy Spaniel"
Style="{StaticResource CaptionStyle}"></TextBlock>
<TextBlock
Text="Pekingese"
Style="{StaticResource CaptionStyle}"></TextBlock>
</telerikNavigation:RadPanelBarItem>
...
</telerikNavigation:RadPanelBar>
The RadPanelBarItem with the Header defined is inserted to the head of the list using the RadPanelBar.
Items.Insert() method, rather than using the Add() method.
The panel bar items that already exist in the XAML use a style defined in UserControl.Resources called
"CaptionStyle". Now that we're adding more items on-the-fly, we need to apply that style to make the
appearance match. Notice the code that extracts "CaptionStyle" to a Style object. When the TextBlock
object is created, the object initializer for the TextBlock assigns the Style property.
Running in the browser, the new "Teacup" RadPanelBarItem shows at the beginning of the list with
three new TextBlock controls added to the Items collection.
Events
You can react to panels opening and closing using the event pairs PreviewExpanded/Expanded and
PreviewCollapsed/Collapsed. All four of these events provide access to the expanding/collapsing panel
through the OriginalSource property. The "Preview" event versions allow you to cancel the event by setting
the Handled property to true. The example below prevents the panel from expanding if there are fewer than
four items in it.
The Selected event fires for all items including the header item and all the items underneath the header.
The RadPanelBarItem Level property can give you a hand figuring out which item you have a reference to, i.
e. "1" for the header item and "2" for the items underneath the header. The RadPanelBarItem Item property
is a reference to the object actually being displayed.
The example below gets a reference to the RadPanelBarItem. If the Level is "2", the Item property is
assumed to be a TextBlock and cast as such.
If item.Level = 2 Then
Dim tb As TextBlock = TryCast(item.Item, TextBlock)
RadWindow.Alert("You clicked item " & tb.Text)
End If
End Sub
if (item.Level == 2)
{
TextBlock tb = item.Item as TextBlock;
RadWindow.Alert("You clicked item " + tb.Text);
}
}
11.5 Customization
While styles can let you change a number of properties all at one time, templates provide the powerful
ability to completely change the makeup of a control without breaking its functionality. In this walk through
you will define new contents for the RadTabItem "TopTemplate" that defines the layout of the control when
the TabStripPlacement is "Top".
Notes
We can define templates in Expression Blend, but it helps sometimes to see how the XAML is
structured so this time we will make these changes manually. Typically you will want to use Blend
not only for the ease-of-use, but because Blend does a much more complete job of making sure
all the styles, templates and brushes are included in your XAML.
Notes
RadTabItem and RadTabControl have four templates each, not just one. These templates change
depending on the TabStripPlacement of the tab control and have corresponding names, i.e.
"BottomTemplate", "TopTemplate", "LeftTemplate" and "RightTemplate". This approach was
chosen by Telerik for performance reasons. The alternative would have been to have visual
elements in a single template with different parts shown or hidden as needed.
"Scoville" Styles
We will use a set of colors that include black, red, yellow and orange in many of the style related
topics and prefix the style names with "Scoville". The "Scoville scale" measures the spiciness of
peppers and other culinary irritants.
Project Setup
1) From the Visual Studio menu choose File > New > Project..., select the Silverlight project type, then
select the Silverlight Application template. Provide a unique name for the project and click the OK
button.
2) In the "New Silverlight Application" dialog make sure that the "Host the Silverlight application in a new
Web site" option is checked, give the project a unique name and verify that the "ASP.NET Web
Application Project" New Web Project Type option is selected. Click OK to close the dialog and create
the project.
3) In the Solution Explorer, right-click the References node and select Add References... from the
context menu. Add Assembly references:
a) Telerik.Windows.Controls
b) Telerik.Windows.Controls.Navigation
XAML Editing
1) Open MainPage.xaml for editing.
2) Add the XAML below to the XML name space attributes section of the UserControl tag.
<UserControl
xmlns:telerikNavigation=
"clr-namespace:Telerik.Windows.Controls;assembly=Telerik.Windows.Controls.Navigation"
xmlns:Telerik_Windows_Controls_Primitives=
"clr-namespace:Telerik.Windows.Controls.Primitives;assembly=Telerik.Windows.Controls.Navigation"
. . .>
3) Inside the UserControl tag, add the UserControl.Resources below. We'll use the comments to include
later the style for the RadTabItem and the top template:
<UserControl.Resources>
<!--ScovilleTopTemplate-->
<!--ScovilleTab Style-->
</UserControl.Resources>
The XAML is a control template that will replace the top tabs. Notice that the template is encapsulated
with a standard Silverlight Grid named "wrapper". Be sure to leave the "wrapper" name in place or the
logic that changes content in response to click ing tabs will not work . Inside the Grid is an Ellipse and a
TabItemContentPresenter. The TabItemContentPresenter renders the header content.
<!--ScovilleTopTemplate-->
<ControlTemplate x:Key="ScovilleTopTemplate" TargetType="telerikNavigation:RadTabItem">
<Grid x:Name="wrapper">
<Ellipse>
<Ellipse.Fill>
<RadialGradientBrush>
<GradientStop Color="Red" Offset="0.1" />
<GradientStop Color="DarkRed" Offset="0.5" />
<GradientStop Color="Maroon" Offset=".7" />
</RadialGradientBrush>
</Ellipse.Fill>
</Ellipse>
<Telerik_Windows_Controls_Primitives:TabItemContentPresenter
x:Name="HeaderElement"
Content="{TemplateBinding Header}"
Foreground="{TemplateBinding Foreground}"
Margin="{TemplateBinding Margin}" />
</Grid>
</ControlTemplate>
5) Replace the "<!--ScovilleTab Style-->" comment with the "ScovilleTab" style XAML below. The style
incorporates the TopTemplate property of the RadTabItem.
<!--ScovilleTab Style-->
<Style x:Key="ScovilleTab" TargetType="telerikNavigation:RadTabItem">
<Setter Property="TopTemplate" Value="{StaticResource ScovilleTopTemplate}" />
<Setter Property="Background" Value="White" />
<Setter Property="Foreground" Value="White" />
<Setter Property="Margin" Value="10" />
</Style>
6) Drag a RadTabControl from the Toolbox to a point inside the main "LayoutRoot" grid. Set the
HorizontalAlignment to "Left", VerticalAlignment to "Top" and BackgroundVisibility to
"Collapsed". Add three RadTabItem and set the Style for each to "ScovilleTab". Set both the Content
and Header to "Very Spicy", "Hot" and "Mild".
<telerikNavigation:RadTabControl HorizontalAlignment="Left"
VerticalAlignment="Top"
BackgroundVisibility="Collapsed">
<telerikNavigation:RadTabItem Header="Very Spicy"
Content="Very Spicy"
Style="{StaticResource ScovilleTab}" />
<telerikNavigation:RadTabItem Header="Hot" Content="Hot"
Style="{StaticResource ScovilleTab}" />
<telerikNavigation:RadTabItem Header="Mild" Content="Mild"
Style="{StaticResource ScovilleTab}" />
</telerikNavigation:RadTabControl>
<UserControl
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:telerikNavigation=
"clr-namespace:Telerik.Windows.Controls;assembly=Telerik.Windows.Controls.Navigation"
xmlns:Telerik_Windows_Controls_Primitives=
"clr-namespace:Telerik.Windows.Controls.Primitives;assembly=Telerik.Windows.Controls.Navigation"
x:Class="TabTemplateTest.MainPage" Width="640" Height="480">
<UserControl.Resources>
<!--ScovilleTopTemplate-->
<ControlTemplate x:Key="ScovilleTopTemplate"
TargetType="telerikNavigation:RadTabItem">
<Grid x:Name="wrapper">
<Ellipse>
<Ellipse.Fill>
<RadialGradientBrush>
<GradientStop Color="Red" Offset="0.1" />
<GradientStop Color="DarkRed" Offset="0.5" />
<GradientStop Color="Maroon" Offset=".7" />
</RadialGradientBrush>
</Ellipse.Fill>
</Ellipse>
<Telerik_Windows_Controls_Primitives:TabItemContentPresenter
x:Name="HeaderElement"
Content="{TemplateBinding Header}"
Foreground="{TemplateBinding Foreground}"
Margin="{TemplateBinding Margin}" />
</Grid>
</ControlTemplate>
<!--ScovilleTab Style-->
<Style x:Key="ScovilleTab"
TargetType="telerikNavigation:RadTabItem">
<Setter Property="TopTemplate"
Value="{StaticResource ScovilleTopTemplate}" />
<Setter Property="Background" Value="White" />
<Setter Property="Foreground" Value="White" />
<Setter Property="Margin" Value="10" />
</Style>
</UserControl.Resources>
...
</UserControl>
Gotcha!
TabStripPlacement must correspond to the template you're defining or you will only see the
default appearance of the control. If you're using TabStripPlacement = "Top", then the
"TopTemplate" must be defined and visa-versa.
Press F5 to run the application. The web page should look something like the screenshot below.
11.6 Wrap Up
In this chapter you learned how to create tabbed navigation systems and interfaces using RadTabControl
and RadPanelBar controls. You built tab controls and panel bars directly in the XAML and
programmatically. You also assigned simple text to the header and content areas of each control and
learned how to add to the header and content with XAML markup of arbitrary complexity. You learned how
to embed controls into the RadTabControl header. You also learned how to handle the significant events of
each control. Finally, you learned how to completely customize a RadTabItem by overriding the
ControlTemplate.
XII
ToolBar
376 RadControls for Silverlight
12 ToolBar
12.1 Objectives
In this chapter you'll see how to organize multiple Silverlight controls into horizontal or vertical strips using
RadToolBar. You will use RadToolBarSeparator to visually divide groups of controls. You will see how the
theming mechanism for RadToolBar automatically styles both the tool bar and its items. You will use the
RadToolBar OverflowMode property to handle overflow behavior when there are more controls than can be
displayed at one time. You will handle events that react to the overflow area expanding and collapsing. You
will add items to the tool bar in XAML, using the programmatic API and through data binding.
You will use the RadToolBarTray to manage RadToolBar position, sizing and order.
Finally, you will create custom templates for the RadToolBar background and RadToolBarSeparator.
12.2 Overview
RadToolBar organizes multiple Silverlight controls into a strip where they can be presented in Horizontal or
Vertical orientation. Multiple RadToolBar controls can be managed using the RadToolBarTray.
When the browser is resized, buttons that don't fit the visible area are automatically relocated to an
"overflow" panel. The "down" overflow button at the right end of the tool bar displays the overflow panel area.
Predefined themes can be applied to the RadToolBarTray and to RadToolBars individually. Common control
primitives, e.g. Button, RadioButton, CheckBox, etc, are styled automatically to agree with the RadToolBar
theme. Take a look at some of the example screenshots below to see how the theme styles the tool bar
and its items:
Office_Silver
Office_Blue
Office_Black
Sum m er
Vista
Project Setup
1) From the Visual Studio menu choose File > New > Project..., select the Silverlight project type, then
select the Silverlight Application template. Provide a unique name for the project and click the OK
button.
2) In the "New Silverlight Application" dialog make sure that the "Host the Silverlight application in a new
Web site" option is checked, give the project a unique name and verify that the "ASP.NET Web
Application Project" New Web Project Type option is selected. Click OK to close the dialog and create
the project.
3) In the Solution Explorer, right-click the References node and select Add References... from the
context menu. Add Assembly references:
a) Telerik.Windows.Controls
b) Telerik.Windows.Controls.Navigation
c) Telerik.Windows.Themes.Vista
4) In the Solution Explorer, right-click the project and select Add > New Folder from the context menu.
Rename the folder "Images".
5) Add the images listed below to the new "Images" folder. The images can be found in the
"\courseware\images" directory:
a) CD_Add.png
b) CD_Burn.png
c) CD_Movies.png
d) CD_Mustic.png
e) Help.png
XAML Editing
1) Open MainPage.xaml for editing.
2) Add a reference to the Telerik.Windows.Controls assembly in the UserControl element. This will allow
us to use themes in the XAML.
<UserControl
...
xmlns:telerik=
"clr-namespace:Telerik.Windows.Controls;assembly=Telerik.Windows.Controls" >
...
3) Add a UserControl.Resources element to the UserControl. Inside the Resources element, add a new
Style with Key "ButtonStackPanelStyle" and TargetType "StackPanel". Add a single Setter element
that assigns the Property to be "Margin" and the Value to be "5".
<UserControl . . . >
<UserControl.Resources>
<Style
x:Key="ButtonStackPanelStyle"
TargetType="StackPanel">
<Setter Property="Margin" Value="5" />
</Style>
</UserControl.Resources>
...
4) Add a RadToolBarTray inside the main "LayoutRoot" Grid element, from the Toolbox.
Adding a RadToolBarTray is not strictly necessary in this example because we will add only a single
RadToolBar. But the RadToolBarTray does a nice job of constraining the RadToolBar dimensions so
that you don't have to define a MaxHeight or HorizontalAlignment. You can try the example both ways,
with and without the RadToolBarTray to see how they differ. In later sections of this chapter we will talk
about how RadToolBarTray manages multiple RadToolBar controls.
5) Inside the RadToolBarTray, add a RadToolBar from the Toolbox. Set the StyleManager.Theme
attribute to "Vista". The markup should look something like the example below:
<UserControl . . .>
...
<Grid x:Name="LayoutRoot">
<telerikNavigation:RadToolBarTray>
<telerikNavigation:RadToolBar telerik:StyleManager.Theme="Vista">
</telerikNavigation:RadToolBar>
</telerikNavigation:RadToolBarTray>
</Grid>
</UserControl>
6) Add a Button inside the RadToolBar element. Inside the Button, add a StackPanel and set its Style to
the "ButtonStackPanelStyle" resource. Inside the StackPanel, add a TextBlock and an Image. Set the
TextBlock Text to "My Movies" and the Image Source to the "images/CD_Movies.png" path.
...
<Button>
<StackPanel Style="{StaticResource ButtonStackPanelStyle}">
<TextBlock Text="My Movies"></TextBlock>
<Image Source="images/CD_Movies.png"></Image>
</StackPanel>
</Button>
...
7) Press F5 to run the application. The application displays the tool bar with a single button in it.
8) Add a series of buttons and a TextBlock to the RadToolBar using the XAML below.
<Button>
<StackPanel Style="{StaticResource ButtonStackPanelStyle}">
<TextBlock Text="My Tunes" ></TextBlock>
<Image Source="images/CD_Music.png"></Image>
</StackPanel>
</Button>
<Button>
<StackPanel Style="{StaticResource ButtonStackPanelStyle}">
<TextBlock Text="Add" ></TextBlock>
<Image Source="images/CD_Add.png"></Image>
</StackPanel>
</Button>
<Button>
<StackPanel Style="{StaticResource ButtonStackPanelStyle}">
<TextBlock Text="Burn" ></TextBlock>
<Image Source="images/CD_Burn.png"></Image>
</StackPanel>
</Button>
<StackPanel Style="{StaticResource ButtonStackPanelStyle}">
<TextBlock Text="Volume Name" ></TextBlock>
<TextBox Text="My Music"></TextBox>
</StackPanel>
9) Drag a RadToolBarSeparator from the Toolbox to a point just below the buttons.
<Button>
<StackPanel Style="{StaticResource ButtonStackPanelStyle}">
<TextBlock Text="Help" ></TextBlock>
<Image Source="images/Help.png"></Image>
</StackPanel>
</Button>
...
<telerikNavigation:RadToolBarTray>
<telerikNavigation:RadToolBar telerik:StyleManager.Theme="Vista" Margin="10">
<Button>
<StackPanel Style="{StaticResource ButtonStackPanelStyle}">
<TextBlock Text="My Movies"></TextBlock>
<Image Source="images/CD_Movies.png"></Image>
</StackPanel>
</Button>
<Button>
<StackPanel Style="{StaticResource ButtonStackPanelStyle}">
<TextBlock Text="My Tunes" ></TextBlock>
<Image Source="images/CD_Music.png"></Image>
</StackPanel>
</Button>
<Button>
<StackPanel Style="{StaticResource ButtonStackPanelStyle}">
<TextBlock Text="Add" ></TextBlock>
<Image Source="images/CD_Add.png"></Image>
</StackPanel>
</Button>
<Button>
<StackPanel Style="{StaticResource ButtonStackPanelStyle}">
<TextBlock Text="Burn" ></TextBlock>
<Image Source="images/CD_Burn.png"></Image>
</StackPanel>
</Button>
<StackPanel Style="{StaticResource ButtonStackPanelStyle}">
<TextBlock Text="Volume Name" ></TextBlock>
<TextBox Text="My Music"></TextBox>
</StackPanel>
<telerikNavigation:RadToolBarSeparator />
<Button>
<StackPanel Style="{StaticResource ButtonStackPanelStyle}">
<TextBlock Text="Help" ></TextBlock>
<Image Source="images/Help.png"></Image>
</StackPanel>
</Button>
</telerikNavigation:RadToolBar>
</telerikNavigation:RadToolBarTray>
...
RadToolBarTray
RadToolBarTray contains multiple RadToolBars and manages their position, sizing and order. The tool bar
tray is divided into bands where each band can contain multiple tool bars. Use the tool bar's Band property
to determine which row (or column if the tool bar Orientation is Vertical) that the tool bar appears on. Use
the BandIndex property to control where the tool bar appears on the Band in relation to other tool bars.
For example, the XAML below defines three tool bars within a RadToolBarTray and sets the Bands and
BandIndex properties for each tool bar.
<telerikNavigation:RadToolBar Band="1">
<TextBlock Text="Tool Bar Three" ></TextBlock>
</telerikNavigation:RadToolBar>
</telerikNavigation:RadToolBarTray>
The screenshot below shows the result of these Band and BandIndex property settings. Tool bars "Tool Bar
One" and "Tool Bar Two" have a Band property of "0" so you can expect to see them both on the first row of
the RadToolBarTray. The Band property of tool bar "Tool Bar Three" is "1" and appears on the second row of
the RadToolBarTray.
The BandIndex property of "Tool Bar Two" is "0" and is placed before "Tool Bar One".
Adding Items
You can add items simply by including Silverlight visual elements within the RadToolBar Items tags:
<telerikNavigation:RadToolBar>
<telerikNavigation:RadToolBar.Items>
<Button />
<telerikInput:RadCalendar />
<TextBlock />
</telerikNavigation:RadToolBar.Items>
</telerikNavigation:RadToolBar>
The Items sub-element is the default and therefore implicit. You can leave out the Items sub-element and
just start adding Silverlight controls inside the RadToolBar element:
<telerikNavigation:RadToolBar>
<Button />
<telerikInput:RadCalendar />
<TextBlock />
</telerikNavigation:RadToolBar>
To add tool bar items in code, add to the Items collection. The example code below adds Silverlight Button,
RadCalendar and TextBlock controls as items to the tool bar.
Without a RadToolBarTray and without any special attributes set to control sizing, the tool bar takes up the
entire available space in the browser.
Orientation
The RadToolBar Orientation property displays the tool bar in a Horizontal (default) or
Vertical layout. The tool bar here is shown with Orientation = Vertical. The
RadToolBarTray also has an Orientation property that takes precedence over the
Orientation property for individual tool bars.
Note: You may need to adjust the horizontal and vertical alignments depending on tool
bar orientation or the container that holds the tool bar.
Overflow
Tool bar items that can't fit in the visible area are automatically relocated to the overflow panel. The
screenshot below shows the overflow panel with two items.
Overflow is managed by setting the OverflowMode attached property to individual items within the tool bar.
The possible values that control how OverflowMode works on a particular item are:
Never: The item will only be placed in the strip panel.
Always: The item will only be placed in the overflow panel.
AsNeeded: The item will be visible in the strip panel if there's available space, otherwise the item will be
located in the overflow panel.
For example, the XAML for the button below determines that it will always be placed in the overflow panel:
</telerikNavigation:RadToolBar>
To set the overflow mode in code, use the static SetOverflowMode() method. Pass a reference to the item
within the tool bar and an OverflowMode enumeration member.
Themes
You can apply predefined themes to the RadToolBar or RadToolBarTray. Themes automatically apply to
certain Silverlight items contained in the tool bar. As of this writing, the controls that are automatically
styled include:
TextBlock
TextBox
Button
CheckBox
RadioButton
ToggleButton
RadToolBarSeparator
Gotcha!
You may encounter a control called "RadSeparator". RadSeparator is technically included in the
above list, but is obsolete and included only for backward compatibility.
Events
As the overflow panel is opened or closed, the OverflowAreaOpened and OverflowAreaClosed routed
events fire.
12.5 Binding
In this walk through you will populate a tool bar from a custom list of objects. In code you will create a
"Tool" object that represents each tool bar item, populate a generic List of Tool objects and assign the list
to the tool bar ItemsSource property. In the XAML markup you will layout all the Silverlight items to be
bound in a RadToolBar ItemTemplate. Along the way you will learn how to make the ItemTemplate markup
into a resource, create a font "shadow" effect and bind a ToolTip for each button.
Project Setup
1) From the Visual Studio menu choose File > New > Project..., select the Silverlight project type, then
select the Silverlight Application template. Provide a unique name for the project and click the OK
button.
2) In the "New Silverlight Application" dialog make sure that the "Host the Silverlight application in a new
Web site" option is checked, give the project a unique name and verify that the "ASP.NET Web
Application Project" New Web Project Type option is selected. Click OK to close the dialog and create
the project.
3) In the Solution Explorer, right-click the References node and select Add References... from the
context menu. Add Assembly references:
a) Telerik.Windows.Controls
b) Telerik.Windows.Controls.Navigation
c) Telerik.Windows.Themes.Vista
4) In the Solution Explorer, add an Image directory. Add the image files below to the Image directory. You
can find these images in the "\courseware\images" directory.
a) 3.5_Disk_Drive.png
b) Folder_Open.png
c) Printer.png
d) RecycleBin.png
This simple "Tool" object will supply our data to the "View", i.e. the tool bar.
XAML Editing
1) Open MainPage.xaml for editing.
2) Drag a RadToolBarTray from the Toolbox to the main "LayoutRoot" Grid element. Add a "telerik:
StyleManager.Theme" attribute to the RadToolBarTray and set its value to "Vista".
3) Drag a RadToolBar from the Toolbox to the RadToolBarTray element. Set the "x:Name" attribute to
"tbMain".
Setting the x:Name attribute will allow us to reference the tool bar later in code.
4) Inside the RadToolBar element, add a RadToolBar ItemTemplate. Inside the RadToolBar ItemTemplate,
add a DataTemplate. The XAML should now look something like the example below.
Each record within the ItemsSource will correspond to a tool bar item. When the ItemsSource property
is assigned data, the ItemTemplate will contain the Silverlight elements that display for each record in
the ItemsSource. Binding expressions in the ItemTemplate will decide where the data for each column
will be placed.
<Grid x:Name="LayoutRoot">
<telerikNavigation:RadToolBarTray
telerik:StyleManager.Theme="Vista">
<telerikNavigation:RadToolBar x:Name="tbMain">
<telerikNavigation:RadToolBar.ItemTemplate>
<DataTemplate>
</DataTemplate>
</telerikNavigation:RadToolBar.ItemTemplate>
</telerikNavigation:RadToolBar>
</telerikNavigation:RadToolBarTray>
</Grid>
5) Add a Button control inside the DataTemplate, set the "x:Name" attribute to "btnItem" and set the
Content attribute to use the binding expression "{Binding Title, Mode=OneTime }".
<Grid x:Name="LayoutRoot">
<telerikNavigation:RadToolBarTray
telerik:StyleManager.Theme="Vista">
<telerikNavigation:RadToolBar x:Name="tbMain">
<telerikNavigation:RadToolBar.ItemTemplate>
<DataTemplate x:Key="ToolBarTemplate">
<Button x:Name="btnItem"
Content="{Binding Title, Mode=OneTime }" />
</DataTemplate>
</telerikNavigation:RadToolBar.ItemTemplate>
</telerikNavigation:RadToolBar>
</telerikNavigation:RadToolBarTray>
</Grid>
<UserControl
...
xmlns:telerikNavigation=
"clr-namespace:Telerik.Windows.Controls;assembly=Telerik.Windows.Controls.Navigation"
xmlns:telerik=
"clr-namespace:Telerik.Windows.Controls;assembly=Telerik.Windows.Controls"
...
Loaded="UserControl_Loaded">. . .
Code Behind
1) Navigate to the code for the UserControl Loaded event handler. Add the code below to create and
populate a generic List of Tool objects.
A tool bar item is created for every Tool object in the list. Inside each item is a Button object with the
Content property bound to the Tool.Title property.
2) Now that you've implemented binding in its simplest form, replace the Button XAML with the new
markup below.
Notes
Notice that the new XAML for the Button adds a number of features:
A ToolTip is added to the Button. The ToolTip is bound to the "Description" property of the
Tool object. The Mode attribute is set to "OneTime": we need to initially load the item from the
data, but we don't need to interact with the data after that.
A Grid with two columns is defined that will layout the elements for the item.
Two TextBlock controls are added to the Grid that are bound to the "Title" property of the Tool
object. The second TextBlock will display the text as a shadow. Styling will be assigned to the
TextBlock later to get the shadow effect.
In Image is added to the second column of the Grid and bound to the "Path" property of the
Tool object.
<Button
ToolTipService.ToolTip="{Binding Description, Mode=OneTime }">
<Grid ShowGridLines="False">
<Grid.ColumnDefinitions>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
</Grid.ColumnDefinitions>
<TextBlock
Text="{Binding Title, Mode=OneTime }"
Grid.Column="0">
</TextBlock>
<TextBlock
Text="{Binding Title, Mode=OneTime }"
Grid.Column="0">
</TextBlock>
<Image
Source="{Binding Path, Mode=OneTime }"
Grid.Column="1" Margin="5"></Image>
</Grid>
</Button>
3) Press F5 to run the application again. The tool bar should look something like the screenshot below.
Verify that the ToolTip shows when you hover the mouse over any of the buttons.
4) Now we can style the text to give a "shadow" effect using the image "reflection" technique explained in
the "Expander" chapter. Add a UserControl.Resources element inside the UserControl element. Add
the two styles shown below.
<UserControl.Resources>
</UserControl.Resources>
Notes
The styles defined above produce a shadow or reflection effect like this small sample below
where the dark text facing straight forward is styled using "ButtonTextStyle" and the "reflection"
is styled using "ButtonTextShadowStyle".
The "ButtonTextStyle" style simply assigns the basic properties for the title TextBlock.
"ButtonTextShadowStyle" uses the "BasedOn" attribute and points back to the
"ButtonTextStyle". The "BasedOn" attribute will let us use all the properties of the first style and
allows us to add or modify additional properties. "ButtonTextShadowStyle" uses the technique
described in the "Expander" chapter to display a "reflection" or "shadow" effect. This style makes
the text color, i.e. "Foreground", a partially transparent gray. The RenderTransformOrigin is
pushed down slightly by setting the Y value to "0.5". A RenderTransform sets ScaleY as "-.5" to
flip and compress the text.
Notice that the Setter for the RenderTransform property uses a TransformGroup. This means
that you can add other transforms inside the group. For example you could add a
SkewTransform to slightly slant the shadow, as if the light source for the shadow was coming
from an angle.
5) Bind the Style of the two TextBlock controls to "ButtonTextShadowStyle" and "ButtonTextStyle"
respectively.
Notice that the first text block is styled to be the text "shadow".
<TextBlock . . .
Style="{StaticResource ButtonTextShadowStyle}">
</TextBlock>
<TextBlock . . .
Style="{StaticResource ButtonTextStyle}" >
</TextBlock>
6) Press F5 to run the application again. Now the text has a slight shadow out in front of it.
7) Now we will "refactor" the XAML and make the entire ItemTemplate into a resource. Locate the
"DataTemplate" element inside the RadToolBar. Cut the entire element and paste it inside the
UserControl.Resources element. Add a "x:Key" attribute to the DataTemplate element and set it to be
"ToolBarTemplate".
<UserControl.Resources>
...
<DataTemplate x:Key="ToolBarTemplate">
<Button
ToolTipService.ToolTip="{Binding Description, Mode=OneTime }">
<Grid ShowGridLines="False">
<Grid.ColumnDefinitions>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
</Grid.ColumnDefinitions>
<TextBlock
Text="{Binding Title, Mode=OneTime }"
Grid.Column="0"
Style="{StaticResource ButtonTextShadowStyle}">
</TextBlock>
<TextBlock
Text="{Binding Title, Mode=OneTime }"
Grid.Column="0"
Style="{StaticResource ButtonTextStyle}">
</TextBlock>
<Image
Source="{Binding Path, Mode=OneTime }"
Grid.Column="1" Margin="5"></Image>
</Grid>
</Button>
</DataTemplate>
</UserControl.Resources>
8) Remove the "ItemTemplate" tags from inside the RadToolBar element. Add a ItemTemplate attribute to
the RadToolBar and assign it the binding expression "{StaticResource ToolBarTemplate". The
RadToolBar should now look like the example below.
<Grid x:Name="LayoutRoot">
<telerikNavigation:RadToolBarTray
telerik:StyleManager.Theme="Vista">
<telerikNavigation:RadToolBar
x:Name="tbMain"
ItemTemplate="{StaticResource ToolBarTemplate}" />
</telerikNavigation:RadToolBarTray>
</Grid>
9) Press F5 to run the application and verify that resourcing the ItemTemplate hasn't changed the
functionality.
12.6 Customization
Walk Through
In this example we will customize the RadToolBar control to have a "Scoville" style. We will also customize
the RadToolBarSeparator to show a gradient block of color.
"Scoville" Styles
We will use a set of colors that include black, red, yellow and orange in many of the style related
topics and prefix the style names with "Scoville". The "Scoville scale" measures the spiciness of
peppers and other culinary irritants.
Project Setup
1) Run Expression Blend.
2) From the File menu select New Project. Note: If you have an existing solution open, right-click the
solution and select Add New Project... from the context menu instead.
3) In the New Project dialog, select "Silverlight" from "Project types" and Silverlight 3 Application from the
right-most list. Enter a unique name for the project and click OK.
4) MainPage.xaml should already be open for editing. If not, locate MainPage.xaml in the Projects pane
and double-click to open the project.
5) In the Projects pane, right-click the References node and select Add Reference... from the context
menu. Add a references to the Telerik.Windows.Controls and Telerik.Windows.Controls.
Navigation assemblies.
6) From the Project menu select Build Project.
7) Open the Assets pane. On the left side of the Assets pane is a tree view. Locate and select the
"Controls" node. In the Assets pane, just above the tree view is the Assets Find entry text box. Type
the first few characters of RadToolBarTray into the Assets Find entry text box. A list of all matching
controls will show to the right of the tree view. Locate the RadToolBarTray control and drag it onto the
MainPage.xaml Artboard.
8) Drag a RadToolBar control from the Assets pane to the Objects and Timeline pane, just under the
RadToolBarTray. The tool tip should read "Create in [RadToolBarTray]".
9) Drag three RadioButton controls from the Assets pane to the Objects and Timeline pane, just under
the RadToolBar. The RadioButton controls should be children of the RadToolBar, i.e. the tool tip should
read "Create in [RadToolBar]".
10)Drag a RadToolBarSeparator control from the Assets pane to the Objects and Timeline pane, just
under the three RadioButton controls, as shown in the screenshot below. The RadToolBarSeparator
should also be a child of the RadToolBar and a sibling of the three RadioButton controls.
11)Drag a CheckBox control from the Assets pane to the Objects and Timeline pane, just under the
RadToolBarSeparator. The CheckBox should be a sibling of the RadioButton and RadToolBarSeparator
controls.
12)Find the Split button in the upper right-hand corner of the Artboard and click it to view both the Design
and XAML at the same time.
13)If Expression Blend has automatically included any dimensions for any of the controls, remove those
attributes manually. The XAML markup should look like the example below:
14)Click each of the RadioButton controls in the Objects and Timeline pane. In the Properties pane set
Common Properties > Content to "Mild", "Hot", "Very Hot", respectively.
15)Click the CheckBox control in the Objects and Timeline pane. In the Properties pane set Common
Properties > Content to "Ice Water?".
16)In the design view of the Artboard, the tool bar should look like the screenshot below.
3) In the Properties pane, locate the Brushes > Background property. Click the Advanced Property
Options button and select Convert to New Resource... from the drop down menu.
4) Enter the new name "ScovilleToolBar_InnerBackground". Click OK to close the dialog and create the
brush resource.
5) Locate the Properties pane, Brushes > Background property. Click the Advanced Property Options
button and select "ScovilleToolBar_InnerBackground" from the Local Resource item of the drop down
menu.
6) In the Properties pane, select the Gradient Brush to use for the background.
7) Click the left-most gradient stop indicator, then drag the eye dropper tool to a red color. Click the right-
most gradient stop indicator, then drag the eye dropper tool to a black color. The gradient should look
like the red to black gradient shown in the screenshot below.
8) Click the Gradient Bar somewhere in the middle to create a new gradient stop. Set the new gradient
stop indicator to an orange color.
9) Click the Return Scope button until you return to editing the tool bar.
10)The tool bar should look something like the screenshot below when viewed in the Artboard:
11)In the Objects and Timeline pane, right-click the RadToolBarSeparator and select Edit Template >
Edit a Copy from the context menu. In the "Create Style Resource" dialog, set the Name (Key) to
"ScovilleToolBarSeparatorControlTemplate". Click OK to create the style resource and close the dialog.
The new template for the separator appears in the screenshot below. The template contains a grid with
two rectangles, one colored black , the other white, to create a contrasting edge.
12)Select the "[Grid]" item from the Objects and Timeline pane.
13)In the Properties pane, set the Layout > MinWidth property to "5".
14)Select the second "[Rectangle]" item under the Grid from the Objects and Timeline pane.
15)In the Properties pane, locate the Brushes > Fill property. Click the Advanced Property Options button
and click the Reset option from the drop down menu.
16)Select the Gradient Brush to use for the new fill. Use the two gradient stop indicators and the eye
dropper tool to create a orange-to-yellow gradient as shown in the screenshot below.
17)Click the Return Scope button until you return to editing the tool bar.
18)The tool bar should look something like the screenshot below when viewed in the Artboard:
Try styling the other parts of the tool bar including the "Grip Ornament", the overflow area and the drop
down button.
Add animation to react to the tool bar's state. See the "Menu Controls" chapter, Customization section
for an example of how this can be done.
Style the tool bar for a consistent look in both horizontal and vertical orientations.
12.7 Wrap Up
In this chapter you learned how to organize multiple Silverlight controls into a horizontal or vertical strip
using RadToolBar. You used RadToolBarSeparator to visually divide groups of controls. You saw how the
theming mechanism for RadToolBar automatically styles both the tool bar and its items. You also saw how
the RadToolBar OverflowMode property handles overflow behavior when there are more controls than can be
displayed at one time. You handled events that reacted to the overflow area expanding and collapsing. You
added items to the tool bar in XAML, using the programmatic API and through data binding.
You used the RadToolBarTray to manage RadToolBar position, sizing and order. You used the Band
property to place each tool bar in a specific row and the BandIndex property to control each tool bar's
position within a band.
Finally, you created custom templates for the RadToolBar background and RadToolBarSeparator. You
created matching color schemes for both custom templates.
XIII
Expander
416 RadControls for Silverlight
13 Expander
13.1 Objectives
This chapter demonstrates using RadExpander to save space and to present Silverlight content as needed.
You will learn how to place simple text material in the Header and Content. You will see which properties
control expansion direction and animation. Then you will use the Header and Content sub-elements to
present multiple Silverlight items of any complexity and arrangement. Along the way you will use control
templates to display images, image reflections and custom buttons. Finally, you will use Expression Blend
to replace the standard "arrow" graphic with an image that rotates when clicked.
13.2 Overview
RadExpander is a flexible, lightweight control that saves page real estate and aids site navigation. You can
place the expander anywhere on the page and fill it with any content. The expander lets you control the
expand animation and direction of expansion. Your end user can press the Space key to toggle the
expansion without having to use the mouse.
Project Setup
1) From the Visual Studio menu choose File > New > Project..., select the Silverlight project type, then
select the Silverlight Application template. Provide a unique name for the project and click the OK
button.
2) In the "New Silverlight Application" dialog make sure that the "Host the Silverlight application in a new
Web site" option is checked, give the project a unique name and verify that the "ASP.NET Web
Application Project" New Web Project Type option is selected. Click OK to close the dialog and create
the project.
3) In the Solution Explorer, right-click the References node and select Add References... from the
context menu. Add Assembly references:
a) Telerik.Windows.Controls
b) Telerik.Windows.Controls.Input
c) Telerik.Windows.Themes.Vista
XAML Editing
1) Open MainPage.xaml for editing.
2) Add an XML namespace reference to Telerik.Windows.Controls to support the StyleManager used in
later steps:
<UserControl
xmlns:telerik="clr-namespace:Telerik.Windows.Controls;assembly=Telerik.Windows.Controls" . . .>
<UserControl.Resources>
<Style x:Key="CalendarStyle"
TargetType="telerikInput:RadCalendar">
<Setter Property="telerik:StyleManager.Theme" Value="Vista" />
<Setter Property="MaxWidth" Value="250" />
<Setter Property="HorizontalAlignment" Value="Left" />
<Setter Property="Margin" Value="10" />
</Style>
</UserControl.Resources>
4) Drag a RadExpander control from the Toolbox to a point between the <Grid> and </Grid> tags.
5) Set the RadExpander Header property to "Set Backup Dates" and the ExpandDirection to "Right".
6) Inside the RadExpander element, add a RadExpander.Content tag. The XAML should look the the
example below at this point.
<Grid x:Name="LayoutRoot">
<telerik:RadExpander ExpandDirection="Down" Header="Set Backup Dates">
<telerik:RadExpander.Content>
</telerik:RadExpander.Content>
</telerik:RadExpander>
</Grid>
The XAML inside the UserControl element should look something like the example below:
<UserControl.Resources>
<Style x:Key="CalendarStyle"
TargetType="telerikInput:RadCalendar">
<Setter Property="telerik:StyleManager.Theme" Value="Vista" />
<Setter Property="MaxWidth" Value="250" />
<Setter Property="HorizontalAlignment" Value="Left" />
<Setter Property="Margin" Value="10" />
</Style>
</UserControl.Resources>
<Grid
x:Name="LayoutRoot">
<telerik:RadExpander
ExpandDirection="Down"
Header="Set Backup Dates"
telerik:StyleManager.Theme="Vista">
<telerik:RadExpander.Content>
<StackPanel>
<TextBlock Text="Start Date:" Margin="10" />
<telerikInput:RadCalendar
Style="{StaticResource CalendarStyle}" />
<TextBlock Text="End Date:" Margin="10" />
<telerikInput:RadCalendar
Style="{StaticResource CalendarStyle}" />
</StackPanel>
</telerik:RadExpander.Content>
</telerik:RadExpander>
</Grid>
Press F5 to run the application. The web page should look something like the screenshot below.
In its simplest form, RadExpander can be assigned Header and Content text. The ExpandDirection
property by default is "Down", but can also be "Left", "Right" or "Up". You can get a lot of styling mileage
simply by defining one of the predefined themes. The example below adds text to the Header and Content.
The ExpandDirection is set to "Left", IsExpanded is true and Theme is "Summer".
<telerik:RadExpander
Header="Expander Header Text"
Content="Content text"
ExpandDirection="Left"
IsExpanded="True"
telerik:StyleManager.Theme="Summer" >
</telerik:RadExpander>
Running in the browser, the RadExpander looks like the screenshot below.
To create more complex arrangements of Silverlight controls in the header or content, Header and Content
sub-elements will let you pack as much XAML markup as you care to have. For example, the screenshot
below shows an expander where the header contains a TextBlock and Image. The content area to the right
of the header contains a RadWrapPanel with a series of TextBlock and Image controls. When one of the
images is clicked, a larger version of the clicked image and the image reflection appears to the right of the
RadExpander.
First lets look at the general layout of the page before breaking each part down for a closer look. The
abbreviated XAML below shows styles defined that will be applied to Button, Image and ContentControl. The
main "LayoutRoot" contains a StackPanel with Horizontal orientation. Inside the StackPanel is the
RadExpander that will contain the thumbnail size image controls and the ContentControl will contain the full
size image.
<UserControl . . .">
<UserControl.Resources>
<Style x:Key="ImageButtonStyle" TargetType="Button">. . .</Style>
<Style x:Key="ImageReflectionStyle" TargetType="Image">. . .</Style>
<Style x:Key="ReflectedImagePanel" TargetType="ContentControl">. . .</Style>
</UserControl.Resources>
<Grid x:Name="LayoutRoot">
<StackPanel Orientation="Horizontal">
<telerik:RadExpander>. . .</telerik:RadExpander>
<ContentControl>. . .</ContentControl>
</StackPanel>
</Grid>
</UserControl>
Notes
Notice the order that the styles are declared in. "ImageReflectionStyle" must be declared first as it
is referenced in "ReflectedImagePanel".
First, let's take a look at the layout of the page, then we will talk about the styles used in this page. In the
XAML below, the RadExpander has two main parts, Header and Content. The RadExpander.Header
contains a StackPanel that in turn holds a TextBlock and an Image. The RadExpander.Content uses a
RadWrapPanel as a container. In the RadWrapPanel are four buttons that hide much of their complexity in
a ControlTemplate stored as a resource called "ImageButtonStyle". At the bottom of this XAML is the
ContentControl that uses a custom style "ReflectedImagePanel" to get an image "reflection" effect.
<Grid x:Name="LayoutRoot">
<StackPanel Orientation="Horizontal">
<telerik:RadExpander x:Name="exMain" ExpandDirection="Right"
telerik:StyleManager.Theme="Summer">
<telerik:RadExpander.Header>
<StackPanel>
<TextBlock>My Pictures</TextBlock>
<Image Source="Images/Camera.png" />
</StackPanel>
</telerik:RadExpander.Header>
<telerik:RadExpander.Content>
<telerik:RadWrapPanel MaxWidth="400">
<Button
Style="{StaticResource ImageButtonStyle}"
Content="images/Blue Hills.jpg" Click="Button_Click" />
<Button
Style="{StaticResource ImageButtonStyle}"
Content="images/Sunset.jpg" Click="Button_Click" />
<Button
Style="{StaticResource ImageButtonStyle}"
Content="images/Water lilies.jpg" Click="Button_Click" />
<Button
Style="{StaticResource ImageButtonStyle}"
Content="images/Winter.jpg" Click="Button_Click" />
</telerik:RadWrapPanel>
</telerik:RadExpander.Content>
</telerik:RadExpander>
</StackPanel>
</Grid>
The Style used on the Button controls is called "ImageButtonStyle". It sets up a ControlTemplate to
completely replace the contents of a Button. Inside the template is a StackPanel. The StackPanel contains
a ContentPresenter that displays an image path and an Image control that displays the image pointed to by
the path. Each button has the same "Click" event handler assigned.
The XAML used for the reflected image is a little more complicated and requires two styles. One style
creates an image "reflection" and the second is a ControlTemplate used to assemble the original image and
the reflected image together.
First lets see how you can create the reflection effect using a style. The first two properties in
"ImageReflectionStyle" are the RenderTransformOrigin property that determines the center point of any
transformation operation and a RenderTransform. Transformations can scale objects to be larger or
smaller, move objects to other locations, rotate objects or skew them. In this case we are changing the
scale along the "Y" axis to a negative number. This has the effect of flipping the image. If you supply a
negative one "-1" you simply flip the image. Negative fractions flip and compress the image at the same
time. A ScaleY value of "-.5" is flipped and compressed to half its original size. Finally the OpacityMask
property is configured to paint the image so that it fades.
The image and the reflected version of the image are assembled into a single ContentControl by way of the
"ReflectedImagePanel" style below. The style places both Images into a grid and applies the
"ImageReflectionStyle" to the second image.
<Style x:Key="ReflectedImagePanel"
TargetType="ContentControl">
<Setter
Property="Template">
<Setter.Value>
<ControlTemplate
TargetType="ContentControl">
<Grid VerticalAlignment="Top">
<Image
Margin="0, 0, 0, 1"
Source="{TemplateBinding Content}"
MaxHeight="200" />
<Image
Source="{TemplateBinding Content}"
MaxHeight="200"
Grid.Row="1"
Style="{StaticResource ImageReflectionStyle}" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Finally, the Click event handler for each button simply assigns the image path to the content of the
ContentControl.
13.4.2 Events
RadExpander has two events you can handle: Expanded and Collapsed. As with most of the Telerik
Silverlight controls, these are routed events and so pass the RoutedEventArgs parameter. You can cast
this parameter to a RadRoutedEventArgs type (from the Telerik.Windows namespace) to get additional
information. The example below changes the header as the control is expanded and collapsed.
Running in the browser, the RadExpander looks like the screenshot below.
13.4.3 Animation
<UserControl
...
xmlns:telerikAnimation=
"clr-namespace:Telerik.Windows.Controls.Animation;assembly=Telerik.Windows.Controls"
...
Then in your XAML markup you can toggle the attached property AnimationManager.
IsAnimationEnabled. . .
<telerik:RadExpander
...
telerikAnimation:AnimationManager.IsAnimationEnabled="True"
>
</telerik:RadExpander>
. . .or in code you can call the AnimationManager.SetIsAnimationEnabled(), passing the RadExpander
instance and a Boolean to turn the animation on or off. You must first add a reference to the Telerik.
Windows.Controls.Animation name space in your "Imports" (VB) or "using" (C#) section of code.
13.5 Customization
Walk Through
In this example we will customize the RadExpander control by swapping out the standard "arrow" button
with an image of a pepper. The pepper image will be animated to swivel the pepper stem up and down as
the expander is expanded and collapsed.
Project Setup
1) Run Expression Blend.
2) From the File menu select New Project. Note: If you have an existing solution open, right-click the
solution and select Add New Project... from the context menu instead.
3) In the New Project dialog, select "Silverlight" from "Project types" and Silverlight 3 Application from the
right-most list. Enter a unique name for the project and click OK.
4) MainPage.xaml should already be open for editing. If not, locate MainPage.xaml in the Projects pane
and double-click to open the page.
5) In the Projects pane, right-click the References node and select Add Reference... from the context
menu.
6) Add a reference to the Telerik.Windows.Controls.dll assembly.
Tip!
If the Assets pane is not visible, select the main menu Windows item, locate "Assets" and click to
re-enable it.
9) In the Objects and Timeline pane, double-click "[RadExpander]" in the tree view. Enter a new name
"expPeppers".
10)Right-click "expPeppers" and select Edit Template > Edit a Copy from the context menu. In the
"Create Style Resource" dialog, set the Name (Key) to "ScovilleExpanderStyle". Click OK to create the
style resource and close the dialog.
11)Right-click "HeaderButton" in the Objects and Timeline pane and select Edit Template > Edit a
Copy.... In the "Create Style Resource" dialog, set the Name (Key) to "ScovilleHeaderButton". Click
OK to create the style resource and close the dialog.
Notice that we're actually editing the "ExpanderDownTemplate" of the RadExpander. By default, the
RadExpander ExpandDirection property is set to "Down", and so Expression Blend edits the template
that's currently "in play".
12)If you open all the nodes of the object tree, the Objects and Timeline pane should look something like
the screenshot below:
13)In the Objects and Timeline pane, select the "arrow" item in the object tree. In the Properties pane, set
the Appearance > Visibility property to "Collapsed".
14)In the Assets pane, type the first few characters of "Image" into the Assets Find entry text box. A list
of all matching controls will show to the right of the tree view. Locate the Image control and drag it to a
point just below the "arrow" node in the Objects and Timeline pane.
15)Double-click "[Image]" and rename it to "peppers". In the Properties pane, locate the Common
Properties > Source property. Click the ellipses and select "pepper.png" located in the
"\courseware\images" directory. Change the Layout > Width property to "120" and the Layout >
Height property to "120".
16)In the Objects and Timeline pane, select "circle". In the Properties pane, set Layout > Width to "128"
and Layout > Height to "128". Set the Layout > HorizontalAlignment property to "Left".
17)In the Objects and Timeline pane, select the Grid located just above the "circle", "arrow" and "peppers"
items.
18)In the Artboard, locate the "lock" icon above the first column of the grid and click the icon. This step
will toggle the column width definition through its various states, i.e. "Pixel Sized", "Auto Sized" and
"Star Sized". Leave the first column at the "Auto sized" setting.
19)Locate the project in the Projects pane, right-click the project and select "Startup Project" from the
context menu. Press F5 to run the application in its present state.
20)Open the States pane. Select the "peppers" image in the Objects and Timeline pane. The following
steps will animate the pepper image as the state toggles between "Check ed" and "Uncheck ed".
21)In the States pane, select the "Checked" state. Notice the indicator in the upper left corner of the
Artboard, that "Check ed state recording is on". This means that property changes you mak e now will
be recorded.
22)In the Objects and Timeline pane, click the "Show Timeline" button.
23)In the Timeline, drag the Timeline marker to the half second mark.
24)Make sure that the "peppers" item is still selected in the Objects and Timeline pane. Navigate to the
Properties pane and set Transform > Rotate > Angle property to "180":
Note: The property will already be "0". Go ahead and re-enter the value anyway. This will add a new "k ey
frame" item to the Timeline representing the Angle value at the half second mark :
28) In the Objects and Timeline pane, select the "Return scope" button. Now that you've finished
spelunk ing into the guts of the control, you can return to editing the RadExpander as a whole.
29)In the Assets pane, type the first few characters of "TextBlock" into the Assets Find entry text box. A
list of all matching controls will show to the right of the tree view. Locate the TextBlock control and drag
it to a point just below the "expPeppers" node in the Objects and Timeline pane.
Notice the light blue insert mark er just before you drop the TextBlock control into place below the
"expPeppers". Be aware of where the mark er sits in relation to the other controls in the object tree. The
mark er can be a peer of "expPeppers", i.e. the hint reads "Create in LayoutRoot", or can be a child of
"expPeppers", i.e. "Create in expPeppers". The TextBlock should be the content for the RadExpander
and so should be a child of "expPeppers".
30)In the Properties pane, locate the Common Properties > Text property and paste the text below:
The "Scoville Scale" was created by Wilbur Scoville in 1912 under the name "Scoville Organoleptic
Test" to measure the hotness of peppers.
13.6 Wrap Up
In this chapter you saw how RadExpander can save space and present Silverlight content as needed. You
learned how to place simple text material in the Header and Content. You saw which properties control
expansion direction and animation. You then used Header and Content sub-elements to present multiple
Silverlight items of arbitrary complexity and arrangement. Along the way you used control templates to
display images, image reflections and custom buttons. Finally, you used Expression Blend to replace the
standard "arrow" graphic with an image that rotates when clicked.
XIV
Drag and Drop.
444 RadControls for Silverlight
14.1 Objectives
In this chapter you will learn how RadDragAndDropManager is used to allow intuitive drag-and-drop
operations between any two Silverlight controls or elements. You will learn the basic property settings that
allow drag from a source element and drop to a destination element as well as the event handling required
to complete the operation. While discussing the drag-and-drop events you will see how
RadDragAndDropManager properties, particularly the DragStatus property, is used to pinpoint the exact
state of the operation at the time the event is called. You will learn how to allow or refuse to continue during
event processing. You will also learn how to assign visual cues to notify the user of the progress of the
operation. Finally, you will see how visual cue templates allow binding and customization.
\Courseware\Projects\<CS|VB>\DragAndDrop\DragAndDrop.sln
14.2 Overview
Although RadDragAndDropManager is already built into the tree view and tab control,
RadDragAndDropManager makes drag and drop possible between any Silverlight control or element.
The DragAndDrop framework events provide complete control over every stage of the drag-and-drop
operation. The drag-and-drop events are all routed and can be handled anywhere in the visual tree. Not only
can you allow or prevent drag-and-drop at several points during the operation, you can display visual cues in
the source, destination, the dragged object and an optional "Arrow" cue that stretches between the source
and destination.
ScrollViewers automatically reveal hidden content when the destination of a drag-and-drop is not visible.
Set the RadDragAndDropManager.AllowDrag attached property to True for any "Source" elements that
you want to drag.
Set the RadDragAndDropManager.AllowDrop attached property to True for any "Destination" elements
that should receive the dragged elements.
Handle DragQueryEvent, DropQueryEvent and DropInfoEvent events.
Project Setup
1) From the Visual Studio menu choose File > New > Project..., select the Silverlight project type, then
select the Silverlight Application template. Provide a unique name for the project and click the OK
button.
2) In the "New Silverlight Application" dialog make sure that the "Host the Silverlight application in a new
Web site" option is checked, give the project a unique name and verify that the "ASP.NET Web
Application Project" New Web Project Type option is selected. Click OK to close the dialog and create
the project.
3) In the Solution Explorer, right-click the References node and select Add References... from the
context menu. Add Assembly references:
a) Telerik.Windows.Controls
XAML Editing
1) Open MainPage.xaml for editing.
2) In the UserControl element, add a reference to the Telerik.Windows.Controls.DragDrop namespace
in the Telerik.Windows.Controls assembly. Also add a handler for the Loaded event.
<UserControl . . .
xmlns:dragdrop=
"clr-namespace:Telerik.Windows.Controls.DragDrop;assembly=Telerik.Windows.Controls"
Loaded="UserControl_Loaded">
3) Inside the main "LayoutRoot" Grid element, add a StackPanel. Notice the comments that indicate
where we will place elements to drag and a destination element that will be the target of the drag
operation.
<Grid x:Name="LayoutRoot">
<StackPanel>
<!--Destination element-->
</StackPanel>
</Grid>
4) Under the comment "<!--Source elements to drag-->", add an Ellipse element with the following
property settings:
a) Width="50"
b) Height="50"
c) HorizontalAlignment="Center"
d) Margin="5"
e) Fill="Blue"
f) dragdrop:RadDragAndDropManager.AllowDrag="True"
5) Add two more Ellipse elements with the same settings. Change the Fill properties for the two ellipses
to be "Red" and "Green", respectively.
6) Under the comment "<!--Destination element-->", add a StackPanel element with the following property
settings:
a) MinHeight="200"
b) Margin="5"
c) Background="Silver"
d) dragdrop:RadDragAndDropManager.AllowDrop="True"
<Grid x:Name="LayoutRoot">
<StackPanel>
<!--Destination element-->
<StackPanel MinHeight="200" Margin="5" Background="Silver"
dragdrop:RadDragAndDropManager.AllowDrop="True"></StackPanel>
</StackPanel>
</Grid>
Code Behind
1) In the code-behind for the page, add references to the "Imports" (VB) or "using" (C#) section of the code
for these name spaces:
a) Telerik.Windows.Controls.DragDrop
2) In the Loaded event handler, call RadDragAndDropManager methods to add handlers for routed events.
The actual handlers will be added in later steps.
3) In the OnDragQuery event handler, set the event arguments QueryResult to "True". Also set the
Options.DragCue to the string "Dragging...".
Setting QueryResult ="True" allows the drag operation to continue. Options.DragCue is the object
that displays visually under the mouse as it moves during the drag operation.
4) In the OnDropQuery event handler, set the event arguments QueryResult = "True" and Handled =
"True". Setting QueryResult allows the drop operation to continue. The Handled property is specific to
routed events and indicates that the event should not propagate any further.
5) In the OnDropInfo event handler, complete the drop operation by removing the dragged ellipse from its
current parent and adding it to the drop Destination StackPanel.
a) Check that the event arguments Options.Status is in a DragStatus.DropComplete state. Note:
DropInfoEvent fires multiple times in varying statuses such as DragStatus.DropPossible.
b) Get a reference to the Ellipse being dragged, get its Parent and remove the Ellipse from the Parent's
Children collection.
c) Get a reference to the destination StackPanel. Add the Ellipse to the StackPanel Children collection.
d) Mark the routed event as Handled.
Change the DragCue to some other representation, such as a faded copy of the source ellipse:
RadDragAndDropManager makes drag and drop possible between any Silverlight control or element (not
just RadControls). RadDragAndDropManager is a static class and not a control and so is not available in
the Toolbox. Instead, you add an XML namespace to Telerik.Windows.Controls.DragDrop in your XAML to
reference RadDragAndDropManager as shown in the example below:
<UserControl . . .
xmlns:dragdrop=
"clr-namespace:Telerik.Windows.Controls.DragDrop;assembly=Telerik.Windows.Controls"
>
To make an control "draggable", set the AllowDrag property to "True". AllowDrag is an attached property
and can be set on an existing Silverlight control:
<Ellipse dragdrop:RadDragAndDropManager.AllowDrag="True"/>
RadDragAndDropManager.SetAllowDrag(BlueEllipse, True)
RadDragAndDropManager.SetAllowDrag(BlueEllipse, true);
Notes
Setting the attached property in code is recommended only when the visual objects are created
in code and a style is not / cannot be applied.
Another strategy that works when you have access to a style for the items, lets you avoid having to code for
each item by setting a property that propagates to all items:
<ListBox x:Name="listBox">
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter
Property="dragDrop:RadDragAndDropManager.AllowDrag"
Value="True" />
</Style>
</ListBox.ItemContainerStyle>
...
</ListBox>
The mirror of the AllowDrag property is the AllowDrop attached property. You can add AllowDrop to any
Silverlight control:
<TextBlock
dragdrop:RadDragAndDropManager.AllowDrop="True"></TextBlock>
To set the same property in code, use the SetAllowDrop() method. Like SetAllowDrag(), SetAllowDrop()
shines when a number of elements that are unknown at design time need to be configured to allow drop.
The example below allows "Time slots" in a RadScheduler to allow drop:
14.4.4 RadDragAndDropManager
Flag the controls that can be dragged and that can accept a drag.
Hook up drag-and-drop events.
Maintain information on all aspects of the Drag-and-Drop operation using the DragDropOptions
property.
Automatically scroll content into view using the AutoBringIntoView property.
Prevent unintended dragging on mouse-down by setting the DragStartThreshold property. This is the
distance in pixels that the user needs to drag an object before a real drag operation starts.
Generate default visual cues.
DragDropOptions
Most aspects of the drag-and-drop operation are tracked through the RadDragAndDropManager Options
property. Some of the key Options sub-properties are:
The Source and Destination FrameworkElement objects represent the drag source and target.
While the drag operation is underway, you can supply feedback to the user with several visual cues:
ArrowCue and DragCue. See the upcoming section "Visual Cues" for detail on how to use these
properties.
The Payload property lets you tuck away arbitrary object data.
The Status property provides fine-grain understanding of the drag progress during drag-and-drop events.
See the upcoming section "Events" for a detailed listing of these events.
When you have popups involved with a drag-and-drop operation, be sure to add them to the
ParticipatingVisualRoots collection. This collection contains visual roots what will participate in the
drag/drop operation but are not descendants of the application root visual.
Question: "Drag & Drop works except inside a Popup. I tried to use a Popup, unfortunately, all
the drop events are not launched, we can see the visual cue, but cannot deliver 'the drop'." How
can I get the drag and drop properties to work?
Answer: "'Windows' in Silverlight come up in popups and by default popups are not part of the
main visual tree. Therefore, RadDragAndDrop does not know about them. There is a property on
the DragDrop option, called ParticipatingVisualRoots. You can add the currently opened child
windows to ParticipatingVisualRoots and they will participate in the DragDrop. You can keep a
static collection with all the opened windows and add it during DragInfo. You can do this
transparently by adding a DragInfo handler to the root visual of the application that will do this
for every successful drag operation."
There is no need to do this for the RadWindow or popups that Telerik controls create. More
typically you may need to do this for the ChildWindow control.
14.4.5 Events.
RadDragAndDropManager generates four events: DragQuery, DragInfo, DropQuery and DropInfo. The
"Query" events fire just before some action is taken, allowing you to change settings or cancel the event
altogether. The "Info" events notify you that some action has occurred. The diagram below shows the
general order that these events occur in. These are all "routed" events, i.e. they can be handled by any of
the elements in the visual or logical tree.
DragQuery lets you allow or cancel the drag and also lets you specify visual cues that let the user
know what's happening.
DragInfo fires when the drag is already under way.
DropQuery lets you allow or cancel dropping onto some other element. The event handler supplies an
"options" parameter with complete information on what is being dragged, where its being dropped, the
status of the operation and all the other data about the drag-and-drop.
DropInfo occurs when the drop is complete. The RadDragAndDropManager infrastructure displays all
the trappings of a drag-and-drop, but doesn't actually change the underlying state of elements in the
page. The DropInfo handler is your opportunity to actually move or copy objects to their new locations in
response to the drop.
RadDragAndDropManager.AddDropQueryHandler(this, OnDropQuery);
}
Event Status
The four events actually fire multiple times as they track progress the operation from the perspective of both
source and destination. Each event handler contains a key property, DragDropOptions, that holds a
Status sub-property. Status lets you know the nature of each event call:
DragQuery A drag is about to take place for an element where AllowDrag = "True". To allow
the drag to proceed, set event handler QueryResult argument to "True".
DragComplete The drag is complete. Source objects may be removed at this point.
DropDestination The destination is asked if the element can be dropped from this particular
Query source.
DropSourceQue The source is asked if the element can be dropped on this particular destination
ry
DropPossible The drop is acknowledged from both parties. You can use this status to signal
visually to the user that a drop is possible.
DropComplete The drop is complete. Copying or moving elements takes place here.
Gotcha!
Question: I'm handling the DragQuery event and have set QueryResult = "True" when the status
is "DragQuery". But the drag does not take place. What is happening?
Answer: There are two checks that must both pass. QueryResult must be set true when the
status is DragQuery and DropSourceQuery. DragQuery occurs at the very beginning of the event
sequence and "Destination" is null at this point. When the status is DropSourceQuery, the
operation can be canceled based on the destination object.
Responding to Events
Now that you know the events and the status, you will want to make your visual layout respond to the drag
operation. What happens here is up to your business logic. For example, you may have a "Tool Box"
scenario where an item is copied onto an element on the page, or you may be developing a "Visio"-like
diagram where node elements are moved around. In this second instance you have to remove an element
from one parent element on the page and add it to another. Use the OnDragInfo event to remove an element
from one location and the OnDropInfo is always used (for either copy or move type operations) to create the
element in its new location. The example below removes an ellipse from its parent, then recreates the
ellipse inside a StackPanel where it was dropped.
End Sub
if (e.Options.Status == DragStatus.DragComplete)
{
// get a reference to the dragged ellipse
Ellipse ellipse = e.Options.Source as Ellipse;
// remove the ellipse from its parent so it can b e added elsewhere
(ellipse.Parent as Panel).Children.Remove(ellipse);
}
}
Display a visual cue under the mouse when dragging and an "Arrow" cue between the source and the
mouse by assigning the options DragCue and ArrowCue properties. The screenshot below shows the
default appearance when a drag is underway.
You can use these defaults by calling the GenerateVisualCue() and GenerateArrowCue() methods. You
may pass an optional Framework element to GenerateVisualCue() and the properties of that element will be
used as a basis for the visual cue.
e.QueryResult = True
e.Options.DragCue = _
RadDragAndDropManager.GenerateVisualCue(TryCast(e.Options.Source, FrameworkElement))
e.Options.ArrowCue = RadDragAndDropManager.GenerateArrowCue()
e.Handled = True
End Sub
e.QueryResult = true;
e.Options.DragCue =
RadDragAndDropManager.GenerateVisualCue(
e.Options.Source as FrameworkElement);
e.Options.ArrowCue = RadDragAndDropManager.GenerateArrowCue();
e.Handled = true;
}
You can use visual elements of the source or destination to help the user decide what to do. The sample
below uses the OnDragInfo when the status is DropPossible to highlight the background when it's ok to drop
the dragged item.
You're not stuck with the built-in visual cues. All cue objects are simply objects, so you can assign a string
or a visual element. The screenshot below shows the DragCue as a "check" image.
Question: "I would like to show an array of objects with a specific foreground and background."
Answer: "You can put anything as the DragCue during DragDrop. What you can try is add a
ContentControl. Then as Content you assign the collection of items being dragged. Then you give it a
ContentTemplate which has an items control with a template for your items. The ItemTemplate of this
inner items control can contain borders, TextBlocks, and other visual items which you can modify. If
you assign a collection of the dragged items to the content of the DragCue then the inner items
control can have its ItemsSource equal to {Binding}, i.e. bound directly to the items.
If do not like all the binding, you can create an ItemsControl in code and set its items source and item
template properties. Alternatively you can build the whole visual object in code, starting from a panel
and adding TextBlocks (or other visuals) as needed. Then you assign the panel as DragCue. In all
these cases you can modify the brushes or fonts of the displayed items."
You can directly assign some object to Content, say a scaled down ellipse to indicate that we're dragging
an ellipse, or you could assign a TextBlock that describes the action. There's no limit as to what you could
assign here. The effective limit is complexity. Let's say you want the cue to show a description, an icon and
an image. In this situation you should use the ContentControl ContentTemplate property and assign a
DataTemplate to it. Now you can build templates of great complexity by hand or using Expression Blend
and they can be used as your cue. The example from the screenshot above actually uses a simple
template that contains an image:
<UserControl.Resources>
<DataTemplate x:Key="DragTemplate">
<Image Source="images/check.png" Stretch="None"
VerticalAlignment="Top" />
</DataTemplate>
</UserControl.Resources>
In code, create a ContentControl instance and assign the template from the Resources collection and cast
it to be a DataTemplate type. Finally, assign the ContentControl to the cue.
e.Handled = True
End Sub
e.Handled = true;
}
14.5 Binding
You can bind data to visual cues data templates for greater flexibility. The screenshot below shows a
slightly more complex template that demonstrates binding.
The code assigns a simple "MyDragInfo" object to the Content property. MyDragInfo has "ImagePath" and
"Comment" properties that are then bound in the ContentTemplate.
e.QueryResult = True
e.Handled = True
End Sub
e.QueryResult = true;
e.Handled = true;
}
The XAML for the template is slightly more involved and contains the binding expressions for the ImagePath
and Comment.
<UserControl.Resources>
<RadialGradientBrush x:Key="DragBackgroundBrush">
<GradientStop Offset="0" Color="Transparent" />
<GradientStop Offset="0.9" Color="White" />
<GradientStop Offset="1" Color="SkyBlue" />
</RadialGradientBrush>
<DataTemplate x:Key="DragTemplate">
<Grid>
<Ellipse MinWidth="50" MinHeight="50"
Fill="{StaticResource DragBackgroundBrush}">
</Ellipse>
<StackPanel>
<TextBlock Text="{Binding Comment}"
HorizontalAlignment="Center"></TextBlock>
<Image Source="{Binding ImagePath}"
Stretch="None" />
</StackPanel>
</Grid>
</DataTemplate>
</UserControl.Resources>
14.6 Wrap Up
In this chapter you learned how RadDragAndDropManager can be used to allow intuitive drag-and-drop
operations between any two Silverlight controls or elements. You learned the basic property settings to
allow drag from a source element and drop to a destination element as well as the event handling required
to complete the operation. While discussing the drag-and-drop events you saw how
RadDragAndDropManager properties, particularly the DragStatus property, is used to pinpoint the exact
state of the operation at the time the event is called. You learned how to allow or refuse to continue during
event processing. You learned how to assign visual cues to notify the user of the progress of the operation.
You also learned how to assign templates to the visual cues to allow binding and customization.
XV
Date, Time and Calendar
468 RadControls for Silverlight
15.1 Objectives
In this chapter you will learn how to use calendar, date picker and time picker controls to collect date and
time data from the user. In the Control Details section of this chapter, you will review the types of date and
time controls and the appropriate uses for each control type. You will learn how to control the number of
dates that can be selected at one time and the manner that they can be selected. You will also learn about
the SelectedDate and SelectedDates properties used to store selections and the SelectionChanged event
that flags when its time to look at these properties. You will become aware of the properties used to limit
the dates that can be selected or viewed. You will also look at the RadTimePicker and RadClock controls
used to select time values. In the Binding section of this chapter you will see how calendar and date picker
controls are bound to custom objects and will also look at a short example of binding RadTimePicker to a
collection of TimeSpan objects. Finally, you will customize the background of RadCalendar using
Expression Blend.
\Courseware\Projects\<CS|VB>\Calendar\Calendar.sln
15.2 Overview
If your user interface requires picking dates and times, use RadCalendar, RadDatePicker and RadClock and
RadTimePicker controls. RadCalendar looks like the page of a calendar where the user can click dates to
select them. The RadDatePicker is a hybrid of masked text box/parser and a button that displays the
calendar. RadClock displays a list of times. RadTimePicker is similar to the RadDatePicker but pops up a
list of times. RadDatePicker and RadTimePicker take the minimum amount of screen real estate.
Calendar Features
Display modes show the calendar in Century, Decade, Year or Month views.
Multiple months can be shown at one time.
Three styles of date selection let the user choose only one date at a time or multiple dates. Explorer
style selection using Ctrl and Shift keys is also supported.
Viewable and selectable dates can be restricted to custom ranges.
The calendar can be bound to Object, XML or WCF services. Two-way binding allows the user to update
the data source simply by clicking the calendar.
The calendar can be internationalized by setting a single property.
Project Setup
1) From the Visual Studio menu choose File > New > Project..., select the Silverlight project type, then
select the Silverlight Application template. Provide a unique name for the project and click the OK
button.
2) In the "New Silverlight Application" dialog make sure that the "Host the Silverlight application in a new
Web site" option is checked, give the project a unique name and verify that the "ASP.NET Web
Application Project" New Web Project Type option is selected. Click OK to close the dialog and create
the project.
3) In the Solution Explorer, right-click the References node and select Add References... from the
context menu. Add Assembly references:
a) Telerik.Windows.Controls
b) Telerik.Windows.Controls.Input
c) Telerik.Windows.Controls.Summer
4) In the Solution Explorer, right-click the project and select Add > New Folder from the context menu.
Rename the folder "Images".
5) Right-click the new "Images" folder and select Add > Existing Item... from the context menu. Locate
the image file "map.png" from the "\courseware\images" directory. Select the file and click the Add
button.
XAML Editing
1) Open MainPage.xaml for editing.
2) Add XML namespace alias for the Telerik.Windows.Controls and Telerik.Windows.Controls.Input
assemblies. Also add a handler for the "Loaded" event.
Note: Each xmlns statement should be all on one line. The example below is split up to fit the size
constraints of the page in this manual.
<UserControl
xmlns:telerikInput=
"clr-namespace:Telerik.Windows.Controls;assembly=Telerik.Windows.
Controls.Input"
xmlns:telerik=
"clr-namespace:Telerik.Windows.Controls;assembly=Telerik.Windows.
Controls"
. . .
Loaded="UserControl_Loaded">
These resources are placed here primarily to let us move property settings out of the control elements
they describe. We can see the structure of controls without the property settings being in the way.
There are a few points of interest you should tak e a look at in this mark up. The SolidColorBrush
named "NormalBrush" uses a shade of color from the "Vista" theme. We will use it to color the titling
and button text. Notice that we also have setters for "telerik :StyleManager.Theme" and that we set the
theme to "Vista".
<UserControl.Resources>
<Style x:Key="DatePickerStyle"
TargetType="telerikInput:RadDatePicker">
<Setter Property="telerik:StyleManager.Theme"
Value="Vista" />
<Setter Property="Margin" Value="0,0,10,10" />
<Setter Property="VerticalAlignment" Value="Bottom" />
</Style>
</UserControl.Resources>
How do I display a background image behind my controls? How can I make this image appear as a
faint, watermark-like picture that recedes in the background and doesn't overpower the foreground?
Examine the "GridBackgroundStyle" XAML below. You can set a Brush property to be an
ImageBrush and assign the brush to the Grid Background. To make the image seem faint, set the
Opacity property to a low value where valid values are "0" to "1".
<UserControl.Resources>
...
<Style x:Key="GridBackgroundStyle" TargetType="Grid">
<Setter Property="Background">
<Setter.Value>
<ImageBrush ImageSource="../images/map.png"
Stretch="Uniform" Opacity=".1">
</ImageBrush>
</Setter.Value>
</Setter>
</Style>
...
</UserControl.Resources>
If Opacity is not set, the image appears like With Opacity set to ".1", the image recedes
this in the browser: and is more suitable for a background.
4) Use the XAML below to replace the main "LayoutRoot" Grid element.
This will provide the general layout where we will add additional elements later. The Stack Panel inside
the grid is centered both vertically and horizontally. The Orientation of this Stack Panel is Vertical by
default. Inside this outer Stack Panel are three more Stack Panel elements, each styled to arrange its
children horizontally.
<StackPanel HorizontalAlignment="Center"
VerticalAlignment="Center">
<!--Title-->
</StackPanel>
<!--Departure Entry-->
</StackPanel>
<!--Arrival Entry-->
</StackPanel>
<!--Search Button-->
<!--Results Calendar-->
</StackPanel>
</Grid>
5) Locate the comment "<!--Title-->" and replace it with the XAML markup for the titling text below.
<!--Title-->
<TextBlock Text="Search for Flights"
Style="{StaticResource TitleStyle}" />
6) Locate the comment "<!--Airport Code Entry-->" and replace it with the XAML markup below. This step
will add an Airport code entry using a RadMask edTextBox. Notice the "Mask edTextBoxStyle" reference
that ties the resource properties to the RadMask edTextBox.
<telerikInput:RadMaskedTextBox x:Name="tbAirportCode"
Style="{StaticResource MaskedTextBoxStyle}" />
7) Locate the comment "<!--Start Entry-->" and replace it with the XAML markup below. This step will add
a RadDatePick er and assign its style.
<!--Start Entry-->
<TextBlock Text="Start Date"
Style="{StaticResource HeadingStyle}" />
<telerikInput:RadDatePicker x:Name="dpStart"
Style="{StaticResource DatePickerStyle}"
SelectionChanged="DateChanged" />
8) Locate the comment "<!--End Entry-->" and replace it with the XAML markup below. This step will add
another RadDatePick er. .
<!--End Entry-->
<TextBlock Text="End Date"
Style="{StaticResource HeadingStyle}" />
<telerikInput:RadDatePicker x:Name="dpEnd"
Style="{StaticResource DatePickerStyle}"
SelectionChanged="DateChanged" />
9) Locate the comment "<!--Search Button-->" and replace it with the XAML markup below. This step
adds a standard Button control and attaches a "Click " event handler.
<!--Search Button-->
<Button x:Name="btnSearch"
Style="{StaticResource ButtonStyle}"
Content="Search"
Click="btnSearch_Click" />
10)Locate the comment "<!--Results-->" and replace it with the XAML markup below. This step adds a
TextBlock to display a status message and a RadCalendar named "calResults". The RadCalendar is
mark ed read-only and invisible. Setting the Columns property to "3", displays three consecutive
months in the calendar. Setting the ViewsHeaderVisibility property to "Visible" displays month titles
above each calendar. HeaderVisibility set to "Collapsed" hides the top title bar graphic. Notice that
SelectionMode is set to "Multiple".
<!--Results -->
<TextBlock x:Name="tbResults"
Style="{StaticResource HeadingStyle}" />
<telerikInput:RadCalendar x:Name="calResults"
Margin="0,10,0,0"
telerik:StyleManager.Theme="Vista"
IsReadOnly="True" IsTodayHighlighted="False"
Columns="3" Rows="1"
ViewsHeaderVisibility="Visible"
HeaderVisibility="Collapsed"
SelectionMode="Multiple">
</telerikInput:RadCalendar>
Code Behind
1) In the code-behind for the page, add a private Random member.
2) In the code-behind for the page, navigate to the Loaded event handler and add the code below.
Set the date pick er selections to "Tomorrow" and the following week . Call the UpdateCalendar() and
SetFocus() methods. Both methods will be written later.
3) Add a helper method to set the focus on the "Airport Code" text box.
Note: This work -around to set focus in Silverlight was discussed briefly in the Input Controls chapter,
Control Details section.
4) In the search button's Click event handler, add the code below.
This code look s at the window of available dates in the calendar and selects up to five of them
randomly. To do this, first calculate the number of days between the date pick er "Start" and "End"
selected dates.
Note: Operator overloads of the DateTime class allow you to subtract one DateTime from another. The
result of the operation is a TimeSpan type.
Call the RadCalendar SelectedDates Clear() method to remove any existing collection members. Then
cycle through a "For" loop and add DateTime instances to the SelectedDates collection. Use the
Random class Next() method to get a number between 1 and the number of selectable days
5) Both RadDatePicker controls share a common SelectionChanged event handler named "DateChanged".
Create the event handler and call UpdateCalendar(), as shown below.
6) Use the selected dates from the two date picker controls to calculate both the "displayable" start and
end dates and the "selectable" start and end dates. To do this, first set the SelectableDateStart and
SelectableDateEnd from the corresponding date picker values, i.e. dpStart.SelectedDate.Value and
dpEnd.SelectedDate.Value. The RadCalendar DisplayDateStart and DisplayDateEnd should range from
the month previous to the date picker start date, to month-end of the date picker end date.
// displayab le dates
calResults.DisplayDateStart =
new DateTime(dtStart.Year, dtStart.AddMonths(-1).Month, 1);
calResults.DisplayDateEnd =
new DateTime(dtEnd.Year, dtEnd.Month, GetLastDayOfMonth(dtEnd));
// selectab le dates
calResults.SelectableDateStart = dtStart;
calResults.SelectableDateEnd = dtEnd;
}
7) Add a private method that calculates the last day of the month. This calculation takes a reference
DateTime value passed in, moves to the following month, then subtracts all the days of that month to
arrive at the last day of the month.
Try selecting different start and end RadDatePicker dates. The calendar should only allow selection
between those dates. The displayable dates should range from a month before and the remainder of the
month containing the end date. Be aware that there is no safety code to ensure that end dates are
greater than start dates.
Overview
RadCalendar is used for displaying and selecting dates in an easy-to-navigate calendar interface. One of
your first choices is to set the DisplayMode property to view the calendar by century, decade, year or
month (the default). The user can navigate views by clicking a calendar cell. The calendar animates the
selected cell expanding to display the next view down in the hierarchy. The screenshot below shows the
relationship of views.
The Columns and Rows properties control how many instances of the calendar you see displayed down
and across the control surface. You can have multiple columns and rows in any of the DisplayModes. The
screenshot below shows the calendar in MonthView display mode where both columns and rows are set to
"3".
The distinct areas that make up the calendar are shown in this next screenshot. The header, by default,
shows a title for the calendar as a whole. The Views Header appears below the header and is most useful
when there are more than one column or row. The Week Days are displayed across the top of the dates
and the Week Numbers display vertically down the side. Use the AreWeekNumbersVisible and
AreWeekNamesVisible properties to toggle their visibility. Selected dates and "Today's" date each have
their own highlight style.
Notes
If you need full featured event calendaring, or behavior that more closely emulates Outlook, be sure
to check out the Scheduler chapter.
Date Selection
The SelectionMode property controls how many dates can be selected at one time and in what manner
they can be selected.
In Single SelectionMode (the default), only one date can be selected at a time. Click a date with the
mouse or press the space bar to toggle selection. Use the arrow keys to move the selection.
The Multiple SelectionMode allows any number of dates to be selected at one time. Click individual
dates with the mouse or press the space bar to toggle selection. Drag with the mouse over a number of
dates at one time to toggle a series of dates.
The Extended SelectionMode also allows any number of dates to be selected, but the behavior is
similar to Windows Explorer. Holding the Shift key down allows a range of dates to be selected with
the mouse or keyboard (using the arrow keys and space bar). The Control key allows individual dates to
be selected even when they are not part of a continuous range.
There are two properties that tell you what dates are selected: SelectedDate and SelectedDates.
SelectedDate is a nullable DateTime object while SelectedDates is a collection of DateTime. If no dates in
the calendar are selected, SelectedDate is null and SelectedDates has a Count = 0. When one or more
dates are selected, SelectedDate and SelectedDates[0] are the same value. Elements of SelectedDates
appear in the order they are added. The screenshot below shows the Visual Studio QuickWatch window
after five dates have been selected in the calendar.
Set new dates in code by assigning a DateTime instance to SelectedDate or add new DateTime instances
to the SelectedDates collection:
The screenshot below shows the code example running in the browser.
Tip!
You can set the DisplayDate property to navigate the calendar to the month or year that contains this
date (depending on the current DisplayMode). Note that the DisplayDate property cannot be set to
null.
Constraints
If you need to restrict the range of dates that can be selected, set the SelectableDateStart and
SelectableDateEnd properties. Dates outside this range will be styled to indicate they can not be
selected.
To restrict dates that can be browsed (displayed), set the DisplayDateStart and DisplayDateEnd
properties. Dates outside this range will not be visible. The screenshot below shows how these properties
play out in a running application. The DisplayDateStart is set to be the first date in the calendar, but is not
selectable. August 1st is the SelectableDateStart and September 15th is the SelectableDateEnd. The
dates between these two properties show in a darker type style and can be selected by the user. Dates
after DisplayDateEnd on September 30 are not visible.
Tip!
RadCalendar has a set of static methods for checking date validity: IsDisplayDateEndValid(),
IsDisplayDateStartValid(), IsDisplayDateValid(), IsSelectableDateEndValid() and
IsSelectableDateStartValid(). Each method takes an instance of the RadCalendar to be checked
against and the DateTime being checked.
Events
To be notified of date selections by the user, subscribe to the SelectionChanged event. You can query
either SelectedDate or SelectedDates as shown in the code sample below where a LINQ statement is used
to extract a collection of short date strings. The String Join() method is used to make a comma delimited
list for display.
RadCalendar also surfaces the DisplayDateChanged and DisplayModeChanged events. Both events
pass arguments that contain the new and old DisplayDate and DisplayMode, respectively. The code
example below shows a DisplayModeChanged event handler that displays the new and old modes.
The screenshot below shows the code sample running in the browser:
Internationalization
To internationalize the calendar, assign a new CultureInfo instance to the Culture property. The
screenshot below shows the result of adding a number of culture codes to a combo box and using the
selected culture code to create a CultureInfo.
The XAML that defines the combo box stores the culture codes, e.g. "en-us" for United States English, in
the combo box item's Tag property...
<telerikInput:RadComboBox
SelectionChanged="RadComboBox_SelectionChanged"
HorizontalAlignment="Left" SelectedIndex="0">
<telerikInput:RadComboBoxItem Content="US English"
Tag="en-us" />
<telerikInput:RadComboBoxItem Content="French"
Tag="fr-FR" />
<telerikInput:RadComboBoxItem Content="German"
Tag="de-DE" />
</telerikInput:RadComboBox>
When a combo box item is clicked, the culture code is retrieved from the tag and fed to the CultureInfo
constructor. The CultureInfo is assigned to the RadCalendar Culture property and the calendar displays
in that language.
Overview
If you need a calendar that takes less space, use the RadDatePicker control. RadDatePicker is essentially
a text box / parser with a drop down button that displays a calendar. The advanced parsing feature allow
you to enter any number or string in the input field and the entered value will be transformed to a valid date.
For example, if you type “1”, the first day of the month will be shown after leaving the input field. If you type
“Monday”, the corresponding date of the current week will be selected.
RadDatePicker properties, for the most part, simply carry over from RadCalendar. One exception to this rule
is the IsDropDownOpen property that reflects when the calendar portion of the control is showing.
Overview
Use RadTimePicker to select from a set of times displayed in a drop down list. The range of times
displayed are bounded by the StartTime and EndTime properties. The TimeInterval property determines
the number of minutes between selectable entries. The example below displays the hours between 9AM
and 5PM with selections spaced 1 hour apart.
The XAML and code below show how to set StartTime, EndTime and TimeInterval properties. In XAML,
the values are set to military (24 hour) format.
StartTime, EndTime and TimeInterval properties are all instances of TimeSpan. The code below
produces equivalent results to the prior XAML example.
Use the HeaderContent property to place text or other content into the time picker header. In the
screenshot below, HeaderContent has been assigned the simple text "Appointment Time".
RadClock
RadClock displays the grid of times without the input and button portions of a RadDatePicker. RadClock is
to RadTimePicker as RadCalendar is to RadDatePicker. It simply displays all the times at once without
having to click the drop down button. Set the heading content by assigning the Header property. In the
screenshot below, Header is set to text "Available Classes".
Internationalization
To internationalize RadTimePicker, assign the Culture property. For example, the RadTimePicker in the
screenshot below displays time in military format because the culture is set to "en-gb", i.e. "English - Great
Britain".
15.5 Binding
Tip!
The SelectedDates property is an observable collection of all the selected dates. Although it is
exposed as a list, you can bind it and cast it to an observable collection if you need. Note that the
SelectedDates property in the case of a Single selection mode will also contain the currently selected
date, but it will be read-only.
The basic binding steps are a) build a custom "View Model" object, b) change the XAML to bind specific
properties, c) add code behind. The code will populate a list of objects and set the data context for each
control that needs to be bound.
For the TwoWay binding to work, your custom object needs to implement the INotifyPropertyChanged
interface. The "Flight" object below handles firing notification if any of the properties are changed.
End Class
get
{
return _departure;
}
set
{
if (_departure != value)
{
_departure = value;
OnPropertyChanged("Departure");
}
}
}
The XAML takes care of hooking up specific properties to the data using binding syntax, e.g. "{Binding
MyProperty, Mode=TwoWay}".
<StackPanel Orientation="Vertical">
<StackPanel Orientation="Horizontal">
<telerikInput:RadComboBox x:Name="cbMain" Margin="5"
telerik:StyleManager.Theme="Office_Silver"
HorizontalAlignment="Left"
SelectionChanged="cbMain_SelectionChanged" />
<Border BorderBrush="Gray" BorderThickness="1"
CornerRadius="5" HorizontalAlignment="Left"
Padding="5" Margin="5">
<StackPanel Orientation="Horizontal">
<TextBlock Text="Flight:" />
<TextBlock x:Name="tbFlightNumber"
Text="{Binding Number, Mode=OneWay}" />
</StackPanel>
</Border>
</StackPanel>
<StackPanel Orientation="Horizontal">
<telerikInput:RadCalendar x:Name="calMain" Margin="5"
telerik:StyleManager.Theme="Office_Silver"
SelectedDate="{Binding Departure, Mode=TwoWay}"
HorizontalAlignment="Left" />
<telerikInput:RadDatePicker x:Name="dpMain" Margin="5"
telerik:StyleManager.Theme="Office_Silver"
SelectedDate="{Binding Arrival, Mode=TwoWay}"
HorizontalAlignment="Left" VerticalAlignment="Top" />
</StackPanel>
The remainder of the work takes place in the code-behind. The code first populates an ObservableCollection
with a series of new Flight objects. This list is assigned to the combo box ItemsSource property and the
DisplayMemberPath is set to "Airline". When the SelectionChanged event fires, the selected item is cast
back to a Flight object type and assigned as the DataContext for all the other controls, i.e. calendar, date
picker and text block.
To bind to time picker controls, first create an ObservableCollection of TimeSpan objects and assign the
collection to the RadTimePicker ClockItemSource property. The snippet below shows a simple example.
ObservableCollection<TimeSpan> classTimes =
new ObservableCollection<TimeSpan>()
{
new TimeSpan(9, 0, 0),
new TimeSpan(10, 0, 0),
new TimeSpan(11, 0, 0),
new TimeSpan(12, 0, 0)
};
tpMain.ClockItemSource = classTimes;
15.6 Customization
In this example we will customize the RadCalendar control background to use the "Scoville" set of colors.
"Scoville" Styles
We will use a set of colors that include black, red, yellow and orange in many of the style related
topics and prefix the style names with "Scoville". The "Scoville scale" measures the spiciness of
peppers and other culinary irritants.
Project Setup
1) Run Expression Blend.
2) From the File menu select New Project. Note: If you have an existing solution open, right-click the
solution and select Add New Project... from the context menu instead.
3) In the New Project dialog, select "Silverlight" from "Project types" and "Silverlight 3" Application from
the right-most list. Enter a unique name for the project and click OK.
2) In the Projects pane, right-click the References node and select Add Reference... from the context
menu.
a) Add a reference to the Telerik.Windows.Controls.dll assembly.
b) Add a reference to the Telerik.Windows.Controls.Input.dll assembly.
3) From the Project menu select Build Project.
4) Add the RadCalendar to the page.
a) Open the Assets pane.
b) On the left side of the Assets pane is a tree view. Locate and select the "Controls" node.
c) In the Assets pane, just above the tree view is the Assets Find entry text box.
d) Type the first few characters of "RadCalendar" into the Assets Find entry text box. A list of all
matching controls will show to the right of the tree view.
e) Locate the RadCalendar control and drag it onto the MainPage.xaml Artboard.
5) In the Objects and Timeline pane, right-click "[RadCalendar]" and select Edit Template > Edit a
Copy from the context menu. In the "Create Style Resource" dialog, set the Name (Key) to
"ScovilleCalendarStyle". Click OK to create the style resource and close the dialog.
6) In the Objects and Timeline pane, open the element tree and find the top two "[Border]" elements.
These two elements represent the calendar background and the calendar header background.
9) Also in the Brushes section of the Properties pane, select the Gradient button. Use the eye dropper
tool to replace the black color of the default gradient with yellow and replace the white color with red.
The Properties pane at this point should look something like the screenshot below.
10)Select the Radial Gradient button from the lower left hand corner of the Brushes section:
11)Going back to the Properties pane, Brushes section, again locate the Background property. Click the
Advanced Property Options button and select Convert to New Resource.. from the drop down menu.
The Create Brush Resource dialog will appear. Enter "ScovilleBrush" as the name for the brush. Click
the OK button to close the dialog and create the brush. This will allow us to reuse the same brush later
in the same project.
12)In the Objects and Timeline pane, click the second "[Border]" element to begin editing the header
background.
13)In the Properties pane, Brushes section, locate the Background property. Click the Advanced
Property Options button and select Local Resource > ScovilleBrush from the drop down menu.
14)Looking to the Artboard, the RadCalendar should look something like the screenshot below.
15)Navigate to the Resources pane, find the "RadCalendar_RowBackground" brush in the list and select
it. Click the drop-down arrow to open the popup editor.
16) In the Editor, locate the Advanced Property Options button and click it.
17)Select Reset from the menu. This will set the Background for the rows underneath the column and row
headers to Transparent
18)Checking the appearance of the calendar in the Artboard, you will see that setting the
"RadCalendar_RowBackground" brush to transparent allows the calendar background color to show
through.
Press F5 to run the application. The web page should look something like the screenshot below.
15.7 Wrap Up
In this chapter you learned how to use calendar, date picker and time picker controls to collect date and
time data from the user. In the Control Details section of this chapter, you reviewed the types of date and
time controls and the appropriate uses for each control type. You learned how to control the number of
dates that can be selected at one time and the manner that they can be selected. You also learned about
the SelectedDate and SelectedDates properties used to store selections and the SelectionChanged event
that flags when its time to look at these properties. You are aware of the properties used to limit the dates
that can be selected or viewed. You have also looked at the RadTimePicker and RadClock controls used to
select times. In the Binding section of this chapter you saw how calendar and date picker controls are
bound to custom objects and also looked at a short example of binding RadTimePicker to a collection of
TimeSpan objects. Finally, you customized the background of RadCalendar using Expression Blend.
XVI
ComboBox
516 RadControls for Silverlight
16 ComboBox
16.1 Objectives
In this chapter you will build a RadComboBox and its items using XAML and in code. You will learn how to
use the Content property assignment for simple text lists and how to bind to templates for more complex
layout. You will learn how to handle the SelectionChanged event of the combo box and the Selection event
of the individual items. In the process, you will find out how to get and set the selected RadComboBoxItem
or custom object. You will also have a brief look at events that respond to drop down opening and closing.
You will learn how various edit modes control the combo box Autocomplete, text search, read-only and
editing behavior. You will learn how to search for items and determine if a specific item exists in a combo
box. You will use ItemTemplate and SelectionBoxItemTemplate to build content of arbitrary complexity. You
will also bind template elements to live data. Along the way you will discover a few helpful classes in the
System.ServiceModel.Syndication namespace for working with RSS (Really Simple Syndication) feeds.
Finally, you will use Expression Blend to style the background color of RadComboBox.
16.2 Overview
RadComboBox has powerful built-in features such as Autocomplete, automatic filtering and text search.
Templates allow the combo box to appear as a multiple column, grid-like list of arbitrary complexity. Using
templates, you can completely customize the selected area at the top of the list and the individual list items
in the drop down area. All aspects of the combo box can be customized in both editable and non-editable
modes.
The text search, Autocomplete and filtering features also work when the items are composed of templates.
Filtering modes show items that either start with or contain characters entered by the user.
RadComboBox keyboard support allows the user to press the arrow keys to move through items in the list,
page up or down to move a page at a time, press Enter to select an item and Escape to close the drop
down list.
Project Setup
1) From the Visual Studio menu choose File > New > Project..., select the Silverlight project type, then
select the Silverlight Application template. Provide a unique name for the project and click the OK
button.
2) In the "New Silverlight Application" dialog make sure that the "Host the Silverlight application in a new
Web site" option is checked, give the project a unique name and verify that the "ASP.NET Web
Application Project" New Web Project Type option is selected. Click OK to close the dialog and create
the project.
3) In the Solution Explorer, right-click the References node and select Add References... from the
context menu. Add Assembly references:
a) Telerik.Windows.Controls
b) Telerik.Windows.Controls.Input
c) Telerik.Windows.Controls.Navigation
d) Telerik.Windows.Themes.Summer
XAML Editing
1) Open MainPage.xaml for editing.
2) Add XML namespace references for Telerik.Windows.Controls and Telerik.Windows.Controls.
Input.assemblies:
Note: Each xmlns statement should be all on one line. The example below is split up to fit the size
constraints of the page in this manual.
<UserControl
xmlns:telerikInput=
"clr-namespace:Telerik.Windows.Controls;assembly=Telerik.Windows.
Controls.Input"
xmlns:telerik=
"clr-namespace:Telerik.Windows.Controls;assembly=Telerik.Windows.
Controls"
. . .>
3) Drag a RadComboBox from the Toolbox to a point just inside the main Grid named "LayoutRoot". Set
the x:Name property to "cbMain", the HorizontalAlignment to "Left", VerticalAlignment to "Top",
Margin to "10", StyleManager.Theme to "Summer" and the SelectionChanged event to
"cbMain_SelectionChanged".
<Grid x:Name="LayoutRoot">
<telerikInput:RadComboBox x:Name="cbMain"
HorizontalAlignment="Left" VerticalAlignment="Top"
Margin="10" telerik:StyleManager.Theme="Summer"
SelectionChanged="cbMain_SelectionChanged">
</telerikInput:RadComboBox>
</Grid>
4) Drag a RadComboBoxItem from the Toolbox to a point just inside the RadComboBox element. Set
the Content attribute to "RadControls Silverlight 3 Official".
<telerikInput:RadComboBox x:Name="cbMain"
HorizontalAlignment="Left" VerticalAlignment="Top"
Margin="10" telerik:StyleManager.Theme="Summer"
SelectionChanged="cbMain_SelectionChanged">
<telerikInput:RadComboBoxItem
Content="RadControls Silverlight 3 Official" />
</telerikInput:RadComboBox>
5) Add four more RadComboBoxItem elements and set the Content attribute for each item to the
following series of strings:
The XAML should now look something like the example below:
<telerikInput:RadComboBox x:Name="cbMain"
HorizontalAlignment="Left" VerticalAlignment="Top"
Margin="10" telerik:StyleManager.Theme="Summer"
SelectionChanged="cbMain_SelectionChanged">
<telerikInput:RadComboBoxItem
Content="RadControls Silverlight 3 Official" />
<telerikInput:RadComboBoxItem
Content="Medium Trust Support for Telerik Reporting" />
<telerikInput:RadComboBoxItem
Content="Telerik Extensions for ASP.NET MVC" />
<telerikInput:RadComboBoxItem
Content="First Level Cache of Telerik OpenAccess" />
<telerikInput:RadComboBoxItem
Content="Animating the RadWindow control for Silverlight and WPF" />
</telerikInput:RadComboBox>
Code Behind
1) Navigate to the code-behind and add a reference to Telerik.Windows.Controls in the "Imports" (VB) or
"using" (C#) section of code.
2) Add a handler for the SelectionChanged event defined previously in the XAML.
This event handler fires when the user selects another item. First the handler retrieves the arguments
AddedItems property and casts the first and only element in the collection to a RadComboBoxItem
type. Then the Content for the item is displayed in an Alert.
Adding Items
To add items at design-time, include one or more RadComboBoxItem elements inside the RadComboBox
element. Set the Content to the text you want to have displayed. At the same time you can set other
RadComboBoxItem properties to tweak behavior and appearance.
<telerikInput:RadComboBox . . .>
<telerikInput:RadComboBoxItem
Content="RadControls for Silverlight"
FontFamily="Corbel" />
<telerikInput:RadComboBoxItem
Content="Telerik Extensions for MVC"
Foreground="SkyBlue" />
<telerikInput:RadComboBoxItem
Content="Animating RadWindow"
IsEnabled="False" />
</telerikInput:RadComboBox>
The results of the XAML show in the screenshot below with item text displaying in a new FontFamily and
Foreground font color. The last item is disabled.
To add items at run-time, create RadComboBoxItem instances in code, then set the Content and any
properties related to behavior and appearance. Add each item to the RadComboBox Items collection. The
example below configures a linear gradient brush and assigns it to the Foreground property. The second
item is disabled and the last item is selected.
' add a new comb o b ox item, change the font family, size and color
cbMain.Items.Add(New RadComboBoxItem() _
With {
.Content = "OpenAccess and the 2nd Level Cache", _
.FontFamily = New FontFamily("Corbel"), _
.FontSize = 15, _
.Foreground = linearGradientBrush})
' add another item, b ut disab le it
cbMain.Items.Add(New RadComboBoxItem() _
With { _
.Content = "Building Advanced Layouts with RadSplitContainer", _
.IsEnabled = False})
' add another item and select it
cbMain.Items.Add(New RadComboBoxItem() _
With { _
.Content = "Multiple Child Views with RadGridView for Winforms", _
.IsSelected = True})
End Sub
// add a new comb o b ox item, change the font family, size and color
cbMain.Items.Add(new RadComboBoxItem()
{
Content = "OpenAccess and the 2nd Level Cache",
FontFamily = new FontFamily("Corbel"),
FontSize = 15,
Foreground = linearGradientBrush
});
// add another item, b ut disab le it
cbMain.Items.Add(new RadComboBoxItem()
{
Content = "Building Advanced Layouts with RadSplitContainer",
IsEnabled = false
});
// add another item and select it
cbMain.Items.Add(new RadComboBoxItem()
{
Content = "Multiple Child Views with RadGridView for Winforms",
IsSelected = true
});
}
You may need to include a "Create New" at the head of the list. Use the Items collection Insert() method,
first passing the index where you want the item placed, then passing a RadComboBoxItem reference. The
snippet below shows adding an item to the start of the list, hooking up its Selected event and displaying the
new clicked item's content.
Item Selection
To get or set the currently selected combo box item, use either the RadComboBox SelectedIndex or
SelectedItem property. You can also set the IsSelected property of an individual RadComboBoxItem.
Here are some examples that show all three possibilities: selecting the first item in the list by index, using
a reference to the last item in the list and using the IsSelected property of a RadComboBoxItem.
Forum question: "Sometimes... the SelectionBoxItemTemplate is not applied until the combo box is
opened, even though an item is selected."
Answer:
"The problem is that the RadComboBoxItem containers are generated after the combo box drop
down is opened for the first time. If the containers are not generated, the container bindings are not
applied and the combo box does not 'know' which data item is selected. The correct way to initially
select an item is through one of the following properties: SelectedItem, SelectedIndex."
Searching Items
You can use LINQ statements or expressions to check whether a certain RadComboBoxItem (or other
object) is present a RadComboBox.Items collection. You can make variations of the LINQ statement
example below. Once you have a reference to the Content, compare the Content object using the
appropriate methods for the type. In this case the Content is a string so we can use the String methods
such as Contains(), StartsWith() or Equals().
Imports System.Linq
'. . .
Private exists As Boolean = cbMain.Items.Any(item => _
(TryCast(item, RadComboBoxItem)).Content.ToString().Contains("OpenAccess"))
If exists Then
tbStatus.Text = "There are OpenAccess articles in the list"
End If
using System.Linq;
//. . .
bool exists = cbMain.Items.Any(item =>
(item as RadComboBoxItem).Content.ToString().Contains("OpenAccess"));
if (exists)
{
tbStatus.Text = "There are OpenAccess articles in the list";
}
Here's another example where a LINQ expression selects all the items that contain the string
"OpenAccess". The resulting collection can be enumerated, converted to a generic list or operated on by
other LINQ methods.
Dim foundItems = _
From item In cbMain.Items _
Where (TryCast(item, _
RadComboBoxItem)).Content.ToString().Contains("OpenAccess") _
Select item
var foundItems =
from item in cbMain.Items
where (item as RadComboBoxItem).Content.ToString().Contains("OpenAccess")
select item;
Edit modes
The behavior of the combo box is controlled by a combination of IsEditable and IsReadOnly property
settings. RadComboBox can be editable, allowing the user to type in its text box, or non-editable, where the
text box is hidden. In addition, you could make the text box read-only, in order to keep the editable look,
but prevent the user from typing. The combinations are shown in the screenshot below.
Both IsEditable and IsReadOnly properties are "False" by default. In this mode the user can perform a text
search by typing a few characters in the combo box. The screenshot below shows a series of RSS feed
titles. When the user types in "M", the selection travels to the 'Medium Trust Support for Telerik Reporting"
item. When the user types in "u", the selection moves down to "Multiple child views with RadGridView for
WinForms" item.
If IsEditable is "True", the combo box provides Auto Complete functionality. The selection behavior is the
same as before. The difference is the "type ahead" behavior. When the user types "M" the text entry area
of the combo box is automatically completed with the nearest entry starting with "M", i.e. "Medium Trust
Support for Telerik Reporting". When the user types "Mu", again the entry is completed with the nearest
match.
By default RadComboBox uses the DisplayMemberPath property to determine which property of a data
source to use for auto complete.
<telerikInput:RadComboBox
DisplayMemberPath="Title.Text">
</telerikInput:RadComboBox>
If you are defining an ItemTemplate, there can be multiple items in the template. Use the TextSearch.
TextPath attached property to specify which bound property to search on. Note: TextSearch.TextPath is in
the Telerik.Windows.Controls namespace.
<telerikInput:RadComboBox
telerik:TextSearch.TextPath="Title.Text">
<telerikInput:RadComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Title.Text}" />
</DataTemplate>
</telerikInput:RadComboBox.ItemTemplate>
</telerikInput:RadComboBox>
Filtering
Filtering capability is built into RadComboBox. The FilteringMode property can be "None", "StartsWith" or
"Contains" to automatically filter the combo. The example below shows the combo where FilteringMode is
set to "StartsWith".
In the screenshot below the "M" character is entered. As additional characters are entered, the list narrows
to meet the criteria.
"When FilteringMode == Contains or StartsWith, RadComboBox searches through its items for items
that match its Text. Those items that match are made visible, the rest are collapsed."
Selection Events
The most significant event to handle is SelectionChanged that fires when the user clicks a new item in the
list or arrow keys down and hits Enter. The Sender parameter represents the RadComboBox and
SelectionChangedEventArgs contains an AddedItems property that holds the selected item. Typically
you will check that there is at least one item in AddedItems and retrieve the first (and only) item in the list.
What you do from here depends on what is in the list. If you added a series of RadComboBoxItem in code
or XAML, you will cast to a RadComboBoxItem type.
Here is an example that uses a collection of RSS objects (available in the System.ServiceModel.
Syndication namespace). The code fragment at the top shows how to create a series of
RadComboBoxItem objects, populate each with content and add to the RadComboBox Items collection. In
the SelectionChanged event handler you can cast the first AddedItems element as a RadComboBoxItem
type and work with that.
'...
For Each item As SyndicationItem In syndicationFeed.Items
Dim cbItem As New RadComboBoxItem() With {.Content = item.Title.Text}
cbMain.Items.Add(cbItem)
Next item
cbMain.SelectedIndex = 0
'...
// . . .
foreach (SyndicationItem item in syndicationFeed.Items)
{
RadComboBoxItem cbItem =
new RadComboBoxItem() { Content = item.Title.Text };
cbMain.Items.Add(cbItem);
}
cbMain.SelectedIndex = 0;
// . . .
If you bound the RadComboBox ItemsSource to a collection of some sort, then you will cast to the
collection item type. One other possibility is that you could add instances of an object directly to the
RadComboBox.Items collection. In both cases DisplayMemberPath indicates the property of the object to
display in the list.
'. . .
' b ind to ItemsSource...
cbMain.ItemsSource = syndicationFeed.Items
//. . .
// b ind to ItemsSource...
cbMain.ItemsSource = syndicationFeed.Items;
If you want notification of individual item selection, handle the RadComboBoxItem Selection event. The
"Sender" parameter passed to the event handler will be the RadComboBoxItem instance (or whatever item
type was added to the Items collection).
DropDown Events
The RadComboBox class introduces the DropDownOpened and DropDownClosed events. You can use
these events for any setup or tear down work just before and after the user opens the list.
The example below clears the text selection of the combo box just before the list is opened and just after it
closes. The ChildrenOfType<> method (an extension method from Telerik.Windows.Controls) gets a
reference to the TextBox contained by RadComboBox and sets the SelectionLength to zero.
16.5 Binding
RadComboBox is an ItemsControl descendant and has an ItemTemplate that handles layout for each of
the individual list items.SelectionBoxItemTemplate handles the layout of the text at the top of the
RadComboBox. Both templates can contain static and bound elements. This walk through creates custom
layouts for both templates.
In the example we will use Silverlight classes that do a nice job of parsing RSS (Really Simple Syndication)
format. These classes are SyndicationFeed and SyndicationItem, both found in the System.
ServiceModel.Syndication namespace. We will use the WebClient from the System.Net namespace to
download the RSS data into a stream, then load the stream into a SyndicationFeed instance. From there,
SyndicationFeed provides a collection of SyndicationItem that can be bound to the combo box.
Project Setup
1) From the Visual Studio menu choose File > New > Project..., select the Silverlight project type, then
select the Silverlight Application template. Provide a unique name for the project and click the OK
button.
2) In the "New Silverlight Application" dialog make sure that the "Host the Silverlight application in a new
Web site" option is checked, give the project a unique name and verify that the "ASP.NET Web
Application Project" New Web Project Type option is selected. Click OK to close the dialog and create
the project.
3) In the Solution Explorer, right-click the References node and select Add References... from the
context menu. Add Assembly references:
a) Telerik.Windows.Controls
b) Telerik.Windows.Controls.Input
c) Telerik.Windows.Themes.Vista
d) System.ServiceModel.Syndication
XAML Editing
1) Open MainPage.xaml for editing.
2) Verify that the XML namespaces for Telerik.Windows.Controls and Telerik.Windows.Controls.
Input exist in the UserControl element. Add them if they do not exist. Also, add a "Loaded" event
handler to the UserControl element.
<UserControl
xmlns:telerikInput=
"clr-namespace:Telerik.Windows.Controls;assembly=Telerik.Windows.Controls.Input"
xmlns:telerik=
"clr-namespace:Telerik.Windows.Controls;assembly=Telerik.Windows.Controls"
...
Loaded="UserControl_Loaded">. . .
3) Drag a RadComboBox from the Toolbox to a point inside the main "LayoutRoot" Grid. Set the x:Name
attribute to "cbMain", the Margin to "10", MinWidth to "200", HorizontalAlignment to "Left",
VerticalAlignment to "Top" and the StyleManager.Theme to "Vista".
<Grid x:Name="LayoutRoot">
<telerikInput:RadComboBox x:Name="cbMain" Margin="10"
MinWidth="200" HorizontalAlignment="Left"
VerticalAlignment="Top" telerik:StyleManager.Theme="Vista">
<!--Templates go here-->
</telerikInput:RadComboBox>
</Grid>
We will put our custom mark up and binding expressions inside these two DataTemplate elements.
5) Add a TextBlock inside the SelectionBoxItemTemplate DataTemplate element. Set the Margin to
"10", MinWidth to "200" and the FontSize to "15". Assign a binding expression to the Text property:
"{Binding Title.Text}".
The SyndicationItem object has a Title property that in turn has a Text sub-property. It's the Text sub-
property that we actually want to display in the upper portion of the RadComboBox.
<telerikInput:RadComboBox.SelectionBoxItemTemplate>
<DataTemplate>
<TextBlock Margin="10" MinWidth="200"
FontSize="15" Text="{Binding Title.Text}" />
</DataTemplate>
</telerikInput:RadComboBox.SelectionBoxItemTemplate>
6) Populate the ItemTemplate DataTemplate element. This is a more complex layout. Start by inserting a
StackPanel with Margin = "10". Inside the StackPanel add a TextBlock and a RadMaskedTextBox.
Set the TextBlock HorizontalAlignment to "Left" and make the binding expression "{Binding Title.Text}".
Set the RadMaskedTextBox HorizontalAlignment to "Left", MinWidth to "200", MaskType to
"DateTime", Mask to "D" and IsReadOnly to "True". Bind the RadMaskedTextBox Value property to
"{Binding PublishDate.DateTime}".
The RadComboBox items displayed when the list is opened will show the title text and the publish date
right underneath the title.
<telerikInput:RadComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Margin="10">
<TextBlock HorizontalAlignment="Left"
Text="{Binding Title.Text}" />
<telerikInput:RadMaskedTextBox
HorizontalAlignment="Left"
MinWidth="200" MaskType="DateTime"
Mask="D" IsReadOnly="True"
Value="{Binding PublishDate.DateTime}" />
</StackPanel>
</DataTemplate>
</telerikInput:RadComboBox.ItemTemplate>
Code Behind
1) Verify that namespace references below are included in the "Imports" (VB) or "using" (C#) section of
code.
System.IO
System.Net
System.ServiceModel.Syndication
System.Windows
System.Windows.Controls
System.Xml
2) Handle the UserControl Loaded event. Create a new WebClient instance to download the RSS data.
Assign a DownloadStringCompleted event handler that will respond once all the data has been
downloaded. Call the WebClient DownloadStringAsync() method and pass a Uri of an RSS web site.
Notes
Be aware that this implementation is specific to this particular RSS site. This code assumes the
return data will be a string and there is no safety code for null fields within the returned XML. This
is to keep the code small and relevant to RadComboBox binding.
The DownloadStringCompletedEventArgs contain a Result string property that holds the RSS XML data.
Pass that Result string to a StringReader constructor, then pass the StringReader to a XmlReader
constructor. Use the XmlReader in the static SyndicationFeed.Load() method.
Now that the SyndicationFeed object is completely initialized and loaded with RSS items, assign the
SyndicationFeed Items collection to the RadComboBox ItemsSource property.
void client_DownloadStringCompleted(
object sender, DownloadStringCompletedEventArgs e)
{
// Create an XmlReader using a StringReader containing the result string
StringReader stringReader = new StringReader(e.Result);
XmlReader xmlReader = XmlReader.Create(stringReader);
// Load and create the SyndicationFeed instance using the XmlReader
SyndicationFeed syndicationFeed = SyndicationFeed.Load(xmlReader);
// b ind to ItemsSource
cbMain.ItemsSource = syndicationFeed.Items;
<telerikInput:RadComboBox.SelectionBoxItemTemplate>
<DataTemplate>
<TextBlock Margin="10" MinWidth="200"
FontSize="15" Text="{Binding Title.Text}" />
</DataTemplate>
</telerikInput:RadComboBox.SelectionBoxItemTemplate>
<telerikInput:RadComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Image Source="../images/Rss.png" Width="50"
Height="50" Stretch="Uniform"
HorizontalAlignment="Left" />
<StackPanel Margin="10">
<TextBlock HorizontalAlignment="Left"
Text="{Binding Title.Text}" />
<telerikInput:RadMaskedTextBox
HorizontalAlignment="Left"
MinWidth="200" MaskType="DateTime"
Mask="D" IsReadOnly="True"
Value="{Binding PublishDate.DateTime}" />
</StackPanel>
</StackPanel>
</DataTemplate>
</telerikInput:RadComboBox.ItemTemplate>
</telerikInput:RadComboBox>
Refactor the XAML to move the DataTemplate out into UserControl.Resources. Just relocate the
DataTemplate elements to the UserControl.Resources element. Be sure to assign the "x:Key" attribute
to the DataTemplate. Then, assign the RadComboBox ItemTemplate and SelectionBoxItemTemplate
attributes. As usual, moving XAML into the Resources area really cleans up the XAML.
<UserControl . . .>
<UserControl.Resources>
</UserControl.Resources>
<Grid x:Name="LayoutRoot">
<telerikInput:RadComboBox x:Name="cbMain" Margin="10"
MinWidth="200" HorizontalAlignment="Left"
VerticalAlignment="Top"
telerik:StyleManager.Theme="Summer"
ItemTemplate="{StaticResource MyItemTemplate}"
SelectionBoxItemTemplate=
"{StaticResource MySelectionBoxItemTemplate}">
</telerikInput:RadComboBox>
</Grid>
</UserControl>
16.6 Customization
In this walk through we will customize the RadComboBox background. Before going into Expression Blend,
you should know that RadComboBox has two control templates and that the IsEditable property
determines which control template is shown: "NonEditableComboBox" or "EditableComboBox". We're going
to edit the drop down button of the "NonEditableComboBox" to use "Scoville" colors.
"Scoville" Styles
We will use a set of colors that include black, red, yellow and orange in many of the style related
topics and prefix the style names with "Scoville". The "Scoville scale" measures the spiciness of
peppers and other culinary irritants.
Project Setup
1) Run Expression Blend.
2) From the File menu select New Project. Note: If you have an existing solution open, right-click the
solution and select Add New Project... from the context menu instead.
3) In the New Project dialog, select "Silverlight" from "Project types" and "Silverlight 3" Application from
the right-most list. Enter a unique name for the project and click OK.
4) In the Projects pane, right-click the References node and select Add Reference... from the context
menu.
5) Add references to Telerik.Windows.Controls.dll and Telerik.Windows.Controls.Input.dll.
b) On the left side of the Assets pane is a tree view. Locate and select the "Controls" node.
c) In the Assets pane, just above the tree view is the Assets Find entry text box.
d) Type the first few characters of "RadComboBox" into the Assets Find entry text box. A list of all
matching controls will show to the right of the tree view.
e) Locate the RadComboBox control and drag it onto the MainPage.xaml Artboard.
4) In the Artboard, select the XAML view button
5) Replace the RadComboBox element with the markup below. This will define several sample
RadComboBoxItem to populate the list.
<telerikInput:RadComboBox
HorizontalAlignment="Left"
Margin="8,8,0,0"
VerticalAlignment="Top">
<telerikInput:RadComboBoxItem Content="Red Savina Habanero"
IsSelected="True" />
<telerikInput:RadComboBoxItem Content="Datil pepper" />
<telerikInput:RadComboBoxItem Content="Chiltepin Pepper" />
<telerikInput:RadComboBoxItem Content="Jalapeño" />
<telerikInput:RadComboBoxItem Content="Pimento" />
<telerikInput:RadComboBoxItem Content="Pepperoncini" />
</telerikInput:RadComboBox>
6) In the Objects and Timeline pane, right-click the "[RadComboBox]" node and select Edit Template >
Edit a Copy from the context menu. In the "Create Style Resource" dialog, set the Name (Key) to
"ScovilleComboBoxStyle". Click OK to create the style resource and close the dialog.
7) In the Objects and Timeline pane, notice that we're looking at the "NonEditableComboBox" control
template. Open the tree view and review the nodes there. Within the "MainGrid" element of this template
we have the "OuterBorder" which represents the combo box in its closed state and "PART_Popup" which
represents the drop down list portion of the combo box.
8) In the Objects and Timeline pane, locate the "PART_DropDownButton" node in the tree view, right-click
and select Edit a Copy... from the context menu. In the "Create Style Resource" dialog, set the Name
(Key) to "ScovilleDropDownButtonStyle". Click OK to create the style resource and close the dialog.
9) In the Resources pane, open the resources for the User Control and locate
"ExpanderButtonNormal_Background". Click the downward pointing arrow button to display a color
editing popup.
The color editing popup defines a gradient color with four gradient stops. The middle two gradient stops
overlap each other. This gradient describes the 3-D, slightly beveled appearance of the RadComboBox
when closed.
10) Move one of the middle gradient stops slightly to the right so that the two middle gradient stops do not
overlap.
There is no design reason to do this. We're separating the two gradient stops merely to mak e it easier to
talk about the process of editing the stops in Expression Blend.
11)Select and change the color for each of the gradient stops using the eye dropper tool. The general color
hues, in order of gradient stop, should be pink, orange, red and red-with-black mixed.
The colors need not be exact to demonstrate customizing the RadComboBox back ground. Feel free to
"get creative".
People will often ask on the forums "how do I change the background color" of "X" control. Given
the flexibility of Silverlight and RadControls, answering that question may not be a one-property
answer, i.e. Background = "Blue". There are multiple brushes that correspond to states in the
control, such as "normal", "mouse over", "selected", etc. You can edit these brushes in Expression
Blend to fit your application look-and-feel. Other properties of the control may also determine
what set of control parts you're looking at, and these parts may use a different set of brushes. For
example, RadComboBox uses two different control templates based on the setting of the
IsEditable property.
The Resources pane contains other brushes for "button pressed" and "mouse hovered" states. Use the
same technique from the steps in this walk through to edit the "ExpanderButtonPressed_Background"
and "ExpanderButtonOver_Background" brushes.
Change the background of the individual RadComboBoxItem instances. Here are some hints to get you
started. In the Objects and Timeline pane, make a copy of the RadComboBoxItem template. the
background is controlled by the "Bd" item. Using the Properties pane, set the Background to the
"ExpanderButtonNormalBackground" brush.
The resulting styled items end up looking something like the screenshot below:
Try creating a completely new brush for the RadComboBoxItem background. You can use the Advanced
Property Options button next to the Background property and select the Convert to New Resource...
option.
16.7 Wrap Up
In this chapter you built a RadComboBox and its items using XAML and in code. You learned how to use
the Content property assignment for simple text lists and how to bind to templates for more complex layout.
You learned how to handle the SelectionChanged event of the combo box and the Selection event of the
individual items. In the process, you found out how to get and set the selected RadComboBoxItem or
custom object. You also had a brief look at events that respond to drop down opening and closing.
You learned how various edit modes control the combo box Autocomplete, text search, read-only and
editing behavior. You learned how to search for items and determine if a specific item exists in a combo
box. You used ItemTemplate and SelectionBoxItemTemplate to compose multiple Silverlight elements. You
also bound template elements to live data. Along the way you discovered a few helpful classes in the
System.ServiceModel.Syndication namespace for working with RSS feeds. Finally, you used Expression
Blend to style the background color of a RadComboBox.
XVII
TreeView
TreeView 555
17 TreeView
17.1 Objectives
In this chapter we will cover a wide range of tasks that exercise many of the RadTreeView features including
defining trees manually, using the API to add/remove/enable/select nodes, locating and accessing nodes,
adding images to nodes, handling node expansion and reacting to node selection. You will also define trees
with mixed groups of radio buttons and checkboxes. You will learn how to work with drag-and-drop
operations, both to enable simple drag-and-drop functionality and to fine-tune the behavior based on multiple
conditions such as source and target nodes and the state of the data associated with a node.
You will bind the tree to a simple list of data as well as bind specific nodes to data sources. Then you will
use Hierarchical Templates to organize data and present a specific appearance based on the level of data.
You will learn how to use Template Selectors to choose templates on-the-fly. You will use the load-on-
demand feature to quickly load only the visible nodes of the tree view.
Finally, you will learn how to customize the appearance of individual nodes using Expression Blend.
\Courseware\Projects\<CS|VB>\Treeview\Treeview.sln
17.2 Overview
RadTreeView provides a powerful platform for building complex hierarchical navigation systems that your
clients will find intuitive and fun to use. The "lookless" nature of the control lets you build unique interfaces
like this team playoff example displayed in a horizontal tree.
The tree view's drag-and-drop capability interacts automatically within the same tree or other tree views. The
drag-and-drop functionality is based on a built-in RadDragAndDropManager, so you can drag between the
tree view and any other Silverlight element.
You can bind to hierarchical data such as file directories or relational data. Bind the entire tree view or mix-
and-match statically defined nodes and individual nodes bound to collections of disparate data. Enable the
"load-on-demand" feature for performant loading of only visible nodes. Hierarchical templates allow you to
paint each level within a hierarchy using a completely unique rendering. Template selectors choose the look
for each node on-the-fly at runtime based on the node's level within the hierarchy, the data in the node, or
any other criteria you can think up.
Nodes can be edited by the user or programmatically in the code behind. The rich event model lets you
control the flow throughout the editing cycle.
RadTreeView supports mixed collections of radio buttons and check boxes. "Tri-state" check boxes
represent intermediate states where child items are both checked and unchecked.
As with all the RadControls for Silverlight, RadTreeView plays nicely in Expression Blend where you can
make simple changes to brush colors or to make deep changes to the tree view makeup.
Project Setup
1) From the Visual Studio menu choose File > New > Project..., select the Silverlight project type, then
select the Silverlight Application template. Provide a unique name for the project and click the OK
button.
2) In the "New Silverlight Application" dialog make sure that the "Host the Silverlight application in a new
Web site" option is checked, give the project a unique name and verify that the "ASP.NET Web
Application Project" New Web Project Type option is selected. Click OK to close the dialog and create
the project.
3) In the Solution Explorer, right-click the References node and select Add References... from the
context menu. Add Assembly references:
a) Telerik.Windows.Controls
b) Telerik.Windows.Controls.Navigation
XAML Editing
1) Open MainPage.xaml for editing.
2) Add the XAML below to the main "LayoutRoot" Grid element. The XAML should be added between the
<Grid> and </Grid> tags.
<Border Margin="10"
CornerRadius="5"
BorderBrush="Red"
BorderThickness="1"
HorizontalAlignment="Left"
VerticalAlignment="Top"
MinHeight="500"
MinWidth="500"
>
</Border>
3) Drag a RadTreeView from the Toolbox to a point under the comment "<!--Tree view goes here-->". Set
the x:Name attribute to "tvMain". Add a Selected event handler.
4) Drag a RadTreeViewItem from the toolbox to a point within the RadTreeView tags. Set the Header
attribute to "Extremely Hot".
5) Add two more RadTreeViewItems below the first and set the Header attributes to "Hot" and "Mild",
respectively.
6) Inside the first RadTreeViewItem ("Extremely Hot"), add three more RadTreeViewItems and set the
Header attributes to
a) "Pure capsaicin"
b) "Naga Jolokia"
c) "Red Savina Habanero"
7) Inside the second RadTreeViewItem ("Hot"), add three more RadTreeViewItems and set the Header
attributes to
a) "Cayenne Pepper"
b) "Tabasco"
c) "Chipotle"
8) Inside the third RadTreeViewItem ("Mild"), add three more RadTreeViewItems and set the Header
attributes to
a) "Bell Pepper"
b) "Pimento"
c) "Peperoncini"
The XAML markup should now look like the example below:
<telerikNavigation:RadTreeView x:Name="tvMain"
SelectionChanged="tvMain_SelectionChanged">
<telerikNavigation:RadTreeViewItem Header="Hot">
<telerikNavigation:RadTreeViewItem Header="Cayenne Pepper" />
<telerikNavigation:RadTreeViewItem Header="Tabasco" />
<telerikNavigation:RadTreeViewItem Header="Chipotle" />
</telerikNavigation:RadTreeViewItem>
<telerikNavigation:RadTreeViewItem Header="Mild">
<telerikNavigation:RadTreeViewItem Header="Bell Pepper" />
<telerikNavigation:RadTreeViewItem Header="Pimento" />
<telerikNavigation:RadTreeViewItem Header="Peperoncini" />
</telerikNavigation:RadTreeViewItem>
</telerikNavigation:RadTreeView>
Code Behind
1) In the code-behind for the page, add references to the "Imports" (VB) or "using" (C#) section of the code
for these namespaces:
a) Telerik.Windows.Controls
This code gets a reference to the RadTreeView and it's SelectedItem property. SelectedItem is an
object type. Because we've added RadTreeViewItems directly (and haven't bound the tree view), we can
cast SelectedItem as RadTreeViewItem to use its properties, e.g. Header. Note that if the TreeView is
bound, the selected item will be a business object. Use the Header ToString() method and display the
results in a MessageBox.
1) Clicking an item should fire the SelectionChanged event and display the MessageBox with the text for
the selected item. The event should also fire when using the keyboard Arrow keys to navigate the tree
view.
2) Expanding or collapsing nodes should not fire the SelectionChanged event.
Adding a node to the tree view is as simple as creating a new RadTreeViewItem object and including it in
the tree view Items collection. The example below creates three instances of RadTreeViewItem and adds
them as root nodes to the Items collection.
tvMain.Items.Add(new RadTreeViewItem()
{
Name = "tviHot",
Header = "Hot"
});
tvMain.Items.Add(new RadTreeViewItem()
{
Name = "tviMild",
Header = "Mild"
});
}
Each RadTreeViewItem has its own Items collection and can be used to create a hierarchy in code. The
example below gets a reference to one of the root level nodes and adds three RadTreeViewItem instances
to the Items collection.
});
mild.Items.Add(new RadTreeViewItem()
{
Name = "tviPimento",
Header = "Pimento"
});
mild.Items.Add(new RadTreeViewItem()
{
Name = "tviPeperoncini",
Header = "Peperoncini"
});
}
The result of using code to add the root level nodes and sub-nodes looks like the screenshot below:
You can locate and access nodes in a variety of ways including using the index of the RadTreeViewItem
within the Items collection or using LINQ expressions against the Items collection. To use LINQ
expressions, remember to add the System.Linq namespace to your "Imports" (VB) or "using" (C#) section
of code.
The code below demonstrates finding a particular node.
// locating b y index
RadTreeViewItem hotItem =
tvMain.Items[1] as RadTreeViewItem;
Gotcha!
The Items property only contains the immediate children of an items control, so you have to
search recursively to handle n-level searches. The example below searches recursively, given an
ItemCollection and a string to search for. This example searches on the Header property, not the
Name.
// recursively search
public RadTreeViewItem FindItemByHeaderRecursive(
ItemCollection nodes, string searchFor)
{
foreach (RadTreeViewItem item in nodes)
{
if (item.Header.Equals(searchFor))
return item;
if (item.Items.Count > 0)
{
RadTreeViewItem result =
FindItemByHeaderRecursive(item.Items, searchFor);
if (result != null)
return result;
}
}
return null;
}
Forum question: "I want to be able to expand the tree to specific nodes."
Answer: Use the ExpandItemByPath() or GetItemByPath() methods. If we have a hierarchy like the small
example below and want to expand the "Pimento" node:
Mild
Bell Pepper
Pimento
Hot
Tabasco
...
ExpandItemByPath() expands the tree view so that the item in the path is exposed. GetItemByPath()
retrieves a reference to the item and also automatically expands the tree view so the item is exposed. The
first parameter is the path itself, the second is the separator character.
Once you locate a specific node, you can disable, expand, select or remove the node. The example below
selects the second node of the tree, expands the third node and disables the "Pimento" node. Use the
IsExpanded, IsSelected and IsEnabled RadTreeViewItem properties to toggle these node states.
' use nodes references to expand, select, disab le and remove nodes
mildItem.IsExpanded = True
hotItem.IsSelected = True
pimento.IsEnabled = False
To delete a node, use the tree view Remove() method and pass the item to be removed or call RemoveAt()
and pass the index of the item within the Items collection to be removed. The example below removes the
same item using both methods. Note: This way you can remove children of the RadTreeView but not
descendants. They have to be removed from their respective parents.
Images can display automatically in response to the default, expanded and selected states of the node.
Use the DefaultImageSrc, ExpandedImageSrc, SelectedImageSrc to assign paths to the images
using code or XAML. If all images reside in the same folder, assign the folder path to the ImagesBaseDir
property. Then you can omit the path for your image source properties. The example below sets the default
and selected images:
The XAML assignment sets the ImagesBaseDir to the path (notice the trailing forward slash). The
DefaultImageSrc and SelectedImageSrc are assigned only the image file name.
<telerikNavigation:RadTreeView x:Name="tvMain"
ImagesBaseDir="images/">
<telerikNavigation:RadTreeViewItem Header="Extremely Hot"
DefaultImageSrc="peppers.png"
SelectedImageSrc="peppers_selected.png">
. . .
</telerikNavigation:RadTreeView>
In code, you can assign the image paths at any time. The image source properties all take either a path or
a ImageSource instance, as shown in the example below.
tvMain.ImagesBaseDir = "images/"
tvMain.ImagesBaseDir = "images/";
tvMain.Items.Add(new RadTreeViewItem()
{
Name = "tviHot",
Header = "Hot",
DefaultImageSrc =
new BitmapImage(
new Uri("images/peppers.png",
UriKind.RelativeOrAbsolute))
,
SelectedImageSrc =
new BitmapImage(
new Uri("images/peppers_selected.png",
UriKind.RelativeOrAbsolute))
});
17.4.2 Selections
By default, you can select one node at a time in the tree view. The SelectionMode property, first introduced
in the Date, Time and Calendar chapter, can be Single, Multiple or Extended.
Note: The TreeView interprets SelectionMode “Multiple” as “Extended”, i.e. there are just two selection
modes “Single” and “Extended”. The same enum is used for convenience.
In Single SelectionMode (the default), only node date can be selected at a time. Click a node with the
mouse or press the space bar to toggle selection. Use the arrow keys to move the selection.
The Extended SelectionMode also allows any number of nodes to be selected, but the behavior is
similar to Windows Explorer. Holding the Shift key down allows a range of nodes to be selected with the
mouse or keyboard (using the arrow keys and space bar). The Control key allows individual nodes to be
selected even when they are not part of a continuous range.
Each RadTreeViewItem has a IsSelected property that can be set programmatically or in XAML. As the
user clicks nodes in the tree, the PreviewSelected, Selected, PreviewUnselected and Unselected
routed events fire on the item. The "Preview" events can be canceled. For example, to make sure the user
doesn't lose unsaved work when navigating between nodes, you could set the Handled argument to cancel
before the selection was complete. The basic idea is sketched out in the code sample below.
Dim removed = _
From item As RadTreeViewItem In e.RemovedItems _
Select item.Header.ToString()
var removed =
from RadTreeViewItem item in e.RemovedItems
select item.Header.ToString();
Expanding an individual item is simply a matter of setting the IsExpanded property to true. To expand or
collapse all items at all levels in the tree, call the ExpandAll() and CollapseAll() methods. ExpandAll() and
CollapseAll() are available not only for the RadTreeView, but for any RadTreeViewItem so that you can
expand or collapse the item and all its children recursively.
When an item expands or collapses, the PreviewExpand, Expanded, PreviewCollapsed and
Collapsed events fire. The "Preview" events can be canceled by setting the Handled argument property to
True.
To only allow expanding one node at a time, set the IsSingleExpandPath property to True. When True,
the tree view will automatically collapse one branch before expanding another.
By default you can click an item to toggle expansion. To allow expansion only when the expansion arrow
is clicked, set the IsExpandOnSingleClickEnabled and IsExpandOnDblClickEnabled properties
False.
You can build complex arrangements of checks and radio buttons, show "tri-state" check boxes that
visually indicate "intermediate" states, and use events to completely control checking and unchecking
behavior.
Start by setting the RadTreeView IsOptionElementsEnabled property to true. After that, you can set the
ItemsOptionListType property for the tree view as a whole, or for any individual RadTreeViewItem, to
OptionList or CheckList. OptionList displays radio buttons and CheckList displays check boxes. The
minimal XAML snippet below displays a list of radio buttons and also introduces the CheckState property,
that sets the check mark On, Off or to an Intermediate state.
<telerikNavigation:RadTreeView
IsOptionElementsEnabled="True"
ItemsOptionListType="OptionList">
<telerikNavigation:RadTreeViewItem
Header="Minimal" />
<telerikNavigation:RadTreeViewItem
Header="Typical" CheckState="On" />
<telerikNavigation:RadTreeViewItem
Header="Custom" />
</telerikNavigation:RadTreeView>
The ItemsOptionListType property acts against all child nodes of a given node. Using this property, you can
display mixed combinations of radio and check boxes. Starting from the previous example, you can add
three child nodes to the "Custom" node and set the ItemsOptionListType to CheckList.
<telerikNavigation:RadTreeView
IsOptionElementsEnabled="True"
ItemsOptionListType="OptionList">
<telerikNavigation:RadTreeViewItem
Header="Minimal" />
<telerikNavigation:RadTreeViewItem
Header="Typical" CheckState="On" />
<telerikNavigation:RadTreeViewItem Header="Custom"
ItemsOptionListType="CheckList"
IsExpanded="True">
<telerikNavigation:RadTreeViewItem
Header="Core Product" CheckState="On" />
<telerikNavigation:RadTreeViewItem
Header="Help Files" CheckState="On" />
<telerikNavigation:RadTreeViewItem
Header="Extras" CheckState="Off">
</telerikNavigation:RadTreeViewItem>
</telerikNavigation:RadTreeViewItem>
</telerikNavigation:RadTreeView>
Now you have a tree view that displays radio buttons at the root level and check boxes underneath the
"Custom" node.
"tri-state" refers to the ability of a check box to display not only checked and unchecked, but to show an
"intermediate" state as well. The "intermediate" state indicates to the user that child nodes have a mix of
checked and unchecked nodes. The specific visual cues used to represent check states vary, depending on
theme and styling, but the screenshot below shows a typical example. To enable the "tri-state" behavior,
you only need to set the tree view IsTriStateMode property to True.
Here is one more example with all of the previously described elements in play:
<telerikNavigation:RadTreeView
IsOptionElementsEnabled="True"
ItemsOptionListType="OptionList"
telerik:StyleManager.Theme="Summer"
IsTriStateMode="True">
<telerikNavigation:RadTreeViewItem Header="Minimal" CheckState="Off" />
<telerikNavigation:RadTreeViewItem Header="Typical" CheckState="On" />
<telerikNavigation:RadTreeViewItem Header="Custom"
ItemsOptionListType="CheckList" IsExpanded="True">
<telerikNavigation:RadTreeViewItem Header="Core Product" CheckState="On" />
<telerikNavigation:RadTreeViewItem Header="Help Files" CheckState="On" />
<telerikNavigation:RadTreeViewItem Header="CheckState = 'Intermediate'"
ItemsOptionListType="CheckList" IsExpanded="True">
<telerikNavigation:RadTreeViewItem Header="CheckState = 'On'"
CheckState="On" />
<telerikNavigation:RadTreeViewItem Header="CheckState = 'Off'"
CheckState="Off" />
<telerikNavigation:RadTreeViewItem Header="Reference Project"
CheckState="Off" />
</telerikNavigation:RadTreeViewItem>
</telerikNavigation:RadTreeViewItem>
</telerikNavigation:RadTreeView>
The screenshot below shows the XAML sample running in the browser:
17.4.5 Drag-and-Drop
Users can rearrange the tree view using the built-in drag-and-drop capability. The user can drop one or more
nodes onto another node, between nodes and onto other tree views. The drag-and-drop infrastructure is built
using the RadDragAndDropManager, so all the RadDragAndDropManager events and properties are
available.
Your first move is to set the IsDragDropEnabled tree view property to "True". If you want to simply move
nodes around in a tree or drag them between trees without any rules, then you're done. But typically there
are business rules that govern what can be dragged and where nodes can be dropped. Look at the
screenshot above for example. We have a set of possible products that can be dragged to a "Shopping
Cart" tree view. So right away we have several implied business rules:
Root level nodes should not be dragged. The "Processors", "Displays" and "Keyboards" nodes should
behave as if fixed into place.
Nodes with Level == 1 should be dropped under Level 0 nodes. We should only drop the "Standard"
keyboard node under one of the root level, "fixed" nodes.
Nodes that describe particular processors should only be dragged to a root level "Processors" node.
Displays should be dragged under the "Displays" node and likewise for keyboards.
Nodes should not be dropped to the same tree.
In some other application it might be ok to drag the root level nodes around. You have to decide that for
yourself and add logic to enforce the behavior. That begs the question, "ok, so how do I enforce the
business logic?"
You can get some minimal control using the IsDropAllowed property of the RadTreeViewItem. But to get
the fine-grain control you really want in a production application, you need to handle
RadDragAndDropManager events.
Notes
There are two "gotchas" you may run into right away. So, to save you some time:
1. Be sure to add Telerik.Windows to the "Imports" (VB) or "using" (C#) sections of code so that the
AddHandler() extension method is available.
2. RadTreeView will mark all the events its responds to as "handled", so, without special measures, you
will not see any events. Call the AddHandler() overload that includes the handledEventsToo
parameter. Set the handledEventsToo parameter "True" when you also want the drag-and-drop events to
fire, even though RadTreeView has already had a crack at them.
Here's what the code looks like for the application shown running in the screenshot above. The AddHandler()
extension method registers the DropQueryEvent, then all the nodes of the tree are expanded.
Dispatcher.BeginInvoke(Function() { tvPossibleOptions.ExpandAll(); })
End Sub
Dispatcher.BeginInvoke(() =>
{
tvPossibleOptions.ExpandAll();
});
}
The OnDropQuery event handler enforces all the business rules we first described. By getting references to
the source and destination RadTreeViewItem objects, we can tell what level they belong to, if the source
and destination parent tree views are the same (i.e. we're trying to drop back to the same tree we're
dragging from) and the position we're attempting to drop into (i.e. inside, before, after). Not shown here is
the XAML that includes a Tag property for each RadTreeViewItem where we define a "group" that the node
belongs to, i.e. "Processors" or "Keyboards". All of this logic is arbitrary and must be modified to fit your
particular business requirements. This example covers a lot of ground though and addresses a number of
issues you may run into.
You may want a small representative sample of the XAML to refer to. This XAML defines the left-most tree
view shown in the screenshot.
<telerikNavigation:RadTreeView IsDragDropEnabled="True"
IsDragPreviewEnabled="True" IsDragTooltipEnabled="True"
IsDropPreviewLineEnabled="True" Style="{StaticResource TreeViewStyle}" >
<telerikNavigation:RadTreeViewItem Header="Processors"
DefaultImageSrc="ram.png" Tag="Processor">
<telerikNavigation:RadTreeViewItem
Header="Intel P8600" Tag="Processor" />
...
<telerikNavigation:RadTreeView
...
TextDropAfter="Drop it right there mister:"
. . . />
The TextDropAfter property set in the XAML above displays like this screenshot example:.
DropExpandDelay: This is a TimeSpan property that determines the wait before an item is expanded
when something is dragged over it.
17.4.6 Editing
By default, the nodes of a tree view cannot be edited. Setting the tree view IsEditable property to True
allows the user to press the F2 key to edit tree view nodes. After changes are entered, the user can press
the Esc key to cancel editing or Enter to commit their changes.
You can also set the IsInEditMode property to True to initiate editing programmatically. For example, if you
might want the user to click a button to add a new node to the tree and automatically make that node
editable:
First create the RadTreeViewItem and add it to the tree view Items collection. Then set IsInEditMode after
the node has been generated.
Much like the tree view drag-and-drop capability, once you make editing available for the tree view, it's "open
season" and the user can edit anything in the tree without restriction. This won't do for the typical
production application. Some nodes should not be editable, or not editable in certain circumstances or
should not allow certain input. The PreviewEditStarted, EditStarted, PreviewEdited, Edited and
EditCanceled routed events allow you to enforce business logic. The sequence of event is:
User presses F2 or IsInEditMode is set to True.
PreviewEditStarted
EditStarted
User presses Enter, focus is moved away from the node, or IsInEditMode is set to False
PreviewEdited
Edited
Once the edit is started and if the user presses the escape key or the CancelEdit() method is called, the
EditCanceled event fires.
As is standard with all Silverlight routed events, the "Preview" events can be canceled by setting the
Handled property to "True". For example, if you wanted to prevent root level nodes from being edited, you
could check the Level property of the node and set Handled to true if the Level was "0". Notice in the
example that you can obtain the item being edited from the parameter arguments Source property.
The MessageBox takes a string containing the new and old text, then sets Handled to True if the user
decides they don't want to confirm the edit. When Handled is set to True, the default behavior is that the
"New" text stays in place and the node is still available for editing. If you want to restore the data to a
previous state, you can set the node's Header property and call CancelEdit().
' show the user the old and new state of the edited text
Dim cancel As Boolean = MessageBox.Show(message, "Make Changes?", _
MessageBoxButton.OKCancel) = MessageBoxResult.Cancel
' the user wants to cancel, so restore the header to the old text and
' stop the edit
If cancel Then
TryCast(e.Source, RadTreeViewItem).Header = e.OldText
TryCast(e.Source, RadTreeViewItem).CancelEdit()
End If
e.Handled = cancel
End Sub
// show the user the old and new state of the edited text
bool cancel =
MessageBox.Show(message,
"Make Changes?", MessageBoxButton.OKCancel) == MessageBoxResult.Cancel;
// the user wants to cancel, so restore the header to the old text and
// stop the edit
if (cancel)
{
(e.Source as RadTreeViewItem).Header = e.OldText;
(e.Source as RadTreeViewItem).CancelEdit();
}
e.Handled = cancel;
}
RadTreeView has a rich set of keyboard commands that deserve special mention:
Arrow Keys navigate, one item at a time, within the tree. The left arrow key collapses an item if
expanded, otherwise it navigates one item up in the tree. The right arrow key expands an item or travels
one item down in the tree.
PageUp, PageDown keys page through items in the view port area.
End, Home keys navigate to last or first visible item in the tree.
Enter toggles the collapsed/expanded state of an item. If the tree is in edit mode, pressing the Enter
key confirms the new item value.
Esc cancels an edit or drag operation.
Add, Subtract expands or collapses a selected item.
Multiply, Divide expands or collapses all child items.
F2, Space puts an item into edit mode.
Shift is used to select multiple contiguous items.
Ctrl is used to select multiple items that may not be contiguous.
17.4.8 Performance
At the time of this writing, tree view aspects relating to performance are in flux, but this forum entry may
help your planning.
Question: Are there some tweaks I can make to help improve performance of the TreeView?
Answer: Generally, you can think of performance in the following terms:
1. A smart use of templates/control logic can achieve good performance for items in the
hundreds (1-1000). Currently the Template of the TreeViewItems has a lot of elements that
you may not need - for example a loading animation, disabled state visual, checkbox, radio
button, lines, edit presenter, etc. The total number of visual elements for a single TreeView
item is 25 and could be reduced to approx 5-6.
2. UI Virtualization can increase the usable items count to thousands - 1000-10000. See the UI
Virtualization section below.
3. Data Virtualization is needed for more items, going to 1 mil. for example.
UI Virtualization
RadTreeView supports virtualization to allow large amounts of nodes to be presented without performance
loss. Things to keep in mind:
The IsVirtualizing property needs to be set to true.
Containers will be generated only for the visible items.
The GetContainerByItem() method becomes expensive and should not be called in a cycle. The tree
view needs to do some “guessing” where the container of an item is, which may take time if the tree
view has deep jagged hierarchy.
Containers for expanded items are currently not virtualized. There are some issues in Silverlight
currently that demand this, so keeping hundreds of expanded items will hurt performance.
The virtualization has two modes: Standard and Recycling. The “Recycling” is the default. It creates a
visual cache of items per-ItemsControl. This way scrolling is faster but the tree view takes more
memory. For “Standard” the TreeView should take less memory but scroll slower. (i.e. this is the
memory-cpu tradeoff with caches). The mode is set through the TreeViewPanel VirtualizationMode
attached property found in the Telerik.Windows.Controls.TreeView namespace.
<telerikNavigation:RadTreeView
IsVirtualizing="True"
telerkTreeView:TreeViewPanel.VirtualizationMode="Recycling" ...
Animation
You can also disable animation like so:
<nav:RadTreeView animation:AnimationManager.IsAnimationEnabled="False" />
17.5 Binding
17.5.1 Basic Binding
Minimally, you can assign an IEnumerable collection to the RadTreeView ItemsSource property to bind it.
For example, it's perfectly OK to assign a collection of strings:
The bound tree view might show up in the browser like the screenshot below. You will be able to select
nodes, edit nodes and perform drag-and-drop operations on the bound data.
Gotcha!
Not only can you bind the tree view, you can bind each of the nodes. The screenshot below shows a single
"Categories" node added to the root of the tree view with our bound items underneath the root node.
The code snippet for this example adds a single RadTreeViewItem with header "Categories" to the Items
collection of the tree view, then binds a collection underneath the programmatically defined node using the
node's ItemsSource property
.
item.ItemsSource =
new ObservableCollection<string>()
{
"Hardware", "Clothing", "Electronics"
};
}
Of course, it's not worthwhile to bind a tree view just to get a flat list. Tree views are all about hierarchical
data. For that you need to use HierarchicalDataTemplates as previously discussed in the "Menu
Controls" chapter. A quick reminder on hierarchical templates work: The RadTreeView ItemTemplate points
to a HierarchicalDataTemplate. Each HierarchicalDataTemplate ItemTemplate attribute points up to another
HierarchicalDataTemplate except the last template that has no children. This last template is a
DataTemplate. Take a look at the example below that represents a corporate hierarchy with President/Vice
President, Director and Manager.
Each level of the hierarchy has its own template. The RadTreeView ItemTemplate points at the
"PresidentTemplate", the "PresidentTemplate" ItemTemplate points to the "VPTemplate" and so on until the
last "ManagerTemplate".
<UserControl.Resources>
...
<DataTemplate x:Key="ManagerTemplate">
. . . content
</DataTemplate>
<telerik:HierarchicalDataTemplate x:Key="DirectorTemplate"
ItemTemplate="{StaticResource ManagerTemplate}">
. . . content
</telerik:HierarchicalDataTemplate>
<telerik:HierarchicalDataTemplate x:Key="VPTemplate"
ItemTemplate="{StaticResource DirectorTemplate}">
. . . content
</telerik:HierarchicalDataTemplate>
<telerik:HierarchicalDataTemplate x:Key="PresidentTemplate"
ItemTemplate="{StaticResource VPTemplate}">
. . . content
</telerik:HierarchicalDataTemplate>
</UserControl.Resources>
...
<telerikNavigation:RadTreeView x:Name="tvMain"
ItemTemplate="{StaticResource PresidentTemplate}">
</telerikNavigation:RadTreeView>
Walk Through
This walk through will first demonstrate how to display a static hierarchy using the templates as explained
above. Then a more maintainable solution will be presented that uses a single HierarchicalDataTemplate.
Project Setup
1) From the Visual Studio menu choose File > New > Project..., select the Silverlight project type, then
select the Silverlight Application template. Provide a unique name for the project and click the OK
button.
2) In the "New Silverlight Application" dialog make sure that the "Host the Silverlight application in a new
Web site" option is checked, give the project a unique name and verify that the "ASP.NET Web
Application Project" New Web Project Type option is selected. Click OK to close the dialog and create
the project.
3) In the Solution Explorer, right-click the References node and select Add References... from the
context menu. Add Assembly references:
a) Telerik.Windows.Controls
b) Telerik.Windows.Controls.Navigation
c) Telerik.Windows.Themes.Summer
XAML Editing
1) Open MainPage.xaml for editing.
2) Add XML namespace references to System.Windows, Telerik.Windows.Controls, Telerik.
Windows.Controls.Navigation and to the project itself. This last reference will allow access to our
data source that we will write later.
<UserControl
xmlns:local=
"clr-namespace:_03B_Binding_Hierarchy"
xmlns:telerikNavigation=
"clr-namespace:Telerik.Windows.Controls;assembly=Telerik.Windows.Controls.Navigation"
xmlns:telerik=
"clr-namespace:Telerik.Windows.Controls;assembly=Telerik.Windows.Controls"
xmlns:controls=
"clr-namespace:System.Windows.Controls;assembly=System.Windows">
3) Add a UserControl Resources element inside the UserControl element as shown below.
The two comments are mark ing where you will add resources in the following steps. The first set of
resources that control appearance related properties of the page aren't important to this example and
can be copied and pasted without examination. The second set of resources, i.e. "<!--data binding
resources-->", is the crucial part of this example that mak es hierarchical data binding work .
<UserControl.Resources>
</UserControl.Resources>
4) Add the following appearance related XAML under the comment "<!--resources for the appearance of
the page-->".
5) Add the following appearance related XAML under the comment "<!--data binding resources-->".
"local:OrgChart" will point to an object we will write later to contain our data. Each of the
HierarchicalDataTemplate tag contains an ItemsSource attribute that specifies a collection property in
the OrgChart object and an ItemTemplate attribute that points to the next template in line. The last
template in line is a DataTemplate called "ManagerTemplate".
<DataTemplate x:Key="ManagerTemplate">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding FullName}" />
<TextBlock Foreground="PowderBlue" Text="{Binding Title}" />
</StackPanel>
</DataTemplate>
<telerik:HierarchicalDataTemplate x:Key="DirectorTemplate"
ItemsSource="{Binding DirectReports}"
ItemTemplate="{StaticResource ManagerTemplate}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding FullName}" />
<TextBlock Foreground="SkyBlue" Text="{Binding Title}" />
</StackPanel>
</telerik:HierarchicalDataTemplate>
<telerik:HierarchicalDataTemplate x:Key="VPTemplate"
ItemsSource="{Binding DirectReports}"
ItemTemplate="{StaticResource DirectorTemplate}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding FullName}" />
<TextBlock Foreground="SlateBlue" Text="{Binding Title}" />
</StackPanel>
</telerik:HierarchicalDataTemplate>
<telerik:HierarchicalDataTemplate x:Key="PresidentTemplate"
ItemsSource="{Binding DirectReports}"
ItemTemplate="{StaticResource VPTemplate}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding FullName}" />
<TextBlock Foreground="Blue" Text="{Binding Title}" />
</StackPanel>
</telerik:HierarchicalDataTemplate>
6) Add the XAML below to the main "LayoutRoot" Grid element. The XAML should be added between the
<Grid> and </Grid> tags.
7) Drag a RadTreeView from the Toolbox and drop it just below the "<!--tree view-->" comment, under the
TextBlock. Set the RadTreeView ItemsSource property to "{StaticResource OrgChart}" and the
ItemTemplate to "{StaticResource PresidentTemplate}". The XAML should look like the example
below when you're done.
In these next steps we will create an "OrgChart" object that has a collection of "Management" objects. The
OrgChart object will form a corporate reporting structure where each Management object will have a
"DirectReports" collection of other Management objects.
1) In the Solution Explorer, right-click the project and select Add > Class... from the context menu. Name
the class file "Management.cs" and click Add to create the class file.
2) In the code-behind for the class, add references to the "Imports" (VB) or "using" (C#) section of the
code for these namespaces:
a) System.Collections.ObjectModel (to support the ObservableCollection class)
b) System.ComponentModel (to support the INotifyPropertyChanged interface)
3) Add a public enumeration "Title" with members "President", "VicePresident", "Director" and
"Manager".
4) Add a Management class to the class file. It should implement the INotifyPropertyChanged interface. It
should have a string "FullName" property, Title property, and DirectReports property. DirectReports will
be an ObservableCollection of Management.
set
{
if (_fullName != value)
{
_fullName = value;
OnPropertyChanged("FullName");
}
}
}
set
{
if (_directReports != value)
{
_directReports = value;
OnPropertyChanged("DirectReports");
}
}
}
set
{
if (_title != value)
{
_title = value;
OnPropertyChanged("Title");
}
}
}
5) In the Solution Explorer, right-click the project and select Add > Class... from the context menu. Name
the class file "OrgChart.cs" and click Add to create the class file.
6) In the code-behind for the class, add references to the "Imports" (VB) or "using" (C#) section of the
code for this namespace:
a) System.Collections.ObjectModel (to support the ObservableCollection class)
It inherits from ObservableCollection of type Management. The class simply instantiates a number of
Management objects and adds them to the collection. notice that the Management objects are nested
by way of the DirectReports property.
{
new Management()
{
FullName = "Aya Sato",
Title = Title.VicePresident,
DirectReports =
{
new Management()
{
FullName = "Hamlima Massri",
Title = Title.Director,
},
new Management()
{
FullName = "Robert Simpson",
Title = Title.Director,
},
new Management()
{
FullName = "Mary Sweitzer",
Title = Title.Director,
},
new Management()
{
FullName = "Lian Ran",
Title = Title.Director,
}
}
},
new Management()
{
FullName = "Petar Sofianski",
Title = Title.VicePresident,
DirectReports =
{
new Management()
{
FullName = "Isabella Benson",
Title = Title.Director,
},
new Management()
{
FullName = "Aaron Silverman",
Title = Title.Director,
DirectReports =
{
new Management()
{
FullName = "Sam Schwartz",
Title = Title.Manager,
},
new Management()
{
FullName = "Adelia Luna",
Title = Title.Manager
}
}
}
}
}
}
});
}
}
The example so far shows how you can customize each level if you have a static hierarchy. The problem
with this approach is that its inflexible and not easy to maintain. The structure of each level in the hierarchy
is identical, so do I need a template for each level? No, a single template will do, as shown below. This
solution will handle additional levels automatically, as long as they follow the same structure where the
ItemsSource refers to the collection for the next level down. Change the example to include only a single
HierarchicalDataTemplate as shown below. Notice that the ItemTemplate is not defined because we don't
want to refer to another template. Also notice the "TitleToBrushConverter" that we will implement in a later
step.
<UserControl.Resources>
...
<local:OrgChart x:Key="OrgChart" />
<local:TitleToBrushConverter x:Name="TitleToBrushConverter" />
<telerik:HierarchicalDataTemplate
x:Key="DirectReportsTemplate"
ItemsSource="{Binding DirectReports}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding FullName}" />
<TextBlock Text="{Binding Title}"
Foreground="{Binding Title, Converter={StaticResource TitleToBrushConverter}}"
Margin="5, 0, 0, 0" />
</StackPanel>
</telerik:HierarchicalDataTemplate>
</UserControl.Resources>
<Grid x:Name="LayoutRoot">
<Border Style="{StaticResource BorderStyle}">
<StackPanel Style="{StaticResource TreeStackPanel}">
<TextBlock Text="Organization Chart"
Style="{StaticResource TitleStyle}" />
<!--tree view-->
<telerikNavigation:RadTreeView
ItemsSource="{StaticResource OrgChart}"
ItemTemplate="{StaticResource DirectReportsTemplate}">
</telerikNavigation:RadTreeView>
</StackPanel>
</Border>
</Grid>
</UserControl>
The TitleToBrushConverter accepts the bound object's Title enumeration property and returns a brush. The
brush is applied to the text Foreground property. Here's the implementation of the new
TitleToBrushConverter class.
#End Region
End Class
Template selectors let you choose between multiple templates at runtime. The screenshot below shows a
template for the president that has an image and where the other templates also have slight differences in
foreground brush colors.
Notes
Templates are selected when an item is first prepared but cannot be changed once shown.
The key piece to making template selectors work is first to inherit from the DataTemplateSelector class
and override its SelectTemplate() method. Use the method's "item" parameter to reference the business
object bound to the template. In this case the business object is a Management object with Name and Title
properties.The example uses the Title property to determine which template should be returned.
"MyTemplateSelector" defined below also has properties to store each of the types of templates it might
select (these properties are set later in the XAML).
Public Overrides Function SelectTemplate(ByVal item As Object, ByVal container As DependencyObject) As DataTemp
MyBase.SelectTemplate(item, container)
The template selector "MyTemplateSelector" defines the template properties, "PresidentTemplate", etc,
that we defined in the MyTemplateSelector code behind. Each template property is hooked up to its
respective template resource.
<UserControl.Resources>. . .
<telerik:HierarchicalDataTemplate x:Key="Manager">
<TextBlock Text="{Binding FullName}" Foreground="{StaticResource ManagerBrush}" />
</telerik:HierarchicalDataTemplate>
<local:MyTemplateSelector x:Key="MyTemplateSelector"
PresidentTemplate="{StaticResource President}"
VPTemplate="{StaticResource VP}"
DirectorTemplate="{StaticResource Director}"
ManagerTemplate="{StaticResource Manager}" />
</UserControl.Resources>
<telerikNavigation:RadTreeView
ItemsSource="{StaticResource OrgChartDataSource}"
ItemTemplateSelector="{StaticResource MyTemplateSelector}">
</telerikNavigation:RadTreeView>
17.5.4 Load-On-Demand
The end-user doesn't need to see every node in the tree. Nodes only
need to be retrieved when they are expanded, i.e. "on demand". In
the running application, a "spinny" graphic automatically displays to
indicate that data is being retrieved for the expanding node.
You can mix-and-match so that some nodes are pre-loaded and expanded, while less used nodes are
loaded when the user clicks the expansion button. The general steps for making load-on-demand work are
the same without regard to the actual source of the data, be it from a web service, from a network source or
from some other origin. The key pieces for getting load-on-demand to work are:
Set the RadTreeView IsLoadOnDemandEnabled property to "True".
Handle the LoadOnDemand event. In the event, get a reference to the node being expanded and
assign its ItemsSource property. The ItemsSource property for the node supplies just the data for the
node's children and no more.
The code below shows a modified "OrgChart" example that demonstrates the load-on-demand feature. The
"DirectReports" property is assigned to each node ItemsSource property as needed. Here are some things
to look for in the code:
In the Loaded event, the IsLoadOnDemandEnabled property is set to "True" and the
LoadOnDemand event is assigned an event handler. Both of these could be defined directly in the
XAML. The root level object in OrgChart is assigned as the root of the tree.
The LoadOnDemand event handler has two major aspects. Part of the code simulates an asynchronous
operation with some latency and is included here just to get a feel for how the tree view behaves when
loading from a remote source over the network. The significant part of the code, that you should pay
attention to, gets a reference to the expanded node and its bound object, then assigns the node's
ItemsSource property if there's any data. Notice that if there is no data to be assigned, the node's
IsLoadOnDemandEnabled property is set "False" to remove the expansion button.
Question: "If I know that a node has no children, I don't want to display the expansion button to
begin with. Is there a way to hide the expansion button when the node is first displayed?"
Answer: Handle the ItemPrepared event and set the IsLoadOnDemandEnabled to "False" when
there are no children available.
17.6 Customization
The tree view is a complex control with many templated elements that let you control the appearance of the
expander button, drag-and-drop elements, header and individual nodes, just to name a few.
To change the appearance of nodes in the tree view, edit the RadTreeViewItem style and assign this new
style to the tree items. Here are some of the basic approaches:
If you code (i.e. create) tree items in XAML than you should modify the Style property.
If you bind the tree's ItemsSource property you should use the tree view ItemContainerStyle property
If you have different items or need to dynamically change styles at runtime than you should use the
ItemContainerStyleSelector property.
The easiest way to get started is to edit an existing Style. Using Expression Blend, for example, you can
add a RadTreeView to a page with at least one RadTreeViewItem. Select the tree item and choose to Edit
Template from the context menu. This action will generate a new item style and can be modified to suit your
requirements. This will allow you to change various elements of the item such as MouseOverVisual and
SelectionUnfocusedVisual. The screenshot below shows the brushes used by the MouseOverVisual and
SelectionUnfocusedVisual changed to fit the "Scoville" set of red, yellow, orange and black colors. The
RadTreeView itself for the most part is transparent. In the example below, a Border was placed around the
RadTreeView and the background was filled with a RadialGradientBrush providing the "sunburst" effect you
see below.
"Scoville" Styles
We will use a set of colors that include black, red, yellow and orange in many of the style related
topics and prefix the style names with "Scoville". The "Scoville scale" measures the spiciness of
peppers and other culinary irritants.
Walk Through
In this example we will customize the RadTreeViewItems of a RadTreeView.
Project Setup
1) Run Expression Blend.
2) From the File menu select New Project. Note: If you have an existing solution open, right-click the
solution and select Add New Project... from the context menu instead.
3) In the New Project dialog, select "Silverlight" from "Project types" and "Silverlight 3" Application from
the right-most list. Enter a unique name for the project and click OK.
5) Add a RadTreeView from the Assets pane into the Border. Again, watch the tool tip to see that the
tree view is created "in" the Border (i.e., not as a sibling of the Border).
6) Drag three RadTreeViewItem from the Assets pane into the RadTreeView. Select each
RadTreeViewItem in the Objects and Timeline pane and set the Header properties in the Properties
pane to "Extremely Hot", "Hot" and "Mild".
Notes
Alternatively, you can switch to the XAML view using the buttons on the upper right hand side of
the Artboard and paste in the same node structure used in the Getting Started chapter. The
XAML is included below for convenience.
<telerikNavigation:RadTreeView>
<telerikNavigation:RadTreeViewItem Header="Hot">
<telerikNavigation:RadTreeViewItem Header="Cayenne Pepper" />
<telerikNavigation:RadTreeViewItem Header="Tabasco" />
<telerikNavigation:RadTreeViewItem Header="Chipotle" />
</telerikNavigation:RadTreeViewItem>
<telerikNavigation:RadTreeViewItem Header="Mild">
<telerikNavigation:RadTreeViewItem Header="Bell Pepper" />
<telerikNavigation:RadTreeViewItem Header="Pimento" />
<telerikNavigation:RadTreeViewItem Header="Peperoncini" />
</telerikNavigation:RadTreeViewItem>
</telerikNavigation:RadTreeView>
7) In the Objects and Timeline pane, select the RadTreeView to edit its properties. In the Properties
pane > Layout panel, set the HorizontalAlignment and VerticalAlignment properties to center. This will
place the tree view in the dead center of the "sunburst" inside the border.
8) In the Objects and Timeline pane, select the Border and edit its properties.
a) In the Layout panel, locate the Advanced Property Options button next to the Width and Height
properties. Click the "Set to Auto" button.
b) In the Properties pane, select "Background" from the Brushes panel. Click the Gradient Brush button.
Click the Radial Gradient button.
9) In the Gradient Bar, select the left-most gradient stop indicator, then choose a yellow color from the
palette. Select the right-most gradient stop indicator and choose a red/orange color from the palette.
Now the tree view in the Artboard should look something like the screenshot below:
10)Right-click the first RadTreeViewItem in the tree view and select Edit Template > Edit a Copy from
the context menu. In the "Create Style Resource" dialog, set the Name (Key) to "ScovilleItem". Click
OK to create the style resource and close the dialog.
11)In the Objects and Timeline pane, open up the "HeaderRow" and select the "MouseOverVisual" node.
b) Click the color block to the left of the "NavigationMouseOverFill" to display the color editing drop
down.
c) Select the gradient stop indicators and select yellow, orange and red from the palette. The exact
colors aren't critical, but could look something like the screenshot below.
Tip!
Also notice that you can set the "A" (Alpha or transparency) percentage to something less than
100%. This allows you to use stronger, darker colors without overpowering the content of the
item.
13)Change the SelectionVisual brush using the same techniques you used to modify the
MouseOverVisual. The brush in this case will be "NavigationSelectFill" brush. Change the colors for this
brush to use the same palette of red, orange and yellow, but make the color selection somewhat
different than the MouseOverVisual.
14)In the Objects and Timeline pane, click the Return Scope button to return to the UserControl scope.
15)Up to this point we have styled the first RadTreeViewItem in the tree. To apply this style to other items,
right-click each RadTreeViewItem in the Objects and Timeline pane and select Edit Template > Apply
Resource > "ScovilleItem" from the context menu.
17.7 Wrap Up
In this chapter we covered a wide range of tasks that exercise many of the RadTreeView features including
defining trees manually, using the API to add/remove/enable/select nodes, locating and accessing nodes,
adding images to nodes, handling node expansion and reacting to node selection. You also defined trees
with mixed groups of radio buttons and checkboxes. You learned how to work with drag-and-drop
operations, both to enable simple drag-and-drop functionality and to fine-tune the behavior based on multiple
conditions such as source and target nodes and the state of the data associated with a node.
You bound the tree to simple lists of data, then bound specific nodes to data sources. Then you used
Hierarchical Templates to organize the data and present a specific appearance based on the level of data.
You learned how to use Template Selectors to choose templates where decisions need to be made on-the-
fly at runtime. You used the load-on-demand feature to allow performant data loading restricted to nodes
being expanded at any one time.
Finally, you learned how to customize the appearance of individual nodes using Expression Blend.
XVIII
GridView
634 RadControls for Silverlight
18 GridView
18.1 Objectives
In this chapter you will be introduced to RadGridView and many of its key features. Starting out you will
bind the grid view to some basic data. Then you will see how to expand the example to use hierarchical
data and how to customize columns.
While delving into the details of RadGridView you will work with selected rows and cells. You will handle
events that notify you when the user is moving within the grid view. You will learn the programming model
commonalities for filtering, sorting and grouping. While working with groups, you will learn how to add
aggregate functions for each group. You will be introduced to column types and learn about special columns
that handle images, hyperlinks and lookups. The "Grid View Elements Visibility" section will demonstrate
how to show and hide the visual elements of the grid view. You will learn how to output and format content
by using the grid view export and print methods.
In the Binding section of this chapter you will connect the grid view to simple .NET objects. From there you
will build a REST service against the Twitter API. Then you will build WCF and WCF RIA services. In the
process you will work with inherent Silverlight security restrictions. You will also learn how to customize the
layout of an entire grid view row.
In the Customization section of this chapter you will customize grid view cells to achieve a unique look.
\Courseware\Projects\<CS|VB>\Gridview\Gridview.sln
18.2 Overview
RadGridView is the ultimate Silverlight grid control that features unrivalled performance through LINQ-based
data engine, remarkably flexible hierarchy model, advanced features such as Excel-like filtering, row details,
totals, export to Word/Excel/CSV and many more. RadGridView is a truly lookless Silverlight Grid that can
be easily customized to blend perfectly into your applications.
RadGridView has a truly impressive list of features that make it a workhorse for your RIA applications:
Project Setup
1) From the Visual Studio menu choose File > New > Project..., select the Silverlight project type, then
select the Silverlight Application template. Provide a unique name for the project and click the OK
button.
2) In the "New Silverlight Application" dialog make sure that the "Host the Silverlight application in a new
Web site" option is checked, give the project a unique name and verify that the "ASP.NET Web
Application Project" New Web Project Type option is selected. Click OK to close the dialog and create
the project.
3) In the Solution Explorer, right-click the References node and select Add References... from the
context menu. Add Assembly references:
a) Telerik.Windows.Controls
b) Telerik.Windows.Controls.GridView
c) Telerik.Windows.Data
d) Telerik.Windows.Controls.Input.dll
XAML Editing
1) Open MainPage.xaml for editing.
2) Drag a RadGridView control from the Toolbox to a point between the "LayoutRoot" <Grid> and </Grid>
tags. Set the x:Name attribute to "gvMain" so the grid view can be referenced later in code.
<Grid x:Name="LayoutRoot">
<telerikGridView:RadGridView x:Name="gvMain">
</telerikGridView:RadGridView>
</Grid>
Code Behind
1) In the code-behind for the UserControl, add a Category class with a single string "Description" field. The
class should be added after and outside the UserControl class.
2) In the constructor, create a generic list of Category and assign it to the RadGridView ItemsSource
property.
public MainPage()
{
InitializeComponent();
.Description = "Kayaks", . _
Products = New List(Of Product) (New Product() {New Product() With { _
.Description = "Touring Kayak"}, New Product() With { _
.Description = "Double Frame"}, New Product() With { _
.Description = "Ocean Kayak"}})})
End Sub
End Class
End Class
this.Add(new Category()
{
Description = "Dinghies",
Products = new List<Product>()
{
new Product() { Description = "Sport Dinghy" },
new Product() { Description = "Ribbed with Wood Floor" },
new Product() { Description = "Fishing Dinghy" },
new Product() { Description = "Yacht Tender" }
}
});
this.Add(new Category()
{
Description = "Canoes",
Products = new List<Product>()
{
new Product() { Description = "Maine River Canoe" },
new Product() { Description = "Seattle Sport" },
new Product() { Description = "Inflatable Canoe" }
}
});
}
}
In the constructor, define a GridViewTableDefinition where the Relation property points to the collection
of child objects.
public MainPage()
{
InitializeComponent();
Running in the browser, the example now looks like this screenshot:
By default, the AutoGenerateColumns property is false and all the columns in the bound object are
created for you. You can control the columns that display and the column properties. For example, you
can eliminate the "Products" column that displays "System.Collection" and set the "Description"
column Header property to read "Category Description". First, set the AutoGenerateColumns property
to "False", then add to the Columns collection.
' Only display the "Description" column, not the "Products" column
gvMain.AutoGenerateColumns = False
gvMain.Columns.Add(New GridViewDataColumn() With { _
.DataMemberBinding = New Binding("Description"), .Header = "Category Description"})
Now, only the new column description with its header displays in the grid.
Style the grid using one of the pre-defined themes. As with all the RadControls for Silverlight, you need
to add the theme assembly (i.e. Telerik.Windows.Themes.Vista), add a XML namespace reference to
the Telerik.Windows.Controls assembly in the XAML markup, and finally, add a StyleManager.Theme
assignment to the theme. The relevant XAML markup will look like this example:
<UserControl
xmlns:telerik=
"clr-namespace:Telerik.Windows.Controls;assembly=Telerik.Windows.Controls"
. . .>
<Grid x:Name="LayoutRoot">
<telerikGridView:RadGridView
telerik:StyleManager.Theme="Vista"
x:Name="gvMain"></telerikGridView:RadGridView>
</Grid>
</UserControl>
The grid in the browser now displays the new theme without having to make any other specific styling
changes:
Row selection is always allowed in the RadGridView and can't be turned off. When you click a row, the
SelectionChanged event fires. The most straight-forward way of getting the object for the selected row is
to access the RadGridView SelectedItem property and cast the item to the appropriate type. In the
example below, the grid view is bound to a list of Category objects. The SelectedItem is retrieved, cast to a
Category type and the properties of Category are used to populate a TextBlock.
You could turn on the MultiSelect property and use the SelectedItems property. When MultiSelect is
"True" the user can hold down the Shift key and click to select a range of contiguous items or hold down
Ctrl and click non-contiguous items anywhere in the list.
The example below uses LINQ to transform the SelectedItems into a collection of String. The String.Join()
method turns the collection into a comma delimited list for display in a TextBlock. "CategoryName" for three
selected items is shown at the top of the screenshot.
If the selected item has scrolled away from view, you can use the ScrollIntoView() method and pass it the
data bound to a particular row. For example, the code below gets the item for the last row, adds it to the
SelectedItems collection if its not there already and then calls ScrollIntoView(), passing it the last item.
You can get a selected cell by handling the CurrentCellChanged event. The event arguments passed in
include OldCell and NewCell references. Not only can retrieve the Content of the cell, but you can alter all
the properties of the cell. In the example below, the OldCell font is set to a bold italic and the NewCell
Content is displayed in TextBox at the top of the page. Note: use the cell DataContext to get the object
bound to the row.
Question: I am currently writing a method to reverse the selected rows in the RadGridView. But
adding items to the selected items in a "for" loop its becomes very slow. (It actually hangs the UI
on the test page as there are about 5000 rows to select.) The SelectAll and UnselectAll methods
on the grid are a lot quicker though (even with the 5000 of rows). So I was wondering is there a
way to load the selected items collection quickly?
On a side note is it possible to display the Asynchronous animation from a call in the code
behind? As I would quite like to display the "busy" message while we wait for a service call to
return or when we invert the selection.
Answer: 1) You could utilize the UnselectAll() and SelectAll() methods to boost the performance
when reverting the selection.
2) RadGridView has a property called IsBusy that controls the visibility of its loading indicator -
when set to true the loading indicator is shown. You can use this property to control the loading
indicator when you are waiting for a response from a service call.
You can select and de-select all items at once using the SelectAll() method and UnselectAll()
methods. You can also add and remove from the SelectedItems collection programmatically, as
shown in this next example where the selections for the entire grid are toggled. The code first
determines if there are a greater number of selected or unselected items in order to determine the
most efficient algorithm. The selected items are saved temporarily, the SelectAll()/UnselectAll()
method is called, then the saved selected items are added or removed from the SelectedItems
collection.
gvMain.IsBusy = False
End Sub
gvMain.IsBusy = false;
}
Filtering, sorting and grouping all follow a similar programming model. Each have a collection of descriptor
objects. The descriptor defines the operation specifics, e.g. what column to sort on. The descriptor is
simply added to the collection to enable the operation. The upcoming sections demonstrate defining each
kind of descriptor, adding to the collection, and viewing the result effect in a screenshots.
Parallel to this mechanism of descriptor collections, each operation also has Filtering, Sorting and
Grouping events. These events allow you to get "down to the metal" so you can react to non-standard or
more complex situations. Each event supplies "new" and "old" descriptor objects as well as a Cancel
property to prevent the operation from occurring.
18.4.2.1 Filtering
You can filter RadGridView on the client, assuming you are not filtering on the database server, or further
up, in a service before returning the data to the Silverlight client. You will need to consider the performance
tradeoffs when designing your solution. By default the user can filter by clicking the "funnel" icons available
on each group header. The pop-up filter dialog lets the user check specific values or specify a filter criteria
using an operation and value. Multiple filters can be "AND"-ed together. The user can filter multiple
columns.
The grid view FilterDescriptors collection lets you add a pre-defined filter or a custom filter of your own that
implements IFilterDescriptor. The example below uses the built-in FilterDescriptor class, a basic filter that
takes a data member name, an operation and value.
You can simply add more filters and they will "AND" together like this example where only one record
survives the process:
FilterDescriptor filter =
new FilterDescriptor("CategoryName", FilterOperator.StartsWith, tbValue1.Text);
gvMain.FilterDescriptors.Add(filter);
FilterDescriptor filter2 =
new FilterDescriptor("Description", FilterOperator.Contains, tbValue2.Text);
gvMain.FilterDescriptors.Add(filter2);
To "OR" filters together, CompositeFilterDescriptor lets you set a logical operator that defines the
relationship between two filters. You can add any IFilterDescriptor implementation to the
CompositeFilterDescriptor FilterDescriptors collection and define the LogicalOperator property as "Or"...
or "AND". The example below uses the same FilterDescriptors and values as the previous example, but
adds these two descriptors to a ComposititeFilterDescriptor with an "Or" logical operator. Now the filter
operation brings back a wider set of data.
gvMain.FilterDescriptors.Add(composite)
FilterDescriptor filter =
new FilterDescriptor("CategoryName", FilterOperator.StartsWith, tbValue1.Text);
FilterDescriptor filter2 =
new FilterDescriptor("Description", FilterOperator.Contains, tbValue2.Text);
composite.FilterDescriptors.Add(filter);
composite.LogicalOperator = FilterCompositionLogicalOperator.Or;
composite.FilterDescriptors.Add(filter2);
gvMain.FilterDescriptors.Add(composite);
Disabling Filtering
By default, users can filter against any column they want. The IsFilteringAllowed property toggles the
capability for the grid view as a whole. IsFilterable toggles filtering only for a single column. When
IsFilterable is "False", the filtering UI for that column is hidden from the user, but still available
programmatically.
Distinct Values
You can get a list of distinct values for a column using the GetDistinctValues() method and passing the
column where the values should come from and a Boolean that indicates if the existing filters in the other
columns should be honored. The example below gets a distinct list of categories. If a CategoryName
appears more than once in the column of the grid view, the returned collection will only have a single
occurrence of the category name.
18.4.2.2 Sorting
Sorting in the grid view, from the user perspective, simply means clicking the heading of any row and
watching the sort rotate between sorted ascending, sorted descending and unsorted. The user can also
hold down the SHIFT key to sort on multiple columns.
The general model for sorting programmatically is similar to filtering. The grid view has a SortDescriptors
collection that you can add one or more SortDescriptor to. The SortDescriptor has properties for the
Member, and a SortDirection that can be Ascending or Descending. Also like the filtering model, simply
call the SortDescriptors Clear() method to reset the sort. Sorting can be disabled by column using the
IsSortable property.
The example below programmatically sorts by ID and Description in descending order. Notice the downward
pointing arrows in the column headers that indicate the descending sort order.
If you need to perform specialized custom sorting, handle the grid view Sort event. The event handler
arguments include the NewSortingState property (Ascending, Descending or None), a Cancel property if
you want to prevent the sort from occurring and a Column property (GridViewColumn type that may be cast
to a specific column). The somewhat capricious example below prevents descending sorts on integer
columns.
18.4.2.3 Grouping
Users can drag column headings up into the group panel to create groupings. The group can be deleted
from the group panel either by dragging the group off the page or clicking the close button, or the user can
click the group to cycle the sort order between ascending, descending and no sort order.
When you group programmatically, you can add aggregate functions that will be handled automatically by
the grid. Again, like the filtering and sorting programming model, we start with a GroupDescriptors
collection that holds a set of GroupDescriptor objects. Each GroupDescriptor defines a data Member
property and a SortDirection. Then you can add to the GroupDescriptor AggregateFunctions collection.
All the usual aggregate suspects are represented: count, sum, min, max, average, first and last. Each has
a corresponding "Function" object, e.g. CountFunction, SumFunction, etc, that describes the source field to
aggregate, the caption that appears before the aggregate result and a formatted string to present the result.
The example below groups on the Category column and sets the sort order to ascending. There are two
aggregates defined. The CountFunction definition demonstrates how to use the Caption and
ResultFormatString. The second is a SumFunction that defines the "InStock" property as the
SourceField.
The SourceField property for the category in the CountFunction code is not defined because the field is
assumed be the group column, i.e. a count of the "Category" source field.
gvMain.GroupDescriptors.Add(group)
End Sub
gvMain.GroupDescriptors.Add(group);
}
18.4.3 Editing
RadGridView can automatically supply an appropriate editor based on the data type of the column. These
all use the GridViewDataColumn type. Here are some of the editors you will see for the
GridViewDataColumn:
You can also define specific-purpose editors, such as the GridViewComboBoxColumn column.
Lookups are handled using a combo box. You can define the value field that's
being updated in the grid view, the display member for the combo box and the
selected value for the combo box.
Let's see how these columns are defined. The GridViewDataColumns are handled automatically simply by
binding to the column name. Minimally, you can set the DataMemberBinding and "you're good to go".
<telerikGridView:RadGridView x:Name="gvMain"
Margin="10" AutoGenerateColumns="False"
ItemsSource="{StaticResource Products}">
<telerikGridView:RadGridView.Columns>
...
<telerikGridView:GridViewDataColumn
DataMemberBinding="{Binding ProductName, Mode=TwoWay}" />
<telerikGridView:GridViewDataColumn
DataMemberBinding="{Binding Discontinued, Mode=TwoWay}" />
<telerikGridView:GridViewDataColumn
DataMemberBinding="{Binding LastUpdated, Mode=TwoWay}" />
</telerikGridView:RadGridView.Columns>
</telerikGridView:RadGridView>
The GridViewComboBoxColumn is used for lookups and requires more attention. In this example,
DataMemberBinding points to the property in the "Products" table that is being updated after the edit,
SelectedValueMemberPath is the value in the lookup collection and DisplayMemberPath is the
property that will display in the combo drop down.
<telerikGridView:RadGridView x:Name="gvMain"
Margin="10" AutoGenerateColumns="False"
ItemsSource="{StaticResource Products}">
<telerikGridView:RadGridView.Columns>
<telerikGridView:GridViewComboBoxColumn
Header="Category"
SelectedValueMemberPath="ID"
DisplayMemberPath="CategoryName"
DataMemberBinding="{Binding CategoryID, Mode=TwoWay}"
ItemsSource="{StaticResource Categories}"
/>
...
</telerikGridView:RadGridView.Columns>
</telerikGridView:RadGridView>
Sometimes its easier to see the relationship in diagram form. A cut down representation of the data shows
the RadGridView ItemsSource (Categories) and the ItemsSource for the GridViewComboBoxColumn
(Products) and how each of the properties relate to the data.
Question: I want to put a HyperlinkButton in my GridView but I want to pass in a query string to
the NavigateUri from my DataSource collection. Is this even possible?
Answer: We introduced two new columns: GridViewHyperlinkColumn and
GridViewDynamicHyperlinkColumn. Here is an example for both columns:
...
<telerik:GridViewHyperlinkColumn DataMemberBinding="{Binding Url}"
ContentBinding="{Binding CompanyName}" />
...
or
The GridViewHyperlinkColumn is used to define simple links where no substitutions to values in the URL
need to happen. Bind DataMemberBinding to the column holding the URL and the ContentBinding to the
column holding the text you want to display.
<telerikGridView:GridViewHyperlinkColumn
DataMemberBinding="{Binding InfoLink}"
ContentBinding="{Binding ProductName}"
/>
The GridViewDynamicHyperlinkColumn is quite useful because you can merge your database data into
a format string to produce searches on more than one column's worth of data. Use the
DataMemberBinding and DataFormatString to build what the user sees in the column, where the
DataMemberBinding replaces the DataFormatString arguments, i.e. "{0}", "{1}", etc. The
NavigateUrlFormatString and NavigateUrlMemberPaths produce the URL that will show when the user
clicks the link.
<telerikGridView:GridViewDynamicHyperlinkColumn
DataMemberBinding="{Binding ProductName}"
DataFormatString="Find out more about {0}"
NavigateUrlFormatString="http://www.bing.com/search?q={0}"
NavigateUrlMemberPaths="ProductName"
TargetName="_blank" />
You may not always want to show headers, footers, grid lines, row indicators and all the other spreadsheet-
like trappings of a standard grid. Erasing these visual cues can be challenging if you don't know where they
are, because some of the properties are not obvious, i.e. where the property names don't end in "Visibility".
The grid below has most of the visual details removed.
Let's turn a few of these settings back on so you can see how each property impacts the visual makeup of
the grid. The Background and BorderBrush properties can be assigned "Transparent" to remove them.
The screenshot below shows a lime green BorderBrush BorderThickness at 3 pixels and a green
background.
The CanUserFreezeColumns property when "True" has a side-effect where visual artifacts to the left of the
row display.
For grouped data, ShowGroupFooters toggles visibility for the element below a group of data rows.
GridLinesVisibility can be Vertical, Horizontal, Both or None (to hide grid lines altogether). The
screenshot below shows the "Both" setting.
The row indicator visually flags the current row. RowIndicatorVisibility can be Visible or Collapsed.
Alternating row styles are the traditional technique for making it easier to visually differentiate between rows
of complex data. They produce that "zebra" effect you see in the screenshot below. The BaseItemsControl
AlternationCount is zero by default, so you don't need to explicitly turn this off.
If the grid is placed in an area too small for the grid, the scroll bars will display. By default, the scrolling
behavior defers the actual scrolling of the grid view contents and instead displays a hint with the data for the
current row. The screenshot below shows that the user has scrolled to the "Cheeses" row. The subtle side-
effect is the pop-up hint itself. If you want to hide the hint, set the ScrollMode to RealTime, otherwise,
leave the property at Deferred (the default).
To summarize what we've learned up to this point, here is the XAML markup that removes most of the
common visual cues:
<telerikGridView:RadGridView
Background="Transparent" BorderBrush="Transparent"
CanUserFreezeColumns="False" ShowColumnFooters="False"
ShowColumnHeaders="False" ShowGroupFooters="False"
ShowGroupPanel="False"
RowIndicatorVisibility="Collapsed"
GridLinesVisibility="None"
ScrollMode="RealTime">
</telerikGridView:RadGridView>
Notes
To load images for each row or to make any other changes to a row or its elements, handle the grid view
RowLoaded event. Use the arguments Row property to find out what kind of a row this is (i.e. header,
footer, "New row" element, detail row), and to access the elements in the row. Important note: be sure to
add the Telerik.Windows.Controls namespace to the "Imports" (VB) or "using" (C#) section of code to get
access to critical extension methods: ChildrenOfType<T>() and ParentOfType<T>(). ChildrenOfType<T>
() is really the all-purpose "Swiss Army Knife" method that makes it easy to get at all the elements in the
row. The sample below shows how you can check the Row type, then use the ChildrenOfType<T>() method
to get a particular element in the row.
If you need to include more information about a row, show hierarchal data or provide a rich user editing
environment with RadGridView you can use the Row Details. Row Details is a DataTemplate defined on
the grid- or row-level and it is used for displaying data without affecting the dimensions of the row and the
cells within it. You can define Row Details template through the RowDetailsTemplate property of the grid.
The display mode is specified by the RowDetailsVisibilityMode property and the available two options -
Visible or VisibleWhenSelected. As the names imply the row details can be visible for each row or only
for the selected one.
You can define the row details template, like:
<telerikGrid:RadGridView x:Name="radGridView"
RowDetailsVisibilityMode="VisibleWhenSelected">
<telerikGrid:RadGridView.RowDetailsTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal"
Margin="10,10,10,10">
<TextBlock Text="City: " />
<TextBlock Text="{Binding City}" />
</StackPanel>
</DataTemplate>
</telerikGrid:RadGridView.RowDetailsTemplate>
...
</telerikGrid:RadGridView>
You can also change the appearance of the Row Details by using the RowDetailsStyle property of the
RadGridView.
18.4.6 Sizing
The ColumnWidth property can be set for the RadGridView. Valid values are Auto(default), Star,
SizeToHeader, SizeToCells or a number of pixels. The property will set Width for auto-generated
columns when they are first generated. Changes made to ColumnWidth after auto-generation will have no
effect.
Notes
Note that if the column is set to "*" then column virtualization will be off (see the following
section Performance > Virtualization for more information). Also for "*" columns to work
correctly you should not place RadGridView in panels/controls that will measure its children with
infinity: e.g. StackPanel, Grid panel with Column Width=Auto or Row with Height=Auto and
ScrollViewer. The problem is that infinite space cannot be distributed. Also see the article at
http://msdn.microsoft.com/en-us/library/cc645025(VS.95).aspx for more information about the
Silverlight layout system.
The Width for individual columns can use a "*" notation to denote width percentages.
18.4.7 Performance
18.4.7.1 Virtualization
Virtualization of rows and columns means that only visible rows and columns are created. Likewise, when a
cell is marked with IsVisible = "False", the cell is not created and does not appear in the Cells collection.
You could turn off the virtualization by setting the EnableRowVirtualization property to false which will
cause all rows to be created but it will also have a negative impact on the performance. You can also turn
off EnableColumnVirtualization and all columns will be created. It is the recommended best practice to
leave virtualization in place. If you need to work with all the data its best to work with the underlying data
instead.
Tip!
In general you should not use visual elements like GridViewRow. The problem with GridViewRow
is that RadGridView uses virtualization and only a handful of rows are available at any time (just
the visible ones).
The demo projects included with the Visual Studio installation contains a GridView > Performance
example that loads a 500,000 rows of 100 columns each. The XAML declaration is very basic:
The code-behind defines a collection of example objects populated with random data. The collection is
simply assigned to the RadGridView ItemsSource property.
Question: Performance can be slow when the RadGridView is placed inside a ScrollViewer. Why?
Answer: Some reports pointed to reduced performance of the RadGridView control when the
grid is placed in a control that measures its children with infinity. Such controls are ScrollViewer,
StackPanel (when vertical it measures with infinite height and when horizontal - with infinite
width), and Grid panel with RowDefinition Height="Auto" or ColumnDefinition Width="Auto".
When RadGridView (or any other grid) is measured with infinity virtualization is turned off which
results in reduced performance. Modify your code so that RadGridView is placed in a container
that will not measure it with infinity and the performance will be back to normal.
18.4.7.2 Paging
You can improve performance by simply limiting the number of records in view at any one time. To add add
paging, use a QueryableCollectionView to serve up the correct set of records, and a DataPager control
on the page to navigate the data. In the XAML, make sure you have a xml namespace reference to the
System.Windows.Controls.Data assembly. Add a DataPager control and set its Source attribute to bind
to the RadGridView element ItemsSource property.
<UserControl
xmlns:telerikGridView=
"clr-namespace:Telerik.Windows.Controls;assembly=Telerik.Windows.Controls.GridView"
xmlns:data=
"clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data"
. . .>
<Grid x:Name="LayoutRoot">
<telerikGridView:RadGridView x:Name="gridView" />
<data:DataPager Source="{Binding ItemsSource, ElementName=gridView}" />
</Grid>
</UserControl>
When you run the application, the DataPager will appear, allowing you to move through the data.
Exporting and printing from RadGridView are supported by extension methods in static classes
ExportExtensions and PrintExtensions, where both classes can be found in the Telerik.Windows.
Controls namespace.
18.4.8.1 Exporting
The following extension methods allow RadGridView to export tab delimited text, comma delimited text,
XML and HTML:
ToText(): Returns a string with tab delimited text.
ToHtml(): Returns an HTML string that can be saved as both "doc" and "xls" files.
ToCsv(): Returns a string with comma delimited text.
ToExcelML(): Returns an XML string.
The contents for all formats can all be written to disk using the same general technique shown below. This
particular example writes an HTML file. Because we're in a Silverlight environment, you will need to use the
SaveFileDialog to access local file storage.
Dim dialog As New SaveFileDialog() With {.Filter = "HTML files (*.html)|*.html|All files (*.*)|*.*"}
If dialog.ShowDialog() = True Then
Using stream As Stream = dialog.OpenFile()
Using writer As New StreamWriter(stream, Encoding.UTF8)
writer.Write(gridView.ToHtml())
End Using
stream.Close()
End Using
End If
18.4.8.2 Formatting
Handle the Exporting event to format elements of the grid. Each element is passed in the
GridViewExportEventArgs. You can find the usage of the element by comparing to the ExportElement
enumeration (i.e. if its a Cell, Row, HeaderRow, etc) and the elements value. The example below simply
bolds the header row text.
18.4.8.3 Printing
Print the contents of the grid view using the host browser by calling the PrintToHtml() method. Use the
same Exporting event to format the HTML content while printing.
18.5 Binding
18.5.1 .NET Objects
As you saw in the Getting Started section of this chapter, you can easily bind a simple generic list if the
user doesn't need to change the data. The typical grid isn't used as a list, but allows the user to change grid
data. For this you must descend any collections from ObservableCollection and implement
INotifyPropertyChanged for each object in a collection. No additional coding happens for the grid view,
only the bound object class definitions change. For example, the code below will run but not display a new
category object in the grid.
To make the button click code above work, begin by changing the generic List<> to an
ObservableCollection<>. Note that ObservableCollection is found in the System.ComponentModel
namespace.
' to this...
Public Class Categories
Inherits List(Of Category)
// change this...
public class Categories : List<Category>
// to this...
public class Categories : ObservableCollection<Category>
You also need to change the Products property from a List<> to ObservableCollection<>.
Then implement the INotifyPropertyChanged interface in each of the objects and sub-objects in the
collection. Implementing the interface simply surfaces the PropertyChanged event. Trigger PropertyChanged
after you assign each new property value. The sample below shows the Product class with a
INotifyPropertyChanged interface implementation. Use this same pattern to implement the interface in the
Category class.
Gotcha!
The grid may still refresh at times even if all the plumbing described here isn't done completely.
For example, if you neglect to change the Products property from List<> to
ObservableCollection, you would see a refresh of the Products when a new category is added.
This could cause hard-to-debug behavior as the project becomes more complex.
Property Paths
How can you flatten out a nested object hierarchy? For example, if our Category object has a Buyer object
that in turn has properties for "First", "Last" and "Home Phone", how do we display this on one line? The
Property Paths feature allows you to drill down and include properties from sub-objects. The example below
shows Category.Description and Category.Buyer.FirstName, both in the same row.
gvMain.AutoGenerateColumns = False
gvMain.Columns.Add(New GridViewDataColumn() With {. _
DataMemberBinding = New Binding("Description")})
gvMain.Columns.Add(New GridViewDataColumn() With {. _
DataMemberBinding = New Binding("Buyer.FirstName")})
gvMain.Columns.Add(New GridViewDataColumn() With {. _
DataMemberBinding = New Binding("Buyer.LastName")})
gvMain.Columns.Add(New GridViewDataColumn() With {. _
DataMemberBinding = New Binding("Buyer.HomePhone")}
gvMain.AutoGenerateColumns = false;
gvMain.Columns.Add(new GridViewDataColumn()
{
DataMemberBinding = new Binding("Description")
});
gvMain.Columns.Add(new GridViewDataColumn()
{
DataMemberBinding = new Binding("Buyer.FirstName")
});
gvMain.Columns.Add(new GridViewDataColumn()
{
DataMemberBinding = new Binding("Buyer.LastName")
});
gvMain.Columns.Add(new GridViewDataColumn()
{
DataMemberBinding = new Binding("Buyer.HomePhone")
}
18.5.2 REST
Twitter exposes a REST service that searches for a string and returns an XML document. The results
presented in the RadGridView look like the screenshot below.
Project Setup
1) From the Visual Studio menu choose File > New > Project..., select the Silverlight project type, then
select the Silverlight Application template. Provide a unique name for the project and click the OK
button.
2) In the "New Silverlight Application" dialog make sure that the "Host the Silverlight application in a new
Web site" option is checked, give the project a unique name and verify that the "ASP.NET Web
Application Project" New Web Project Type option is selected. Click OK to close the dialog and create
the project.
3) In the Solution Explorer, right-click the References node and select Add References... from the
context menu. Add Assembly references:
a) Telerik.Windows.Controls
b) Telerik.Windows.Controls.GridView
c) Telerik.Windows.Data
d) Telerik.Windows.Controls.Input.dll
<UserControl
xmlns:telerikControlsGridView=
"clr-namespace:Telerik.Windows.Controls;assembly=Telerik.Windows.Controls.GridView"
xmlns:telerikGridView=
"clr-namespace:Telerik.Windows.Controls.GridView;assembly=Telerik.Windows.Controls.GridView"
xmlns:telerik=
"clr-namespace:Telerik.Windows.Controls;assembly=Telerik.Windows.Controls"
Loaded="UserControl_Loaded">
This step will replace the existing "LayoutRoot" Grid element. In following steps we will replace the
comments with work ing XAML. The grid defines three rows of that will contain the search controls at the
top, the grid view in the middle and the paging controls of the bottom.
<UserControl.Resources>
<!--brushes-->
<!--styles-->
<!--custom row template-->
</UserControl.Resources>
<Grid x:Name="LayoutRoot">
<Grid.RowDefinitions>
<RowDefinition Height="50" />
<RowDefinition />
<RowDefinition Height="40" />
</Grid.RowDefinitions>
<!--search controls row-->
<!--search results-->
<!--pager-->
</Grid>
3) Replace the "<!--brushes-->" comment with the XAML below. These will color the back ground and
border to harmonize with the Twitter logo color.
<!--brushes-->
<LinearGradientBrush x:Key="RowBackgroundBrush">
<GradientStop Color="White" Offset="0" />
<GradientStop Color="#FF33CCFF" Offset="1" />
</LinearGradientBrush>
<LinearGradientBrush x:Key="RowBorderBrush">
<GradientStop Color="SkyBlue" Offset="0" />
<GradientStop Color="LightBlue" Offset=".3" />
<GradientStop Color="AliceBlue" Offset="1" />
</LinearGradientBrush>
4) Replace the "<!--custom row template-->" comment with the XAML below.
Notes
The XAML creates the layout for each GridViewRow. The screenshot below is a sample of a single
row. The outer-most Border sets the border and the back ground color of the row as a whole. Inside
that, a Stack Panel arranges groups of elements from left to right. The left-most element is an Image
that contains the "Avatar" or image representing each member mak ing a Twitter post (or "Tweet"). To
the right of the image, at the top is a Hyperlink Button that points to the author of the tweet with a link
to their page. Then, moving to the right is the publish date and time, followed by a "View Tweet"
Hyperlink Button that link s to a URL for that specific post. Underneath all of this is the content of the
tweet displayed in a TextBlock .
Tak e a moment to review the style called "GridViewStyle" that will be applied to the RadGridView. The
XAML here is mainly concerned with removing the lines, headers and other style details that might get in
the way of the custom row style we apply later.
Also tak e a careful look at "RowStyle". This style points to a ControlTemplate that we shall add later.
The ControlTemplate defines the arrangement of elements for each grid view row.
<!--styles-->
<Style x:Key="TextBlockStyle" TargetType="TextBlock">
<Setter Property="Margin" Value="10" />
</Style>
<Style x:Key="TextBoxStyle" TargetType="TextBox">
<Setter Property="Margin" Value="10" />
</Style>
<Style x:Key="ButtonStyle" TargetType="Button">
<Setter Property="Margin" Value="10" />
</Style>
<Style x:Key="GridViewStyle"
TargetType="telerikControlsGridView:RadGridView">
<Setter Property="Margin" Value="10" />
<Setter Property="ShowGroupPanel" Value="False" />
<Setter Property="ShowColumnHeaders" Value="False" />
<Setter Property="ScrollMode" Value="RealTime" />
<Setter Property="VerticalGridlinesVisibility"
Value="Collapsed" />
<Setter Property="VerticalGridlinesBrush"
Value="Transparent" />
</Style>
<Style x:Key="StackPanelStyle" TargetType="StackPanel">
<Setter Property="Orientation" Value="Horizontal" />
<Setter Property="HorizontalAlignment" Value="Left" />
</Style>
<Style x:Key="RowStyle"
TargetType="telerikGridView:GridViewRow">
<Setter Property="Template"
Value="{StaticResource MyCustomRowTemplate}" />
</Style>
6) Replace the <!--search controls row--> comment with the XAML below.
Notes
This step defines the Twitter logo image, the search text box and button. The screenshot below
shows the arrangement of controls at runtime.
This step defines the RadGridView itself. The grid view is named "gvMain" so we can refer to it in code.
The Style points back to the "GridViewStyle" we defined earlier and "RowStyle" points back to the
GridViewRow style definition. The RowLoaded event handler is defined so we can populate the images as
we go.
<!--search results-->
<telerikControlsGridView:RadGridView x:Name="gvMain"
Grid.Row="1" Style="{StaticResource GridViewStyle}"
RowStyle="{StaticResource RowStyle}"
RowLoaded="gvMain_RowLoaded">
</telerikControlsGridView:RadGridView>
Notes
This step defines a "pager", actually an arrangement of controls at the bottom of the page where two
Buttons flank a TextBlock . The screenshot below shows the pager controls in action.
<!--pager-->
<StackPanel x:Name="Pager" Grid.Row="2"
Style="{StaticResource StackPanelStyle}"
Visibility="Collapsed">
<Button x:Name="btnNewer"
Style="{StaticResource ButtonStyle}"
Content="Newer" Click="Button_Click" />
<TextBlock x:Name="PageInfo"
Style="{StaticResource TextBlockStyle}" />
<Button x:Name="btnOlder"
Style="{StaticResource ButtonStyle}"
Content="Older" Click="Button_Click" />
</StackPanel>
1) From the Solution Explorer, right-click the project and select Add > Class... from the context menu.
Name the class file "Twitter" and click the Add button.
2) Add the code below to the class file.
The code here defines "TwitterEntry" and "TwitterAuthor" classes that store information returned from
the service. These two classes have no logic of their own.
End Get
Set(ByVal value As DateTime)
privatePublished = value
End Set
End Property
Private privateUrl As String
Public Property Url() As String
Get
Return privateUrl
End Get
Set(ByVal value As String)
privateUrl = value
End Set
End Property
Private privateTitle As String
Public Property Title() As String
Get
Return privateTitle
End Get
Set(ByVal value As String)
privateTitle = value
End Set
End Property
Private privateContent As String
Public Property Content() As String
Get
Return privateContent
End Get
Set(ByVal value As String)
privateContent = value
End Set
End Property
Private privateUpdated As DateTime
Public Property Updated() As DateTime
Get
Return privateUpdated
End Get
Set(ByVal value As DateTime)
privateUpdated = value
End Set
End Property
Private privateImageUrl As String
Public Property ImageUrl() As String
Get
Return privateImageUrl
End Get
Set(ByVal value As String)
privateImageUrl = value
End Set
End Property
Private privateAuthor As TwitterAuthor
Public Property Author() As TwitterAuthor
Get
Return privateAuthor
End Get
3) In the code-behind for the page, add references to the "Imports" (VB) or "using" (C#) section of the code
for these namespaces:
a) System.Windows.Media.Imaging (supports BitmapImage)
b) Telerik.Windows.Controls (supports ChildrenOfType<T>())
c) Telerik.Windows.Controls.GridView (supports GridViewRow types, RowLoadedEventArgs)
4) Define a private integer member "currentPageIndex" to track the location of paging through multiple
pages of data.
It should tak e a search string as a parameter. Create a new WebClient instance, hook up a
DownloadStringCompleted event handler and call the WebClient DownloadStringAsync() method. The
string passed to DownloadStringAsync() is defined in the "urlFormat" constant below. The format of the
string is Twitter service specific, so if you use a REST service from some other site you will need to
research the appropriate format. Before calling DownloadStringAsync() you should check that the
search string is not empty and that a WebClient request is not already in process. Show the button
labeled "Newer" based on the position of the current page index.
if (!String.IsNullOrEmpty(searchString))
{
if (!webClient.IsBusy)
webClient.DownloadStringAsync(
new Uri(String.Format(urlFormat, searchString, pageSize,
currentPageIndex)));
}
btnNewer.Visibility = currentPageIndex > 1 ?
Visibility.Visible : Visibility.Collapsed;
}
The purpose of this handler is to parse the XML returned in the Result parameter and load new
instances of the TwitterEntry object. The collection of TwitterEntry objects is assigned to the grid view
ItemsSource property. This event handler also sets the pager visibility, grid view visibility and pager
text.
var twitterData =
from item in xDocument.Descendants(atomNamespace + "entry")
select new TwitterEntry
{
ID = item.Element(atomNamespace + "id").Value,
Published =
DateTime.Parse(item.Element(atomNamespace + "published").Value),
Url =
item.Element(atomNamespace + "link").Attribute("href").Value,
Title =
item.Element(atomNamespace + "title").Value,
Content =
item.Element(atomNamespace + "content").Value,
Updated =
DateTime.Parse(item.Element(atomNamespace + "updated").Value),
ImageUrl =
item.Descendants(atomNamespace +
"link").Last().Attribute("href").Value,
Author = new TwitterAuthor()
{
Name = ((XElement)item.Element(atomNamespace +
"author").FirstNode).Value,
Url = ((XElement)item.Element(atomNamespace +
"author").LastNode).Value
}
};
gvMain.ItemsSource = twitterData;
}
7) Add a new method "LoadImage()" that will take a string parameter that defines the URL where the
image is located, and an Image object that will be loaded with the new image.
Check that the URL points to a ".png" or ".jpg" file. These are the only supported image types in
Silverlight at the time of this writing. Create a new WebClient instance, hook up a OpenReadCompleted
event handler and call the OpenReadAsync() method. OpenReadAsync() should tak e a URI object that
points to the image URL and pass a reference to the Image to be loaded.
if (!isValidSilverlightImageType)
return;
8) Handle the OpenReadCompleted event to retrieve the streamed image and assign it to an Image
object.
Create a BitmapImage and use the SetSource method to assign the stream held in Result. Then
assign the BitmapImage to the source of the Image. Remember that Image will be passed as the
UserState argument when OpenReadAsync() is first called. To handle security restrictions where no
ClientAccessPolicy.xml exists on the server where the image resides, trap for the
TargetInvocationException. Inside the "Catch", verify that the InnerException is a SecurityException.
The code below ignores the security exception, assuming that we're trying to access an image across
domains but without the permissions granted by the presence of a ClientAccessPolicy.xml file. If there
is any other k ind of exception, re-throw the exception.
9) Handle the grid view RowLoaded event as shown in the code below.
This is your opportunity to alter elements of the row programmatically. First check that this is not a
header, footer or "new row" using the Row property of the event argument. Use the ChildrenOfType<T>()
method to get a reference to the Image object in the grid row template. There is only one Image object
in the template, so we can call FirstOrDefault() to get the instance. Use the DataElement property of
the event argument to get the bound TwitterEntry object. Finally, call the private LoadImage() method
you created earlier and pass the ImageUrl of the TwitterEntry and the instance of the Image object that
you retrieved from the template.
10)Handle the Button Click events for the "Newer" and "Older" buttons as well as the "Search" button.
The "Search" button click simply calls the private DownloadTwitterPage() method you wrote earlier and
passes the text entered into the search text box. The "Button_Click " event handler is triggered by both
the "Newer" and "Older" buttons. Depending on the identity of the button, the current page index is
advanced or decreased. Then, lik e the search button, the DownloadTwitterPage() method is called,
passing the search text.
DownloadTwitterPage(tbSearch.Text)
End Sub
DownloadTwitterPage(tbSearch.Text);
}
11)Handle the UserControl Loaded event. Here we simply load the Twitter logo to the top of the page.
LoadImage(twitterLogoUrl, imageTwitterLogo)
End Sub
LoadImage(twitterLogoUrl, imageTwitterLogo);
}
5) Click the "View Tweet" link to navigate to the page for that specific tweet.
6) Use the "Newer" and "Older" buttons to page through the available data.
18.5.3 WCF
The walk through that follows demonstrates building a simple WCF service that supplies a list of customers
and populates a RadGridView in a Silverlight client.
2) Navigate to the RadControls for Silverlight installation directory and find the Customer.cs class file in
the "\Examples\GridView" directory (there is also a copy for your convenience located in the
"\courseware\datasources" directory). Drag this file to the Solution Explorer and drop it in the root of the
project.
3) In the Solution Explorer, double click the Properties node of the project, then click the Settings tab.
You will see a message "This project does not contain a default settings file. Click here to create one".
Click the link to create the settings file.
4) In the Settings, configure the first setting so that Name = "Northwind", Type = "(Connection string)
and Scope = "Application". Click in the Value entry box, then click the ellipses to edit the connection.
This will display the Connection Properties window.
5) In the Connection Properties window, click the Change... button to display the Change Data Source
dialog. Select "Microsoft SQL Server Database File" from the list and click the OK button to close the
dialog, returning you to the Connection Properties window.
6) Click the Browse... button. Navigate to the "Northwind.mdf" file located in the RadControls for Silverlight
installation directory under the "\Examples\DataSources" path (there is also a copy for your
convenience located in the "\courseware\datasources" directory). Click the OK button to close the
dialog and create the connection string.
7) In the Solution Explorer, right-click and select Add > New Item... from the context menu.
8) Select the "Silverlight-enabled WCF Service" template, name it "Northwind.svc" and click the Add
button to create the service and close the dialog.
System.Collections.Generic
System.Configuration
System.Data
System.Data.SqlClient
System.ServiceModel
System.ServiceModel.Activation
Telerik.Windows.Examples (supports the Customer.cs class you copied to this project)
11)Replace the default "DoWork()" method that already exists in the service with the GetCustomers()
method using the code below. Important note: Be sure to replace the string "<project name>" passed
to connection strings, with the actual name of your project. So, for a project named
"Binding_WCF_Service" and connection string named "Northwind", the connection string is named:
"Binding_WCF_Service.Properties.Settings.Northwind"
In this example a SqlCommand is used to select a set of customers and a SqlDataReader populates
customer objects. Customer instances are added to a generic list and passed back as the result of the
service method.
You could use a variety of other databases and data retrieval mechanisms here. As long as the
contract is satisfied and the method returns a generic list of Customer, how you fill the list can vary
according to your situation.
<OperationContract> _
Public Function GetCustomers() As List(Of Customer)
Dim connectionString As String = _
ConfigurationManager.ConnectionStrings( _
"<project name>.Properties.Settings.Northwind").ConnectionString
Dim result = New List(Of Customer)()
Using conn As New SqlConnection(connectionString)
Const sql As String = "SELECT Top 10 Address, Bool, City, " & _
"CompanyName, ContactName, ContactTitle, " & _
"Country, CustomerID, Fax, Phone, PostalCode FROM Customers"
conn.Open()
Using command As New SqlCommand(sql, conn)
Dim reader As SqlDataReader = _
command.ExecuteReader(CommandBehavior.CloseConnection)
Do While reader.Read()
Dim customer = New Customer With { _
.Address = reader.GetValue(0).ToString(), _
.Bool = reader.GetBoolean(1), _
.City = reader.GetValue(2).ToString(), _
.CompanyName = reader.GetValue(3).ToString(), _
.ContactName = reader.GetValue(4).ToString(), _
.ContactTitle = reader.GetValue(5).ToString(), _
.Country = reader.GetValue(6).ToString(), _
.CustomerID = reader.GetValue(7).ToString(), _
.Fax = reader.GetValue(8).ToString(), _
.Phone = reader.GetValue(9).ToString(), _
.PostalCode = reader.GetValue(10).ToString()}
result.Add(customer)
Loop
Return result
End Using
End Using
End Function
[OperationContract]
public List<Customer> GetCustomers()
{
string connectionString =
ConfigurationManager.ConnectionStrings[
"<project name>.Properties.Settings.Northwind"].ConnectionString;
var result = new List<Customer>();
using (SqlConnection conn = new SqlConnection(connectionString))
{
const string sql = @"SELECT Top 10 Address, Bool, City, " +
"CompanyName, ContactName, ContactTitle, " +
"Country, CustomerID, Fax, Phone, PostalCode FROM Customers";
conn.Open();
using (SqlCommand command = new SqlCommand(sql, conn))
{
SqlDataReader reader =
command.ExecuteReader(CommandBehavior.CloseConnection);
while (reader.Read())
{
var customer = new Customer
{
Address = reader.GetValue(0).ToString(),
Bool = reader.GetBoolean(1),
City = reader.GetValue(2).ToString(),
CompanyName = reader.GetValue(3).ToString(),
ContactName = reader.GetValue(4).ToString(),
ContactTitle = reader.GetValue(5).ToString(),
Country = reader.GetValue(6).ToString(),
CustomerID = reader.GetValue(7).ToString(),
Fax = reader.GetValue(8).ToString(),
Phone = reader.GetValue(9).ToString(),
PostalCode = reader.GetValue(10).ToString()
};
result.Add(customer);
}
return result;
}
}
}
12)In the Solution Explorer, right-click the project and select Add > New Item... from the context menu.
Name the file "ClientAccessPolicy.xml", then click the Add button to create the file and close the
dialog.
This XML allows requests from all domains to get resources from all locations on the server.
<?xml version="1.0" encoding="utf-8"?>
<access-policy>
<cross-domain-access>
<policy>
<allow-from http-request-headers="*">
<domain uri="*"/>
</allow-from>
<grant-to>
<resource path="/" include-subpaths="true"/>
</grant-to>
</policy>
</cross-domain-access>
</access-policy>
14)In the Solution Explorer, right-click the "ClientAccessPolicy.xml" file and select "Properties" from the
context menu. Set the "Copy to Output Directory" property to "Copy if Newer". This step will mak e
sure that policy file ends up in the \bin directory, i.e. the root directory for the service. When Silverlight
tries to access the service, it will find the policy file there and can continue interacting with the service.
Project Setup
1) From the Visual Studio menu choose File > New > Project..., select the Silverlight project type, then
select the Silverlight Application template. Provide a unique name for the project and click the OK
button.
2) In the "New Silverlight Application" dialog make sure that the "Host the Silverlight application in a new
Web site" option is checked, give the project a unique name and verify that the "ASP.NET Web
Application Project" New Web Project Type option is selected. Click OK to close the dialog and create
the project.
3) In the Solution Explorer, right-click the References node and select Add References... from the
context menu. Add Assembly references:
a) Telerik.Windows.Controls
b) Telerik.Windows.Controls.GridView
c) Telerik.Windows.Data
d) Telerik.Windows.Controls.Input.dll
XAML Editing
1) Drag a RadGridView from the Toolbox to a point between the main "LayoutRoot" grid. Set the "x:Name"
attribute to "gvMain" so we can reference it later in code.
Code Behind
1) Add a namespace reference for the proxy in the "Imports" (VB) or "using" (C#) section of code. This will
be the name of your project + "." + "NorthwindServiceReference", i.e. "MyProject.
NorthwindServiceReference".
2) Handle the UserControl Loaded event.
The client proxy methods are asynchronous, so this will be very much lik e work ing with the WebClient
against a REST service. The proxy will have a "Completed" event, i.e. "GetCustomersCompleted" and
an "Async" method, i.e. "GetCustomersAsync()". Create an instance of the client proxy object, hook up
the "GetCustomersCompleted" event, then call the "GetCustomersAsync()" method.
3) Handle the GetCustomersCompleted event. The result of the method is passed back in e.Result.
Simply assign the result to the grid view ItemsSource property. Note: The result, even though it left
the service as a List<Customer>, ends up in Silverlight as ObservableCollection<Customer>.
Press F5 to run the application. The web page should look something like the screenshot below.
Change the service method or add a new method that allows parameters. You can either parameterize
the query itself or use LINQ to filter the contents. Add a text box and search button to the client. You
can send the search text when calling GetCustomersAsync() using one of the overloads that allows an
"userState" object parameter.
The example service consumes the Northwind "Categories" table using LINQ to Entities. The Silverlight
client instantiates a proxy client, retrieves a list of entities and binds them to the grid view.
1) In Visual Studio, create a new Silverlight Application. This will display the New Silverlight Application
dialog. Check the "Enable WCF RIA Services" option. Leave the other defaults and click OK to close the
dialog and create the projects.
The RIA service layer will be created in the ASP.NET host application, in this case, the
Gridview_RIA_Demo.Web project.
2) Take a look at the Solution Explorer and notice that you now have two projects, the ASP.NET host
project ("Gridview_RIA_Demo.Web in the screenshot below) that will contain the RIA service and the
Silverlight client application ("Gridview_RIA_Demo").
1) Add a "ADO.NET Entity Data Model" item to the ASP.NET project. Name the data model "Northwind.
edmx" and click the Add button.
2) In the first page of "Entity Data Model Wizard", select the "Generate from Database" icon and click the
Next button.
3) Create a connection to the Northwind database file that ships with RadControls for Silverlight.
a) In the "Choose your Data Connection" page of the wizard, click the New Connection button. This will
display the Connection Properties dialog.
b) Click the Change button to show the "Change Data Source" dialog, select the "Microsoft SQL Server
Database File" option and click the OK button to return to the "Change Data Source" dialog.
c) Click the Browse button, locate the file "Northwind.mdf" file in the RadControls for Silverlight
installation directory under \Examples\DataSources.
d) Click OK to create the connection and return to the "Entity Data Model Wizard" "Choose Your Data
Connection" page.
4) In the "Choose Your Data Connection" page of the wizard, enter "NorthwindEntities" in the "Save entity
connection settings in Web.Config" text box and click the Next button.
5) In the "Choose Your Database Objects" page of the wizard, expand the "Tables" node in the tree view
and select the "Categories" table check box.
7) Build the ASP.NET project. This step is important: the following step where you create the Domain
Service Class will not see the entity data model information without building the project.
8) Add a "Domain Service Class" item to your project. Name it "NorthwindService" and click the Add button
to create the service and close the dialog. This step will display the "Add New Domain Service Class"
dialog.
9) In the "Add New Domain Service Class" dialog, name the Domain Service Class "NorthwindService",
make sure that the "Enable client access" option is checked (this allows the client proxy code to be
generated), select "NorthwindEntities" from the drop down list of available context objects and check the
"Categories" entity check box. Click the OK button to create the domain service class and close the
dialog.
11)Review the generated NorthwindService code. Notice that a GetCategories() method has been created
that returns an IQueryable<> of Categories.
2) In the Solution Explorer, right-click the References node and select Add References... from the context
menu. Add references to these assemblies:
a) Telerik.Windows.Controls
b) Telerik.Windows.Data
c) Telerik.Windows.Controls.GridView
d) Telerik.Windows.Controls.Input.dll
3) Open "MainPage.xaml" for editing.
4) Drag a RadGridView from the Toolbox into the main "LayoutRoot" grid element of the page. Set the "x:
Name" attribute to "gvMain" so that we can refer to it later in code.
5) Open "MainPage.xaml.cs" for editing.
6) Add a namespace reference to the ASP.NET service project "Imports" (VB) or "using" (C#) portion of
code.
7) In the constructor for the UserControl, get the data from the domain service by way of the context object
and bind it to the grid view using the code below:
a) Create the "context" object (the generated client counterpart to the domain service).
b) Assign the context "Categories" property to the grid view ItemsSource property.
c) Call the context object Load() method and pass the context "GetCategoriesQuery()" method.
public MainPage()
{
InitializeComponent();
18.6 Customization
The "A RESTful Walk Through" section of this chapter demonstrated customizing an entire row for that free
form "Card" look. In this section we will look at individual customization of a column using a cell template.
This example extends the "WCF RIA Services Walk Through" by displaying the "Picture" column. In the
process we will cover the following:
Format a CellTemplate of a GridViewDataColumn. The first column will contain a TextBlock bound to the
category description. It will use a variation on the "shadow" technique discussed in the ToolBar chapter to
place a second TextBlock "reflection" of the first. The second column will contain an Image surrounded by
a Border.
Use an IValueConverter to convert the binary picture data to a BitmapImage. Note: Be sure to check the
latest features of RadControls for Silverlight for the new GridViewImageColumn type that replaces the
need for converters in most cases.
Hide all visual clues that we're working with a grid except the category description and the picture.
We will look at just those key parts that have changed from the original RIA Services Walk Through project,
starting with the cell template. First, to get oriented in the XAML before we dig down into the cell template,
take a look at the RadGridView element. It contains a Columns element and within that, multiple
GridViewDataColumn elements, one for each column.
<telerikGridView:RadGridView . . .">
<telerikGridView:RadGridView.Columns>
<telerikGridView:GridViewDataColumn
DataMemberBinding="{Binding Description}">
<!--template here-->
</telerikGridView:GridViewDataColumn>
<telerikGridView:GridViewDataColumn
DataMemberBinding="{Binding Picture}" >
<!--template here-->
</telerikGridView:GridViewDataColumn>
</telerikGridView:RadGridView.Columns>
</telerikGridView:RadGridView>
Now we can look at the detail of the "Description" column and see how the template is put together. Inside
the GridViewDataColumn, add a GridViewDataColumn CellTemplate element. Inside the CellTemplate is a
DataTemplate that holds whatever elements suit your purpose. Here we're adding a StackPanel to organize
the two TextBlocks. The Text property of both TextBlock elements is bound to the "Description" property.
<telerikGridView:GridViewDataColumn
DataMemberBinding="{Binding Description} ">
<telerikGridView:GridViewDataColumn.CellTemplate>
<DataTemplate>
<StackPanel>
<TextBlock HorizontalAlignment="Right"
Text="{Binding Description}"
Style="{StaticResource TextStyle}" />
<TextBlock HorizontalAlignment="Right"
Text="{Binding Description}"
Style="{StaticResource ShadowTextStyle}" />
</StackPanel>
</DataTemplate>
</telerikGridView:GridViewDataColumn.CellTemplate>
</telerikGridView:GridViewDataColumn>
The "Picture" column follows the same pattern, but the DataTemplate contains a Border surrounding an
Image element. One difference to this column is that we're using an IValueConverter called
"ImageConverter" to create a BitmapImage from the raw database data.
<telerikGridView:GridViewDataColumn
DataMemberBinding="{Binding Picture}">
<telerikGridView:GridViewDataColumn.CellTemplate>
<DataTemplate>
<Border Style="{StaticResource BorderStyle}">
<Image Margin="5"
Source="{Binding Picture,
Converter={StaticResource ImageConverter}}"
Width="50" Height="50" />
</Border>
</DataTemplate>
</telerikGridView:GridViewDataColumn.CellTemplate>
</telerikGridView:GridViewDataColumn>
We looked briefly at IValueConverter in the Input Controls chapter to demonstrate converting a number
to a string displayed on a Slider control. Refer back to that chapter for a more detailed explanation of
IValueConverter. The "value" parameter passed to Convert is the byte array from the "Picture" column in
the database. Use that byte array to populate a new MemoryStream and then, set the source of a
BitmapImage to be the MemoryStream. Finally, the BitmapImage is returned from the method.
For reference, here are the resources used in the project. Remember that you will need to add an XML
namespace reference to the project that contains the IValueConverter implementation (shown below as
"local"). The other resources are styles that you can copy or change as you wish.
<UserControl.Resources>
<LinearGradientBrush x:Key="BorderBrush">
<GradientStop Color="DarkGray" Offset=".7" />
<GradientStop Color="Silver" Offset=".8" />
<GradientStop Color="LightGray" Offset=".9" />
<GradientStop Color="Gray" Offset="1" />
</LinearGradientBrush>
<Style x:Key="BorderStyle" TargetType="Border">
<Setter Property="BorderBrush" Value="{StaticResource BorderBrush}" />
<Setter Property="BorderThickness" Value="3" />
<Setter Property="Margin" Value="3" />
</Style>
<Style x:Key="TextStyle" TargetType="TextBlock">
<Setter Property="FontFamily" Value="Comic Sans MS" />
<Setter Property="Margin" Value="0" />
<Setter Property="FontSize" Value="13" />
<Setter Property="Foreground" Value="#FF222222" />
<Setter Property="FontWeight" Value="Bold" />
</Style>
<Style x:Key="ShadowTextStyle" TargetType="TextBlock"
BasedOn="{StaticResource TextStyle}">
<Setter Property="OpacityMask">
<Setter.Value>
<LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
<GradientStop Color="Transparent" Offset="0" />
<GradientStop Color="#33000000" Offset="1" />
</LinearGradientBrush>
</Setter.Value>
</Setter>
<Setter Property="RenderTransformOrigin" Value="0,0.5" />
<Setter Property="RenderTransform">
<Setter.Value>
<TransformGroup>
<ScaleTransform ScaleY="-.5"></ScaleTransform>
<SkewTransform AngleX="0.1"></SkewTransform>
</TransformGroup>
</Setter.Value>
</Setter>
</Style>
<local:ImageConverter x:Key="ImageConverter" />
</UserControl.Resources>
The finished project will look something like the screenshot below:
18.7 Wrap Up
In this chapter you were introduced to RadGridView and many of its key features. You started out by
binding the grid view to some basic data. Then you saw how to expand that example to use hierarchical
data and how to customize the columns.
While delving into the details of RadGridView you worked with selected rows and cells. You handled events
that notified you when the user was moving within the grid view. You learned the programming model
commonalities for filtering, sorting and grouping. While working with groups, you learned how to add
aggregate functions for each group. You were introduced to column types and learned about special
columns that handle images, hyperlinks and lookups. "Grid View Elements Visibility" demonstrated how to
show and hide the visual elements of the grid view.
In the Binding section of this chapter you bound the grid view to simple .NET objects. From there you built a
REST service that queried Twitter. Then you built WCF and WCF RIA services. In the process you also
learned about how to work with inherent Silverlight security restrictions. You also learned how to customize
the layout of an entire grid view row.
In the Customization section of this chapter you customized grid view cells to achieve a unique look.
XIX
Scheduler
Scheduler 733
19 Scheduler
19.1 Objectives
This chapter starts out by using RadScheduler in a simple project to create a single appointment. Then you
will be introduced to view modes that allow you switch between month, week and day views. You will create
and configure appointments and also learn role of IAppointment and AppointmentBase in building custom
appointment classes. You will select appointments programmatically and respond to user selections in the
scheduler. You will use RecurrenceRule and RecurrencePattern classes to specify the recurrence behavior
of an appointment. We will take a brief look at scheduler commands and how they can be used
programmatically and declaratively within XAML. RadDragAndDropManager will be used to drag-and-drop
ListBox items into time slots. You will localize the scheduler using the LocalizationManager with pre-
defined cultures and custom resource files.
You will perform basic data binding to a collection of Appointments and also work with building and binding
to custom appointment objects. Finally, you will create a custom Theme for RadScheduler where the
Appointment Editing dialog user interface is modified to include a custom appointment property.
\Courseware\Projects\<CS|VB>\Scheduler\Scheduler.sln
19.2 Overview
RadScheduler serves up Office-like appointment scheduling packed with features:
Binds to collections of pre-defined Appointment objects or to a custom classes you define for your
specific business requirements.
Fully templated to allow customization at any level. Alter the appearance of any view, time slots,
appointments and appointment editing dialog.
Recurrent appointment behavior handles appointments that repeat within a time range or fit a pattern, e.
g. "every Thursday, weekly".
RadScheduler can easily adjust to any culture or use resources to completely modify the terminology
used in the scheduler.
Project Setup
1) From the Visual Studio menu choose File > New > Project..., select the Silverlight project type, then
select the Silverlight Application template. Provide a unique name for the project and click the OK
button.
2) In the "New Silverlight Application" dialog make sure that the "Host the Silverlight application in a new
Web site" option is checked, give the project a unique name and verify that the "ASP.NET Web
Application Project" New Web Project Type option is selected. Click OK to close the dialog and create
the project.
3) In the Solution Explorer, right-click the References node and select Add References... from the
context menu. Add Assembly references:
a) Telerik.Windows.Controls
b) Telerik.Windows.Controls.Scheduler
c) Telerik.Windows.Controls.Input.dll
d) Telerik.Windows.Controls.Navigation.dll
e) Telerik.Windows.Data.dll
f) Telerik.Windows.Themes.Vista
XAML Editing
1) Open MainPage.xaml for editing.
2) Add XML namespace references to the Telerik.Windows.Controls assembly. We will use this later
when we apply a theme to the scheduler.
xmlns:telerik=
"clr-namespace:Telerik.Windows.Controls;assembly=Telerik.Windows.Controls"
<UserControl
...
Loaded="UserControl_Loaded">
4) Drag a RadScheduler from the Toolbox to a point inside the main "LayoutRoot" Grid element. The
XAML should be added between the <Grid> and </Grid> tags. Name the RadScheduler "schTasks".
Add attributes to the RadScheduler so that the ViewMode="Week" and telerik:StyleManager.
Theme="Vista".
<telerikScheduler:RadScheduler x:Name="schTasks"
ViewMode="Week"
telerik:StyleManager.Theme="Vista">
</telerikScheduler:RadScheduler>
Code Behind
1) In the code-behind for the page, add references to the "Imports" (VB) or "using" (C#) section of the code
for these name spaces:
a) Telerik.Windows.Controls
b) Telerik.Windows.Controls.Scheduler
2) Handle the Loaded event of the UserControl.
a) Create a new Appointment instance and add it to the scheduler's Appointments collection.
b) Set the Start property to the current date and time using the DateTime.Now property.
c) Set the End property to 15 minutes after the current time using the AddMinutes() method of
DateTime.
d) Set the Subject property to "Backup servers"
e) Set the Body to "Backup server contents to network storage".
Press F5 to run the application. The web page should look something like the screenshot below.
3) Edit and save the appointment. Open it again and verify the changes have persisted.
4) From the edit dialog, click the Edit Recurrence button and make changes to the Appointment
Recurrence dialog. When you're through making edits, Click OK. Then click the Save & Close button
of the edit dialog.
Time slots are the areas of time in the scheduler that appointments can be placed in. You can select a time
slot of any duration by assigning the scheduler SelectedTimeSlot property. SelectedTimeSlot is a
TimeSlot defined in the Telerik.Windows.Controls.Scheduler namespace.
schTasks.SelectedTimeSlot = _
New TimeSlot(appointment1.Start, appointment1.End.AddHours(1))
schTasks.SelectedTimeSlot =
new TimeSlot(appointment1.Start, appointment1.End.AddHours(1));
Running in the browser, the TimeSlot selection appears underneath an Appointment that happens to fall
within the slot.
19.4.2 Views
RadScheduler has views for Day, Week and Month controlled by the ViewMode property.
The ViewMode can be set in XAML or in code to show a particular view when your page starts up.
The ViewMode also controls the ActiveViewDefinition property. ActiveViewDefinition controls how the
times and dates are arranged in the view and how the view behaves. The instance contained in
ActiveViewDefinition may be DayViewDefinition, WeekViewDefinition or MonthViewDefinition types. All three
descend from the abstract ViewDefinitionBase and implement some of these properties:
VisibleDays: The number of days that show in the view. You can use this property to have multiple
days in Day mode or have a week of any length in Week mode.
LargeChangeInterval: The amount of time to move in response to the forward and backward buttons.
By default, the amount is one month for MonthViewDefinition, seven days for WeekViewDefinition and a
single day for DayViewDefinition. The screenshot below shows the navigation buttons being used to
move between days in the Day view.
The XAML below shows how DayViewDefinition and WeekViewDefinition are configured. Take a look at the
DayViewDefinition markup:
Only one day is shown at a time
Time slots are two hours long
Large changes, triggered from the forward and back navigation buttons, move two days at a time
The day starts at 9AM and ends at 5PM.
<telerikScheduler:RadScheduler x:Name="schTasks"
telerik:StyleManager.Theme="Vista">
<telerikScheduler:RadScheduler.DayViewDefinition>
<telerikScheduler:DayViewDefinition VisibleDays="1"
TimeSlotLength="2:0:0"
LargeChangeInterval="2d" DayStartTime="9:0:0"
DayEndTime="17:0:0">
</telerikScheduler:DayViewDefinition>
</telerikScheduler:RadScheduler.DayViewDefinition>
<telerikScheduler:RadScheduler.WeekViewDefinition>
<telerikScheduler:WeekViewDefinition VisibleDays="5"
TimeSlotLength="1:0:0"
LargeChangeInterval="7d">
</telerikScheduler:WeekViewDefinition>
</telerikScheduler:RadScheduler.WeekViewDefinition>
</telerikScheduler:RadScheduler>
Running in the browser, the day view looks like the screenshot below.
19.4.3 Appointments
RadScheduler can consume any appointment classes that implement IAppointment. You can implement
IAppointment yourself, descend from the abstract AppointmentBase or use the Appointment class right-
out-of-the-box. The easiest route of course is to create instances of Appointment and add them to the
Appointments collection:
schTasks.Appointments.Add( _
New Appointment() With { _
.Start = DateTime.Now, _
.End = DateTime.Now.AddHours(1), _
.Subject = "RadControls for Silverlight Class", _
.Body = "A three day, on-site course with mixed _
lecture and hands-on practical labs", _
.IsAllDayEvent = False, _
.Location = "Capitola, California", _
.Url = "http://www.telerik.com", _
.TimeZone = TimeZoneInfo.Local, _
.UniqueId = "1234"})
schTasks.Appointments.Add(new Appointment()
{
Start = DateTime.Now,
End = DateTime.Now.AddHours(1),
Subject = "RadControls for Silverlight Class",
Body = "A three day, on-site course with mixed lecture" +
"and hands-on practical labs",
IsAllDayEvent = false,
Location = "Capitola, California",
Url = "http://www.telerik.com",
TimeZone = TimeZoneInfo.Local,
UniqueId = "1234"
});
Removing appointments is relatively straightforward and can be accomplished in several ways. You can use
RemoveAt(index) to remove the appointment at a given index, Remove(IAppointment) takes an instance of
the appointment to remove and RemoveAll() lets you remove every appointment that fits a certain filter. The
example below uses a Lambda expression in the RemoveAll() parameter list to filter for all appointments
where IsAllDayEvent is true.
Much like RadCalendar or selection in a ListBox control, appointments can be selected by assigning the
SelectedAppointment property or adding to the SelectedAppointments collection.
schTasks.SelectedAppointment = appointment1
schTasks.SelectedAppointments.Add(appointment2)
schTasks.SelectedAppointment = appointment1;
schTasks.SelectedAppointments.Add(appointment2);
Tip!
By default, the scheduler displays today's date. Use the scheduler's SetFirstVisibleDate() method
to bring a specific day into view.
schTasks.SetFirstVisibleDate(appointment1.Start)
schTasks.SetFirstVisibleDate(appointment1.Start);
19.4.4 Recurrence
The code example demonstrates how the RecurrencePattern is setup, assigned to the RecurrenceRule and
finally the RecurrenceRule is assigned to the Appointment.
19.4.5 Resources
RadScheduler provides the ability to define different resource types and also group the appointments by
these resource types. The code example below shows how to add resources
to the RadScheduler's ResourceTypes collection:
As a result to the above code the edit dialog will look like the image shown below. The predefined resources
are "Speaker" and "Level". To assign the appointment to certain resources you should select
the respective "Speaker" and/or "Level".
After you have assigned an appointment to certain resources you can group appointments by the
resources, to which they have been assigned. The code example below demonstrates how to group
the appointments by "Speaker".
As a result, the grouped appointments will appear as shown in the image below:
In addition you can customize the look and feel of each resource group. To achieve this you need to create
a ResourceStyleMapping object for every resource group and set the following properties of this object:
ResourceName - defines the name of the resource that should be associated with this style
ResourceBrush - sets the color of the header of the resource group and to the background of the
appointments in this group
AppointmentBrush - sets the color of the appointments in this group. The color is different from the
background of the group's header
SelectedAppointmentBrush - sets the color of the appointments when they have been clicked
HeaderTemplate - used to customize the header of the resource group. The value of this property
should be of DataTemplate type
The code example below shows how to use all the aforementioned properties to customize the appearance
of the appointments and the resource groups.
19.4.6 Events
RadScheduler has a full set of events to cover all the basic CRUD (Create, Read, Update, Delete)
operations and then some. The events are paired so that they occur just before and after the actual
operation. The "before" events end in the postfix "ing", e.g. "AppointmentCreating". The "after" events end in
the postfix "ed", e.g. "AppointmentCreated".
For example, when an appointment about to be added, the AppointmentAdding event fires. Based on the
content of the appointment, you can decide to cancel the event. Once the Appointment is added, the
AppointmentAdded event fires, again passing a reference to the Appointment.
There are similar event pairs for creating, deleting and editing appointments. Along with these event
pairs is a lone AppointmentSaving event that fires after pressing the "Save & Close" button of the
appointment editing dialog.
19.4.7 Commands
To act on the scheduler programmatically, or to implement your own custom templates and bind to actions
directly in XAML, use methods of the RadSchedulerCommands class. Each command is a
RoutedCommand type and so has an Execute() method. The signature for Execute() includes "parameter"
that can be null or another value appropriate to the command. The second parameter is "target", a
UIElement that the command executes against. Here is the Execute() method signature:
In the case of RadScheduler commands, the parameter may be an Appointment or a TimeSlot, depending
on the context. The UIElement is the scheduler itself. For example, if you want a button click to trigger a
new appointment you could call it in code like this:
The button click fires the command and the Appointment dialog displays, all without directly touching the
scheduler. When the first parameter is null, the SelectedTimeSlot is used to set the Start and End time.
You can add a TimeSlot instance as the first parameter instead:
Running this code displays the dialog with the Start and End time set to match the TimeSlot parameter:
The EditAppointment(), DeleteAppointment() and SaveAppointment() methods all follow the same format as
CreateAppointment().
RadSchedulerCommands.SaveAppointment.Execute( _
schTasks.SelectedAppointment, schTasks)
End If
End Sub
RadSchedulerCommands.SaveAppointment.Execute(
schTasks.SelectedAppointment, schTasks);
}
}
Here are the list of all the possible commands you can invoke as of this writing:
ChangeRecurrenceState
ChangeTimePickersVisibility
DecreaseVisibleDateLarge
DecreaseVisibleDateSmall
DeleteRecurrenceRule
EditParentAppointment
EditRecurrenceRule
IncreaseVisibleDateLarge
IncreaseVisibleDateSmall
SaveRecurrenceRule
SetDayViewMode
SetMonthViewMode
SetWeekViewMode
These commands are especially useful when you want to customize the scheduler. When you add new
elements to customized templates, commands allow you to bind functionality to the new elements. The
snippet below is from the resources that define a RadScheduler theme. The snippet defines the "Save"
button. Notice how the MouseBinding Command attribute is assigned the SaveAppointment command.
<Button
telerik:StyleManager.Theme="{StaticResource SchedulerSystemControlsTheme}"
telerik:LocalizationManager.ResourceKey="SaveAndCloseCommandText">
<telerik:CommandManager.InputBindings>
<telerik:InputBindingCollection>
<telerik:MouseBinding
Command="telerikScheduler:RadSchedulerCommands.SaveAppointment"
Gesture="LeftClick" />
</telerik:InputBindingCollection>
</telerik:CommandManager.InputBindings>
</Button>
See the Customization section of this chapter for more information about customizing scheduler and using
themes.
Notes
A RoutedCommand is a WPF construct where a command source, e.g. a Button, can be bound
to a command and the command is executed when the command source is activated, e.g. the
button is clicked. RoutedCommands are executed from code, but also can be bound to a
command source in XAML. RoutedCommand doesn't exist in Silverlight yet, but Telerik has built
a RoutedCommand implementation in the Telerik.Windows.Controls namespace.
19.4.8 Drag-and-Drop
19.4.8.1 Drag-and-Drop Overview
If you remember from the "Drag and Drop" chapter, the basic steps for implementing drag-and-drop are:
Set the RadDragAndDropManager.AllowDrag attached property to True for any "Source" elements that
you want to drag.
Set the RadDragAndDropManager.AllowDrop attached property to True for any "Destination" elements
that should receive the dragged elements.
Handle DragQueryEvent, DropQueryEvent and DropInfoEvent events.
If you want to drag items from some collection container to the scheduler, the steps are the same. To drag
from a ListBox, you need to set the RadDragAndDropManager AllowDrag = "True" for every item in the list.
ListBox items have an ItemContainerStyle property that handles this automatically without having to
assign the property to each item individually. Inside the ItemContainerStyle, just add a Style with
"ListBoxItem" as its TargetType. Include a single Setter element where the property is
RadDragAndDropManager.AllowDrag and the Value is "True". The relevant parts of the XAML are shown
below:
<ListBox x:Name="listBox">
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter
Property="dragDrop:RadDragAndDropManager.AllowDrag"
Value="True" />
</Style>
</ListBox.ItemContainerStyle>
...
</ListBox>
You also need to set the RadDragAndDropManager AllowDrop property for every time slot in the current
scheduler view. This is a little more involved because AllowDrop is set programmatically and must be reset
every time the view changes. The view can change for a variety of reasons, including the initial load of the
scheduler, if the visible range dates change or if the active view changes. When any of these occur, you
need to roll through the current set of time slots and set the AllowDrop property. InitializeTimeSlotItems() is
a private method that sets the AllowDrop property and will be discussed momentarily.
Setting AllowDrop on each item is made easier by the ChildrenOfType() extension method that collects all
of the TimeSlotItem objects from the scheduler. Once you have the TimeSlotItems, iterate and call the
SetValue() method to assign AllowDrop.
Now we're going to walk through the complete steps, including the event handlers for the Drag-and-Drop
operation. The application simulates a series of servers to be scheduled for backup.
Project Setup
1) From the Visual Studio menu choose File > New > Project..., select the Silverlight project type, then
select the Silverlight Application template. Provide a unique name for the project and click the OK
button.
2) In the "New Silverlight Application" dialog make sure that the "Host the Silverlight application in a new
Web site" option is checked, give the project a unique name and verify that the "ASP.NET Web
Application Project" New Web Project Type option is selected. Click OK to close the dialog and create
the project.
3) In the Solution Explorer, right-click the References node and select Add References... from the
context menu. Add Assembly references:
a) Telerik.Windows.Controls
b) Telerik.Windows.Controls.Scheduler
c) Telerik.Windows.Controls.Input.dll
d) Telerik.Windows.Controls.Navigation.dll
e) Telerik.Windows.Data.dll
f) Telerik.Windows.Themes.Summer
XAML Editing
1) Open MainPage.xaml for editing.
2) Add the XML name space references below to the UserControl element to support the scheduler, Drag-
and-Drop and themes.
<UserControl . . .
xmlns:telerikScheduler=
"clr-namespace:Telerik.Windows.Controls;assembly=Telerik.Windows.Controls.Scheduler"
xmlns:dragDrop=
"clr-namespace:Telerik.Windows.Controls.DragDrop;assembly=Telerik.Windows.Controls"
xmlns:telerik=
"clr-namespace:Telerik.Windows.Controls;assembly=Telerik.Windows.Controls">
3) Add the XAML below to replace the main "LayoutRoot" Grid element. This XAML defines the ListBox
and the scheduler. The ListBox has the ItemContainerStyle defined as discussed in the previous "Drag-
and-Drop Overview" section. The ListBox also contains a relatively simple ItemTemplate that has a
single bound TextBlock that will display an ObservableCollection of strings.
<Grid x:Name="LayoutRoot">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<ListBox x:Name="listBox">
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter
Property="dragDrop:RadDragAndDropManager.AllowDrag"
Value="True" />
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<telerikScheduler:RadScheduler Grid.Column="1" ViewMode="Day"
telerik:StyleManager.Theme="Summer" Name="Scheduler">
</telerikScheduler:RadScheduler>
</Grid>
Code Behind
1) In the code-behind for the page, add references to the "Imports" (VB) or "using" (C#) section of the code
for these namespaces:
a) System.Collections.ObjectModel
b) Telerik.Windows.Controls
c) Telerik.Windows.Controls.DragDrop
d) Telerik.Windows.Controls.Scheduler
2) Add two private members as shown in the code below: A Boolean "viewChanged" variable to signal
between event handlers when the time slots need to be re-initialized with the AllowDrop property
setting, and "listData" that contains a collection of strings to display in the ListBox.
3) Change the constructor for the page to add event handlers for the scheduler, add drag-and-drop routed
event handlers and assign ListBox data. Note: be sure to leave the call to InitializeComponent() in
place.
public MainPage()
{
InitializeComponent();
4) Add a private method to assign AllowDrop to each scheduler time slot for the current view.
Try dragging from another type of list object, e.g. combo box or tab strip.
19.4.9 Internationalization
19.4.9.1 Using Predefined Cultures
The scheduler Culture property has been deprecated. In its place use the LocalizationManager
DefaultCulture property. You also need to have the scheduler recognize the changes. This currently
involves a workaround where the scheduler's template is set to null, then reassigned. For the sake of
example, we can bind a List of CultureInfo objects to a RadComboBox ItemsSource.
<telerikInput:RadComboBox
x:Name="cbCultures"
DisplayMemberPath="DisplayName"
SelectionChanged="cbCultures_SelectionChanged"
HorizontalAlignment="Left" />
When the user clicks on a culture name, the SelectionChanged event handler picks up the selected culture
and assigns it to the LocalizationManager.DefaultCulture. The ResetTemplate() method takes care of the
workaround that allows the new culture to be displayed.
When selected from the combo box, the new culture is reflected in the scheduler and all its child
elements, including the drop down date picker.
If there is no predefined translation for the culture you want to use or if you want the scheduler to reflect
some particular language, dialect or professional terminology, use a custom resource file and assign it to
the scheduler. The screenshot below shows the scheduler language customized for online webinars.
To customize the language, you need a resource file populated with specific names that correspond to
strings in the scheduler. You can use the SchedulerStrings.resx file found at "\courseware\resources"
directory. Open the resource file in Visual Studio and edit the Value columns to use the specific language
or terminology.
In the code behind, you need to assign the resource file's ResourceManager to the LocalizationManager.
DefaultResourceManager. Currently, you also need to perform the work-around explained earlier in this
chapter to reset the scheduler Template.
Tip!
Notice that we're using the scheduler DayHeaderFormat property to set the language at the top of
each day. There are several similar properties appended with "Format" that you can use to customize
parts of the scheduler. The tool tip for each property provides the default format you can use as a
starting point. The screenshot below shows the tool tip displayed in the Visual Studio editor.
This particular property setting results in the heading shown below that reads "Webinars for
Thursday Oct 15".
19.5 Binding
19.5.1 Basic Binding
RadScheduler can bind to any IAppointment implementation. Telerik provides an Appointment class that
serves nicely where you don't need any custom fields. To setup binding, add Appointment instances to an
ObservableCollection, then assign the collection to the scheduler AppointmentsSource property. The walk
through below demonstrates just that.
Project Setup
1) From the Visual Studio menu choose File > New > Project..., select the Silverlight project type, then
select the Silverlight Application template. Provide a unique name for the project and click the OK
button.
2) In the "New Silverlight Application" dialog make sure that the "Host the Silverlight application in a new
Web site" option is checked, give the project a unique name and verify that the "ASP.NET Web
Application Project" New Web Project Type option is selected. Click OK to close the dialog and create
the project.
3) In the Solution Explorer, right-click the References node and select Add References... from the
context menu. Add Assembly references:
a) Telerik.Windows.Controls
b) Telerik.Windows.Controls.Scheduler
c) Telerik.Windows.Controls.Input.dll
d) Telerik.Windows.Controls.Navigation.dll
e) Telerik.Windows.Data.dll
f) Telerik.Windows.Themes.Vista
XAML Editing
1) Open MainPage.xaml for editing.
2) Add XML namespace references to the Telerik.Windows.Controls and Telerik.Windows.Controls.
Scheduler assemblies. Also add a Loaded event handler.
<UserControl
xmlns:telerikScheduler=
"clr-namespace:Telerik.Windows.Controls;assembly=Telerik.Windows.Controls.Scheduler"
xmlns:telerik=
"clr-namespace:Telerik.Windows.Controls;assembly=Telerik.Windows.Controls"
...
Loaded="UserControl_Loaded">
...
</UserControl>
3) Drag a RadScheduler from the Toolbox to a point inside the main "LayoutRoot" grid. Set the name to
"schTasks" and the Theme to "Vista".
<Grid x:Name="LayoutRoot">
<telerikScheduler:RadScheduler
x:Name="schTasks"
telerik:StyleManager.Theme="Vista" />
</Grid>
Code Behind
1) In the code-behind for the page, add references to the "Imports" (VB) or "using" (C#) section of the code
for these name spaces:
a) System.Collections.ObjectModel (Supports the ObservableCollection class)
b) Telerik.Windows.Controls.Scheduler
2) Add the code below to the Loaded event handler.
This code creates the series of Appointment objects and adds them to the ObservableCollection. The
populated collection is assigned to the RadScheduler AppointmentsSource.
schTasks.AppointmentsSource = webinars
End Sub
webinars.Add(new Appointment()
{
Subject = "Go with the CoverFlow",
Start = DateTime.Today,
End = DateTime.Today.AddHours(1)
});
webinars.Add(new Appointment()
{
Subject = "TreeView Tips and Tricks",
Start = DateTime.Today.AddHours(1),
End = DateTime.Today.AddHours(2)
});
RecurrencePattern pattern =
new RecurrencePattern(null, RecurrenceDays.Monday,
RecurrenceFrequency.Monthly, 1, null, null);
RecurrenceRule rule = new RecurrenceRule(pattern);
webinars.Add(new Appointment()
{
Subject = "Customizing RadScheduler",
Start = DateTime.Today.AddHours(3),
End = DateTime.Today.AddHours(4),
RecurrenceRule = rule
});
schTasks.AppointmentsSource = webinars;
}
Telerik provides an abstract AppointmentBase class that includes all the basics, i.e. "Subject", "Start",
"End". AppointmentBase also has INotifyPropertyChanged support baked in. Once you have an
AppointmentBase descendant, you can create an ObservableCollection of your custom appointment
objects.
Here's a sample AppointmentBase descendant called WebinarAppointment that adds a single new property
"Presenter". Notice that plumbing for Start, End, Subject, Recurrence, IsAllDayEvent and TimeZone
properties is already taken care of by the AppointmentBase class. Also notice that the call to the
OnPropertyChanged() method inside the "Presenter" property setter. Likewise, the CopyFrom()
IAppointment method calls the base method and only adds the new "Presenter" property assignment.
#End Region
End Class
set
{
if (_presenter != value)
{
_presenter = value;
OnPropertyChanged("Presenter");
}
}
}
#endregion
Like the previous example from the "Basic Binding" section that binds Appointment class instances,
WebinarAppointment instances are created and added to an ObservableCollection, then assigned to the
scheduler's AppointmentsSource. The major difference here, other than the use of WebinarAppointment
instead of Appointment, is the inclusion of the new "Presenter" property.
schTasks.AppointmentsSource = webinars
End Sub
webinars.Add(new WebinarAppointment()
{
Subject = "Go with the CoverFlow",
Presenter = "Hristo Borisov",
Start = DateTime.Today,
End = DateTime.Today.AddHours(1)
});
webinars.Add(new WebinarAppointment()
{
Subject = "TreeView Tips and Tricks",
Presenter = "Valeri Hristov",
Start = DateTime.Today.AddHours(1),
End = DateTime.Today.AddHours(2)
});
webinars.Add(new WebinarAppointment()
{
Subject = "Customizing RadScheduler",
Presenter = "Rosi F",
Start = DateTime.Today.AddHours(3),
End = DateTime.Today.AddHours(4),
RecurrenceRule = rule
});
schTasks.AppointmentsSource = webinars;
}
19.6 Customization
In the previous Binding section of this chapter we created a new bit of data called "Presenter". Its all very
good that the data is somehow available, but how do you get this data to show in the scheduler? As with all
the Silverlight controls, control templates let you compose arbitrary arrangements of elements and bind
them to data. The scheduler provides access to the control template using a custom theme. Custom
themes are implemented using XAML resource files stored in a separate assembly. The general steps that
display a new bound data property in a custom theme are:
Create a new Silverlight Class Library project. The class library will hold a XAML file that contains all the
resources for the scheduler and a Telerik.Windows.Controls.Theme descendant.
Edit the XAML to alter the specific template resource you're interested in.
Reference the new theme in a Silverlight application.
You would use these steps for any customization of the scheduler, such as including additional properties
or changing the layout and appearance of any scheduler aspect.
In this next walk through, you will create a new "MyCustomTheme" class. By altering the
"EditAppointmentTemplate" of the theme XAML file, you will include an additional row containing a TextBox
bound to the "Presenter" property.
Notes
In the sample projects that accompany this courseware, the theme project is called
"04B_CustomTheme", so the correct path for the XAML resource file is "04B_CustomTheme;
component/themes/Generic.xaml". See the screenshot of the Solution Explorer below for reference.
5) Drag a copy of the "\Themes" folder and its contents from the "\courseware\resources" directory into
your Silverlight Class Library. Note that this file can also be obtained from the project source available
from download page of your Telerik account.
6) Open the Generic.xaml file for editing.
7) Locate the "EditAppointmentTemplate" ControlTemplate. We will be editing the grid within this
element.
8) Set the Grid Height attribute to "350". This will grow the edit appointment dialog slightly to
accommodate the new row you are about to add.
9) Find the Grid.RowDefinitions element within the grid. Replace the row definitions with the XAML
below: This will provide one extra row.
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
10)Locate the TextBox element named "Subject", i.e., 'x:Name="Subject"'. Add the following XAML just
below this TextBox element.
Notice in the XAML below that the TextBox is bound to the Presenter property.
Project Setup
1) In the Solution Explorer, right-click the solution and select Add > New Project..., select the Silverlight
project type, then select the Silverlight Application template. Provide a unique name for the project and
click the OK button.
2) In the "New Silverlight Application" dialog, make sure that the "Host the Silverlight application in a new
Web site" option is checked, give the project a unique name and verify that the "ASP.NET Web
Application Project" New Web project Type option is checked. Click OK to close the dialog and create
the project.
3) In the Solution Explorer, right-click the References node and select Add References... from the
context menu. Add references to these assemblies:
a) Telerik.Windows.Controls
b) Telerik.Windows.Controls.Scheduler
c) Your Silverlight Class Library assembly. You can find your custom theme project in the Projects tab of
the Add Reference dialog.
4) In the Solution Explorer, create a WebinarAppointment class file and copy the code below into the file.
Note: This is the exact same WebinarAppointment file created in the Binding section of this chapter.
Feel free to copy this file over to save time.
Imports System
Imports Telerik.Windows.Controls.Scheduler
Namespace _04_Customization
Public Class WebinarAppointment
Inherits AppointmentBase
End Class
End Namespace
using System;
using Telerik.Windows.Controls.Scheduler;
namespace _04_Customization
{
public class WebinarAppointment : AppointmentBase
{
private string _presenter;
public string Presenter
{
get { return _presenter; }
set
{
if (_presenter != value)
{
_presenter = value;
OnPropertyChanged("Presenter");
}
}
}
XAML Editing
1) Open MainPage.xaml for editing.
2) Add the XML namespace references below to the UserControl element to support the scheduler and
your custom theme. Also add a Loaded event handler.
Important Note: In the XML namespace "customTheme", replace the portion to the right of the "=" with
your own custom theme assembly reference. Put the cursor to the right of the equal sign and press
Ctrl-Spacebar to get a list of available assemblies. Select your custom theme assembly reference from
the list.
<UserControl
xmlns:telerik=
"clr-namespace:Telerik.Windows.Controls;assembly=Telerik.Windows.Controls"
xmlns:customTheme=
"clr-namespace:_04B_CustomTheme;assembly=04B_CustomTheme"
Loaded="UserControl_Loaded">
3) Add a UserControl.Resources element. In the Resources element, add a reference to your custom
theme.
<UserControl.Resources>
<customTheme:MyCustomTheme x:Key="theme" />
</UserControl.Resources>
4) Drag a RadScheduler control from the Toolbox to a point inside the main "LayoutRoot" grid element.
Name the scheduler "schTasks" and set the Theme to "{StaticResource theme}".
Just to recap, "{StaticResource theme}" points back to the UserControl.Resources element with
k ey="theme". That element points back to "customTheme", a reference to your Silverlight Class
Library and its "MyCustomTheme" class.
<telerikScheduler:RadScheduler x:Name="schTasks"
telerik:StyleManager.Theme="{StaticResource theme}" />
Notes
An alternative to the last two steps would be to assign the theme in code using the StyleManager.
SetTheme() method. SetTheme() takes a reference to the scheduler and an instance of the
custom theme class.
Code Behind
1) In the code-behind for the page, add references to the "Imports" (VB) or "using" (C#) section of the code
for these namespaces:
a) System.Collections.ObjectModel
b) Telerik.Windows.Controls.Scheduler
2) Create an ObservableCollection of WebinarAppointment objects and assign it to the scheduler
AppointmentsSource.
This code is replicated from the Binding section regarding Custom Appointments.
schTasks.AppointmentsSource = webinars
End Sub
webinars.Add(new WebinarAppointment()
{
Subject = "Go with the CoverFlow",
Presenter = "Hristo Borisov",
Start = DateTime.Today,
End = DateTime.Today.AddHours(1)
});
webinars.Add(new WebinarAppointment()
{
Subject = "TreeView Tips and Tricks",
Presenter = "Valeri Hristov",
Start = DateTime.Today.AddHours(1),
End = DateTime.Today.AddHours(2)
});
webinars.Add(new WebinarAppointment()
{
Subject = "Customizing RadScheduler",
Presenter = "Rosi F",
Start = DateTime.Today.AddHours(3),
End = DateTime.Today.AddHours(4),
RecurrenceRule = rule
});
schTasks.AppointmentsSource = webinars;
}
The edit appointment dialog appears with the new "Presenter:" title TextBlock and TextBox bound to
the Presenter property of the WebinarAppointment object.
Notes
This example, although it covers a fair amount of ground, only scratches the surface of the
customization possibilities. You could also, for example:
Color time slots that fall within "work hours" by customizing the TimeSlotItem
ControlTemplate (http://www.telerik.com/community/forums/silverlight/scheduler/shading-
work-hours-but-show-all-day.aspx).
Remove the "Delete" button from the appointment template (http://www.telerik.com/
community/forums/silverlight/scheduler/how-to-disable-some-features-of-radscheduler.aspx)
.
Display a tool tips for each appointment (http://demos.telerik.com/silverlight/#Scheduler/
FirstLook)
The commonality here is that each example creates a custom theme as demonstrated in this walk
through.
Tip!
Another way to customize the scheduler without creating a custom theme is to use built-in style
properties. All the RadScheduler property names appended with "Style" are candidates for
customization, e.g .EditAppointmentStyle, TimeRulerHostStyle. For example, the
NavigationHeaderStyle can be used if you wanted to hide the top navigation bar:
With the Visibility property set to "Collapsed", the navigation header displaying "Day Week
Month" disappears.
19.7 Wrap Up
In this chapter you covered a wide range of material in the process of learning how to use RadScheduler to
best effect. You started out by using RadScheduler in a simple project that created a single appointment.
You learned how to switch the view between month, week and day. You also learned how to create and
configure appointments and the role of IAppointment and AppointmentBase in building custom appointment
classes. You selected appointments programmatically and responded to user selections in the scheduler.
You worked with RecurrenceRule and RecurrencePattern classes to specify the recurrence behavior of an
appointment. You looked briefly at scheduler commands and how they can be used programmatically or to
add functionality to controls defined in XAML. You learned how to drag-and-drop ListBox items into time
slots using the RadDragAndDropManager control. You also learned how to localize the scheduler using the
LocalizationManager and you also saw how to provide a custom translation using resource files.
You performed basic data binding to a collection of the supplied Appointment class and you also worked
with building and binding to custom appointment objects. Finally, you built a custom Theme for
RadScheduler and modified the Appointment Editing dialog user interface.
XX
Gauges
802 RadControls for Silverlight
20 Gauges
20.1 Objectives
This chapter demonstrates how to use gauges to support dynamic data visualization. "First up", you will
learn how to build a gauge starting with RadGauge and adding its constituent elements. You'll learn how to
work with radial and numeric gauges and how these two element can be swapped without affecting
application behavior. You will drill down into the gauge elements to see how scales, indicators and ranges
work together and how some of the important properties are used to tweak behavior and appearance. Next,
you'll learn how to bind individual indicators to data. Finally, you will use Expression Blend to customize
gauge appearance.
20.2 Overview
Use Telerik Gauge controls to build dashboards for business, science, electronics or any purpose where
dynamic data visualization is required.
RadGauge has circular, linear and numeric gauges that work with a many indicator types, such as needle,
bar, state and marker. RadGauge comes with several predefined themes that provide visual "pop" at the
cost of a single property assignment. The gauge and its elements can be composed and styled in endless
ways to provide a unique, striking appearance for any purpose. For example, a car dashboard can be
assembled using multiple radial and numeric gauges combined with any other Silverlight elements you
might care to use.
Features
Radial Gauge: a circular scale with numbers and tick marks. You can adjust the radius, center point,
sweep angle, start angle and end angle of the scale.
Linear Gauge: a rectangular scale that can be used for many familiar interfaces such as a
thermometer or graphic equalizer.
Indicators: There are five types of indicators that you can mix and match: marker, needle, bar, state
indicator and numeric indicator. "State" indicators help you signal a change in the status of the data, e.
g. "over temperature threshold" or "stock sell price".
Ranges: Use ranges to stake out areas along the scale for special attention, particularly thresholds or
other areas that may call for some decision to be made in response to the current data. The "State"
indicator automatically mirrors the background of the corresponding range.
Rich Customization Capabilities: The look-and-feel of the gauge can be articulated separately from
the gauge behavior. The gauge and its constituent parts can be styled for a completely custom look.
Each of the gauge elements, i.e. indicators, ranges, etc., has a rich set of properties and events to
handle the most demanding requirements.
Animations: gauges are smoothly animated, right out of the box.
Events: indicators and ranges generate events to alert you to value changes, when indicator values are
entering or leaving a range or when a value stays in a range for a given time period.
Project Setup
1) From the Visual Studio menu choose File > New > Project..., select the Silverlight project type, then
select the Silverlight Application template. Provide a unique name for the project and click the OK
button.
2) In the "New Silverlight Application" dialog make sure that the "Host the Silverlight application in a new
Web site" option is checked, give the project a unique name and verify that the "ASP.NET Web
Application Project" New Web Project Type option is selected. Click OK to close the dialog and create
the project.
3) In the Solution Explorer, right-click the References node and select Add References... from the
context menu. Add Assembly references:
a) Telerik.Windows.Controls
b) Telerik.Windows.Controls.Gauge
c) Telerik.Windows.Themes.Vista
d) Telerik.Windows.Themes.Summer
XAML Editing
1) Open MainPage.xaml for editing.
2) Add an event handler to the UserControl element for the "Loaded" event.
3) Add XML namespace references for the Telerik.Windows.Controls and Telerik.Windows.Controls.
Gauges namespaces found in the Telerik.Windows.Controls.Gauge assembly. Also add an XML
namespace reference to the Telerik.Windows.Controls namespace found in the Telerik.Windows.
Controls assembly.
Note: Each xmlns statement should be all on one line. The example below is split up to fit the size
constraints of the page in this manual.
xmlns:control=
"clr-namespace:Telerik.Windows.Controls;assembly=Telerik.Windows.Controls.Gauge"
xmlns:gauge=
"clr-namespace:
Telerik.Windows.Controls.Gauges;assembly=Telerik.Windows.Controls.Gauge"
xmlns:telerik=
"clr-namespace:Telerik.Windows.Controls;assembly=Telerik.Windows.Controls"
4) Drag a RadGauge control to a point inside the main "LayoutRoot" Grid element.
<UserControl . . .>
<Grid x:Name="LayoutRoot">
<control:RadGauge>
</control:RadGauge>
</Grid>
</UserControl>
If you run the application now, the web page will be blank . RadGauge is a ContentControl descendant,
whose purpose is to hold other Silverlight controls. In particular, RadGauge can contain RadialGauge,
LinearGauge and NumericScale controls.
5) Add a LinearGauge inside the RadGauge element.
<Grid x:Name="LayoutRoot">
<control:RadGauge>
<gauge:LinearGauge></gauge:LinearGauge>
</control:RadGauge>
</Grid>
6) Press F5 to run the application. Now the web page will display the default background of a
LinearGauge. As yet, there is no other gauge functionality added, just the background appearance:
<gauge:LinearGauge telerik:StyleManager.Theme="Vista">
</gauge:LinearGauge>
9) Press F5 to run the application again to see the Vista theme in action.
<control:RadGauge>
<gauge:LinearGauge telerik:StyleManager.Theme="Vista">
<gauge:LinearScale Min="0" Max="100"></gauge:LinearScale>
</gauge:LinearGauge>
</control:RadGauge>
<control:RadGauge>
<gauge:LinearGauge telerik:StyleManager.Theme="Vista">
<gauge:LinearScale Min="0" Max="100">
<gauge:IndicatorList>
<gauge:LinearBar x:Name="linearBar"></gauge:LinearBar>
</gauge:IndicatorList>
</gauge:LinearScale>
</gauge:LinearGauge>
</control:RadGauge>
Code Behind
1) In the code-behind for the page, add references to the System.Windows.Threading namespace in the
"Imports" (VB) or "using" (C#) section of code.
2) Add a DispatcherTimer and a Random object. Instantiate both objects.
3) In the UserControl Loaded event handler, set the DispatcherTimer Interval to a half second time span,
add a Tick event handler and call the Start() method.
4) Add a Tick event handler. Inside the new handler, set the LinearBar Value property using the Random.
Next() method. Pass the Next() method the minimum and maximum values it can return.
Change the LinearScale to RadialScale and LinearBar to RadialBar. The screenshot below shows the
resulting RadialGauge/RadialScale/RadialBar combination. You can make these changes to the XAML
alone. No other code changes are required.
RadGauge
The RadGauge control can contain all gauge types (radial, linear or numeric). Layout is completely flexible
and any Panel object (StackPanel, Grid, etc.) can be used as content. RadGauge will typically contain one
or more gauge objects such as RadialGauge or LinearGauge. RadGauge is themeable and can set the style
for all contained gauges. In the XAML below, the RadGauge has a "Summer" theme and contains a
StackPanel as its content. Inside the StackPanel a LinearGauge and a RadialGauge are displayed side-by-
side.
<control:RadGauge telerik:StyleManager.Theme="Summer">
<StackPanel Orientation="Horizontal">
<gauge:LinearGauge>
</gauge:LinearGauge>
<gauge:RadialGauge>
</gauge:RadialGauge>
</StackPanel>
</control:RadGauge>
The result of the markup shows the two gauges with the Summer theme applied:
Gauges
RadialGauge and LinearGauge classes provide a visual background for radial, linear and numeric scales.
Both classes have a similar construction shown in the simplified XAML control template below. The
template contains a ContentControl for the background, an ItemsPresenter to contain scales or indicators
and another ContentControl for the foreground. You can use the predefined themes to handle the
background and foreground or you can design your own unique look for the control.
Tip!
While the typical layout of a gauge is gauge\scale\indicators, there is some latitude to mix-and-match.
Scale objects for example, can be contained directly in the RadGauge or in any Panel. At minimum you
need a scale object to render your indicators, ranges, etc. The XAML below uses a StackPanel to contain
a LinearScale.
<UserControl.Resources>
<LinearGradientBrush x:Key="OrangeBrush">
<GradientStop Color="Yellow" Offset="1" />
<GradientStop Color="Orange" Offset="0.9" />
<GradientStop Color="OrangeRed" Offset="0.2" />
<GradientStop Color="Red" Offset="0" />
</LinearGradientBrush>
</UserControl.Resources>
<Grid x:Name="LayoutRoot">
<StackPanel Orientation="Vertical">
<gauge:LinearScale Min="0" Max="25">
<gauge:IndicatorList>
<gauge:LinearBar Value="10" />
<gauge:StateIndicator Value="8" Top="0.2" Left="0.2" />
</gauge:IndicatorList>
<gauge:RangeList>
<gauge:LinearRange Background="Yellow"
Min="0" Max="5" StartWidth="0.1" EndWidth="0.1" />
<gauge:LinearRange Background="{StaticResource OrangeBrush}"
Min="5" Max="20" StartWidth="0.1" EndWidth="0.1" />
<gauge:LinearRange Background="Red"
Min="20" Max="25" StartWidth="0.1" EndWidth="0.1" />
</gauge:RangeList>
</gauge:LinearScale>
</StackPanel>
</Grid>
Scales
LinearScale and RadialScale are used to control the overall layout of tick marks, labels, indicators, ranges
and an optional scale bar. Both of these classes descend from ScaleBase (in the Telerik.Windows.
Controls.Gauges namespace). Starting with a LinearScale, lets walk through some of the rich set of
properties available in ScaleBase.
The first task should be to set the Min and Max values. In this example the Min is "0" and Max is "25".
This will automatically place a set of tick marks and labels in the scale area.
<control:RadGauge telerik:StyleManager.Theme="Office_Silver">
<gauge:LinearGauge>
<gauge:LinearScale Min="0" Max="25">
</gauge:LinearScale>
</gauge:LinearGauge>
</control:RadGauge>
Set the IsReversed attribute "True" to swap the Min and Max locations:
<control:RadGauge telerik:StyleManager.Theme="Office_Silver">
<gauge:LinearGauge>
<gauge:LinearScale Min="0" Max="25" IsReversed="True">
</gauge:LinearScale>
</gauge:LinearGauge>
</control:RadGauge>
Values along the scale are marked with "Major", "Middle" and "Minor" ticks. The "Major" ticks are labeled.
You can control the number of ticks by setting the MajorTicks, MiddleTicks and MinorTicks attributes to
integer values.
<control:RadGauge telerik:StyleManager.Theme="Office_Silver">
<gauge:LinearGauge>
<gauge:LinearScale Min="0" Max="25"
MajorTicks="2" MiddleTicks="5" MinorTicks="2">
</gauge:LinearScale>
</gauge:LinearGauge>
</control:RadGauge>
In the screenshot below, there are two major ticks, five middle ticks and two minor ticks.
Note: The ShowFirstLabel and ShowLastLabel attributes control visibility of the first and last major ticks.
Labels are only shown for the major ticks.
You can actually have the ticks start at a particular value by using the StartTickOffset to set the starting
point for drawing ticks. In this example, the ticks are drawn beginning with the StartTickOffset value of "10".
<control:RadGauge telerik:StyleManager.Theme="Office_Silver">
<gauge:LinearGauge>
<gauge:LinearScale Min="0" Max="25"
StartTickOffset="10">
</gauge:LinearScale>
</gauge:LinearGauge>
</control:RadGauge>
The scale width can be wider at one end using the StartWidth and EndWidth properties. In this example
the EndWidth is slightly wider than the StartWidth. The "Office_Black" theme is being used here for better
contrast so you can see the scale outline.
<control:RadGauge telerik:StyleManager.Theme="Office_Black">
<gauge:LinearGauge>
<gauge:LinearScale Min="0" Max="25"
StartWidth=".1" EndWidth=".2">
</gauge:LinearScale>
</gauge:LinearGauge>
</control:RadGauge>
By default, the numbers on the scale are distributed in a linear fashion. Distribute the numbers using a
logarithmic scale by setting IsLogarithmic to "True". Base 10 is used by default, but the
LogarithmicBase property can be set to a custom value. The screenshot below shows the same scale
with and without IsLogarithmic enabled.
Indicators
You will want to populate your scale objects with "Indicators", i.e. visual objects that report a value or state
to the user. These indicators can be bars, needles, markers or state indicators. Most of the indicators
descend from IndicatorBase (except for NumericIndicator which is an ItemsControl). IndicatorBase has
several important common properties:
Value is the key property that drives everything. In a "Bar" indicator, the Value is the location of the top
of the bar in the scale. For a "Needle" indicator, Value is the position of the needle along the scale.
Refresh Behavior: RefreshMode can be Average, Last, Min or Max. If the user is clicking the
indicator with a mouse, RefreshMode determines the new value for the indicator. RefreshRate is a
TimeSpan and determines when to supply a new value.
Animation: By default, the IsAnimated property is true, the indicators are animated and move
smoothly between points on the scale. Duration controls the amount of time the animation takes to
complete.
Snap Behavior: SnapType can be None, ToGrid and ToInterval. "None" turns off snapping.
"ToGrid" causes the indicator to snap to the nearest tick mark. "ToInterval" causes the indicator to snap
to the next value that is SnapInterval away from the starting value.
IndicatorBase descends from a ScaleBase object that adds a few more important properties including:
Location is the position of the indicator relative to parts of the scale and can be Outside, OverOutside
, OverCenter, OverInside, CenterOutside, CenterInside and Inside. The exact meaning of these
settings is dependant on the type of scale (radial or linear) that the indicator is part of. See the online
help for detailed information.
Offset is the distance of the indicator from the scale.
A scale bar is an indicator that is rendered as a continuous band spanning the gauge and stopping at a
point matching its Value property setting. The scale bar is also used as a platform for the placement of child
elements, such as tick marks and labels. To add an indicator to your scale element, first add an
IndicatorList element, then one or more indicators inside this list element. In the example below there is a
single LinearBar added to the list with its value set at "10". The Background brush is defined in a resource
not shown here in the interests of brevity.
<control:RadGauge telerik:StyleManager.Theme="Vista">
<gauge:LinearGauge>
<gauge:LinearScale Min="0" Max="25">
<gauge:IndicatorList>
<gauge:LinearBar Value="10"
Background="{StaticResource BarBrush}" />
</gauge:IndicatorList>
</gauge:LinearScale>
</gauge:LinearGauge>
</control:RadGauge>
The output for the XAML in the screenshot shows the bar ending at "10".
If you want the user to set the values of the indicators using the mouse, set the scale IsInteractive
property to "True".
<control:RadGauge telerik:StyleManager.Theme="Vista">
<gauge:LinearGauge>
<gauge:LinearScale Min="0" Max="25"
IsInteractive="True">
<gauge:IndicatorList>
<gauge:LinearBar
Background="{StaticResource BarBrush}" />
</gauge:IndicatorList>
</gauge:LinearScale>
</gauge:LinearGauge>
</control:RadGauge>
In the screenshot below, the mouse sets the LinearBar Value at "6".
In this next example we add a Marker, a NumericIndicator and a StateIndicator to the indicator list.
Notice that the NumericIndicator has several NumberPosition elements defined in order to show up in the
scale, one for each digit that will be displayed. Both the Marker and NumericIndicator will respond to mouse
clicks when the IsInteractive property is set to "True". You can set the Format attribute if you have a
specific number of decimals to display or want to display as currency {C} or percentage {P}. This example
uses the floating point format.
The StateIndicator will be used a little later when we discuss ranges. For now, know that it signals some
state of the data. For example, if you had a "danger zone" temperature in a scale, the StateIndicator might
change to a red color to alert the user.
<control:RadGauge telerik:StyleManager.Theme="Vista">
<gauge:LinearGauge>
<gauge:LinearScale Min="0" Max="25"
IsInteractive="True">
<gauge:IndicatorList>
<gauge:LinearBar Value="10"
Background="{StaticResource IndicatorBrush}" />
<gauge:Marker Value="13"
Background="{StaticResource IndicatorBrush}" />
<gauge:NumericIndicator Format="{}{0:F0}"
Left="0.2" Top="0.08"
RelativeWidth="0.1"
RelativeHeight="0.05" Value="10"
Background="{StaticResource IndicatorBrush}">
<gauge:NumberPosition />
<gauge:NumberPosition />
<gauge:NumberPosition />
</gauge:NumericIndicator>
<gauge:StateIndicator Value="10" Top="0.19"
Left="0.2" RelativeWidth="0.07"
RelativeHeight=".07"
Background="{StaticResource IndicatorBrush}" />
</gauge:IndicatorList>
</gauge:LinearScale>
</gauge:LinearGauge>
</control:RadGauge>
Running in the browser, you can see that the numeric indicator shows in the upper left hand corner. The
marker shows to the right of the bar. IsInteractive is set to "True", so the value selected by the mouse is
reflected by bar, marker and numeric indicators.
Everything you've learned up to this point is also valid for the "Radial", versions of the gauge elements. The
XAML below is essentially the same as the previous example except that RadialGauge, RadialScale and
RadialBar have replaced their "Linear" counterparts. A few minor tweaks to size and location make it easier
to see the numeric and state indicators.
<control:RadGauge telerik:StyleManager.Theme="Vista">
<gauge:RadialGauge>
<gauge:RadialScale Min="0" Max="25"
IsInteractive="True">
<gauge:IndicatorList>
<gauge:RadialBar Value="10"
Background="{StaticResource IndicatorBrush}" />
<gauge:Marker Value="13"
Background="{StaticResource IndicatorBrush}" />
<gauge:NumericIndicator Format="{}{0:F0}"
Left="0.3" Top="0.3"
RelativeWidth="0.1"
RelativeHeight="0.1" Value="10"
Background="{StaticResource IndicatorBrush}">
<gauge:NumberPosition />
<gauge:NumberPosition />
<gauge:NumberPosition />
</gauge:NumericIndicator>
<gauge:StateIndicator Value="10" Top="0.5"
Left="0.3" RelativeWidth="0.1"
RelativeHeight=".1"
Background="{StaticResource IndicatorBrush}" />
</gauge:IndicatorList>
</gauge:RadialScale>
</gauge:RadialGauge>
</control:RadGauge>
Running in the browser, the gauge performs in the same manner as the "Linear" version.
Ranges
Ranges represent a continuous set of values along a scale. Every range has minimum/maximum values and
can fire events when an indicator enters, leaves or stays in a range for a specified time span. You can
define multiple ranges. When a StateIndicator value falls within a range, the StateIndicator fill color matches
the range color.
To create a range, first add a RangeList element at the same level within a scale as an IndicatorList.
Greater Offset numbers moves the range away from the center of the scale. StartWidth and EndWidth can
be equal to make the range appear as an even bar, or can be asymmetrical to display as a bevel (perhaps
to communicate that values farther up the range are larger).
There are three ranges in the example below that divide the scale between 0..5, 5..20 and 20..25.
...
<gauge:RangeList>
<gauge:LinearRange Min="0" Max="5"
Background="{StaticResource BottomRangeBrush}"
Offset="0.1" StartWidth="0.1"
EndWidth="0.1" />
<gauge:LinearRange Min="5" Max="20"
Background="{StaticResource MiddleRangeBrush}"
Offset="0.1" StartWidth="0.1"
EndWidth="0.1" />
<gauge:LinearRange Min="20" Max="25"
Background="{StaticResource TopRangeBrush}"
Offset="0.1" StartWidth="0.1"
EndWidth="0.1" />
</gauge:RangeList>
...
Notice that when the application runs, the StateIndicator color matches the range that the user clicks in.
Range Events
Respond to indicator activity within a range using the EnterRange, LeaveRange and RangeTimeout
events. The RangeTimeout event fires based on the setting of the Timeout property of the range. You can
assign the Timeout at the same time you subscribe to the event. The event arguments for all three events
pass a reference to the Indicator object that's generating the activity and the Range object that is
responding to the activity. The sample code below shows some of the possibilities.
LinearScale Specifics
Orientation can be Horizontal or Vertical. You can set the location of the scale relative to its container
using the Left and Top properties. Use the RelativeHeight property to set the height of the scale relative
to its container.
RadialScale Specifics
By default the scale is automatically centered within the gauge with a Center property of "0.5,0.5". That
works out nicely because the gauge graphics for a given style form a frame around the scale. You can use
the Center property to move the scale to any Point. Set the Radius property to a value between 0 and 1. A
Radius of "0.5", for instance, renders the scale at the half way point of the container.
The origin and length of a radial scale are controlled by StartAngle and SweepAngle properties. These
properties are in degrees and follow Silverlight standards where zero degrees points due east and positive
angles result in a clockwise rotation. The screenshot below shows the result when StartAngle = "0" and
SweepAngle = "180".
20.5 Binding
To bind data using the current edition of RadGauge you actually bind the individual indicator elements. In
this walk through you will bind a series of "Stock" market objects to linear bar elements and update them
periodically.
Gotcha!
At the time of this writing there is a Silverlight issue where animation and binding conflict. As a
workaround you can leave animation off or create your own external animation.
Project Setup
1) From the Visual Studio menu choose File > New > Project..., select the Silverlight project type, then
select the Silverlight Application template. Provide a unique name for the project and click the OK
button.
2) In the "New Silverlight Application" dialog make sure that the "Host the Silverlight application in a new
Web site" option is checked, give the project a unique name and verify that the "ASP.NET Web
Application Project" New Web Project Type option is selected. Click OK to close the dialog and create
the project.
3) In the Solution Explorer, right-click the References node and select Add References... from the
context menu. Add Assembly references:
a) Telerik.Windows.Controls
b) Telerik.Windows.Controls.Gauge
This class will represent a single stock mark et symbol and "quote" or price. Stock implements the
INotifyPropertyChanged interface so that changes to the Quote property will be reflected automatically
in a bound LinearBar Value property.
2) In the Solution Explorer, right-click the project and select Add > Class... Rename the class file
"Stocks.cs". Verify that references to the System.Collections.Generic and System.Collections.
ObjectModel namespaces are included in the "Imports" (VB) or "using" (C#) section of code. Copy
and paste the code below.
This class will represent a series of Stock objects that will be bound to gauge controls.
XAML Editing
1) Open MainPage.xaml for editing.
2) Verify that the XML namespaces for Telerik.Windows.Controls exist in the UserControl element. Add
them if they do not exist. Also, add a "Loaded" event handler to the UserControl element.
<UserControl
...
xmlns:telerik=
"clr-namespace:Telerik.Windows.Controls;assembly=Telerik.Windows.Controls"
...
Loaded="UserControl_Loaded">. . .
<UserControl.Resources>
<LinearGradientBrush x:Key="BackgroundBrush">
<GradientStop Color="LightBlue" Offset="0.01" />
<GradientStop Color="SkyBlue" Offset="0.02" />
<GradientStop Color="SlateBlue" Offset="0.8" />
<GradientStop Color="SkyBlue" Offset="0.99" />
<GradientStop Color="LightBlue" Offset="1" />
</LinearGradientBrush>
</UserControl.Resources>
4) Drag a RadGauge control from the Toolbox to a point inside the main "LayoutRoot" grid element. Inside
the RadGauge element, add two nested StackPanel controls using the XAML below.
The first stack panel provides the back ground. The second stack panel contains a series of text
block s and linear scale elements, centered in the browser.
<telerik:RadGauge>
<StackPanel
Background="{StaticResource BackgroundBrush}"
HorizontalAlignment="Stretch">
<StackPanel x:Name="spScales"
Orientation="Horizontal"
HorizontalAlignment="Center"
Margin="10" />
</StackPanel>
</telerik:RadGauge>
Code Behind
1) Navigate to the code-behind for the page. Verify that references to the System.Windows.Controls,
System.Windows.Data, System.Windows.Media, System.Windows.Threading and Telerik.
Windows.Controls.Gauges namespaces are included in the "Imports" (VB) or "using" (C#) section of
code.
2) Add private members using the code below.
The Random object will generate random numbers for stock quotes and linear bar colors. The
DispatcherTimer will update the quotes every half second.
3) Navigate to the code for the UserControl Loaded event handler. Add the code below to create a stack
panel for each stock and populate each stack panel with a text label and linear bar.
The code iterates the collection of Stock objects and generates a text block label and linear bar
element for each stock . The GetLinearBar() method tak es a Stock reference, creates the linear bar
and binds the Value property to the Stock object's Quote property. At the end of this event handler, a
timer is initialized and started. The timer will tak e care of updating the data.
4) Add a private method to create a LinearBar element and bind it to the Stock Quote property.
"Binding" is System.Windows.Data object that connects the properties of binding targets and data
sources. Because we're getting our data from a "Stock " object, the Stock object is assigned as the
Binding Source. PropertyPath describes the property that we're binding to in the source. In this case,
PropertyPath is the "Quote" property of the Stock object. BindingMode describes when and how the
data will propagate. In this case, we want the data to be travel "one way" from the Stock object to the
target. The SetBinding() method associates the binding information with the LinearBar Value property.
When changes occur to the Stock Quote property, the new data propagates to the bound LinearBar'
Value property automatically.
' generate a linear b ar and b ind Value to the Stock "Quote" property
Private Function GetLinearBar(ByVal stock As Stock) As LinearBar
Dim linearBar As New LinearBar()
linearBar.Value = stock.Quote
linearBar.Background = New SolidColorBrush(GetRandomColor())
linearBar.StartWidth =.1
linearBar.EndWidth =.1
linearBar.RelativeHeight = 0.9
This method is used by the GetLinearBar() method to set the Back ground brush for the linear bar
element.
6) Add the Tick event handler for the timer. Iterate the Stock objects in the Stocks collection and refresh
the Quote property using the Random.Next() method.
20.6 Customization
In this example we will customize the RadGauge control. Specifically, we will change the background of a
LinearGauge to fit with the "Scoville" style.
"Scoville" Styles
We will use a set of colors that include black, red, yellow and orange in many of the style related
topics and prefix the style names with "Scoville". The "Scoville scale" measures the spiciness of
peppers and other culinary irritants.
Project Setup
1) Start with the "Getting Started" project or a copy. Open the project in Expression Blend.
3) In the Objects and Timeline pane, the template contains a border with a grid. Inside the grid you find a
"[ContentControl]" that you may remember from the Control Details section as the "
LinearGaugeBackground". Right-click the [ContentControl] and select Edit Template > Edit a Copy
from the context menu. In the "Create Style Resource" dialog, set the Name (Key) to "Scoville
LinearGaugeBackground". Click OK to create the style resource and close the dialog.
4) In the Objects and Timeline pane, open the object tree and select the innermost "[Border]" object.
5) In the Properties pane, select the Brushes > Fill property. The Border object will already have a
gradient brush assigned. Notice the series of existing gradient stop indicators at the bottom of the
Brushes pane. Select each gradient stop indicator and choose a color (you can use the eye dropper
tool or the color slider just above the eye dropper). Choose between shades of black, red and orange.
6) Select the Radial Gradient button from the lower left of the Brushes pane.
The animation for the application should run with the same functionality as when tested during the
"Getting Started" walk through.
20.7 Wrap Up
This chapter demonstrated how to use gauges to support dynamic data visualization. "First up", you learned
how to build a gauge starting with RadGauge and adding its constituent elements. You learned how to work
with radial and numeric gauges and how these two element can be swapped without affecting the
applications behavior. You drilled down into the gauge elements to see how scales, indicators and ranges
work together and how some of the important properties are used to tweak behavior and appearance. Next,
you learned how to bind individual indicators to data. Finally, you used Expression Blend to customize
gauge appearance.
XXI
ProgressBar
850 RadControls for Silverlight
21 ProgressBar
21.1 Objectives
In this chapter you will use the RadProgressBar control to visually notify users about processes of a known
or indeterminate length. You will learn how to work with the progress bar orientation, minimum/maximum
values and how to update the progress bar.
21.2 Overview
RadProgressBar, like the slider and up-down controls, is a RangeBase descendant and so has Minimum,
Maximum and Value properties. RadProgressBar also uses the RangeBase ValueChanged event to
notify your application when the progress bar value moves. Typically you won't code the ValueChanged
event but instead code an event handler for some other control that acts on the progress bar.
RadProgressBar includes a new SkipValue property that starts the progress indicator at some midpoint
between Minimum and Maximum. The image shows a SkipValue of "50" where the Minimum is "1" and the
Maximum is "100".
The IsIndeterminate property, when true, animates a striped "barbershop" background indicating that the
operation length is unknown.
Project Setup
1) From the Visual Studio menu choose File > New > Project..., select the Silverlight project type, then
select the Silverlight Application template. Provide a unique name for the project and click the OK
button.
2) In the "New Silverlight Application" dialog make sure that the "Host the Silverlight application in a new
Web site" option is checked, give the project a unique name and verify that the "ASP.NET Web
Application Project" New Web Project Type option is selected. Click OK to close the dialog and create
the project.
3) In the Solution Explorer, right-click the References node and select Add References... from the
context menu. Add Assembly references:
a) Telerik.Windows.Controls
XAML Editing
1) Open MainPage.xaml for editing.
2) In the UserControl tag, add a "Loaded" event handler. We will code this event later to initialize the timer
that updates the progress bar.
<UserControl
...
Loaded="UserControl_Loaded">
3) Add the XAML below to the main "LayoutRoot" Grid element. The XAML should be added between the
<Grid> and </Grid> tags. This XAML defines a basic structure of Stack Panel elements where labels,
buttons and the progress bar will be placed. The comments mark locations where you will add XAML in
later steps.
<StackPanel
HorizontalAlignment="Center">
<StackPanel
Orientation="Horizontal"
HorizontalAlignment="Center"
Margin="20">
<!--Loading text-->
</StackPanel>
<StackPanel
Width="Auto"
Orientation="Horizontal"
VerticalAlignment="Center"
HorizontalAlignment="Center"
Height="30">
<!--Buttons-->
</StackPanel>
<!--ProgressBar-->
</StackPanel>
4) Locate the comment "<!--Loading text-->" and replace it with the XAML below. This step adds two
TextBlocks that will display the "Loading..." and current progress bar percentage value.
<!--Loading text-->
<TextBlock
x:Name="loadingText"
HorizontalAlignment="Center"
Text="Loading... " />
<TextBlock
x:Name="loadingPercentage"
HorizontalAlignment="Center"
FontWeight="Bold"
Text="" />
5) Locate the comment "<!--Buttons-->" and replace it with the XAML below. This step will add the "Start",
"Stop" and "Reset" buttons. Notice that the Click event has already been defined for each button. We
will define the actual event handling code in a later step.
<!--Buttons-->
<Button
x:Name="btn_start"
Margin="0 0 10 0"
Click="Btn_start_Click"
VerticalAlignment="Top"
Content=" Start " />
<Button
x:Name="btn_stop"
Margin="0 0 10 0"
Click="Btn_stop_Click"
VerticalAlignment="Top"
Content=" Stop " />
<Button
x:Name="btn_restart"
Margin="0 0 10 0"
Click="Btn_restart_Click"
VerticalAlignment="Top"
Content=" Restart " />
6) Drag a RadProgressBar from the Toolbox to a point just under the <!--ProgressBar--> comment. Edit
the RadProgressBar tag to add the following properties.
a) Name = "pb"
b) Minimum = "1"
c) Maximum = "100"
d) MinHeight = "25"
Code Behind
1) Locate the <UserControl> tag at the top of MainPage.xaml. Right-click the Loaded event and select
"Navigate to Event Handler" from the context menu.
2) Leave the Loaded event handler empty. Above the Loaded event handler, add a private member for the
timer.
3) Add the code below to the Loaded event handler. This code creates an instance of a timer and sets the
timer Interval to fire a Tick event every 10 milliseconds.
4) Add a private helper method GetValuePercentage(). This method calculates the Value as a percentage,
given a RangeBase Minimum and Maximum properties.
7) Add event handlers for each of the button Click events. These handlers start, stop and reset the timer
and progress bar.
Change the RadProgressBar in MainPage.xaml to use different Minimum and Maximum values. Be
aware that there is no safety code checking that Minimum is not greater than or equal to the Maximum
value.
In the code behind for the UserControl Loaded event, change the Interval so that updates are more or
less frequent.
Set the SkipValue property to a value between Minimum and Maximum.
Turn on the IsIndeterminate property and observe the effect on the Value property and on the progress
indicator.
21.4 Wrap Up
In this chapter you used RadProgressBar to visually notify the user about processes of known and
indeterminate length. You worked with the progress bar orientation, minimum/maximum values and also
learned how to update the progress bar value.
XXII
Charting
Charting 859
22 Charting
22.1 Objectives
In this chapter you will first build a chart declaratively using default settings wherever possible. In the
process you will learn the differences between default vs. custom layouts. You will define a series with
individual data points and specify the legend and chart title. You will also learn about how the series
definition displays the data in a particular arrangement, i.e. bar, pie, 3D line, etc. You will enable the
interactivity feature of the chart.
During the exploration of chart control details you will take a tour of the many chart series types that include
the standard bar/line/pie, stacked versions that compare contributions of values across categories, stacked
bar 100% that show the stacked values as percentages, special purpose charts like "candlestick" for
showing stock price and currency changes, and many 3D chart series types as well. You will learn how to
create chart series and data points programmatically. If you need to integrate the chart with existing ASP.
NET applications, the section on "Integration with ASP.NET" will walk you through how this is done.
As part of the binding chapter you will first learn about series and item mappings that route data to various
elements in the chart, including the data point and axis labels. You will display tool tips as simple text, use
special formatting tokens to show data as currency or as percentages of all categories and you will learn
how to display a tool tip that shows another chart as a drill-down.
You will apply styles to customize elements of the chart. You will use the MVVM pattern to display chart
elements in colors corresponding to data in the model.
22.2 Overview
With RadChart you can transform business scenarios into interactive, rich, animated charts. RadChart
takes data visualization to another dimension and is the first commercial 3D Chart for Silverlight. RadChart
features include:
Flexible API
MVVM Support
Advanced X Axis Capabilities
Project Setup
1) From the Visual Studio menu choose File > New > Project..., select the Silverlight project type, then
select the Silverlight Application template. Provide a unique name for the project and click the OK
button.
2) In the "New Silverlight Application" dialog make sure that the "Host the Silverlight application in a new
Web site" option is checked, give the project a unique name and verify that the "ASP.NET Web
Application Project" New Web Project Type option is selected. Click OK to close the dialog and create
the project.
3) In the Solution Explorer, right-click the References node and select Add References... from the
context menu. Add Assembly references:
a) Telerik.Windows.Controls
b) Telerik.Windows.Controls.Charting
c) Telerik.Windows.Data
XAML Editing
1) Open MainPage.xaml for editing.
2) Add references to Telerik.Windows.Controls and Telerik.Windows.Controls.Charting XML
namespaces, both from the Telerik.Windows.Controls.Charting assembly.
<UserControl
xmlns:control=
"clr-namespace:Telerik.Windows.Controls;assembly=Telerik.Windows.Controls.Charting"
xmlns:chart=
"clr-namespace:Telerik.Windows.Controls.Charting;assembly=Telerik.Windows.Controls.Charting"
. . .>
3) Drag a RadChart from the Toolbox to a point between the main "LayoutRoot" grid begin and end tags.
4) Setup the basic chart structure.
a) Inside the RadChart element, add a RadChart.DefaultView. Notice that DefaultView uses the
"control" XML namespace reference.
b) Inside the DefaultView, add a ChartDefaultView. Notice that ChartDefaultView uses the "chart" XML
namespace reference.
c) Add the comments shown below to indicate where we will place the ChartArea, ChartLegend and
ChartTitle elements.
<control:RadChart>
<control:RadChart.DefaultView>
<chart:ChartDefaultView>
<!--ChartArea-->
<!--ChartLegend-->
<!--ChartTitle-->
</chart:ChartDefaultView>
</control:RadChart.DefaultView>
</control:RadChart>
If you ran the application right now, it would report "No Data Series.", as shown in the screenshot
below.
Notes
RadChart has a UseDefaultLayout property. By default, this property is true and the chart
expects a single ChartArea, ChartLegend and ChartTitle. When UseDefaultLayout is false
RadChart can contain an arbitrary arrangement of any number of chart elements (analogous to
how gauges allow any number of gauge types - see the Gauges chapter). UseDefaultLayout is a
handy way to avoid completely overriding a control template. For example, you could have a pie
chart along side another chart showing a bar graph, and place the legend centered below the two
chart areas and leave the chart title off altogether. The arrangement can be defined solely by your
requirements.
The ChartDefaultView contains a ChartArea and within the ChartArea, a DataSeries. The DataSeries.
Definition element determines the type of series it will be, i.e. pie, line, 3D bar, etc. Then a collection of
DataPoint elements list the data that will display in the series. Bar charts are relatively simple and only
need a single "Y" value for each data point. Note that the properties you fill out for each DataPoint vary
depending on the type of series and the amount of data it requires. For example, a "Candlestick " series
require four different values for each data point.
The series below defines a Bar3DSeriesDefinition with four data points, each with a YValue defined.
<!--ChartArea-->
<chart:ChartDefaultView.ChartArea>
<chart:ChartArea>
<chart:ChartArea.DataSeries>
<chart:DataSeries>
<chart:DataSeries.Definition>
<chart:Bar3DSeriesDefinition />
</chart:DataSeries.Definition>
<chart:DataPoint YValue="15" />
<chart:DataPoint YValue="5" />
<chart:DataPoint YValue="34" />
<chart:DataPoint YValue="11" />
</chart:DataSeries>
</chart:ChartArea.DataSeries>
</chart:ChartArea>
</chart:ChartDefaultView.ChartArea>
If you ran the project, now you would see the data displayed as a 3D bar chart. There is a default
"Legend" label in the upper right corner and no title as yet.
6) Replace the "<!--ChartLegend-->" comment with the XAML below to define the ChartLegend and use its
defaults.
In a moment we will go back and point the LegendName property of the ChartArea at this element.
<!--ChartLegend-->
<chart:ChartDefaultView.ChartLegend>
<chart:ChartLegend x:Name="CustomLegend" />
</chart:ChartDefaultView.ChartLegend>
7) Go back to the ChartArea element and add a LegendName attribute with a value of "CustomLegend".
Also, add a Label attribute to the DataSeries element and set the value to "Store Sales".
You can see that the "Store Sales" label shows up in the legend. Note: The ChartLegend element has
a UseAutoGeneratedItems property ("True" by default) that mak es all DataSeries labels display
automatically in the legend.
8) Replace the "<!--ChartTitle-->" comment with the XAML below. This new element will display a chart
title "Sales Summary" at the top of the page.
<!--ChartTitle-->
<chart:ChartDefaultView.ChartTitle>
<chart:ChartTitle Content="Sales Summary" />
</chart:ChartDefaultView.ChartTitle>
Press F5 to run the application. The web page should look something like the screenshot below.
Try this: copy the DataSeries element labeled "Store Sales". Change the copied DataSeries Label to
"Comparable Stores" and change the DataSeries.Definition to Line3DSeriesDefintion. Change the
DataPoint YValue attributes to different values.
<chart:ChartArea.DataSeries>
<chart:DataSeries Label="Store Sales">
<chart:DataSeries.Definition>
<chart:Bar3DSeriesDefinition />
</chart:DataSeries.Definition>
<chart:DataPoint YValue="15" />
<chart:DataPoint YValue="5" />
<chart:DataPoint YValue="34" />
<chart:DataPoint YValue="11" />
</chart:DataSeries>
<chart:DataSeries Label="Comparable Stores">
<chart:DataSeries.Definition>
<chart:Line3DSeriesDefinition/>
</chart:DataSeries.Definition>
<chart:DataPoint YValue="1" />
<chart:DataPoint YValue="6" />
<chart:DataPoint YValue="38" />
<chart:DataPoint YValue="12" />
</chart:DataSeries>
</chart:ChartArea.DataSeries>
The second series should display over the first as a 3D line chart and add a new "Comparable Stores"
entry in the legend.
Try this: arrange the elements of the chart to make your own custom layout.
Set the chart UseDefaultLayout property to "False". Then remove all the references to ChartDefaultView.
The example below uses a StackPanel to arrange the chart elements vertically. There are two
ChartArea elements, where the second is a 3D pie chart, the Legend is removed altogether and the
ChartTitle appears at the bottom of the page.
<control:RadChart UseDefaultLayout="False">
<StackPanel>
<!--ChartArea-->
<chart:ChartArea MaxHeight="300" MaxWidth="300">
<chart:ChartArea.DataSeries>
<chart:DataSeries>
<chart:DataSeries.Definition>
<chart:Bar3DSeriesDefinition />
</chart:DataSeries.Definition>
<chart:DataPoint YValue="15" />
<chart:DataPoint YValue="5" />
<chart:DataPoint YValue="34" />
<chart:DataPoint YValue="11" />
</chart:DataSeries>
</chart:ChartArea.DataSeries>
</chart:ChartArea>
<!--ChartArea-->
<chart:ChartArea MaxHeight="300" MaxWidth="300">
<chart:ChartArea.DataSeries>
<chart:DataSeries>
<chart:DataSeries.Definition>
<chart:Pie3DSeriesDefinition/>
</chart:DataSeries.Definition>
<chart:DataPoint YValue="15" />
<chart:DataPoint YValue="5" />
<chart:DataPoint YValue="34" />
<chart:DataPoint YValue="11" />
</chart:DataSeries>
</chart:ChartArea.DataSeries>
</chart:ChartArea>
<!--ChartTitle-->
<chart:ChartTitle MaxHeight="100" MaxWidth="100">
<chart:ChartTitle Content="Sales Summary" />
</chart:ChartTitle>
</StackPanel>
</control:RadChart>
The XAML above results in a page layout similar to the screenshot below.
Question: Is RadChart interactive out of the box? Can I interact with the chart, i.e rotate in 3d
space, using my mouse pointer?
Answer: RadChart supports mouse interactivity through instantiating a CameraExtension
instance.
Now the user can use the mouse to drag across the chart and rotate the chart image in all directions.
RadChart supports extensibility for the creation of custom chart extensions via the Extensible
Object Pattern. This pattern enables an object to participate in custom behavior, such as
registering for events, or watching state transitions. The camera extension is included "out of the
box". See the online help article "Creating Chart Extensions" for more information.
At the time of this writing there are eighteen 2D charts and nine 3D charts. The following is a quick
reference to the available chart types and some of the typical uses. Be sure to check www.telerik.com for
the latest developments!
Stacked Spline Use the Stacked Spline when you need to show
the correlation between two or more series of data
visualized as splines.
Spline Area The Spline Area chart type defines one or more
spline curves and fills in the area defined by the
spline. Can be used for data modeling in that it
takes a limited number of data points and
interpolates the intervening values. This chart type
also emphasizes the area between the spline curve
and a mid-point of the spline.
Stacked Line Use the Stacked Line when you need visibility to
the combined values of two or more series.
The three main areas of the chart are ChartTitle, ChartArea and ChartLegend. Inside the ChartArea are
the chart series that in turn contain data points. At the outer edge of the ChartArea you can find the AxisY
and AxisX objects.
You can replicate everything you did declaratively during the Getting Started walk through, in code. If you
have a chart with essentially no content...
You can build up the individual elements that parallel the XAML. You still need to create a DataSeries,
assign its Definition and fill it with DataPoints. You create a ChartLegend and add the DataSeries, create a
ChartLegend and assign its Name and finally, create a ChartTitle and assign its Content. A few notes to
remember here:
The ChartArea LegendName needs to match the ChartLegend's Name property.
Be aware that the ChartArea DataSeries is a DataSeriesCollection while the stand-alone DataSeries
type is a collection of DataPoint.
Use the Axis properties to control the axis Title, visibility of the axis itself, the grid lines and the strip lines
(strip lines are the alternating bands of color on the background). These properties are controlled by the
Telerik.Windows.Controls.Charting Axis class. Properties specific to each axis, i.e. X or Y, are controlled
by the corresponding AxisX and AxisY classes. AxisY for instance has an ExtendDirection property that
controls the addition of "head room" above or below the chart area. AxisX has a LabelRotationAngle used
to spin the label such that more data points can fit
The example below sets the titles for both axis. The visibility for the axis, strip lines and grid lines are
enabled explicitly for your reference.
Set the IsDateTime property "True" to display DateTime values on the X-axis. You can also use the
DefaultLabelFormat to display dates according to a date and time format. In the example, the format is
"dddd" and displays the day names.
chart.DefaultView.ChartArea.AxisX.Title = "X-Axis"
chart.DefaultView.ChartArea.AxisX.Visibility = Visibility.Visible
chart.DefaultView.ChartArea.AxisX.StripLinesVisibility = _
Visibility.Visible
chart.DefaultView.ChartArea.AxisX.MajorGridLinesVisibility = _
Visibility.Visible
chart.DefaultView.ChartArea.AxisX.AutoRange = True
chart.DefaultView.ChartArea.AxisX.IsDateTime = True
chart.DefaultView.ChartArea.AxisX.LabelRotationAngle = 45
chart.DefaultView.ChartArea.AxisX.DefaultLabelFormat = "dddd"
chart.DefaultView.ChartArea.AxisY.Title = "Y-Axis"
chart.DefaultView.ChartArea.AxisY.Visibility = Visibility.Visible
chart.DefaultView.ChartArea.AxisY.StripLinesVisibility = _
Visibility.Visible
chart.DefaultView.ChartArea.AxisY.MajorGridLinesVisibility = _
Visibility.Visible
chart.DefaultView.ChartArea.AxisY.ExtendDirection = _
AxisExtendDirection.Smart
chart.DefaultView.ChartArea.AxisX.Title = "X-Axis";
chart.DefaultView.ChartArea.AxisX.Visibility = Visibility.Visible;
chart.DefaultView.ChartArea.AxisX.StripLinesVisibility = Visibility.Visible;
chart.DefaultView.ChartArea.AxisX.MajorGridLinesVisibility = Visibility.Visible;
chart.DefaultView.ChartArea.AxisX.AutoRange = true;
chart.DefaultView.ChartArea.AxisX.IsDateTime = true;
chart.DefaultView.ChartArea.AxisX.LabelRotationAngle = 45;
chart.DefaultView.ChartArea.AxisX.DefaultLabelFormat = "dddd";
chart.DefaultView.ChartArea.AxisY.Title = "Y-Axis";
chart.DefaultView.ChartArea.AxisY.Visibility = Visibility.Visible;
chart.DefaultView.ChartArea.AxisY.StripLinesVisibility = Visibility.Visible;
chart.DefaultView.ChartArea.AxisY.MajorGridLinesVisibility = Visibility.Visible;
chart.DefaultView.ChartArea.AxisY.ExtendDirection = AxisExtendDirection.Smart;
22.4.3 Animations
For perceived performance or aesthetic reasons you may need to disable animation altogether. Set the
EnableAnimations property of the ChartArea:
RadChart1.DefaultView.ChartArea.EnableAnimations = False
RadChart1.DefaultView.ChartArea.EnableAnimations = false;
Animation timings can be modified for the entire chart or for an individual series. The settings for the
example below cause the animation for a SeriesDefinition to take 10 seconds. The spline series is drawn
slowly, as if being traced on a white board. This setting isn't performant of course, but you may want to
display some series more slowly to point out the relationship of data. Quicker animation settings provide
visual "pop" without noticeable pause in the rendering. The ItemAnimationDuration property controls the
time span for the animation of each individual item in the chart. TotalSeriesAnimationDuration controls
the time span for the animation of the entire series. ItemDelay and DefaultSeriesDelay delay the the item
and series animation.
chart.DefaultSeriesDefinition.AnimationSettings = _
New AnimationSettings() With { _
.TotalSeriesAnimationDuration = TimeSpan.FromSeconds(10)}
chart.DefaultSeriesDefinition.AnimationSettings =
new AnimationSettings()
{
TotalSeriesAnimationDuration = TimeSpan.FromSeconds(10)
};
You may not have the luxury of building Silverlight-only solutions for your business. But you can introduce
Silverlight elements into existing ASP.NET applications. Silverlight RadChart, RadGridView or any of the
other RadControls can be including as-needed. The technique is to wrap a Silverlight control in a web user
control, then supply the plumbing to communicate from the ASP.NET web page that contains the user
control, all the way back to the Silverlight control. In the example below, we pass a chart title string all the
way from an attribute on the ASP.NET page back to a RadChart on the Silverlight user control.
The key steps are described in the diagram below where the ASP.NET page sets the "ChartTitle" property
of a web user control. The web user control uses a startup script to save the ChartTitle value, then, when
the Silverlight plugin loads, the saved value is passed to a "Scriptable" property in a Silverlight user control
("Scriptable" means that the Silverlight user control property is available to Javascript). The "Scriptable"
ChartTitle is then assigned when the RadChart first loads.
All the pieces described below have to be in place for the technique to work, but we will follow the process
sequentially from Silverlight page, through the web user control and finally, to the ASP.NET page.
Before getting started with the steps below, first create a Silverlight Application along with a host ASP.NET
application.
Silverlight Page
The Silverlight XAML page can be any arbitrary set of Silverlight elements. In this example we will re-use the
XAML from the RadChart Getting Started walk through.
1) In the code-behind for the page add a reference to the System.Windows.Browser namespace to
support HtmlPage and ScriptableMember objects.
2) In the constructor for the page, call HtmlPage.RegisterScriptableObject() and pass a string
"slChartPage" and a reference to the page. This line of code registers the page as an object that can be
accessed in Javascript. Also in the constructor, add a Loaded event handler to the RadChart.
3) In the Loaded event handler, assign the Content for the chart title. The content will come from a string
property of the page to be defined called "ChartTitle". Its important to put this assignment after the chart
has been loaded, and we can make that explicit by putting the assignment in the chart's Loaded event
handler.
4) Include a string property called "ChartTitle" and decorate it with the ScriptableMember attribute. The
combination of RegisterScriptableObject to surface the page and the ScriptableMember attribute to
expose the ChartTitle will make both objects accessible later in Javascript from the ASP.NET page.
The code for the page should look like the example below:
[ScriptableMember]
public string ChartTitle
{ get; set; }
}
User Control
1) In the Solution Explorer, References node of the ASP.NET application, add a reference to the System.
Web.Silverlight assembly.
2) In the host ASP.NET application, create a Web User Control.
3) Configure the web user control in the designer:
a) Add a reference to System.Web.Silverlight in the markup of the Web User Control.
b) Add a block of script to the user control with two functions. The global variable "chartTitle" at the top of
the script block is used by both functions.
The pluginLoaded() function is triggered from an event of the Silverlight plugin. "sender" is the
Silverlight control. The content for the Silverlight control holds a reference to the "slChartPage" object
we defined earlier and through that we can also reference the "ChartTitle" property. When we assign
the slChartPage.ChartTitle, we're actually setting the chart title of the RadChart back on the Silverlight
page.
The second function, setTitleValue(), will be called later from a startup script when the user control
first loads.
<script type="text/javascript">
var chartTitle;
function pluginLoaded(sender) {
// get reference to the silverlight control on the page
var silverlightControl = sender.get_element();
function setTitleValue(title) {
chartTitle = title;
}
</script>
c) Add a Silverlight server control inside the web user control. Point the Source property at the path of the
"xap" file in the ClientBin directory. Also be sure to hookup the OnPluginLoaded event to the
pluginLoaded() function you created in the last step.
ASP.NET Page
1) Add a reference to the web user control.
2) Add a ScriptManager to the page if one doesn't already exist and add the web user control to the page.
Set the ChartTItle property of the web user control to "Category Sales".
<asp:ScriptManager ID="ScriptManager1" runat="server">
</asp:ScriptManager>
<div>
<uc1:ChartUserControl ID="ChartUserControl1" runat="server"
ChartTitle="Category Sales" />
</div>
22.5 Binding
22.5.1 Binding Basics
chart.ItemsSource = kayaks.Products
chart.DefaultSeriesDefinition = New Bar3DSeriesDefinition()
End Sub
chart.ItemsSource = kayaks.Products;
chart.DefaultSeriesDefinition = new Bar3DSeriesDefinition();
}
Its more likely we will want control of where the data goes. Each SeriesMapping has an ItemMappings
collection that holds DataPoint objects. The relationship is roughly:
RadChart.SeriesMappings
SeriesMapping
ItemMappings
ItemMapping
DataPointMember
Now we can tell the chart to bind just the "InStock" data to the YValue property of each DataPoint and
the chart now looks something like the screenshot below.
chart.ItemsSource = kayaks.Products
chart.SeriesMappings.Add(seriesMapping)
chart.ItemsSource = kayaks.Products;
seriesMapping.ItemMappings.Add(new ItemMapping()
{
DataPointMember = DataPointMember.LegendLabel,
FieldName = "ProductName"
});
seriesMapping.ItemMappings.Add(new ItemMapping()
{
DataPointMember = DataPointMember.YValue,
FieldName = "InStock"
});
chart.SeriesMappings.Add(seriesMapping);
Notice that in the last bit of code we attempt to bind both "InStock" to the YValue property and
"ProductName" to the LegendLabel property. But LegendLabel doesn't show up in the running example.
To fix that, set the series definition LegendDisplayMode property to DataPointLabel instead of the
default SeriesLabel. Now, each element of the "Products" collection shows up both as a data point and a
legend element. Because we bound the "ProductName" to the LegendLabel, each legend element display
the appropriate product name.
We can polish the results by positioning the Header text to the top of the legend. You can access this
through the chart DefaultView.ChartLegend.Header property.
chart.ItemsSource = kayaks.Products
chart.DefaultView.ChartLegend.Header = "Products"
chart.ItemsSource = kayaks.Products;
chart.DefaultView.ChartLegend.Header = "Products";
Let's look at three series together, displaying the "In Stock", "On Order" and "Cost" data. The example
below refactors the code involved with creating a series mapping to a new method "AddSeriesMapping()".
This new method also reverts to using the LegendLabel LegendDisplayMode instead of the DataPointLabel
because we're going to show multiple series of data and it will be easier to display in the legend this way, i.
e. one legend label per series.
chart.ItemsSource = kayaks.Products
chart.DefaultView.ChartLegend.Header = "Products"
chart.SeriesMappings.Add(AddSeriesMapping( _
New Bar3DSeriesDefinition(), "In Stock", "InStock"))
End Sub
Return mapping
End Function
chart.ItemsSource = kayaks.Products;
chart.DefaultView.ChartLegend.Header = "Products";
chart.SeriesMappings.Add(
AddSeriesMapping(new Bar3DSeriesDefinition(), "In Stock", "InStock"));
}
mapping.ItemMappings.Add(new ItemMapping()
{
DataPointMember = DataPointMember.YValue,
FieldName = fieldName
});
return mapping;
}
Question: I can get the data to appear as a bar series with the correct values but how do I put
labels on the x axis?
Answer: One of the DataPointMember enumeration values is XCategory. Bind the property that
you want to use for labels to the ItemMapping FieldName and set the DataPointMember to
DataPointMember.XCategory.
seriesMapping.ItemMappings.Add(new ItemMapping()
{
DataPointMember = DataPointMember.XCategory,
FieldName = "ProductName"
});
22.5.3 Tooltips
There are several methods that populate tool tips, each with tradeoffs to consider. First, set the
SeriesDefinition ShowItemToolTips property "True" before any of the methods will work.
When you create ItemMappings, you can bind the DataPointMember to the DataPointMember.ToolTip
enumeration member. Assign FieldName to the name of the property that will supply the value.
mapping.ItemMappings.Add(new ItemMapping()
{
DataPointMember = DataPointMember.Tooltip,
FieldName = "OnOrder"
});
Instead of using an ItemMapping, set the ItemToolTipFormat property to a Label Format Expression (see
the upcoming Label Format Expressions section for more detail on syntax). In the example below,
ItemToolTipFormat displays the "Y" value in a formatted string.
To get fine-grained control over tool tips, handle the ItemToolTipOpening event. The event passes the
ItemToolTip2D that encapsulates the tool tip element and a ItemToolTipEventArgs that contains additional
information about where the tool tip opened from, including the DataPoint, DataSeries, ItemIndex and
MouseData. You have unlimited latitude to make tooltip Content be just about anything, from simple text to
complex layouts including grids or even other RadCharts. For example, you can display a drill down chart
showing detail for a data point under the mouse.
The example below displays a Border with a detail RadChart. You can see in the code that we can access
the bound data using the DataPoint.DataItem property. In this case, the DataItem is a "Category" object
that contains a "Products" collection that can then be bound to the detail chart. We can even apply the
same theme to the detail chart for a consistent look and feel. This example only shows a Border and chart,
but by adding any container, such as a Grid or StackPanel to the tooltip.Content, you can build tool tips of
arbitrary complexity.
detailChart.SeriesMappings.Add(mapping)
mapping.ItemMappings.Add(new ItemMapping()
{
DataPointMember = DataPointMember.XCategory,
FieldName = "ProductName"
});
mapping.ItemMappings.Add(new ItemMapping()
{
DataPointMember = DataPointMember.YValue,
FieldName = "Cost"
});
detailChart.SeriesMappings.Add(mapping);
inStockMapping.SeriesDefinition.ItemLabelFormat = "#Y"
inStockMapping.SeriesDefinition.ItemLabelFormat = "#Y";
Formats are made up of a pound sign ("#") + a predefined token + an optional format string. Here are the
defined tokens and how they relate to DataPoint or TickPoint properties.
#% Series Items Label, DataPoint.YValue / (The sum of all YValues in the current data
Tooltip series)
#STSUM Series Items Label, Represents the sum of all stacked items for a given index
Tooltip
#STPERCENT Series Items Label, The percent representation of the value of a given item with
Tooltip respect to all stacked items for the respective index.
#DATAITEM. Series Items Label, Used to access the DataPoint.DataItem and read the value from
<Property> Tooltip a property of the underlying business object.
#VAL X-Axis, Y-Axis TickPoint.Value. This will work only when formatting axis labels.
Here's a slightly larger example that demonstrates several formatting expressions at once. Both the label
and the tool tip are formatted.
The example uses a "Sales" class that knows how to create a set of DateTime values and random
"Amounts".
While configuring the series mappings, the ItemToolTipFormat and the DefaultLabelFormat are
assigned. The ItemToolTipFormat lets you try out several formats all at once. Here we're showing the
"XCategory", the "Y" value as a currency with zero decimal places and a percentage of the all the values
with two decimal places. The Default label format also displays the YValue as a currency with no decimal
places.
22.6 Customization
22.6.1 Coloring Chart Elements
A very common issue discussed in the forums is the need to color series items based on data values. The
recommended technique employs the MVVM pattern. The "View Model" layer translates some property of
the "Model" into a Brush. The chart as a whole is bound to the view model and a custom template element
for a given series item type, e.g. "Bar", is bound to the brush. For example, we can have a series of
"Pepper" objects that have a "Scoville" scale rating that measures relative spiciness. The
"PepperViewModel" object translates the scale rating into a brush that represents the Scoville scale rating.
The brush is used in the binding of the bar element in the template.
Let's take a look at the relevant pieces that make this work.
Model
The Model defines the data you want to see in the chart. This may be your own object or data returned by a
service. In this example we have the Pepper class that defines a name, a numeric "Scoville" scale rating
and a Spiciness enumeration value that tells us the range of the scale rating. The specifics of the class
implementation are less important than the fact that this model has a property that will be translated by the
view model into a visually relevant property.
View Model
The view model wraps the model in a nice tidy package suitable for direct use by the view. The example
below defines a PepperViewModel class. Notice that the constructor takes an instance of the model.
The purpose of the view model here is to translate the Spiciness property value on the model to a Brush that
can be used in the View. Look at the implementation of the ScovilleColor Brush property. The Spiciness
property is used to return an appropriate Brush color.
switch (this.Pepper.Spiciness)
{
case Spiciness.Mild: return lightYellow;
case Spiciness.Medium: return new SolidColorBrush(Colors.Orange);
case Spiciness.Hot: return new SolidColorBrush(Colors.Red);
default: return darkRed;
}
}
}
}
Template
We use a custom template to allow binding view model properties to elements arranged in a container (e.g.
Canvas, Grid, etc). In this case we are templating the Bar series item. The Bar contains a Rectangle with a
Fill property that can be bound to DataItem.ScovilleColor. DataItem in this case is a PepperViewModel.
Remember that the style is called "ScovilleStyle". We will assign the style later in code.
For reference, here's the entire control template for the Bar series item. You can also get at this information
using Expression Blend.
Code Behind
The first tasks is to create a collection of model "Pepper" objects. Then create a collection of matching view
model "PepperViewModel" objects. For your own purposes, the "model" objects may be coming from
another source, such as a web, RIA or REST service.
During assignment of the series definition and item mapping, you have the opportunity to assign ItemStyle
to "ScovilleStyle" and to tie view model properties to elements on the chart. In this example, the Pepper.
ScovilleRating is bound to the YValue and the Pepper.Name is bound to the XCategory that displays along
the X-Axis of the chart.
You may have noticed from the last example that the axis lines were red axis text has a larger-than-default
size. The chart surfaces many styles that make it possible to change the appearance of chart elements
without completely re-templating the entire control. The appearance of the chart below is disorganized. The
title and legend text are not legible, the axis lines are barely visible and the X axis text is horizontal.
There are a few tweaks you can make without having to style the chart. The code snippet below sets
properties for the ChartLegend, ChartTitle and ChartArea AxisX properties.
chart.DefaultView.ChartLegend.Visibility = Visibility.Collapsed
chart.DefaultView.ChartTitle.Content = "Scoville Scale"
chart.DefaultView.ChartTitle.Foreground = New SolidColorBrush(Colors.Red)
chart.DefaultView.ChartArea.AxisX.LabelRotationAngle = 45
chart.DefaultView.ChartLegend.Visibility = Visibility.Collapsed;
chart.DefaultView.ChartTitle.Content = "Scoville Scale";
chart.DefaultView.ChartTitle.Foreground = new SolidColorBrush(Colors.Red);
chart.DefaultView.ChartArea.AxisX.LabelRotationAngle = 45;
These changes go a long way to cleaning up the appearance. Removing the legend gets rid of the legend
key artifact and lets the chart take up more room. The X Axis labels are rotated and this also relieves
crowding on the page. But we still can't see the axis lines text or markings very well.
We can define styles that apply to elements of the chart and apply them in XAML or later, in code. Two
styles are defined below that set the look for an axis line and label text.
These can be applied against both X and Y axis elements as shown in the code snippet below.
chart.DefaultView.ChartArea.AxisY.AxisStyles.AxisLineStyle = _
TryCast(Me.Resources("CustomAxisStyle"), Style)
chart.DefaultView.ChartArea.AxisY.AxisStyles.ItemLabelStyle = _
TryCast(Me.Resources("CustomItemLabelStyle"), Style)
chart.DefaultView.ChartArea.AxisX.AxisStyles.AxisLineStyle = _
TryCast(Me.Resources("CustomAxisStyle"), Style)
chart.DefaultView.ChartArea.AxisX.AxisStyles.ItemLabelStyle = _
TryCast(Me.Resources("CustomItemLabelStyle"), Style)
chart.DefaultView.ChartArea.AxisY.AxisStyles.AxisLineStyle =
this.Resources["CustomAxisStyle"] as Style;
chart.DefaultView.ChartArea.AxisY.AxisStyles.ItemLabelStyle =
this.Resources["CustomItemLabelStyle"] as Style;
chart.DefaultView.ChartArea.AxisX.AxisStyles.AxisLineStyle =
this.Resources["CustomAxisStyle"] as Style;
chart.DefaultView.ChartArea.AxisX.AxisStyles.ItemLabelStyle =
this.Resources["CustomItemLabelStyle"] as Style;
Now all visual aspects of the chart are clear and legible.
22.7 Wrap Up
In this chapter you built a chart declaratively using default settings wherever possible. In the process you
learned the differences between default vs. custom layouts. You defined a series with individual data points
and specified the legend and chart title. You also learned about how the series definition displays the data
in a particular arrangement, i.e. bar, pie, 3D line, etc. You enabled the interactivity feature of the chart.
During the exploration of chart control details you toured chart series types that include the standard bar/
line/pie, stacked versions that compare contributions of values across categories, stacked bar 100% that
show the stacked values as percentages, special purpose charts like "candlestick" for showing stock price
and currency changes, and many 3D chart series types as well. You learned how to create chart series and
data points programmatically. The section on "Integration with ASP.NET" walked you through how to
include RadChart with existing ASP.NET applications.
As part of the binding chapter you first learned about series and item mappings that route data to various
elements in the chart, including the data point and axis labels. You displayed tool tips as simple text, used
special formatting tokens to show data as currency or as percentages of all categories and you learned how
to display a tool tip that shows another chart as a drill-down.
You applied styles to customize elements of the chart. You used the MVVM pattern to display chart
elements in colors corresponding to data in the model.
XXIII
Docking
Docking 921
23 Docking
23.1 Objectives
In this chapter you will learn how to create flexible layouts using the RadDocking control. You will start by
creating a layout entirely in XAML. During this initial project you will learn how to control user interaction
with panes including floating, closing and pinning behaviors.
In the Control Details section of the chapter you will learn how to create split containers, groups and panes
all in code, how to size and position floating panes, how to save and load panes, how to control pinning
behavior, how to hide and show panes and how to use "Preview" events.
During the Binding section of this chapter you will learn how to build a Silverlight "mashup" client application
with docking that aggregates various REST services. The service output will be bound to both the pane
content and the pane title area.
During the Customization section of the chapter you will modify the pane title area to include a button and
image.
23.2 Overview
Handling multiple dockable windows is a no-hassle operation with the RadDocking control. Incorporate
splitters, tabbed documents, float and auto-hide panes to your application. RadDocking maintains a clear
separation between window management and content.
Floating Windows
ToolWindow Control and Behavior
Pin, Unpin and Hide
Tabbed Documents
Project Setup
1) From the Visual Studio menu choose File > New > Project..., select the Silverlight project type, then
select the Silverlight Application template. Provide a unique name for the project and click the OK
button.
2) In the "New Silverlight Application" dialog make sure that the "Host the Silverlight application in a new
Web site" option is checked, give the project a unique name and verify that the "ASP.NET Web
Application Project" New Web Project Type option is selected. Click OK to close the dialog and create
the project.
3) In the Solution Explorer, right-click the References node and select Add References... from the
context menu. Add Assembly references:
a) Telerik.Windows.Controls
b) Telerik.Windows.Controls.Docking
c) Telerik.Windows.Controls.Navigation
XAML Editing
1) Open MainPage.xaml for editing.
2) Drag a RadDocking control from the Toolbox to a point between the "LayoutRoot" <Grid> and </Grid>
tags.
<Grid x:Name="LayoutRoot">
<telerikDocking:RadDocking>
</telerikDocking:RadDocking>
</Grid>
3) Drag a RadSplitContainer from the Toolbox to a point inside the RadDocking element. Name the
element "TopContainer". Set the InitialPosition property to "DockedTop".
<telerikDocking:RadDocking>
<telerikDocking:RadSplitContainer x:Name="TopContainer"
InitialPosition="DockedTop">
</telerikDocking:RadSplitContainer>
</telerikDocking:RadDocking>
4) Create two more new RadSplitContainer elements just under the first RadSplitContainer. Name the
elements "LeftContainer" and "RightContainer" respectively. Set the InitialPosition properties to
"DockedLeft" and "DockedRight". The XAML should now look like the example below.
<telerikDocking:RadDocking>
<telerikDocking:RadSplitContainer x:Name="TopContainer"
InitialPosition="DockedTop">
</telerikDocking:RadSplitContainer>
<telerikDocking:RadSplitContainer x:Name="LeftContainer"
InitialPosition="DockedLeft">
</telerikDocking:RadSplitContainer>
<telerikDocking:RadSplitContainer x:Name="RightContainer"
InitialPosition="DockedRight">
</telerikDocking:RadSplitContainer>
</telerikDocking:RadDocking>
5) Below the first three RadSplitContainer elements and within the RadDocking element, add a
RadDocking.DocumentHost element. Inside the DocumentHost element place one more
RadSplitContainer. Name the container "CenterContainer".
<telerikDocking:RadDocking.DocumentHost>
<telerikDocking:RadSplitContainer x:Name="CenterContainer">
</telerikDocking:RadSplitContainer>
</telerikDocking:RadDocking.DocumentHost>
If you were to run the application now, the page would look something like this screenshot:
6) Drag a new RadPaneGroup from the Toolbox to a point inside each of the RadSplitContainer elements.
The XAML should now look something like the example below.
<telerikDocking:RadDocking>
<telerikDocking:RadSplitContainer x:Name="TopContainer"
InitialPosition="DockedTop">
<telerikDocking:RadPaneGroup></telerikDocking:RadPaneGroup>
</telerikDocking:RadSplitContainer>
<telerikDocking:RadSplitContainer x:Name="LeftContainer"
InitialPosition="DockedLeft">
<telerikDocking:RadPaneGroup></telerikDocking:RadPaneGroup>
</telerikDocking:RadSplitContainer>
<telerikDocking:RadSplitContainer x:Name="RightContainer"
InitialPosition="DockedRight">
<telerikDocking:RadPaneGroup></telerikDocking:RadPaneGroup>
</telerikDocking:RadSplitContainer>
<telerikDocking:RadDocking.DocumentHost>
<telerikDocking:RadSplitContainer x:Name="CenterContainer">
<telerikDocking:RadPaneGroup></telerikDocking:RadPaneGroup>
</telerikDocking:RadSplitContainer>
</telerikDocking:RadDocking.DocumentHost>
</telerikDocking:RadDocking>
7) Navigate back to the "TopContainer" element. Add a RadPane from the Toolbox to a point inside the
RadPaneGroup element. Set RadPane properties as follows:
a) Header="Powered by..."
b) CanUserClose="False"
c) CanUserPin="False"
d) CanFloat="False"
e) CanDockInDocumentHost="False"
<telerikDocking:RadSplitContainer x:Name="TopContainer"
InitialPosition="DockedTop">
<telerikDocking:RadPaneGroup>
<telerikDocking:RadPane Header="Powered by..."
CanUserClose="False" CanUserPin="False"
CanFloat="False"
CanDockInDocumentHost="False">
</telerikDocking:RadPane>
</telerikDocking:RadPaneGroup>
</telerikDocking:RadSplitContainer>
8) Navigate to the "LeftContainer" element. Add a RadPane from the Toolbox to a point inside the
RadPaneGroup element. Set RadPane properties as follows:
a) Header="Search Criteria"
b) CanUserClose="False"
c) CanUserPin="True"
d) CanFloat="False"
e) CanDockInDocumentHost="False"
<telerikDocking:RadSplitContainer x:Name="LeftContainer"
InitialPosition="DockedLeft">
<telerikDocking:RadPaneGroup>
<telerikDocking:RadPane Header="Search Criteria"
CanDockInDocumentHost="False"
CanFloat="False" CanUserClose="False"
CanUserPin="True">
</telerikDocking:RadPane>
</telerikDocking:RadPaneGroup>
</telerikDocking:RadSplitContainer>
9) Navigate to the "RightContainer" element. Add two RadPane controls from the Toolbox to the
RadPaneGroup element. Set RadPane Header properties to "Chart" and "Data" respectively.
<telerikDocking:RadSplitContainer x:Name="RightContainer"
InitialPosition="DockedRight">
<telerikDocking:RadPaneGroup>
<telerikDocking:RadPane Header="Chart">
</telerikDocking:RadPane>
<telerikDocking:RadPane Header="Data">
</telerikDocking:RadPane>
</telerikDocking:RadPaneGroup>
</telerikDocking:RadSplitContainer>
10)Navigate to the "CenterContainer" element. Add a RadDocumentPane from the Toolbox to a point
inside the RadPaneGroup element. Set RadDocumentPane Header property to "Top Stories" and
CanFloat to "False". Inside the RadDocumentPane add a TextBlock with some arbitrary text.
<telerikDocking:RadDocking.DocumentHost>
<telerikDocking:RadSplitContainer
x:Name="CenterContainer">
<telerikDocking:RadPaneGroup>
<telerikDocking:RadDocumentPane
Header="Top Stories" CanFloat="False">
<TextBlock
Text="Place content between the RadDocumentPane tags"
TextWrapping="Wrap" />
</telerikDocking:RadDocumentPane>
</telerikDocking:RadPaneGroup>
</telerikDocking:RadSplitContainer>
</telerikDocking:RadDocking.DocumentHost>
2) Test the behavior of each pane relative to its CanUserClose, CanUserPin and
CanDockInDocumentHost property settings.
3) Open the drop-down menus in the panes. The menus control the current state of the pane and also
reflect the property settings of each pane.
4) Find a pane with the "pin" icon and click it. The "pin" icon controls the "Auto hide" functionality and
causes the pane to become a button on the side of the RadDocking area.
RadDocking
RadSplitContainer
RadPaneGroup
RadPane
RadPane
RadPane
<your content>
Tabbed documents have a similar structure, but are wrapped by a DocumentHost element and have a
RadDocumentPane instead of a RadPane.
RadDocking
RadDocking.DocumentHost
RadSplitContainer
RadPaneGroup
RadDocumentPane
<your content>
paneGroup.Items.Add(pane)
container.Items.Add(paneGroup)
docking.Items.Add(container)
End Sub
paneGroup.Items.Add(pane);
container.Items.Add(paneGroup);
docking.Items.Add(container);
}
The example running in the browser looks like the screenshot below.
container.Items.Add(paneGroup)
docking.DocumentHost = container
container.Items.Add(paneGroup);
docking.DocumentHost = container;
The example running in the browser looks like the screenshot below.
Relative Sizing
Docking panes sizes are dynamic and so its doesn't make sense to set absolute size. Instead, you can set
the RelativeSize attached property of any two RadSplitContainer or RadPaneGroup elements. The layout
in the screenshot below was achieved by setting RelativeSize of RadSplitContainer elements docked top
and bottom, and setting RelativeSize of RadPaneGroup elements docked side by side.
Examine the XAML below to see how the RelativeSize property of the ProportionalStackPanel is applied.
The relative vertical sizes for the containers are "70" and "30". Relative horizontal sizes for the two
RadPaneGroup elements are "20" and "80". You can use any set of numbers and the relative proportions
are calculated automatically.
<telerikDocking:RadDocking x:Name="docking">
<telerikDocking:RadSplitContainer
InitialPosition="DockedTop"
telerikDocking:ProportionalStackPanel.RelativeSize="0, 70">
<telerikDocking:RadPaneGroup>
<telerikDocking:RadPane Header="top" />
</telerikDocking:RadPaneGroup>
</telerikDocking:RadSplitContainer>
<telerikDocking:RadSplitContainer
InitialPosition="DockedBottom"
telerikDocking:ProportionalStackPanel.RelativeSize="0, 30">
<telerikDocking:RadPaneGroup
telerikDocking:ProportionalStackPanel.RelativeSize="20, 0">
<telerikDocking:RadPane Header="left" />
</telerikDocking:RadPaneGroup>
<telerikDocking:RadPaneGroup
telerikDocking:ProportionalStackPanel.RelativeSize="80, 0">
<telerikDocking:RadPane Header="right" />
</telerikDocking:RadPaneGroup>
</telerikDocking:RadSplitContainer>
</telerikDocking:RadDocking>
A floating pane doesn't have to be proportionally sized to another window, so we should be able to assign a
specific size. While we're at it, can we place the pane in a specific location? The example below places a
floating pane 50 pixels from the top left of the page. The pane is sized 300x300 pixels.
Again, this is done through attached properties. See how the XAML below assigns the RadDocking
FloatingLocation and FloatingSize attached properties to a RadSplitContainer.
<telerikDocking:RadDocking x:Name="docking">
<telerikDocking:RadSplitContainer
InitialPosition="FloatingDockable"
telerikDocking:RadDocking.FloatingLocation="50, 50"
telerikDocking:RadDocking.FloatingSize="300, 300">
<telerikDocking:RadPaneGroup>
<telerikDocking:RadPane Header="Floating Pane" />
</telerikDocking:RadPaneGroup>
</telerikDocking:RadSplitContainer>
</telerikDocking:RadDocking>
Question: The SizeChanged event is not raised, when a pane is resized while floating.
Answer:
Here are a few details about the architecture of the Docking control that will help you better
understand when each event is fired and why.
The Docking control contains SplitContainers and they contain PaneGroups. PaneGroups inherit
from TabControl and they contain Panes that inherit from TabControlItem. The Pane has Header
and Content part (like the TabControlItem) and the Header part is displayed in the TabStrip panel
at the top of the TabControl. The actual Content of the TabItem is displayed in an area in the
TabControl, because only one Pane's content is visible in any moment. As the TabControl hosts
the Content of the TabItem, it is in the TabControl's visual tree.
The TabItem's visual representation is only the button with the header. It is not resized with the
whole TabControl. This is the reason why the SizeChanged event of Pane is not fired when you
resize the PaneGroup (as the PaneGroup is a TabControl and the Pane is a TabItem). The
SizeChaged event is fired on the PaneGroup and on the Content of the currently selected Pane. If
you need to handle this event we would recommend you to use the event of the Content of the
Pane instead of the event of the PaneGroup as the groups are created and destroyed
dynamically.
Set the IsPinned property false to have the RadPane "Auto Hide" against one of the edges of the docking
area. The pane is collapsed to the side of the docking area where it is docked. The InitialPosition of the
RadPane in the screenshot below is DockedRight. When the mouse moves over the unpinned pane button,
the pane expands. Use the AutoHideHeight and AutoHideWidth properties to set the dimensions the
pane will occupy when the pane expands. Only the AutoHideWidth property will be taken into account if the
pane is docked right or left and only the AutoHideHeight will be taken into account if the pane is docked top
or bottom.
If you want to examine the conditions of the docking layout before an action occurs, use one of the
"Preview" events: PreviewShow, PreviewPin, PreviewClose, PreviewUnPin, PreviewCloseWindow
and PreviewShowCompass. You can set the Canceled property based on the arguments passed to the
event handler. The last event listed here, PreviewShowCompass, can be used to prevent docking in a
particular area. The example below checks if the group being dragged to has a SerializationTag property of
"DocumentGroup" and prevents the compass from showing.
Each docking group or pane can be identified with a SerializationTag property. You can persist the layout
using the RadDocking SaveLayout(stream) method. Only those panes and groups with the
SerializationTag will be saved. Later, when you want to restore the layout, call the LoadLayout(stream)
method.
The SerializationTag is an attached property that can be added to groups or panes as shown in the XAML
below:
<telerikDocking:RadPaneGroup telerikDocking:RadDocking.SerializationTag="Group1">
Or you can assign the SerializationTag programmatically using the SetValue() method.
container.Items.Add(paneGroup)
docking.DocumentHost = container
container.Items.Add(paneGroup);
docking.DocumentHost = container;
To use the SaveLayout() method, you need to pass it a stream that contains layout information. Any
accessible storage mechanism will do, but you may want to consider Isolated Storage. Isolated Storage is
a convenient bucket to drop user specific information that may be larger than cookie size. The example
below obtains a reference to a IsolatedStorageFileStream that's passed to the SaveLayout() and
LoadLayout() methods.
Notes
The isolated storage file is hidden deep within the local documents. You can find the actual path
for the document by setting a breakpoint and examining the IsolatedStorageFileStream object in
the QuickWatch window.
The actual saved layout is in XML form and looks something like the fragment below. Notice the
inclusion of the SerializationTag.
23.5 Binding
RadDocking is a good fit where you are aggregating services from various sources. For example, you could
pull in data from related REST based services and display them as one coherent application. A real-estate
application can draw on data from sites that expose REST services such as zillow.com or trulia.com.
Google and Bing also provide REST API's that return articles about just about any subject. RadGridView
and RadChart can be placed in panes and tabbed documents to assemble a high-quality UI.
This section discusses the major tasks you would need to get this done, including:
Providing logos and links to service provider sites.
Working around security issues for otherwise "trustworthy" sites that do not have client access policy
files.
Binding to content controls within panes as well as binding to the panes themselves.
Re-using techniques from earlier chapters including "Input Controls", "ToolBar", "GridView" and
"Charting".
Notes
Important note! You will need to visit the developer API sites for any services you use, read any
agreements and typically, apply for a user and application key. Many REST API's require the
application key to be sent with each call to the service. The application keys have been removed from
these examples. The configuration file for the WCF service in the sample solution has a "Mashups"
section ("Mashup" being a presentation of aggregate services) that has the developer site urls.
Also be aware that services can change their API without notice. You may need to research and alter
the code to keep up with these changes. The larger, more stable sites can be expected to keep their
API more consistent over time.
What happens when a REST service or other external resource has information you need, but does not have
a ClientAccessPolicy.xml or Crossdomain.xml file? For example, the "Bing" web search API has a
ClientAccessPolicy.xml in place (as you might expect from a Microsoft site), but the "Trulia" real estate
search site does not have a client access file, so attempts to access from Silverlight fall in a hail of security
exceptions. You can step around the issue by wrapping all your REST calls in a WCF service and include
the policy file in the root of the service. By accessing external resources through WCF, even from sites
without policy files, security exceptions are avoided.
Our WCF service will have a series of calls that total no more than a page of code. The other pieces we
need to build to support the service calls are:
A set of web.config entries to describe the REST API's we intend to consume. The config entries will
hold the application keys, paths to logo images, paths to the main site and the developer API reference
site and a tool tip.
A custom ConfigurationSection object to parse the web.config entries. A custom ConfigurationSection
allows us to describe N number of services without having to add more code later.
A ClientAccessPolicy.xml file.
A set of objects to wrap various REST services: a "RestServiceBase" base class to handle tasks
common to all services, such as reading the configuration file and retaining the basic information about
the service, "Logo" to contain the services logo image and a link to the site and two RestServiceBase
implementations "Bing" and "Trulia".
Notes
This section will not completely reiterate all details on how to build a WCF service. Please refer to the
"Data Binding" and "GridView chapters for more complete information.
1) Create a new ASP.NET web site to host the WCF service. Note: Remember that this project must be
in the same solution with the Silverlight client application that consumes it.
2) Add a "Silverlight-enabled WCF Service" item to the project. Note: Be sure to create a "Silverlight-
enabled" WCF service item and not a general "WCF service application". The "Silverlight-Enabled"
WCF item brings along the proper attributes and configuration entries to work with a Silverlight client
application.
3) Add a ClientAccessPolicy.xml file to the project and place the following XML in it:
<?xml version="1.0" encoding="utf-8"?>
<access-policy>
<cross-domain-access>
<policy>
<allow-from http-request-headers="*">
<domain uri="*"/>
</allow-from>
<grant-to>
<resource path="/" include-subpaths="true"/>
</grant-to>
</policy>
</cross-domain-access>
</access-policy>
4) Add a new section to the web.config file (being sure not to overwrite any existing configuration
sections.
The "<section>" element describes the "mashupsSection" to follow and points to a class that will read
and parse the custom section. We will write the class in a following step. The format of the "type"
section attribute is "<name space>.<class name>, assembly name". Notice that the custom section,
"mashupsSection", contains a <services> element and N number of "<add>" elements. Each "<add>"
describes a single service.
<configuration>
<configSections>
<!--custom section-->
<section
name="mashupsSection"
<!--name space + "." + custom section class name, assembly name-->
type="_03_Binding_Service_Host.MashupsSection, 03_Binding_Service_Host"/>
</configSections>
<mashupsSection>
<services>
<add
name="Trulia"
applicationKey="yourapplicationkeyhere"
logoUrl="http://images.trulia.com/images/logos/trulia_logo_100x60.jpg"
linkUrl="http://www.trulia.com"
developerUrl="http://developer.trulia.com/"
toolTip="The best place to start your real estate search"
/>
<add name="Bing"
applicationKey="yourapplicationkeyhere"
logoUrl="http://www.bing.com/siteowner/s/siteowner/Logo_63x23_Dark.png"
linkUrl="http://www.live.com"
developerUrl="http://www.bing.com/developers"
toolTip="When it comes to decisions that matter, Bing and Decide"
/>
</services>
</mashupsSection>
5) Create a new class file called "MashupsSection.cs" and place the code below in it.
End Get
End Property
6) Create a "Logo.cs" class file and populate with the code below. The "Logo" class is used to pass the
service branding information back to the client application for display.
The class holds the basic service information consumed from the custom configuration section object.
The LINQ statement in the constructor filters for the "<add>" element that matches a service name, e.
g. "Trulia". The class also has a separate GetLogo() method that downloads the logo image byte array
on-demand.
The class descends from our RestServiceBase object and passes the name of the service, e.g.
"Bing", to the constructor. Much lik e the example in the "GridView" chapter section on "REST" binding,
this class sets up a URL used to mak e an API call to a REST service. A WebClient object is used to
download XML as a string, the results are parsed and returned from GetResults() as a collection of
XElement. The collection of XElement is filtered through a LINQ statement and output placed into new
"Result" objects, one for each search result.
Notes
This set of tasks can be similar but not identical from one service to another. Many REST services
have a specific URL format published on their developer site that wants your application ID (you
need to apply for your own application ID from the vendor site), and some set of parameters. The
return value format for many sites usually includes XML but may also be ATOM, JSON, etc.. The
XDocument.Parse() method makes short work of converting raw XML into object form where
you can then divide and conquer. Typically the developer site will show sample XML output so
that you can make some intelligent guesses about what elements to access. Be aware that you
will need to see the actual data returned from the service to be sure of the format.
LINQ allows you to filter and convert collections of XElement objects output from the
XDocument Parse() into your own custom objects.
' format the url, call the api, return "<results>" elements
Private Function GetResults(ByVal searchString As String) As IEnumerable(Of XElement)
Using webClient As New WebClient()
Dim url As String = String.Format(urlFormat, Me.ApplicationKey, searchString, "web")
Dim xml As String = webClient.DownloadString(New Uri(url))
If (Not xml.Equals(String.Empty)) Then
Dim document As XDocument = XDocument.Parse(xml)
Dim webElement As XElement = document.Root.Element(nsWeb + "Web")
Dim resultsElement As XElement = webElement.Element(nsWeb + "Results")
If resultsElement IsNot Nothing Then
Return resultsElement.Elements()
End If
End If
End Using
Return Nothing
End Function
From r In results _
Select New Result() With {.Title = r.Element(nsWeb + "Title").Value, .Description = r.Element(nsWeb + "Title").Va
End If
Return Nothing
End Function
9) Create a "Trulia" class. The class file is available from the reference projects for this chapter and is
omitted here for the sake of brevity.
The Trulia class also descends from RestServiceBase. The class is slightly more involved than the
Bing class but performs the same basic steps, i.e. formats a URL that includes the application k ey
and some set of parameters. Trulia has two sets of API functions, one for "LocationInfo" that returns
US states and cities, and "Stats" that return statistics about a location. Again, the results are filtered
using LINQ and placed into generic lists of simple Location and Stat objects.
10)Implement the WCF service using the code below. Each method to be used by the Silverlight client is
surfaced by a method marked with the OperationContract attribute. Each contract simply surfaces Bing
or Trulia object functionality.
<ServiceContract(Namespace := ""), _
AspNetCompatibilityRequirements(RequirementsMode := _
AspNetCompatibilityRequirementsMode.Allowed)> _
Public Class MashupService
<OperationContract> _
Public Function GetLogos() As List(Of Logo)
Return New List(Of Logo)()
CType(New Bing(), Bing).GetLogo(), New Trulia().GetLogo()
End Function
<OperationContract> _
Public Function GetStates() As List(Of Trulia.Location)
Return New Trulia().GetStates()
End Function
<OperationContract> _
Public Function GetCities(ByVal state As String) As List(Of Trulia.Location)
Return New Trulia().GetCities(state)
End Function
<OperationContract> _
Public Function GetStateStats(ByVal state As String, _
ByVal start As DateTime, ByVal [end] As DateTime) As List(Of Trulia.Stat)
Return New Trulia().GetStateStats(state, start, [end])
End Function
<OperationContract> _
Public Function GetCityStats(ByVal state As String, _
ByVal city As String, ByVal start As DateTime, _
ByVal [end] As DateTime) As List(Of Trulia.Stat)
Return New Trulia().GetCityStats(state, city, start, [end])
End Function
<OperationContract> _
Public Function GetBingSearchResults( _
ByVal searchString As String) As List(Of Bing.Result)
Return New Bing().Search(searchString)
End Function
End Class
[ServiceContract(Namespace = "")]
[AspNetCompatibilityRequirements(RequirementsMode =
AspNetCompatibilityRequirementsMode.Allowed)]
public class MashupService
{
[OperationContract]
public List<Logo> GetLogos()
{
return new List<Logo>()
{
new Bing().GetLogo(), new Trulia().GetLogo()
};
}
[OperationContract]
public List<Trulia.Location> GetStates()
{
return new Trulia().GetStates();
}
[OperationContract]
public List<Trulia.Location> GetCities(string state)
{
return new Trulia().GetCities(state);
}
[OperationContract]
public List<Trulia.Stat> GetStateStats(
string state, DateTime start, DateTime end)
{
return new Trulia().GetStateStats(state, start, end);
}
[OperationContract]
public List<Trulia.Stat> GetCityStats(
string state, string city, DateTime start, DateTime end)
{
return new Trulia().GetCityStats(state, city, start, end);
}
[OperationContract]
public List<Bing.Result> GetBingSearchResults(string searchString)
{
Notes
This section will not completely reiterate all details on how to build a WCF Silverlight client. Please refer
back to the "Data Binding" and "GridView for more complete information.
1) Add an ImageConverter class. This is the same code used in the "GridView" chapter in the
"Customization" section. The converter will be used when binding the logo images.
2) In the code behind for the page, add references to the following namespaces:
a) System.Windows.Media
b) System.Windows.Media.Imaging
c) Telerik.Windows.Controls
d) Telerik.Windows.Controls.Charting
3) Add a reference to the namespace for the WCF proxy client. This will be the name of the client
project + "." + the reference name for the proxy client. If you have trouble finding the name through
IntelliSense, be sure to rebuild the project.
4) Create a new SummerTheme object in the constructor before the call to InitializeComponent() and set
the IsApplicationTheme property to "True". This will apply the Summer theme to all RadControls in
the application. Note: Be sure to leave the call to InitializeComponent in the constructor.
public MainPage()
{
new SummerTheme().IsApplicationTheme = true;
InitializeComponent();
}
5) Add a reference to the WCF client proxy object so that we can reference it throughout the code.
6) Add a method to hook up all the WCF client proxy events and add the events handlers.
Notice that most of the "Completed" events simply assign the Result property of the event arguments
to the ItemsSource of a RadControl. The GetLogosCompleted() method uses LINQ to locate a Logo
object for one of the services, i.e. "Bing" or "Trulia", and assigns the object to the DataContext of a
RadPane where the service is being used. The asynchronous events that are used to retrieve data are
discussed in the "Data Binding" and "GridView" chapters.
AddressOf client_GetStateStatsCompleted
AddHandler client.GetCityStatsCompleted, _
AddressOf client_GetCityStatsCompleted
AddHandler client.GetBingSearchResultsCompleted, _
AddressOf client_GetBingSearchResultsCompleted
End Sub
client.GetCityStatsCompleted +=
new EventHandler<GetCityStatsCompletedEventArgs>(
client_GetCityStatsCompleted);
client.GetBingSearchResultsCompleted +=
new EventHandler<GetBingSearchResultsCompletedEventArgs>(
client_GetBingSearchResultsCompleted);
}
The code is very similar to the code in the "Charting" chapter, "Binding" section dealing with "Binding
Basics". The code sets up two series mappings, one for "average listing price" and the second for
"median listing price". Notice that the "Week ending date" data is mapped to the XCategory data point
member so that the dates are listed along the bottom of the chart. See the "Charting" chapter for more
information on work ing with series mapping, axis and legends.
chart.SeriesMappings.Add(averageMapping)
chart.SeriesMappings.Add(medianMapping)
chart.DefaultView.ChartArea.AxisX.LabelRotationAngle = 45
chart.DefaultView.ChartArea.AxisX.IsDateTime = True
chart.DefaultView.ChartArea.AxisX.Title = "Properties Listing in Week Ending"
chart.DefaultView.ChartArea.AxisX.DefaultLabelFormat = "d"
chart.DefaultView.ChartLegend.Header = "Property Listings"
End Sub
averageMapping.ItemMappings.Add(new ItemMapping()
{
DataPointMember = DataPointMember.XCategory,
FieldName = "WeekEndingDate"
});
averageMapping.ItemMappings.Add(new ItemMapping()
{
DataPointMember = DataPointMember.YValue,
FieldName = "AverageListingPrice",
});
chart.SeriesMappings.Add(averageMapping);
medianMapping.ItemMappings.Add(new ItemMapping()
{
DataPointMember = DataPointMember.XCategory,
FieldName = "WeekEndingDate"
});
medianMapping.ItemMappings.Add(new ItemMapping()
{
DataPointMember = DataPointMember.YValue,
FieldName = "MedianListingPrice",
});
chart.SeriesMappings.Add(medianMapping);
chart.DefaultView.ChartArea.AxisX.LabelRotationAngle = 45;
chart.DefaultView.ChartArea.AxisX.IsDateTime = true;
chart.DefaultView.ChartArea.AxisX.Title = "Properties Listing in Week Ending";
chart.DefaultView.ChartArea.AxisX.DefaultLabelFormat = "d";
chart.DefaultView.ChartLegend.Header = "Property Listings";
}
The method tak es the state and city, formats a search string and calls the asynchronous Bing search
method. The search is hard coded to bring back results about "Real Estate" in a particular state and
city, i.e. "San Francisco, California Real Estate".
client.GetBingSearchResultsAsync(searchString)
End Sub
if (city != null)
{
searchString =
city.Name + "," + state.Name + " " + RealEstate;
}
else if (state != null)
{
searchString = state.Name + searchString;
}
client.GetBingSearchResultsAsync(searchString);
}
The event handler calls the Hook upServiceEvents() method so that the WCF client proxy events will
fire, calls client proxy methods to get the logos and load up the "State" combo box. The selected dates
for the date pick er controls are hard coded to a date range k nown to have data at the time of this
writing. Finally, the SetupChart() method is called to setup the series and items mappings.
client.GetLogosAsync()
client.GetStatesAsync()
SetupChart()
End Sub
client.GetLogosAsync();
client.GetStatesAsync();
SetupChart();
}
This event handler starts the Bing search and calls a WCF client proxy method to get state or city
statistics depending on user selection in the state and city combo boxes.
BingSearch(state, city)
BingSearch(state, city);
if (city != null)
{
client.GetCityStatsAsync(
state.ID, city.Name,
(DateTime)dpStart.SelectedDate, (DateTime)dpEnd.SelectedDate);
}
else if (state != null)
{
client.GetStateStatsAsync(
state.ID, (DateTime)dpStart.SelectedDate, (DateTime)dpEnd.SelectedDate);
}
}
This event handler calls a WCF client proxy method to get cities for the currently selected state.
xmlns:telerikNavigation=
"clr-namespace:Telerik.Windows.Controls;assembly=Telerik.Windows.Controls.Navigation"
xmlns:telerikGridView=
"clr-namespace:Telerik.Windows.Controls;assembly=Telerik.Windows.Controls.GridView"
xmlns:telerikChart=
"clr-namespace:Telerik.Windows.Controls;assembly=Telerik.Windows.Controls.Charting"
xmlns:telerikInput=
"clr-namespace:Telerik.Windows.Controls;assembly=Telerik.Windows.Controls.Input"
xmlns:telerikDocking=
"clr-namespace:Telerik.Windows.Controls;assembly=Telerik.Windows.Controls.Docking"
xmlns:local="clr-namespace:_03_Binding"
2) Add a Loaded event handler to the UserControl that points at the Loaded event handler we added earlier
to the code behind.
3) Add a UserControl.Resources element with a style for the grid view and a reference to the
ImageConverter.
The grid view style uses the settings described in the "GridView" chapter "Control Details" section
dealing with "Grid View Elements Visibility". The style turns off the usual visual accoutrements of the
grid view so that we will only see a list of hyperlink s to search results.
<UserControl.Resources>
<Style x:Key="GridlessStyle"
TargetType="telerikGridView:RadGridView">
<Setter Property="Background" Value="Transparent" />
<Setter Property="BorderBrush" Value="Transparent" />
<Setter Property="CanUserFreezeColumns" Value="False" />
<Setter Property="ShowColumnFooters" Value="False" />
<Setter Property="ShowColumnHeaders" Value="False" />
<Setter Property="ShowGroupPanel" Value="False" />
<Setter Property="ShowGroupFooters" Value="False" />
<Setter Property="RowIndicatorVisibility"
Value="Collapsed" />
<Setter Property="GridLinesVisibility" Value="None" />
<Setter Property="ScrollMode" Value="RealTime" />
<Setter Property="ColumnWidth" Value="SizeToHeader" />
</Style>
</UserControl.Resources>
4) Add the XAML below to the main "LayoutRoot" grid element. This will setup the basic RadDocking
structure. The setup is similar to the "Getting Started" walk through. Take a minute to review the XAML
and locate the comments where RadControls will be placed, e.g. "<!--add tool bar here-->". Also notice
the RadPane settings.
<telerikDocking:RadDocking>
<!--top panel-->
<telerikDocking:RadSplitContainer
InitialPosition="DockedTop" Orientation="Vertical"
MaxHeight="100" MinHeight="100">
<!--logo area-->
<telerikDocking:RadPaneGroup>
<telerikDocking:RadPane Header="Powered by..."
CanUserClose="False" CanUserPin="False"
CanFloat="False"
CanDockInDocumentHost="False">
<!--add tool bar here-->
</telerikDocking:RadPane>
</telerikDocking:RadPaneGroup>
</telerikDocking:RadSplitContainer>
<!--search panel-->
<telerikDocking:RadSplitContainer
InitialPosition="DockedLeft">
<telerikDocking:RadPaneGroup>
<telerikDocking:RadPane Header="Search Criteria"
CanDockInDocumentHost="False"
CanFloat="True" CanUserClose="False"
CanUserPin="True">
5) Locate the "<!--add tool bar here-->" comment and add the following XAML to define a RadToolBar in
the top pane.
Here we define the RadToolBar ItemTemplate that contains a Hyperlink Button surrounding an Image for
each Logo we return from the WCF service. Notice the bindings for the NavigateUri, ToolTip and
Source. See the "ToolBar" Binding section for more information.
<telerikNavigation:RadToolBar x:Name="tbLogos">
<telerikNavigation:RadToolBar.ItemTemplate>
<DataTemplate>
<HyperlinkButton
NavigateUri="{Binding LinkUri, Mode=OneTime }"
ToolTipService.ToolTip="{Binding ToolTip, Mode=OneTime }"
TargetName="_blank"
Margin="5">
<Image
Source="{Binding Image, Converter={StaticResource ImageConverter}}"
Width="75" Height="75"
Stretch="Uniform"></Image>
</HyperlinkButton>
</DataTemplate>
</telerikNavigation:RadToolBar.ItemTemplate>
</telerikNavigation:RadToolBar>
6) Locate the "<!--add search controls here-->" comment and add the following XAML.
Here we define a series of input controls in a Stack Panel that will allow the user to define search
criteria. Notice that the DisplayMemberPath for both combo boxes is the "Name" property of "Location"
objects returned from the WCF service. See the "HTMLPlaceholder", "Date, Time and Calendar" and
"ComboBox" chapters for more information.
<StackPanel Margin="5">
<TextBlock Text="State"></TextBlock>
<telerikInput:RadComboBox x:Name="cbState"
DisplayMemberPath="Name"
HorizontalAlignment="Stretch"
SelectionChanged="cbState_SelectionChanged" />
<TextBlock Text="City"></TextBlock>
<telerikInput:RadComboBox x:Name="cbCity"
DisplayMemberPath="Name"
HorizontalAlignment="Stretch" />
<TextBlock Text="Start Date"></TextBlock>
<telerikInput:RadDatePicker
x:Name="dpStart"
HorizontalAlignment="Stretch" />
<TextBlock Text="EndDate"></TextBlock>
<telerikInput:RadDatePicker x:Name="dpEnd"
HorizontalAlignment="Stretch" />
<Button x:Name="btnSearch"
HorizontalAlignment="Right"
Margin="5" Content="Search"
Click="btnSearch_Click" />
</StackPanel>
7) Locate the "<!--add grid view here-->" comment and add the following XAML.
The grid view uses the "GridlessStyle" defined in the UserControl Resources. AutoGenerateColumns is
turned off and a single hyperlink column is bound to the results of the Bing search where the link is to
the "Url" property and the visible content displays the "Title" property. See the "GridView" chapter
"Binding" section for more information.
<telerikGridView:RadGridView
x:Name="gvResults"
Style="{StaticResource GridlessStyle}"
AutoGenerateColumns="False">
<telerikGridView:RadGridView.Columns>
<telerikGridView:GridViewHyperlinkColumn
DataMemberBinding="{Binding Url}"
ContentBinding="{Binding Title}"
TargetName="_blank" />
</telerikGridView:RadGridView.Columns>
</telerikGridView:RadGridView>
8) Locate the "<!--add chart here-->" comment and add the following XAML.
The chart is named so we can access it and setup series mappings in code. See the "Charting"
chapter "Binding" section for more information.
<telerikChart:RadChart x:Name="chart"></telerikChart:RadChart>
Press F5 to run the application. The web page should look something like the screenshot below.
23.6 Customization
One question that comes up often in the forums is how to get a button in the title area of a pane. RadPane
has a number of templates that you can override to put your own stamp on the control. Depending on the
circumstances of the pane, different templates will be applied. For instance, the DocumentHostTemplate
is applied when the RadPane is placed in the DocumentHost. Depending on the docked position of the
pane, the BottomTemplate, LeftTemplate, RightTemplate or TopTemplate might be in play. We will
define the TitleTemplate and add a button that will act on the content of the pane.
Inside the RadPane element, place the TitleTemplate element followed by the DataTemplate element. Inside
that you can place whatever arbitrary markup suits your purpose. Here we add a StackPanel to contain a
Button, Image inside the button and a TextBlock for the title text. Notice the Click event handler hooked
up to the Button element.
<telerikDocking:RadDocking>
<telerikDocking:RadSplitContainer>
<telerikDocking:RadPaneGroup>
<telerikDocking:RadPane x:Name="paneRSS">
<telerikDocking:RadPane.TitleTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Button Click="Button_Click"
Width="16" Height="16">
<Image Source="Rss.png"
Stretch="Uniform" />
</Button>
<TextBlock Text="RSS Feeds" />
</StackPanel>
</DataTemplate>
</telerikDocking:RadPane.TitleTemplate>
</telerikDocking:RadPane>
</telerikDocking:RadPaneGroup>
</telerikDocking:RadSplitContainer>
</telerikDocking:RadDocking>
The Click event handler simply assigns some test content to the panel Content property.
23.7 Wrap Up
In this chapter you learned how to create flexible layouts using the RadDocking control. You started by
creating a layout entirely in XAML. During the initial project you learned how to control user interaction with
panes including floating, closing and pinning behaviors.
In the Control Details section of the chapter you learned how to create split containers, groups and panes
all in code, how to size and position floating panes, how to save and load panes, how to control pinning
behavior, how to hide and show panes and how to use various "Preview" events.
During the Binding section of this chapter you learned how to build a Silverlight "mashup" client application
with docking that aggregated various REST services. The service output was bound to both the pane
content and the pane title area.
During the Customization section of the chapter you customized the pane title area to include a button and
image.
XXIV
Windows
Windows 975
24 Windows
24.1 Objectives
In this chapter you will learn how to build RadWindows with free-form content and control them as a group
with RadWindowManager. You will work with predefined Alert, Confirm and Prompt dialogs. You will learn
how to control window appearance, both with simple background and title color modification as well as
customization using Expression Blend. You will react to windows opening, closing, state change and
moving using RadWindow events. You will learn how to alter the initial state of a window. You will also how
to bind data RadWindow properties.
24.2 Overview
Use RadWindow to frame important information or collect user input. RadWindow is a themed, floating
container that can be displayed free floating or as a modal dialog. The event model lets you keep track of
window movement and state changes, allowing you to intercede with custom logic. Alerts, Confirmation
dialogs and Prompts are all built in. The RadWindowManager provides access and control to all windows as
a collection and PopupManager can be used for special situations that require tweaking the Z-Order.
Project Setup
1) From the Visual Studio menu choose File > New > Project..., select the Silverlight project type, then
select the Silverlight Application template. Provide a unique name for the project and click the OK
button.
2) In the "New Silverlight Application" dialog make sure that the "Host the Silverlight application in a new
Web site" option is checked, give the project a unique name and verify that the "ASP.NET Web
Application Project" New Web Project Type option is selected. Click OK to close the dialog and create
the project.
3) In the Solution Explorer, right-click the References node and select Add References... from the
context menu. Add Assembly references:
a) Telerik.Windows.Controls
b) Telerik.Windows.Controls.Navigation
c) Telerik.Windows.Themes.Summer
XAML Editing
1) Open MainPage.xaml for editing.
2) Add the XAML below to the main "LayoutRoot" Grid element. The XAML should be added between the
<Grid> and </Grid> tags. This will define a RadToolBar with two buttons and their respective Click
handlers.
<telerikNavigation:RadToolBar VerticalAlignment="Top">
<telerikNavigation:RadToolBar.Items>
<Button x:Name="btnNewWindow" Content="New Window"
Click="btnNewWindow_Click" />
<Button x:Name="btnCloseAll" Content="Close All"
Click="btnCloseAll_Click" />
</telerikNavigation:RadToolBar.Items>
</telerikNavigation:RadToolBar>
Code Behind
1) In the code-behind for the page, add references to the "Imports" (VB) or "using" (C#) section of the code
for the Telerik.Windows.Controls namespace.
2) In the constructor, create a new SummerTheme instance and set the IsApplicationTheme property
to "True". This will style both the tool bar and any windows that are created.
public MainPage()
{
new SummerTheme().IsApplicationTheme = true;
InitializeComponent();
}
The code creates a RadWindow instance and calls the Show() method. Here we also set the window
Header, Content, starting position and dimensions.
4) Handle the Click event for the "Close All" button. This event handler calls the RadWindowManager
CloseAllWindows() method of the singleton RadWindowManager.
Gotcha!
RadWindowManager has several deprecated methods that are static. You may get a semi-cryptic
message to "use RadWindowManager instance methods". By this, the message means to use the
singleton "Current" property of the RadWindowManager as shown in the code snippet above.
RadWindow comes with predefined Alert(), Prompt() and Confirm() dialog methods. These "workhorse"
static methods of RadWindow can be very simple or fully tailorable.
24.4.1.1 Alert
For quick-and-dirty alerts, simply call the the Alert() method and pass the content.
RadWindow.Alert(
"The server will shutdown for maintenance in 10 minutes");
To get specific control over all aspects of the dialog including the modal background, icon and content of
each part of the dialog, pass DialogParameters. This example changes the theme of the dialog, adds an
icon to the left corner of the header, adds text to the header and changes the content of the OK button and
handles the Opened and Closed events.
RadWindow.Alert(new DialogParameters()
{
Content = message,
Theme = new SummerTheme(),
IconContent = icon,
OkButtonContent = "Proceed",
Header = "Maintenance Alert",
Closed = new EventHandler<WindowClosedEventArgs>(OnDialogClosed),
Opened = new EventHandler(OnDialogOpened)
});
}
Notes
You can also set the DialogParameters ModalBackground property. ModalBackground is a Brush
that sets the background behind the dialog, not the background of the dialog itself.
RadWindow.Alert(new DialogParameters()
{
Content = "The background behind the dialog is light gray",
ModalBackground = new SolidColorBrush(Colors.LightGray)
});
24.4.1.2 Confirm
The next step up is the static Confirm() method where we can find out if the user clicked the OK or Cancel
buttons. The code is very similar to the Alert() example except for the addition of assigning the
CancelButtonContent. Now the Closed event becomes much more important because the Boolean
DialogResult is assigned. Notice that DialogResult is a nullable value so you will have to cast it to
Boolean.
string message =
"The server is scheduled to shutdown for maintenance in 10 minutes" +
Environment.NewLine +
"Do you need to wait?";
RadWindow.Confirm(new DialogParameters()
{
Content = message,
Theme = new SummerTheme(),
IconContent = icon,
OkButtonContent = "Proceed",
CancelButtonContent = "Please wait",
Header = "Maintenance",
Opened = new EventHandler(OnConfirmOpened),
Closed = new EventHandler<WindowClosedEventArgs>(OnConfirmClosed)
});
}
24.4.1.3 Prompt
From Confirm() we move onto the static Prompt() method where the user enters some text. You can enter
a DefaultPromptResultValue that will appear in the text entry box when the dialog first shows. If the
DialogResult is true you can retrieve the user entry from the Closed event PromptResult argument
Gotcha!
Be aware that PromptResult is only assigned when the DialogResult is true, i.e. when the user has
clicked the "OK" button. This is the observed behavior at the time of this writing. That is, if you
assigned the text "Shut Down" to the OkButtonContent, prompted the user to enter a shut down
reason, and the user clicked the "Shut Down", i.e. Cancel button, the PromptResult would be
empty. You will have to arrange your prompt to work with these semantics.
Dim message As String = "Please enter a reason for shutting down: "
RadWindow.Prompt(new DialogParameters()
{
Content = message,
Theme = new SummerTheme(),
IconContent = icon,
OkButtonContent = "Shut Down",
CancelButtonContent = "Keep Running",
DefaultPromptResultValue = "Monthly Maintenance",
Header = "Maintenance",
Opened = new EventHandler(OnPromptOpened),
Closed = new EventHandler<WindowClosedEventArgs>(OnPromptClosed)
});
}
24.4.2 Brushes
RadWindow surfaces three brushes to allow changing the basic dialog appearance easily:
SolidColorBrush titleBrush =
new SolidColorBrush(Color.FromArgb(255, 200, 200, 200));
SolidColorBrush modalBackgroundBrush =
new SolidColorBrush(Color.FromArgb(255, 100, 100, 100));
24.4.3 Events
You can monitor the state of your windows using events surfaced by RadWindow. We already worked
briefly with the Opened and Closed events. The Activated event fires when a window is brought to the front.
You can also use:
LocationChanged: Fires when the window Top or Left changes.
WindowStateChanged: Fires when WindowState changes between Normal, Minimized and
Maximized.
PreviewClosed: Fires just before the window closes.
The PreviewClosed event allows you to prevent a window from closing. In the example below, the Click event
for the "Search" button calls the window Close() method. The PreviewClosed event fires and Cancel is set
"True" if there is no value in either date picker.
Window State
The CanClose property when false removes the close button from the window but still allows the window to
be closed programmatically. Likewise, the CanMove can be set false to prevent the window from being
dragged. ResizeMode can be set to CanResize (the default), CanMinimize, CanMaximize or NoResize
.
Z-Order
Question: "How do I handle relative Z-Index positioning of windows and other controls that
popup?"
Answer: "We introduced a PopupManager that handles Z-Index on all popups (RadWindow,
RadWindows with TopMost=true, and RadPopup - used in combobox, menuitem, datepicker,
timepicker).The PopupManager exposes four zones: Window, TopWindow, Popup and
DockWindow (for future use). If you want your popup (not RadPopup) to show on top you can
set its Child Z-Index to be bigger then 300,000 (this is the starting Z-Index for Popup zone)."
24.5 Binding
You can bind the properties of RadWindow in a similar manner to other Silverlight controls. The example
below binds the Header of each window to the "Title" property of a "NewsCategory" object. Each
RadWindow Activated event has a handler that assigns the DataContext of the top-most window to a
TextBox and also binds the "Title" property. Changes to the text box show up right away in the top window.
Bringing other windows to the top changes the title shown in the text box. The underlying NewsDataSource
object is an ObservableCollection<NewsCategory> where NewsCategory is an INotifyPropertyChanged
implementation with two properties: a "Title" and a List<string> of "Articles".
Gotcha!
Are changes in the text box not showing up in the window? Be sure that you have implemented
INotifyPropertyChanged in the bound object and that the collection is ObservableCollection<>
and not List<>.
24.6 Customization
Walk Through
In this example we will customize the RadWindow control border and background.
Project Setup
1) Run Expression Blend.
2) From the File menu select New Project. Note: If you have an existing solution open, right-click the
solution and select Add New Project... from the context menu instead.
3) In the New Project dialog, select "Silverlight" from "Project types" and "Silverlight 3" Application from
the right-most list. Enter a unique name for the project and click OK.
9) In the Objects and Timeline pane, locate the "MainBorder" node and click it.
10)In the Resources pane, locate the "RadWindow_Outerbackground" resource and click the drop down
arrow to edit the brush. Click each of the gradient stop indicators, then use the color editor above the
gradient stop indicators to change the color for each.
The window border may look something like the screenshot below. The actual colors chosen will
determine the final visual makeup of the outer window border area.
11)Also in the Resources pane, locate the "RadWindow_InnerBackground" resource and click the drop
down arrow to edit the brush.
12)Click the "Gradient Brush" button at the top of the editor. Click on both gradient stop indicators and
select a yellow and a red color, respectively. Click the "Radial Gradient" button at the bottom of the
editor.
In the Artboard, the window may look something like this, depending on your color choices.
24.7 Wrap Up
In this chapter you learned how to build RadWindows with free-form content and control them as a group
with RadWindowManager. You worked with the predefined dialogs Alert, Confirm and Prompt. You learned
how to control window appearance, both for simple modifications like background and title colors as well as
customization using Expression Blend. You worked with events that react to windows opening, closing,
state changes and moving. You learned how to alter the initial state of a window. You learned how to bind
data to a control.
XXV
HTMLPlaceholder
HTMLPlaceholder 1003
25 HTMLPlaceholder
25.1 Objectives
In this chapter you will learn how RadHtmlPlaceholder is used to host HTML content in Silverlight
applications. First you will add custom HTML content to a place holder to see how it renders, then you will
point to an external web page. You will learn how to host place holders in the page, in other controls and
within RadWindows. You will learn how to respond to events when the content page is loaded. You will learn
how to interact between Javascript and managed code behind. Finally, you will learn how to bind a series of
place holder controls within an ItemsControl to custom objects.
25.2 Overview
RadHtmlPlaceholder is a powerful tool that lets you blend standard HTML, ASP.NET and Silverlight
elements. You can load content by assigning custom HTML or by loading external web pages. Place
holders can be added as content to standard Silverlight and RadControls including tab controls and
windows.
Project Setup
1) From the Visual Studio menu choose File > New > Project..., select the Silverlight project type, then
select the Silverlight Application template. Provide a unique name for the project and click the OK
button.
2) In the "New Silverlight Application" dialog make sure that the "Host the Silverlight application in a new
Web site" option is checked, give the project a unique name and verify that the "ASP.NET Web
Application Project" New Web Project Type option is selected. Click OK to close the dialog and create
the project.
3) In the Solution Explorer, right-click the References node and select Add References... from the
context menu. Add Assembly references:
a) Telerik.Windows.Controls
XAML Editing
1) Open MainPage.xaml for editing.
2) Add the XAML below to the main "LayoutRoot" Grid element. The XAML should be added between the
<Grid> and </Grid> tags.
<StackPanel>
<!--place holder that uses literal html-->
3) Drag a RadHtmlPlaceholder from the Toolbox to a point just under the comment "<!--place holder that
uses literal html-->". Name the place holder "phHtml" so we can access it later in code.
4) Drag a RadHtmlPlaceholder from the Toolbox to a point just under the comment "<!--place holder that
uses external site-->". Name the place holder "phUrl". Set the SourceUrl property to "http://www.
telerik.com".
5) In the Solution Explorer, navigate to the host ASP.NET application and locate the startup page for the
project. Open the startup page for editing. Add a "windowless" parameter to the SilverlightControlHost
and set it to "true" (Look at the last parameter in the example below).
<body>
<form id="form1" runat="server" style="height:100%">
<div id="silverlightControlHost">
<object data="data:application/x-silverlight-2,"
type="application/x-silverlight-2"
width="100%" height="100%">
<param name="source" value="ClientBin/01_GettingStarted.xap"/>
<param name="onError" value="onSilverlightError" />
<param name="background" value="white" />
<param name="minRuntimeVersion" value="3.0.40624.0" />
<param name="autoUpgrade" value="true" />
<param name="windowless" value="true" />
...
Gotcha!
The main point is to be aware of the origin of the hosting page and make sure the windowless
parameter exists there. If you're unsure of the host page origin, check the URL in the browser
when project runs.
Code Behind
1) Add the code below to the UserControl constructor that assigns the HtmlSource property. Optionally,
you can place some other arbitrary HTML here.
public MainPage()
{
InitializeComponent();
phHtml.HtmlSource =
"<div style=" +
"\"width:100%;height:100px;padding:10px;background-color:SkyBlue;\"" +
">Literal HTML here</div>";
}
In a Silverlight Page
As you saw in the "Getting Started" walk through, you can assign markup directly to the HtmlSource
property. This might be useful if you have HTML content stored in a database returned from a service or
saved to isolated storage locally. The example below shows that as expected, divs, spans and styles can
be placed within the HTML.
phHtml.HtmlSource = _
"The HtmlSource property can contain" & _
String.Format(tag, "divs, spans and styles") & _
"and any other arbitrary markup"
The alternative is to assign the SourceUrl property. SourceUrl takes a Uri object that can point to an
external site.
In RadTabControl
RadHtmlPlaceholder can be used as content for other Silverlight and RadControls, such as a RadTabItem.
In the example below, the place holder is included in the Content portion of the RadTabItem element.
<telerikNavigation:RadTabControl>
<telerikNavigation:RadTabItem Header="Wiki">
<telerik:RadHtmlPlaceholder
SourceUrl="http://www.wikipedia.org" />
</telerikNavigation:RadTabItem>
<telerikNavigation:RadTabItem Header="Dictionary">
<telerik:RadHtmlPlaceholder
SourceUrl="http://www.dictionary.com" />
</telerikNavigation:RadTabItem>
</telerikNavigation:RadTabControl>
In RadWindow
When RadHtmlPlaceholder is placed in a RadWindow, and the window is dragged, the RadHtmlPlaceholder
does not redraw quickly enough, showing an unpleasant artifact.
The solution is subscribe to the LocationChanged event of the window and call the RadHtmlPlaceholder
InvalidateArrange() method. The XAML for the sample below simply wraps the previous RadTabControl
example in a RadWindow, names it "winHtml" and hooks up a LocationChanged event handler. Notice that
the UrlLoaded event is used to notify us that all content is available in particular place holder.
<telerikNavigation:RadWindow x:Name="winHtml"
LocationChanged="winHtml_LocationChanged">
<StackPanel>
<telerikNavigation:RadTabControl>
<telerikNavigation:RadTabItem Header="Wiki">
<telerik:RadHtmlPlaceholder Tag="Wiki"
SourceUrl="http://www.wikipedia.org"
UrlLoaded="RadHtmlPlaceholder_UrlLoaded" />
</telerikNavigation:RadTabItem>
<telerikNavigation:RadTabItem Header="Dictionary">
<telerik:RadHtmlPlaceholder Tag="Dictionary"
SourceUrl="http://www.dictionary.com"
UrlLoaded="RadHtmlPlaceholder_UrlLoaded" />
</telerikNavigation:RadTabItem>
</telerikNavigation:RadTabControl>
<Border Background="WhiteSmoke" CornerRadius="5"
Margin="5" Padding="5" BorderThickness="1"
BorderBrush="LightGray">
<TextBlock x:Name="tbStatus" FontSize="12" />
</Border>
</StackPanel>
</telerikNavigation:RadWindow>
In this example, the code-behind has to deal with multiple place holders, so a little extra code is required.
We first need to get a RadHtmlPlaceholder reference before calling InvalidateArrange(). The handler gets a
reference to the RadWindow, then drills down to its content (a StackPanel), then uses the ChildrenOfType()
extension method to get the RadTabControl. Then the event handler finally gets the RadHtmlPlaceholder
from the RadTabControl SelectedContent property. Your coding requirements may or may not be as
involved, but the key point is to get a reference to the place holder and call the InvalidateArrange() method.
Now when you move the window, the contents appear to track correctly.
Question: The RadHtmlPlaceholder is always on top of everything. What problems might this
cause and can this be changed?
Answer: This question has multiple permutations, such as why doesn't the place holder follow
the window, or why doesn't the place holder handle z-order relative to other Silverlight controls
as I expect, or why doesn't a container for a place holder resize as expected. The cause for all of
these behaviors has the same root. The content of the RadHtmlPlaceholder is rendered in an
IFrame element, which floats above everything else on the page. If the RadHtmlPlaceholder
content overlaps with another element, that element will always be covered. The IFrame can also
prevent Silverlight controls that it covers from receiving mouse events.
25.4.2 Events
RadHtmlPlaceholder has a single event, UrlLoaded that fires, not when the page first shows, but when all
the content has loaded to the page. The example below has two RadHtmlPlaceholder controls hooked up to
the same UrlLoaded event. Sender is the RadHtmlPlaceholder that triggered the event. In this example the
title is placed in the Tag property of the RadHtmlPlaceholder.
<StackPanel>
<telerikNavigation:RadTabControl>
<telerikNavigation:RadTabItem Header="Wiki">
<telerik:RadHtmlPlaceholder Tag="Wiki"
SourceUrl="http://www.wikipedia.org"
UrlLoaded="RadHtmlPlaceholder_UrlLoaded" />
</telerikNavigation:RadTabItem>
<telerikNavigation:RadTabItem Header="Dictionary">
<telerik:RadHtmlPlaceholder Tag="Dictionary"
SourceUrl="http://www.dictionary.com"
UrlLoaded="RadHtmlPlaceholder_UrlLoaded" />
</telerikNavigation:RadTabItem>
</telerikNavigation:RadTabControl>
<Border Background="WhiteSmoke" CornerRadius="5" Margin="5" Padding="5"
BorderThickness="1" BorderBrush="LightGray">
<TextBlock x:Name="tbStatus" FontSize="12" />
</Border>
</StackPanel>
Use standard Silverlight properties to position and size the place holder control: Height, Width, MaxWidth,
MaxHeight, MinWidth, MinHeight, VerticalAlignment and HorizontalAlignment. Scrollbars are added
automatically for content that does not fit the control.
You can make calls in both directions between the page that hosts the Silverlight plugin and the Silverlight
code itself. From the Silverlight client you can trigger Javascript functions on a page from managed code.
Javascript functions can also call managed code in your Silverlight application. This is especially powerful
because you get the full might of managed code accessed from Javascript, without a trip to the server.
The example explained below has a Silverlight button "Call Javascript from Managed Code" at the top of the
page. Below that are two place holder controls. The top place holder, bordered in green in the screenshot
below, loads a local page "MyPage.htm" stored with the host ASP.NET application. It contains the banner
"Enter a new Url", a text input element and a button input element "Call Managed Code".
When the "Call Javascript from Managed Code" button is clicked, code from inside the Silverlight application
calls a Javascript function that sets the text input element. When the "Call Managed Code" button is
clicked, a Javascript function is fired that gets a reference to a "Scriptable" method in the Silverlight
application, that in turn sets the URL of the second place holder control (bordered in blue). Using these two
basic techniques you can interact between the page, the managed code and ultimately, between HTML
elements in multiple place holders.
The general steps to call Javascript from managed code in your Silverlight client application are:
1. Get the HtmlPresenter property from the place holder control and index into the first element of the
Children collection. This will be your reference to the "iframe" that contains the HTML displayed in the
place holder.
2. Set the Id attribute of the iframe element so we can get at it easily from a Javascript statement.
3. Use HtmlPage.Window.Eval() from managed code to call Javascript.
The "MyPage.html" file lives at the root of the host ASP.NET application. For the managed-code-to-
Javascript piece, we're interested in the setUrlText() Javascript method and the "tbUrl" input element. Walk
through this markup briefly before reading about how to call the Javscript function from managed code.
<html>
<head>
<title>Test Page</title>
<script type="text/javascript">
function setUrlText(text) {
var tbUrl = document.getElementById("tbUrl");
if (tbUrl) {
tbUrl.value = text;
}
}
//. . .
</script>
</head>
<body>
<h1>
Enter a new Url</h1>
<input id="tbUrl" />
...
</body>
</html>
The example below calls a custom Javascript method added to the page called "setUrlText()".
Notes
The "iframe" variable is an HtmlElement that represents an HTML element in the Document
Object Model (DOM) of the page. The HtmlElement class lets you get at the parent HtmlElement,
CssClass, Id, tag name, and has a series of methods that match counterparts you would normally
call from Javascript directly. SetAttribute() and SetStyleAttribute() methods are particularly
useful for setting all the properties of an element. For example, you could call SetStyleAttribute()
to set scroll bar settings for the place holder control:
This second example reverses the direction of communication. Now we're going to kick off a managed
"Scriptable" method from Javascript. The basic steps are:
1) In managed code, identify one or more methods as "scriptable"
2) Register the managed object for access in Javascript.
3) In the ASP.NET host application, add an "id" attribute to the Silverlight host control object so we can
access it in Javascript.
4) Add a Javascript method that gets references to the Silverlight control and the registered managed
object. Use the Javascript managed object reference to call one of the Scriptable methods.
<ScriptableMember()> _
Public Sub SetPlaceholderUrl(ByVal url As String)
phTarget.SourceUrl = New Uri(url, UriKind.RelativeOrAbsolute)
End Sub
[ScriptableMember()]
public void SetPlaceholderUrl(string url)
{
phTarget.SourceUrl = new Uri(url, UriKind.RelativeOrAbsolute);
}
Gotcha!
Don't forget to make your scriptable methods public or you will get the error "Object does not
have a ScriptableAttribute or any scriptable members".
public MainPage()
{
InitializeComponent();
// register a managed ob ject for access b y javascript
HtmlPage.RegisterScriptableObject("MySilverlightPage", this);
}
<body>
<form id="form1" runat="server" style="height:100%">
<div id="silverlightControlHost">
<object id="silverlightControl" <-- . . .
<html>
<head>
<title>Test Page</title>
<script type="text/javascript">
//. . .
function callManagedCode() {
// get the textb ox text from this page
var tbUrl = document.getElementById("tbUrl");
var text = tbUrl.value;
</head>
<body>
<h1>
Enter a new Url</h1>
<input id="tbUrl" />
<input id="btnUrl" type="button" onclick="javascript:callManagedCode()"
value="Call Managed Code" />
</body>
</html>
25.5 Binding
In this walk through you will bind a RadTabControl to an ObservableCollection of "Article", where each
article will have a "Title" and "SourceUri". The content of each tab will contain a RadHtmlPlaceHolder bound
to "SourceUri".
Project Setup
1) From the Visual Studio menu choose File > New > Project..., select the Silverlight project type, then
select the Silverlight Application template. Provide a unique name for the project and click the OK
button.
2) In the "New Silverlight Application" dialog make sure that the "Host the Silverlight application in a new
Web site" option is checked, give the project a unique name and verify that the "ASP.NET Web
Application Project" New Web Project Type option is selected. Click OK to close the dialog and create
the project.
3) In the Solution Explorer, right-click the References node and select Add References... from the
context menu. Add Assembly references:
a) Telerik.Windows.Controls
b) Telerik.Windows.Controls.Navigation
c) Telerik.Windows.Themes.Vista
XAML Editing
1) Open MainPage.xaml for editing.
2) Add XML namespace references for Telerik.Windows.Controls and Telerik.Windows.Navigation
namespaces. Also add a "Loaded" event handler to the UserControl element.
xmlns:telerik=
"clr-namespace:Telerik.Windows.Controls;assembly=Telerik.Windows.Controls"
xmlns:telerikNavigation=
"clr-namespace:Telerik.Windows.Controls;assembly=Telerik.Windows.Controls.Navigation"
3) Add a UserControl.Resources section that defines the RadTabControl layout so that each tab will
contain a bound RadHtmlPlaceholder. Name the resource "TabHtmlHolderStyle" so we can reference it
later from the RadTabControl definition.
Most of the work is in this style definition, so tak e a minute to review how this is assembled. The
HeaderTemplate contains only a text block bound to the "Title" property of our custom "Article" object.
The ContentTemplate is the area below the tab where the RadHtmlPlaceholder lives. The place holder
SourceUrl property is bound to the "Article" object "SourceUri" property.
<UserControl.Resources>
<Style x:Key="TabHtmlHolderStyle"
TargetType="telerikNavigation:RadTabItem">
<Setter Property="HeaderTemplate">
<Setter.Value>
<DataTemplate>
<TextBlock Text="{Binding Title}" Margin="5" />
</DataTemplate>
</Setter.Value>
</Setter>
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<telerik:RadHtmlPlaceholder x:Name="phArticle"
SourceUrl="{Binding SourceUri}" />
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</UserControl.Resources>
4) Add the XAML below to the main "LayoutRoot" Grid element. The XAML should be added between the
<Grid> and </Grid> tags. The RadTabControl added to the grid should be named "tbArticles" so we can
reference it in code later when we want to assign data. Set the VerticalAlignment property to "Top" and
point the ItemContainerStyle back to our "TabHtmlHolderStyle".
<Grid x:Name="LayoutRoot">
<telerikNavigation:RadTabControl x:Name="tbArticles"
VerticalAlignment="Top"
ItemContainerStyle="{StaticResource TabHtmlHolderStyle" />
</Grid>
Code Behind
1) In the Solution Explorer, add a new class file and populate it with the code below. This will define the
"Article" INotifyPropertyChanged implementation and the "Articles" ObservableCollection.
End Class
});
this.Add(new Article()
{
Title = "Google Translation Services",
SourceUri = new Uri("http://translate.google.com/?hl=en#")
});
this.Add(new Article()
{
Title = "Thesaurus",
SourceUri = new Uri("http://thesaurus.reference.com/")
});
}
}
set
{
if (title != value)
{
title = value;
OnPropertyChanged("Title");
}
}
}
set
{
if (sourceUri != value)
{
sourceUri = value;
OnPropertyChanged("SourceUri");
}
}
}
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
2) In the code-behind for the page, add references to the "Imports" (VB) or "using" (C#) section of the code
for these namespaces:
a) Telerik.Windows.Controls
3) In the constructor, before the InitializeComponent() method call, create a new VistaTheme instance
and set the IsApplicationTheme property to True. Be sure to leave the InitializeComponent() method
call in place.
public MainPage()
{
new VistaTheme().IsApplicationTheme = true;
InitializeComponent();
}
4) In the UserControl Loaded event handler, create an instance of the Articles object and assign it to the
tab control ItemsSource property.
Press F5 to run the application. The web page should look something like the screenshot below.
25.6 Wrap Up
In this chapter you learned how RadHtmlPlaceholder is used to host HTML content in Silverlight
applications. First you added custom HTML content to a place holder to see how it renders, then you used
an external web page. You learned how to host place holders in the page, in other controls and within
RadWindows. You also learned how to respond to events when a page loads into the place holder. You
learned about interaction between Javascript and managed code. Finally, you learned how to bind a series
of place holder controls within an ItemsControl to custom objects.
XXVI
MediaPlayer
1028 RadControls for Silverlight
26 MediaPlayer
26.1 Objectives
In this chapter you will learn how to incorporate media into your Silverlight applications. First you will build a
play list using static XAML and in the process learn how to define RadMediaItem elements. You will learn
about the media types supported by the media player, how to work with video size and full screen, how to
add chapters to a media item and about the available events for the media player. You will construct an
application where the media player consumes data from an RSS service including the titles, descriptions,
images and the video itself. Finally, you will learn how to create a play list with a unique appearance using a
custom template.
26.2 Overview
RadMediaPlayer makes it easy to incorporate rich media content to your web site. Create your own fixed
play lists, create play lists in code or bind the play list for complete flexibility. You can set chapters at
custom intervals for intuitive navigation. Multiple size and stretch options let you control media proportions
while the built-in full-screen mode lets you make maximum use of the screen real-estate.
RadMediaPlayer features include:
Set Chapters
Create Playlists
Set Video Size
Full Screen Mode
Project Setup
1) From the Visual Studio menu choose File > New > Project..., select the Silverlight project type, then
select the Silverlight Application template. Provide a unique name for the project and click the OK
button.
2) In the "New Silverlight Application" dialog make sure that the "Host the Silverlight application in a new
Web site" option is checked, give the project a unique name and verify that the "ASP.NET Web
Application Project" New Web Project Type option is selected. Click OK to close the dialog and create
the project.
3) In the Solution Explorer, right-click the References node and select Add References... from the
context menu. Add Assembly references:
a) Telerik.Windows.Controls
b) Telerik.Windows.Controls.MediaPlayer
c) Telerik.Windows.Themes.Vista
XAML Editing
1) Open MainPage.xaml for editing.
2) Add XML namespace references to the Telerik.Windows.Controls namespace in both the Telerik.
Windows.Controls and Telerik.Windows.Controls.MediaPlayer assemblies.
<UserControl
xmlns:telerik=
"clr-namespace:Telerik.Windows.Controls;assembly=Telerik.Windows.Controls"
xmlns:telerikMedia=
"clr-namespace:Telerik.Windows.Controls;assembly=Telerik.Windows.Controls.MediaPlayer"
. . .>
3) Add a RadMediaPlayer from the Toolbox to the main "LayoutRoot" Grid element. The XAML should be
added between the <Grid> and </Grid> tags. Set the StyleManager.Theme to "Vista" and the
IsPlaylistVisible to "True".
<telerikMedia:RadMediaPlayer
telerik:StyleManager.Theme="Vista"
IsPlaylistVisible="True">
</telerikMedia:RadMediaPlayer>
4) Add a RadMediaItem from the Toolbox to a point inside the RadMediaPlayer element begin and end
tags and set properties:
a) ImageSource = "http://neosmart.net/blog/wp-content/uploads/microsoft-silverlight.png"
b) Title = "Adding Instant Messaging to Any Site"
c) Description = "Learn how your web site visitors can engage with Windows Live Messenger"
d) Source = "http://msstudios.vo.llnwd.net/o21/mix08/08_WMVs/T03.wmv"
Notes
The ImageSource is the path to the image on the left. Title and Description are the text labels
displayed top and bottom right, respectively. Source is the path to the video that will play when one of
the items is clicked.
5) Add a second RadMediaItem from the Toolbox to a point just below the first RadMediaItem.
a) ImageSource = "http://neosmart.net/blog/wp-content/uploads/microsoft-silverlight.png"
b) Title = "The Dynamics Duo talk about CRM and Silverlight"
c) Description = "CRM and Silverlight Topics"
d) Source = "http://mschnlnine.vo.llnwd.net/d1/ch9/7/1/5/1/2/4/DynamicsDuoCRMSilverlight_ch9.wmv"
The RadMediaPlayer and RadMediaItem elements together should now look like the example below.
<telerikMedia:RadMediaPlayer x:Name="mediaPlayer"
telerik:StyleManager.Theme="Vista"
IsPlaylistVisible="True" >
<telerikMedia:RadMediaItem
ImageSource="http://neosmart.net/blog/wp-content/uploads/microsoft-silverlight.png"
Title="Adding Instant Messaging to Any Site"
Description="Learn how your web site visitors can engage with Windows Live Messenger"
Source="http://msstudios.vo.llnwd.net/o21/mix08/08_WMVs/T03.wmv">
</telerikMedia:RadMediaItem>
<telerikMedia:RadMediaItem
ImageSource="http://neosmart.net/blog/wp-content/uploads/microsoft-silverlight.png"
Title="The Dynamics Duo talk about CRM and Silverlight"
Description="CRM and Silverlight Topics"
Source="http://mschnlnine.vo.llnwd.net/d1/ch9/7/1/5/1/2/4/DynamicsDuoCRMSilverlight_ch9.wmv">
</telerikMedia:RadMediaItem>
</telerikMedia:RadMediaPlayer>
Gotcha!
Be sure to run the application from the ASP.NET host application. In other words, the startup
project should be the ASP.NET host application, not the Silverlight application itself. If you run
with the Silverlight application, the application will run, but the images will not display and the
WMV files will not show.
Chapters
Videos can be split up into arbitrary time periods called "Chapters". Each chapter has a Position and Title.
The position is a string property in the format of "00:10:00" in the format of hours : minutes : seconds. Title
is a string that displays
Moving the mouse over the timeline causes a panel with the chapter titles to display. Clicking on a title
moves the current marker on the timeline to the beginning of that chapter. You can also navigate chapters
by clicking the forward and back buttons.
The full screen button will make the media element maximize to the size of the control. If you want the
entire browser full screen, handle the FullScreenChanged event. There you can set the IsFullScreen
property of the browser and the media player control both.
Another way to get at the dimensions of the area where the video plays is the VideoStretch, VideoWidth
and VideoHeight properties. Here are a few screenshots that will give you an idea of how these settings
interact. Be aware that these are properties of the RadMediaItem, not the player. You can use the
RadMediaPlayer.CurrentItem to get access.
None
Fill
Uniform
UniformToFill
Events
CurrentStateChanged: Use this event to find out when the media player CurrentState property
changes. CurrentState is a MediaElementState enumeration and can be Closed, Opening, Buffering,
Playing, Paused, Stopped, Individualizing or AquiringLicense.
ChapterReached: This event fires when the timeline progress to a new chapter. Use the ChapterTitle
property to get the text for the current media item and chapter. You can also use the
MediaElementTotalSeconds property to get the current progress through the timeline.
MediaOpened fires when the video first opens, DownloadProgressChanged then fires as the current
media item progressively downloads and finally, the MediaEnded event fires as the timeline completes.
26.5 Binding
In this walk through we will use an RSS feed to populate screen elements, the play list text, images and the
individual videos. We will not need to provide any custom templating because we will transform our data to a
collection of RadMediaItem before binding to the media player ItemsSource property. This particular site,
HubbleSite.org, at the time of this writing has a Crossdomain.xml policy file in place, so we should be able
to access all media directly.
Project Setup
1) From the Visual Studio menu choose File > New > Project..., select the Silverlight project type, then
select the Silverlight Application template. Provide a unique name for the project and click the OK
button.
2) In the "New Silverlight Application" dialog make sure that the "Host the Silverlight application in a new
Web site" option is checked, give the project a unique name and verify that the "ASP.NET Web
Application Project" New Web Project Type option is selected. Click OK to close the dialog and create
the project.
3) In the Solution Explorer, right-click the References node and select Add References... from the
context menu. Add Assembly references:
a) Telerik.Windows.Controls
b) Telerik.Windows.Controls.MediaPlayer
c) Telerik.Windows.Themes.Vista
XAML Editing
1) Open MainPage.xaml for editing.
2) Add XML namespace references for the following assemblies to the UserControl element.
<UserControl
xmlns:telerikMedia=
"clr-namespace:Telerik.Windows.Controls;assembly=Telerik.Windows.Controls.MediaPlayer"
xmlns:telerik=
"clr-namespace:Telerik.Windows.Controls;assembly=Telerik.Windows.Controls"
. . .>
3) Locate the TwilightBlueTheme control from the "Silverlight XAML Controls" tab of the Toolbox and
drop it between the begin and end tags of the main "LayoutRoot" grid.
This will style the back ground and other elements with a gradient blue that should blend nicely with the
Telerik "Vista" theme of the media player to be added later.
<Grid x:Name="LayoutRoot">
<twilightBlue:TwilightBlueTheme>
</twilightBlue:TwilightBlueTheme>
</Grid>
4) Inside the "TwilightBlueTheme" element, add the XAML below. This XAML contains a stack panel
arranged vertically. We will use the XAML comments in following steps to include additional elements.
<StackPanel>
<!--logo and title-->
<!--media player-->
</StackPanel>
5) Replace the "<!--logo and title-->" comment with the XAML below. The XAML includes a new
StackPanel element arranged horizontally. Inside the stack panel, is a HyperlinkButton that navigates
to the site and a tool tip that displays a short introduction to the site. Inside the HyperlinkButton, an
Image control displays the logo image for the site. Below the HyperlinkButton a text block displays the
title of the site.
6) Drag a RadMediaPlayer control from the Toolbox to a point below the "<!--media player-->" comment.
Set the name to be "mediaPlayer" so that we can access the control later in code. Set the
IsPlaylistVisible property to "True" and Height to "500". Add an event handler for the Loaded event.
<!--media player-->
<telerikMedia:RadMediaPlayer x:Name="mediaPlayer"
Loaded="mediaPlayer_Loaded"
IsPlaylistVisible="True" Height="500">
</telerikMedia:RadMediaPlayer>
1) To understand what's happening during the coding phase of this project, you should take a look at the
RSS feed XML and understand the structure of the data. Using Internet Explorer, navigate to the page
below and save the page as XML:
http://hubblesite.org/explore_astronomy/hubbles_universe/rss.php?feed=windows-320
2) The screenshot below shows a small sample from the top of the file. There's a node at the top called
"channel" that contains a description for the site, a "title" and an "image" that contains a path to the logo
for the site. Below that are "N" number of "item" tags, each containing one article's worth of information in
a "description" tag. Each article has text describing the article, a link to an image and a link to a video in
"*.wmv" format.
The "gotcha" is that the description node contains "CDATA", i.e. free formatted data. In this case the
CDATA is HTML. We need to parse the html to get the description, image and video. We will use regular
expressions to make this job relatively painless, but will not go into detail on how regular expressions
work.
This class will be a simple container for each that will be shown later in the play list of the media player.
2) Add a new class file to the project and name the file "HubbleInfo.cs".
This class will contain header information about the Hubble site and a collection of "HubbleItem".
3) In the "HubbleInfo.cs" class file, add references to the "Imports" (VB) or "using" (C#) section of the code
for these namespaces:
a) System.Collections.Generic
b) System.Linq
c) System.Net
d) System.Text.RegularExpressions
e) System.Xml.Linq (supports XDocument, XElement)
4) Add a delegate "LoadedEventHandler" to the top of the class file. This will be used later to define a
"Loaded" event.
5) Add the code below to the "HubbleInfo" class to define a series of string constants.
The constants include a URL for the Hubble site, the Url for the RSS feed on the Hubble site and two
patterns to be used later in regular expressions.
6) Add the code below to the HubbleInfo class. It should include an IEnumerable collection of HubbleItem, a
title, description and Uri's to the site and site logo.
8) Define a Load() method using the code below. The method creates a WebClient instance, sets up a
DownloadStringCompleted event handler and calls the DownloadStringAsync() method. Pass the
"feedUrl" constant to the Uri constructor. The call will download the RSS XML from the Hubble site.
The "e.Result" here is the RSS feed as an XML string. Use the XDocument Parse() method to create an
XDocument. From there, get the top level elements "channel", "image" and "description". Populate
HubbleInfo properties using element values. Call the GetHubbleItems private method (to be written
later). Finally, trigger the Loaded event by calling the OnLoaded() method.
10)Create a new method GetHubbleItems that takes an XElement as a parameter and returns an
IEnumerable of HubbleItem.
This method is primarily a LINQ expression that extracts "item" elements and populates HubbleItem
objects. The title is tak en from the element value of the same name. A series of LINQ "let" statements
extract "CData" data from the "description" element, then uses Regex to get the remaining detail. Regex
returns the "<p>" tag if it exists and we also strip the tags themselves and assign it to the temporary
"description" variable. Regex is also used to get the image URL. The video url is tak en from the
"<enclosure><url>" elements. Finally, all this data is used to populate new HubbleItem instances.
from i in channel.Elements("item")
let title = i.Element("title").Value
// get the CData section of the xml
let cData = (i.Element("description").FirstNode as XCData).Value
// find the description "<p>" tag in the CData
let tempMatches = regxPTag.Matches(cData)
// only retain if there are "<p>" tags
let descTags =
tempMatches.Count > 0 ? tempMatches[0].ToString() : String.Empty
// remove the <p> tags from the description
let description = Regex.Replace(descTags, "<.*?>", string.Empty)
// find the image url in the CData
let imageUri = new Uri(regxUrls.Matches(cData)[0].ToString())
// get the image url that will appear in the playlist
let videoUri =
new Uri(i.Element("enclosure").Attribute("url").Value)
Code Behind
1) In the code behind for the main page, verify that these namespace references exist and add them if
necessary:
a) System.Linq
b) System.Windows
c) System.Windows.Controls
d) System.Windows.Media.Imaging
e) Telerik.Windows.Controls
2) In the constructor for the page, create a new VistaTheme instance and set its IsApplicationTheme
property to "True". This should be done before the InitializeComponent() method call.
public MainPage()
{
new VistaTheme().IsApplicationTheme = true;
InitializeComponent();
}
3) In the media player Loaded event, create a new HubbleInfo instance, hook up its Loaded event and
call the Load() method.
Indirectly, the Load() method call will k ick off an asynchronous call from WebClient to retrieve the RSS
XML and populate the HubbleInfo object. When that completes, the custom HubbleInfo Loaded event
will fire.
4) In the Loaded event handler of the HubbleInfo object set the DataContext of the title StackPanel to the
HubbleInfo instance. Use a LINQ statement to transform the collection of HubbleItem to a collection of
RadMenuItem and assign the lot to the media player ItemsSource property.
Press F5 to run the application. The web page should look something like the screenshot below.
3) The site logo and title should appear at the head of the page. Passing the mouse over the logo image
should display a tool tip. Clicking the logo should bring up a separate browser with the Hubble site.
26.6 Customization
You can customize the player list by overriding the ControlTemplate of the RadMediaItem. The screenshot
below shows a slightly different arrangement of elements with a triangular "Play" button at the right of each
item.
The XAML for the template can be defined in the User.Resources or other resource area. Here the template
is named "MediaItemTemplate" for reference later in the RadMediaItem itself. The ControlTemplate contains
a series of Grids that arrange bound elements. TextBlock elements are bound to the Title and Description.
The button is named "PlayButton" to correspond to the media player element of the same name. In its own
ControlTemplate, the ContentPresenter surfaces the original button functionality so that when its clicked,
the video plays. The Path element simply creates the triangular area for the button.
<ControlTemplate x:Key="MediaItemTemplate"
TargetType="telerikMedia:RadMediaItem">
<Grid Height="50" Margin="10,5,10,5">
<Border BorderThickness="0,0,0,2" BorderBrush="#FFFFFFFF">
<Grid>
<Grid VerticalAlignment="Center">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<!--Title TextBlock-->
<TextBlock TextWrapping="Wrap" Text="{TemplateBinding Title}"
FontSize="12" />
<!--Description TextBlock-->
<TextBlock Grid.Row="1" TextWrapping="Wrap" Margin="10,7,0,0"
Text="{TemplateBinding Description}" FontSize="9" />
</Grid>
<Button x:Name="PlayButton" Height="30" HorizontalAlignment="Right"
VerticalAlignment="Center" Width="25">
<Button.Template>
<ControlTemplate><Grid><ContentPresenter /></Grid></ControlTemplate>
</Button.Template>
<Path Fill="#FFFFFFFF" Stretch="Fill" Data="M0,0 L50,25 L50,25 L0,50 z" />
</Button>
</Grid>
</Border>
</Grid>
</ControlTemplate>
When declaring each of the RadMediaItem elements in the RadMediaPlayer, the Template property
simply needs to be pointed at the ControlTemplate "MediaItemTemplate" resource you declared earlier.
<telerikMedia:RadMediaItem
Template="{StaticResource MediaItemTemplate}"
. . ./>
You can also add new RadMediaItem instances to the collection. The additional step is to extract the
ControlTemplate from the resource file and assign it to the Template property in code, as shown below.
26.7 Wrap Up
In this chapter you learned how to incorporate media into your Silverlight applications. You first built a play
list using static XAML and in the process learned how to define RadMediaItem elements. You learned about
the media types supported by the media player, how to work with video size and full screen, how to add
chapters to a media item and about the available events for the media player. You constructed an
application where the media player consumed data from an RSS service including the titles, descriptions,
images and the video itself. Finally, you learned how to create a play list with a unique appearance using a
custom template.
XXVII
CoverFlow
CoverFlow 1055
27 CoverFlow
27.1 Objectives
In this chapter you will learn how to configure RadCoverFlow to include a set of items. In the process you
will use properties to control the coverflow position, the position of the "camera" in relation to the items item
rotation and item scale. You will learn how to bind lists of images, videos and Silverlight elements to the
coverflow control. Finally, you will customize the coverflow navigation panel to display a RadSlider instead of
a scrollbar.
27.2 Overview
RadCoverFlow turns media navigation into a dazzling visual experience. RadCoverFlow uses real 3D
transitions to navigate through the items. Users can flip through the list of images intuitively by selecting
images with the mouse, rolling the mouse wheel or using arrow and page keys. RadCoverFlow can display
a series of images, videos or any Silverlight element. Feel free to configure camera position, item rotations
or position.
Project Setup
1) From the Visual Studio menu choose File > New > Project..., select the Silverlight project type, then
select the Silverlight Application template. Provide a unique name for the project and click the OK
button.
2) In the "New Silverlight Application" dialog make sure that the "Host the Silverlight application in a new
Web site" option is checked, give the project a unique name and verify that the "ASP.NET Web
Application Project" New Web Project Type option is selected. Click OK to close the dialog and create
the project.
3) In the Solution Explorer, right-click the References node and select Add References... from the
context menu. Add Assembly references:
a) Telerik.Windows.Controls
b) Telerik.Windows.Controls.Navigation
4) From the Solution Explorer, right-click the project and select Add > New Folder from the context
menu. Name the folder "Images".
5) Add five images named "wave1.jpg", "wave2.jpg", "wave3.jpg", "wave4.jpg" and "wave5.jpg" to the
"Images" folder. These images can be found in the "\courseware\images" directory.
XAML Editing
1) Open MainPage.xaml for editing.
2) Add a UserControl.Resources element with a Style defined against the Image type. The style will be
applied to all images within the cover flow.
<UserControl.Resources>
<Style x:Key="ImageStyle" TargetType="Image">
<Setter Property="Width" Value="150" />
<Setter Property="Height" Value="100" />
<Setter Property="Stretch" Value="Uniform" />
</Style>
</UserControl.Resources>
3) From the Toolbox, drag a RadCoverFlow control to a point within the main "LayoutRoot" Grid element.
Set the CameraViewpoint property to "Top"
4) Inside the RadCoverFlow element tag, add five Image elements that use "ImageStyle" and with the
following Source paths:
a) "images/wave1.jpg"
b) "images/wave2.jpg"
c) "images/wave3.jpg"
d) "images/wave4.jpg"
e) "images/wave5.jpg"
<telerikNavigation:RadCoverFlow CameraViewpoint="Top">
<Image Source="images/wave1.jpg" Style="{StaticResource ImageStyle}" />
<Image Source="images/wave2.jpg" Style="{StaticResource ImageStyle}" />
<Image Source="images/wave3.jpg" Style="{StaticResource ImageStyle}" />
<Image Source="images/wave4.jpg" Style="{StaticResource ImageStyle}" />
<Image Source="images/wave5.jpg" Style="{StaticResource ImageStyle}" />
</telerikNavigation:RadCoverFlow>
RadCoverflowItem
Other than that you can work with RadCoverflowItem in much the same way as
ListBoxItem including assigning content and binding to items. RadCoverFlowItem
content can contain arrangements of elements of any arbitrary complexity. The
example here has a single RadCoverFlowItem that contains a StackPanel in its
Content element. The StackPanel holds images, text blocks and hyperlinks.
Note that the styles for this example are not included in the listing. The idea here is to show the flexibility of
RadCoverFlowItem and that you're not limited as to amount or type of content.
<telerikNavigation:RadCoverFlow>
<telerikNavigation:RadCoverFlowItem Width="200" Height="120"
Background="{StaticResource BackgroundBrush}">
<telerikNavigation:RadCoverFlowItem.Content>
<StackPanel Margin="5" HorizontalAlignment="Center" VerticalAlignment="Center">
<StackPanel Orientation="Horizontal">
<Image Source="images/CoverFlow.png" Style="{StaticResource ImageStyle}"
telerikNavigation:RadCoverFlow.EnableLoadNotification="True"/>
<TextBlock Text="RadCoverFlow" Style="{StaticResource TitleStyle}" />
</StackPanel>
<HyperlinkButton Content="Help"
NavigateUri="http://www.telerik.com/help/silverlight/introduction.html"
Style="{StaticResource HyperlinkStyle}" />
<HyperlinkButton Content="Forums"
NavigateUri="http://www.telerik.com/community/forums"
Style="{StaticResource HyperlinkStyle}" />
<HyperlinkButton Content="Product Page"
NavigateUri="http://www.telerik.com/products/silverlight"
Style="{StaticResource HyperlinkStyle}" />
</StackPanel>
</telerikNavigation:RadCoverFlowItem.Content>
</telerikNavigation:RadCoverFlowItem>
</telerikNavigation:RadCoverFlow>
What if I want to add items for "RadColorPicker" or "RadMediaPlayer" content? To scale this up nicely you
can bind data to the cover flow and use binding expressions in an ItemTemplate.
The example uses a collection of "ControlInfo" objects that contain the name, help path, logo image path,
etc. and binds the collection to the RadCoverFlow ItemsSource. These are bound to the title TextBlock,
logo image and HyperlinkButton controls in the template. For a walk through on binding to the
RadCoverFlow, see the Binding section of this chapter.
<telerikNavigation:RadCoverFlow
ItemsSource="{StaticResource ControlInfoList}">
<telerikNavigation:RadCoverFlow.ItemTemplate>
<DataTemplate>
<Border BorderBrush="{StaticResource BorderBrush}" BorderThickness="2">
<StackPanel Style="{StaticResource PanelStyle}">
<StackPanel Orientation="Horizontal">
<Image Source="{Binding Logo}" Style="{StaticResource ImageStyle}" />
<TextBlock Text="{Binding Name}" Style="{StaticResource TitleStyle}" />
</StackPanel>
<HyperlinkButton Content="Help" NavigateUri="{Binding Help}"
Style="{StaticResource HyperlinkStyle}" />
<HyperlinkButton Content="Forums" NavigateUri="{Binding Forums}"
Style="{StaticResource HyperlinkStyle}" />
<HyperlinkButton Content="Product Page" NavigateUri="{Binding Product}"
Style="{StaticResource HyperlinkStyle}" />
</StackPanel>
</Border>
</DataTemplate>
</telerikNavigation:RadCoverFlow.ItemTemplate>
</telerikNavigation:RadCoverFlow>
Video
In the Getting Started section you saw that images can be added as items in the RadCoverFlow markup.
You can also load video content using the standard MediaElement as items. The MediaElement can be
played in response to selecting the item. When the video plays, even the reflection stays in sync with the
content.
The example below includes several MediaElement items with AutoPlay properties turned off. Because
CoverFlow is a ListBox descendant, you can use the SelectionChanged event to know when the user
navigates through the items.
<UserControl.Resources>
<Style x:Key="MediaElementStyle" TargetType="MediaElement" >
<Setter Property="Width" Value="150" />
<Setter Property="Height" Value="100" />
<Setter Property="AutoPlay" Value="False" />
</Style>
</UserControl.Resources>
<StackPanel x:Name="LayoutRoot">
<telerikNavigation:RadCoverFlow x:Name="coverFlow"
CameraViewpoint="Top" SelectionChanged="coverFlow_SelectionChanged">
<MediaElement Style="{StaticResource MediaElementStyle}"
Source="http://msstudios.vo.llnwd.net/o21/mix08/08_WMVs/T03.wmv" />
<MediaElement Style="{StaticResource MediaElementStyle}"
Source="http://mschnlnine.vo.llnwd.net/d1/ch9/7/1/5/1/2/4/DynamicsDuoCRMSilverlight_ch9.wmv" />
<MediaElement Style="{StaticResource MediaElementStyle}"
Source="http://mschnlnine.vo.llnwd.net/d1/ch9/6/7/3/8/1/4/BTCRebeccaNorlander_ch9.wmv" />
</telerikNavigation:RadCoverFlow>
</StackPanel>
The SelectionChanged event handler stops any currently playing video, starts the currently selected item
and stores the current element so we can stop the video playing the next time the event fires.
The RotationY property rotates each non-selected item around the Y-axis.
The series of screenshots below show the effect of increasing RotationY values when there are several
items present.
RotationY = "0"
RotationY = "45"
RotationY = "85"
Use OffsetX and OffsetY properties to move cover flow items horizontally and vertically. OffsetX moves
cover flow items horizontally; with larger values shifting the items to the right. OffsetY moves items vertically
with larger values pushing the items down.
ItemScale scales non-selected items. The screenshot shows the result when ItemScale is "0.5", i.e. 50%
of the original size.
27.4.3 Distance
Control the spacing between items using the DistanceFromSelectedItem (distance between the selected
item and non-selected items) and the DistanceBetweenItems properties.
The screenshot above was produced using the settings in the XAML below:
27.4.4 Camera
The "camera" is the viewpoint of the observer when looking at cover flow items in 3D space. The
CameraDistance is the simulated "Z" position of the camera where smaller values bring the camera closer
to the items and larger values move the camera farther away. As the camera draws closer to the cover flow,
the perspective effect on the items becomes more exaggerated. By default, the CameraDistance is
"1000" (at the time of this writing). The screenshot below actually has the same settings as the previous
example for "Distance" but where the CameraDistance property value is "100".
CameraRotation determines the view angle toward the control where a "0" angle represents the camera
pointed straight on and larger values roll the camera angle. The screenshots below should give you an idea
of how this works.
CameraRotation = "0"
CameraRotation = "45"
CameraRotation = "90"
27.4.5 Animation
When an item is selected in the coverflow, an animation plays that rearranges the items and the selected
item is moved to the front. The speed of the animation is controlled by the ItemChangeDelay, a TimeSpan
property that is currently "600" milliseconds by default. The EasingFunction provides a profile for the
animation to follow, giving the animation a more realistic feel. At the time of this writing, the EasingFunction
defaults to a "Circle".
27.4.6 Reflection
IsReflectionEnabled toggles the appearance of reflection below the cover flow items.
27.5 Binding
This next walk through demonstrates retrieving a series of images from the "Flickr" REST API and binding
the images to the coverflow.
Project Setup
1) From the Visual Studio menu choose File > New > Project..., select the Silverlight project type, then
select the Silverlight Application template. Provide a unique name for the project and click the OK
button.
2) In the "New Silverlight Application" dialog make sure that the "Host the Silverlight application in a new
Web site" option is checked, give the project a unique name and verify that the "ASP.NET Web
Application Project" New Web Project Type option is selected. Click OK to close the dialog and create
the project.
3) In the Solution Explorer, right-click the References node and select Add References... from the
context menu. Add Assembly references:
a) Telerik.Controls
b) Telerik.Controls.Navigation
4) Using the Solution Explorer, right-click the project and select Add > New Folder from the context
menu. Name the folder "Images".
5) Drag an image file "search.png" to the "Images" folder. You can find this file in the
"\courseware\images" directory.
XAML Editing
1) Open MainPage.xaml for editing.
2) Add a UserControl.Resources element just above the main "LayoutRoot" Grid element. These
resources style text, borders and panels, but are not central to learning about binding RadCoverFlow.
Feel free to copy and paste this XAML and review it at your leisure.
<UserControl.Resources>
<!--colors-->
<Color x:Key="FadedWhite">#AAFFFFFF</Color>
<Color x:Key="FadedBlue">#AA0000FF</Color>
<Color x:Key="FadedLightBlue">#AA000099</Color>
<!--brushes-->
<LinearGradientBrush x:Key="SkyBrush" StartPoint="0, 0"
EndPoint="0, 1">
<GradientStop Color="SkyBlue" Offset="0" />
<GradientStop Color="{StaticResource FadedLightBlue}" Offset="1" />
</LinearGradientBrush>
<LinearGradientBrush x:Key="SkyBrushStreak">
<GradientStop Color="SkyBlue" Offset="0" />
<GradientStop Color="{StaticResource FadedWhite}" Offset="0.2" />
<GradientStop Color="{StaticResource FadedBlue}" Offset=".5" />
<GradientStop Color="{StaticResource FadedLightBlue}" Offset="1" />
</LinearGradientBrush>
<Style x:Key="CaptionStyle" TargetType="TextBlock">
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="FontFamily" Value="Comic Sans MS" />
<Setter Property="FontSize" Value="12" />
</Style>
<!--styles-->
<Style x:Key="HyperlinkStyle" TargetType="HyperlinkButton">
<Setter Property="TargetName" Value="_blank" />
<Setter Property="Foreground" Value="Blue" />
</Style>
<Style x:Key="ImageStyle" TargetType="Image">
<Setter Property="Stretch" Value="Uniform" />
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="Width" Value="150" />
<Setter Property="Height" Value="150" />
<Setter Property="Margin" Value="5" />
</Style>
<Style x:Key="PictureFrameStyle" TargetType="Border">
<Setter Property="BorderBrush" Value="{StaticResource SkyBrush}" />
<Setter Property="BorderThickness" Value="1" />
<Setter Property="Background" Value="{StaticResource SkyBrushStreak}"/>
<Setter Property="Padding" Value="10" />
</Style>
</UserControl.Resources>
This will setup our basic layout. The main Grid has two rows configured so that the first row will size
itself to the elements it contains and the second row will expand to tak e any available space.
The top "tool bar" row will have its own grid with two columns. The first column on the left will size itself
to the elements it contains. This first column will hold a Hyperlink Button with an Image of the site logo.
The button will navigate to the main Flick r site and have a "powered by Flick r" tool tip. The rightmost
column will tak e up the remaining width that contains a TextBox for search criteria and a search button.
Underneath the "tool bar, the RadCoverFlow will tak e must of the client area.
<Grid x:Name="LayoutRoot">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<!--flickr logo-->
<!--search panel-->
</Grid>
</Border>
<!--cover flow-->
</Grid>
<!--flickr logo-->
<HyperlinkButton Grid.Column="0" Margin="5 0 0 0"
NavigateUri="http://www.flickr.com"
ToolTipService.ToolTip="Powered by Flickr"
TargetName="_blank">
<Image Stretch="Uniform" Width="64"
Source="http://l.yimg.com/g/images/logo_home.png.v2" />
</HyperlinkButton>
Notes
The XAML defines a HyperlinkButton that in turn holds a logo image. The HyperlinkButton
NavigateUri points to the main Flickr site and will open in a new window by virtue of the
TargetName = "_blank" property setting. The HyperlinkButton also has the tooltip "Powered by
Flickr" defined. The Image Source is hard coded.
<!--search panel-->
<StackPanel Orientation="Horizontal"
HorizontalAlignment="Right" Grid.Column="1">
<TextBlock Text="Search On:"
Style="{StaticResource CaptionStyle}"
Foreground="Black" />
<TextBox x:Name="tbSearch" MinWidth="200"
Margin="5, 0, 5, 0" />
<Button x:Name="btnGo" Click="btnGo_Click">
<Button.Content>
<StackPanel Orientation="Horizontal">
<TextBlock Text="Search" />
<Image Source="images/search.png"
Width="16" Height="16"></Image>
</StackPanel>
</Button.Content>
</Button>
</StackPanel>
Notes
The XAML defines a horizontal StackPanel with all the elements used for searching. Points to
notice: The "Search On" TextBlock uses one of the Styles we defined earlier. The magnifying glass
image uses "search.png" that we added to the project early on. A handler is defined for the
button's Click event where the main search logic takes place.
<!--cover flow-->
<Border Background="{StaticResource SkyBrushStreak}"
Grid.Row="1">
<telerikNavigation:RadCoverFlow x:Name="coverFlow" OffsetY="60" RotationY="45"
CameraRotation="30" DistanceBetweenItems="30">
<telerikNavigation:RadCoverFlow.ItemTemplate>
<DataTemplate>
<Border Style="{StaticResource PictureFrameStyle}">
<StackPanel>
<Image Source="{Binding ImageUrl}" Style="{StaticResource ImageStyle}" />
<TextBlock Text="{Binding ImageTitle}" Style="{StaticResource CaptionStyle}"
Foreground="LightSkyBlue" />
</StackPanel>
</Border>
</DataTemplate>
</telerikNavigation:RadCoverFlow.ItemTemplate>
</telerikNavigation:RadCoverFlow>
</Border>
Notes
Code Behind
1) In the Solution Explorer, right-click the project and select Add > Class... from the context menu. Name
the class file "FlickrItem.cs". Replace the code for the Flickr class with the code below.
This class will encapsulate a single image returned from the Flick r REST service. The "ImageUrl" will
be added to a collection and assigned to the coverflow ItemsSource property.
2) Add a second class file and name it "Flickr.cs". Add namespace references to the "Imports" (VB) or
"using" (C#) section of code for these assemblies:
a) System.Collections.Generic
b) System.Linq
c) System.Net
d) System.Xml.Linq
3) Replace the "Flickr.cs" class definition with the code below.
Notes
Most of the work is being done in the "Flickr" class. The point of entry for the Flickr class is the
Load() method where a WebClient downloads an XML string from the Flickr REST service. The
URL for the DownloadStringAsync() method call is formatted to include whatever search criteria
the user has entered. When the call returns in the DownloadStringCompleted event handler a
custom Loaded event is fired. The XML is converted to a collection of FlickrItem instances. We
have custom arguments, FlickrLoadedEventArgs, that have a single property that contains the
collection. The conversion from XML to collection occurs in the private GetFlickrItems() method
where the XDocument Parse() method converts the raw XML into objects that can be sliced-
and-diced in a LINQ statement. Notice that the Take() extension method returns just the top set
of items.
4) In the code-behind for the main page, add a namespace reference to Telerik.Windows.Controls.
5) In the code-behind for the main page add the code below.
The Click event for the Search button, "btnGo_Click ", creates a new Flick r instance, hook s up the
custom Loaded event to a handler and calls Load(), passing the search text. After the Flick r class gets
done with retrieving XML from the REST service and converting to a collection of Flick rItem instances,
the Flick r class fires its Loaded event where the collection is assigned to the cover flow ItemsSource
property.
Press F5 to run the application. The web page should look something like the screenshot below.
27.6 Customization
27.6.1 Navigation
RadCoverFlow does not have built-in navigation controls, but using Silverlight 3 element binding you can
hook up your own using a slider, up-down, scroll bar or other element that navigates through a set of values.
The screenshot below shows a RadSlider bound two-way with RadCoverFlow so that clicking the slider
navigates the cover flow and clicking items in the cover flow reflects in the slider.
Navigation is implemented in the XAML below. Pay particular attention to the RadSlider Value and
Minimum and Maximum properties. The key is to bind the navigation Value property to the RadCoverFlow
SelectedIndex property. To completely "dial-in" the behavior you must set the Minimum and Maximum
properties to match the count of items in the cover flow. The Minimum should be "0" and Maximum should
be one less than the count of items in the cover flow. To assign the correct Maximum, you will need a
simple value converter that takes the Count of items in the cover flow and subtracts one (code for the
converter follows). To use the converter, be sure to add an XML namespace that references the assembly
that contains the converter, add a resource that points to the converter and finally, use the resource
reference in the binding statement for the Maximum attribute.
<UserControl x:Class="_02_ControlDetails.MainPage"
xmlns:local="clr-namespace:<Your project assembly>" . . .>
<UserControl.Resources>
<local:IntToIntValueConverter x:Key="IntToIntValueConverter" /> . . .
</UserControl.Resources>
<StackPanel x:Name="LayoutRoot">
<telerikNavigation:RadCoverFlow x:Name="coverFlow" >
<telerikNavigation:RadCoverFlowItem . . ./>
<telerikNavigation:RadCoverFlowItem . . ./>
<telerikNavigation:RadCoverFlowItem . . ./>
<telerikNavigation:RadCoverFlowItem . . ./>
<telerikNavigation:RadCoverFlowItem . . ./>
</telerikNavigation:RadCoverFlow>
<telerik:RadSlider
Value="{Binding SelectedIndex, ElementName=coverFlow, Mode=TwoWay}"
Minimum="0"
Maximum="{Binding Items.Count, ElementName=coverFlow,
Converter={StaticResource IntToIntValueConverter}, ConverterParameter=-1}" />
</StackPanel>
</UserControl>
The IValueConverter implementation code adds the parameter passed in ("-1") and returns the result.
27.7 Wrap Up
In this chapter you learned how to define RadCoverFlow to include a set of items. In the process you used
properties to control the coverflow position, the position of the "camera" in relation to the items and item
properties. You learned how to bind lists of images, videos and Silverlight elements to the coverflow control.
Finally, you customized the coverflow navigation panel to use a RadSlider for navigation.
XXVIII
Upload
Upload 1083
28 Upload
28.1 Objectives
In this chapter you will learn the minimal configuration needed to upload files from a Silverlight client to a
server. You will learn the basics for creating the server upload handler along with some of the "Gotchas" that
could cause the upload to fail.
In the section on "Control Details" you will become familiar with controlling file access on the client,
including properties that limit file types and limiting the number of files and bytes allowed. You will also
learn about the properties that show or hide buttons in the UI. You will create your own custom upload
handler and learn how to pass parameters to and from the client. You will learn about the events that cover
the entire upload life-cycle and the methods that can be used to trigger the file operations.
During the "Customization" section you will walk through using Expression Blend to modify the RadUpload
dialog layout.
28.2 Overview
RadUpload saves end-user time and effort by allowing multiple files and automatic uploads. This dedicated
file-upload control is a fast performer that allocates a minimum of server memory, while enabling optimized
and fully configurable single and multi-file uploads. The server can automatically save to a folder in your
project, or customize the server handler for fine-grained control, allowing you to add compression, additional
security and saving to database or other persistence medium.
Project Setup
1) From the Visual Studio menu choose File > New > Project..., select the Silverlight project type, then
select the Silverlight Application template. Provide a unique name for the project and click the OK
button.
2) In the "New Silverlight Application" dialog make sure that the "Host the Silverlight application in a new
Web site" option is checked, give the project a unique name and verify that the "ASP.NET Web
Application Project" New Web Project Type option is selected. Click OK to close the dialog and create
the project.
3) In the Solution Explorer, right-click the References node and select Add References... from the
context menu. Add Assembly references:
a) Telerik.Windows.Controls.Input
5) Navigate to the code-behind for the handler and replace the class declaration with the example that
follows.
Be sure not to eliminate the namespace surrounding the class. The fully qualified class name must
match the mark up for the handler. Accidentally deleting the namespace is one way to generate a
"Handler not found" error.
Notes
You can code the upload handler to perform more specific tasks later, but for now, descending
from RadUploadHandler is sufficient to get the base uploading functionality.
6) In the Solution Explorer, right-click the project node and select Properties. Select the Web tab. In the
Servers section, make sure the "Use Visual Studio Development Server" radio button is selected and
that the "Specific port" button is checked. Enter a specific port of "1234".
Notes
You could also select the "Use Local IIS Web Server" or host the handler in some other web
server. The important point is to know the specific location of the handler so that we can refer to
it in XAML markup later when we define the RadUpload control.
XAML Editing
1) Open the Silverlight project MainPage.xaml for editing.
2) Add the XAML below to the main "LayoutRoot" Grid element. The XAML should be added between the
<Grid> and </Grid> tags. The UploadServiceUrl must point to the exact path of the handler you
defined in previous steps. TargetFolder must point to the exact name of the folder in the project you
created earlier.
<telerikInput:RadUpload
UploadServiceUrl="http://localhost:1234/MyHandler.ashx"
TargetFolder="MyFolder"
OverwriteExistingFiles="True"
IsAutomaticUpload="false">
</telerikInput:RadUpload>
Gotcha!
public MainPage()
{
new SummerTheme().IsApplicationTheme = true;
InitializeComponent();
}
You can limit the files being uploaded based on several criteria and also receive notification from events:
File Size: You can limit the size of a single file to a specific number of bytes by setting the
MaxFileSize property. If the size is exceeded, the FileTooLarge event fires. The FileTooLarge event
passes a parameter containing the name, size and all the file system information about the file in
question.
Total File Size: To limit the maximum total upload size in bytes, use the MaxUploadSize property. If
this size is exceeded, the UploadSizeExceeded event fires.
Number of Files: Limit the number of files using MaxFileCount properties. The FileCountExceeded
event fires when the user attempts to upload more than MaxFileCount files.
You can also limit what files can be sent to the server by defining extension filters. Filter is a string
property that determines the choices that appear in the "Open File Dialog" box. For each filtering option, the
filter string contains a description of the filter, followed by the vertical bar (|) and the filter pattern. The strings
for different filtering options are separated by the vertical bar. You can add several filter patterns to a filter
by separating the file types with semicolons. Use the FilterIndex property to set which filtering option is
shown first to the user. By default all file extensions are allowed.
Note: See http://msdn.microsoft.com/en-us/library/system.windows.controls.openfiledialog.filter(VS.95).
aspx for specifics on filter syntax.
Hide buttons in the UI by setting the IsAppendFilesEnabled, IsDeleteEnabled, and IsPauseEnabled
properties that control the corresponding named buttons. IsMultiselect, when true, allows more than one
file to be selected in the "Open File Dialog" box. Turn on the IsAutomaticUpload property if you want the
upload to begin immediately after the user selects a file.
Even though the upload handler is an *.ashx and the RadUpload control is working from a Silverlight client,
you can communicate both ways. You can also add custom logic to the handler itself, allowing you an
opportunity to save files to a data base or other storage and to perform any other operations with the file
stream that suit your purpose.
The example below adds a custom parameter called "CompanyID" that will be sent with every upload.
Custom parameters are added to the RadUpload AdditionalPostFields dictionary.
Use the FileUploadStarting event when you want to send parameters along with individual files. The
arguments to this event include a FileParameters dictionary. The example sends a parameter with key
"InvoiceNumber". For the sake of brevity, an invoice number is extracted from the file name where the format
is similar to "Invoice_12345.xml".
To perform your own custom processing in the upload handler, override the ProcessStream() method. The
brief example below shows parameters from the Silverlight client being passed in, some mock processing,
then parameters are passed back. Parameters can be sent back to the client using one of two methods:
The AddReturnParam() method. The key to each Dictionary element is a predefined key from
RadUploadContants, e.g. RadUploadContants.ParamNameMessage. The AddReturnParam() method
can be called during the ProcessStream() method.
Overriding the GetAssociatedData() method. This method runs before the ProcessStream() method
and can contain sets of keys and values.
Here are some of the key points to the example below:
Parameters coming from the client "AdditionalPostFields" and "FileParameters" are retrieved by
indexing into Request.Form.
A check against IsFinalFileRequest() lets us know that the entire file has been downloaded. Note:
Larger files are broken into "chunks", so the ProcessStream() method may be called multiple times for
a single file.
After processing the file with custom logic, parameters are returned to the client using
AddReturnParam(). The predefined ParamNameMessage key has a value "Processed invoice
#12345" and the ParamNameSuccess value is "True". If our custom "IsAuthorized" member is false,
the ParamNameSuccess value is "False" and the ParamNameMessage value is "Not authorized". The
important issue here isn't the specific program logic, but how the RadUploadConstants are used along
with the AddReturnParam() method.
In the GetAssociatedData() method we get the "CompanyID" sent from the client with every request
and do a primitive check against a hard coded company number and store the result in a private
"IsAuthorized" member. "IsAuthorized" is included in the dictionary and passed back to the client.
int invoiceNumber =
int.Parse(Request.Form["InvoiceNumber"].ToString());
If everything runs without exception, then the RadUpload control shows that all the selected files have been
uploaded.
If we send the wrong company ID from the client, ParamNameSuccess gets a value of "False" and
ParamNameMessage is set to "Not authorized". As a result, RadUpload displays a warning icon and a tool
tip with the ParamNameMessage value.
Question: Is it possible to just have the byte array and not save the file to disk? Is there a switch
to turn off the save to disk?
Answer: Yes - it is possible. If you skip the call to base.ProcessStream(), then you will have full
control over the upload process. Please note that this will not stop the upload by chunks. If you
want to stop the upload by chunks you can set a bigger BufferSize, and to set a MaxFileSize
smaller than the BufferSize.
byte[] buffer = Convert.FromBase64String(this.Request.Form[RadUploadConstants.ParamNameData]);
The event model of RadUpload lets you track every phase of the selecting and uploading process. "Upload"
events track the state of the upload as a whole and include UploadStarted, UploadPaused,
UploadResumed and UploadCanceled and UploadFinished. UploadStarted passes an argument with
a SelectedFiles property that lists all the files being uploaded. You can also handle the FilesSelected
event when the user closes the "Open File" dialog and this will also pass back the SelectedFiles property.
When a file doesn't upload for some reason, FileUploadFailed will fire and pass back arguments that
include the error message and the selected file.
The ProgressChanged event doesn't pass any interesting arguments, but instead you can use the
RadUpload CurrentSession property to get the CurrentProgress for the entire session,
CurrentFileProgress for the progress of the file uploading and CurrentFile for all the information about the
file being uploaded. Note: CurrentSession also has collections of FailedFiles, SelectedFiles,
TooLargeFiles, UploadedFiles and ValidFiles.
RadUpload methods complement these events by letting you control the uploading process completely from
code-behind: ShowFileDialog(), StartUpload(), PauseUpload() and CancelUpload(). You can in fact
use the RadUpload as a "silent" control without the UI portion by following these steps:
1) Add reference to the Telerik.Windows.Controls.Input assembly;
2) Add a member of type RadUpload to your class. Do not initialize or create the RadUpload in XAML.
3) In the XAML code, add a place holder control to host the hidden RadUpload.
4) Initialize the upload control. The Page.Load event would be an appropriate spot for this logic.
a) Set the addresses for the TargetFolder and the UploadServiceUrl;
b) Set the values to determine RadUpload behavior, i.e. buffer, file and upload sizes, multi-selection,
etc.;
c) To allow interaction, implement handlers for FilesSelected, UploadStarted, UploadFinished,
UploadCanceled, UploadPaused and UploadResumed.
d) To hide the RadUpload, set the Opacity property to Zero.
5) Add the RadUpload control as a child of its place holder.
6) Add Browse and Upload buttons to the page. These buttons will be used to call ShowFileDialog() and
StartUpload() methods.
28.5 Customization
In this example we will customize RadUpload to be more compact.
Project Setup
1) Run Expression Blend.
2) Open the "Getting Started" project or a copy.
3) In the Objects and Timeline pane, open the tree view and locate the "[ItemsPresenter]" and select it.
You should find it as a child of the ScrollViewer under the RootElement.
4) In the Properties pane, Layout section, set the Width of the ItemsPresenter to "220".
5) Select the ScrollViewer. Use the Properties pane to set the Width property at "230".
6) Select the "[Border]" node located just above the ScrollViewer. Use the Properties pane to set the
Width property at "230".
7) Select the "[Border]" located just above the "RootElement". Use the Properties pane to set the Width
property at "260".
8) Below the ScrollViewer, find the "TotalProgressArea" and select it. Use the Properties pane to set the
Width property at "230".
The RadUpload in the Artboard should look something like the screenshot below.
28.6 Wrap Up
In this chapter you used the minimal configuration required to upload files from a Silverlight client to a
server. You learned the basics for creating the server upload handler along with some of the "Gotchas" that
could cause an upload to fail.
In the section on "Control Details" you became familiar with how to control access to files during the upload
including properties that limit file types and limiting the number of files and bytes allowed. You also learned
about the properties that control the operations allowed by showing or hiding buttons in the UI. You created
your own upload handler and learned how to pass parameters to and from the client. You learned about the
events that cover the entire upload life-cycle and the methods that can be used to trigger the file
operations.
During the "Customization" section you walked through using Expression Blend to modify RadUpload into a
more compact dialog.
Index Appointment
AppointmentAdded
736, 742, 778
752
AppointmentAdding 752
AppointmentBase 742, 782
-"- Appointments 736
AppointmentSaving 752
"Handler not found or execution of the handler failed!"
AppointmentsSource 778
1085
AreWeekNamesVisible 486
"Only a single enumeration is supported by this
IEnumerable" 116 AreWeekNumbersVisible 486
"Pattern constraint failed" 116 ArrowCue 455, 462
"Sample" data 154 Artboard 37, 132, 140, 143, 146, 154, 163, 166, 188
"Unable to start debugging" 67 Assets Find entry text box 37
Assets pane 37, 43, 132, 143, 146, 188
Assigning the Context Menu in Code 297
-A- AsyncState 116
ATOM 86, 111, 941
Absolute 297 AutoBringIntoView 455
AcceleratorKey 206 AutoGenerateColumns 637, 680
accessible 203 AutoHideHeight 935
AccessKey 206 AutoHideWidth 935
Activated 991, 994 Automation Elements Tree 203
ActiveViewDefinition 740 AutomationPeer 211
Add sample data source 158 AutomationProperties 206, 211
AddDragInfoHandler 456 AutoPlay 1057
AddDragQueryHandler 456 AutoReverse 242
AddDropInfoHandler 456 Axis 880
AddDropQueryHandler 456 AxisX 880
AddedItems 521, 572 AxisY 880
AddHandler() 297, 581
AdditionalPostFields 1090
AddReturnParam( 1090 -B-
ADO.NET Data Services 110, 121
ADO.NET Entity Data Model 110 Band 385
Advanced Property Options button 138, 146 BandIndex 385
AggregateFunctions 661 Bar3DSeriesDefinition 889
Alert() 979 BasedOn 53
Align 349 BaseItemsControl 669
AllowDrag 445, 453, 757 BeginExecute() 116
AllowDragReorder 349 Binding 63, 74
AllowDrop 445, 454, 757 Bing 940, 941
AllTabsEqualHeight 349 BitmapImage 292, 294, 692
AlternationCount 669 BottomTemplate 971
AngleX 394 Boundary Detection 283
AngleY 394 Braille 203
Animation 429, 432, 813 Bubbling 62
AnimationManager 429, 591
Apply Resource 140
ContentPresenter 421
-C- ContentTemplate
ContentText 242
462, 465
ContextMenu 297
CameraDistance 1065
ControlTemplate 60, 686, 1051
CameraExtension 861
Convert to New Resource 138
CameraRotation 1065
CopyFrom() 782
CameraViewpoint 1056
Create Data Binding 166
CancelButtonContent 983
Create Empty 140
Canceled 935
CreatePeerForElement() 211
CancelEdit() 587
Creating a Simple Animation 146
CancelUpload() 1096
Cross Domain 86
CanClose 992
Crossdomain.xml 86, 941
CanDockInDocumentHost 923
Culture 198, 231, 486, 497, 771
CanFloat 923
CultureInfo 198, 486
CanMove 992
CurrentCell 166
CanUserClose 923
CurrentCellChanged 646
CanUserPin 923
CurrentFileProgress 1096
CellTemplate 727
CurrentItem 1032
ChangeAcceleration 242
CurrentProgress 1096
ChapterReached 1032
CurrentSession 1096
Chapters 1032
CurrentStateChanged 1032
ChapterTitle 1032
Custom Processing in the Upload Handler 1090
ChartArea 861, 877, 881
CustomUnit 242
ChartDefaultView 861
ChartLegend 861, 877, 889
ChartTitle 861, 877, 883
Checkable Items 283
-D-
Checked 292 Data pane 132, 154, 160, 163, 166
ChildTableDefinitions 637 Data Services Wizard 121, 124
ChildWindow 455 DataContext 63, 70, 154, 163, 500
Click 292 DataElement 673
ClickToOpen 283, 284, 290 DataFormatString 663
ClientAccessPolicy.xml 86, 101, 102, 692, 706, DataItem 898, 908
941 DataMemberBinding 663, 680
ClockItemSource 500 DataPager 676
CloseAllWindows() 976 DataPoint 861, 889, 898
Closed 979 DataPointLabel 889
CollapseAll() 576 DataPointMember 897, 898
CollapseAllGroups() 661 DataSeries 861, 877
Collapsed 362, 428, 576 DataSeriesCollection 877
CollapseGroup() 661 DataTemplate 60, 80, 81, 163, 264, 303, 394, 462,
Columns 471, 486, 637, 680 595
ColumnWidth 674 DataTemplateSelector 613
CompositeFilterDescriptor 653 Date/Time Masks 231
ConfigurationSection 941 DayEndTime 740
Confirm() 983 DayHeaderFormat 774
ContentBinding 663 DayStartTime 740
ContentControl 462 DefaultCulture 194, 198, 771
Gauge 803
General Accessibility 203
-I-
GenerateArrowCue() 462 IAppointment 742, 778
GenerateVisualCue() 462 IAsyncResult 116
GET 110 Icon 292
GetAssociatedData() 1090 Identification 203
GetContainerByItem() 591 IEnumerable 592
GetDistinctValues() 653 IFilterDescriptor 653
GetItemByPath() 567 IFrame 1008
GetStringOverride() 199 IGroup 661
Google 940 ImagesBaseDir 570
GridLinesVisibility 669 ImageSource 570, 1029
GridViewComboBoxColumn 663 Import Sample Data From XML... 158, 166
GridViewDataColumn 663, 680, 727 Indicator 813
GridViewDynamicHyperlinkColumn 663 IndicatorBase 813
GridViewExportEventArgs 679 IndicatorList 805
MaxFileCount 1089
PluginLoaded 883
-O- PopupManager
PopupStartingZIndex
975, 992
992
Position 1032
Object Relational Mapping 121
POST 110
Objects and Timeline pane 37, 43, 132, 140, 143,
146, 188 PreviewClose 935
ObservableCollection 500, 680, 713 PreviewClosed 991
ObservableCollection<> 158, 303 PreviewCloseWindow 935
ObservableCollection<T> 75 PreviewCollapsed 362, 576
Offset 813 PreviewEdited 587
OffsetX 1062 PreviewEditStarted 587
OffsetY 1062 PreviewExpand 576
OkButtonContent 986 PreviewExpanded 362
OldCell 646 PreviewPin 935
OldValue 264 PreviewSelected 572
OnDragInfo 462 PreviewShow 935
OnDropInfo 445 PreviewShowCompass 935
OneTime 70 PreviewUnPin 935
OneWay 70 PreviewUnselected 572
Opacity 188 PrintExtensions 677
OpacityMask 421 PrintToHtml() 679
OpenAccess 121 Prism 66
Opened 979 ProcessStream() 1090
OpenReadAsync() 692 Projects pane 37, 132
OpenReadCompleted 86, 692 Prompt() 986
Options 445, 455 PromptResult 986
Orientation 385, 813, 850 Properties pane 37, 132, 143, 146
OriginalSource 362 Property Paths 680
Overflow 385 PropertyChanged() 75
OverflowAreaClosed 385 PUT 110
OverflowAreaOpened 385
OverflowMode 385
OverwriteExistingFiles 1085
-Q-
QueryableCollectionView 676
PageSize 676
ParamNameMessage 1090
-R-
Parse() 83, 1067 RadButton 143, 186
ParticipatingVisualRoots 455 RadCalendar 59, 469, 471, 486, 497, 500, 507
Parts pane 132 RadChart 860, 861, 940
Path 63, 70, 74 RadClock 469, 497
Patterns 203 RadColorPaletteView 251
PauseUpload() 1096 RadColorPicker 201, 216, 251
Payload 455 RadColorSelector 201, 251
Placement 297 RadComboBox 500, 517, 518, 521, 546
PlacementRectangle 297 RadComboBoxAutomationPeer 211
-S- SetFirstVisibleDate()
SetIsAnimationEnabled()
742
429
SetOverflowMode() 385
SaveLayout(stream) 936
SetResourceKey() 200
ScaleBase 813
SetStyleAttribute() 1015
Scope Up button 140
SetTheme() 177
screen readers 203
ShadowDepth 188
ScriptableMember 1018
Show Context Menu Using Right-Click 297
ScriptManager 883
Show Timeline 188
ScrollIntoView() 646
ShowButtons 242
ScrollMode 669
ShowColumnFooters 669
ScrollViewer 675
ShowColumnHeaders 669
SelectableDateEnd 471, 486
ShowDelay 290
SelectableDateStart 471, 486
ShowFileDialog() 1096
SelectAll 646
ShowFirstLabel 813
Selected 362, 521, 559, 572
ShowGroupPanel 669
SelectedColor 251
ShowItemToolTips 898
SelectedColorChanged 216, 251
ShowLastLabel 813
SelectedContainer 572
Silverlight Application 132
SelectedContent 349
Silverlight Application template 31
SelectedDates 500
Silverlight Class Library 180
SelectedFiles 1096
Silverlight Control Library 132
SelectedImageSrc 570
Silverlight Life Cycle 26
SelectedIndex 349, 521, 1079
Silverlight Plugin 26
SelectedItem 349, 521, 572, 646
Silverlight Resource Dictionary 180
SelectedItems 572, 646
Silverlight SDK 27
SelectedTimeSlot 739
Silverlight Toolkit 27
SelectedValueMemberPath 663
Silverlight-enabled WCF Service template 102
SelectionBoxItemTemplate 521, 538
sip & puff 203
SelectionChanged 349, 471, 486, 518, 521, 559,
572, 646, 1057 SketchFlow 132
SelectionChangedEventArgs 521, 572 SkewTransform 394
SelectionEnd 216, 264 SkipValue 850, 851
SelectionMode 486, 572 SmallChange 242
SelectionRangeChanged 216, 264 SnapInterval 813
SelectionStart 216, 264 SnapType 813
SelectionUnfocusedVisual 621 SOAP 86
SelectTemplate() 613 SortDescriptor 658
Sending Parameters to the Upload Handler 1090 SortDescriptors 658
Separator 292 SortDirection 658, 661
SerializationTag 936 Source 70, 1029
Series Mapping 889 SourceField 661
SeriesDefinition 898, 902 SourceUri 1021
SeriesLabel 889 SourceUrl 1004, 1008
SeriesMapping 889 SqlCommand 706
-U- -W-
UI Automation Verify 203 WCF 101, 121, 705, 940
UI Automation Verify Tool 206 WebClient 86, 538, 685, 692, 713
UI Virtualization 591 Window State 992
UIA 203 Windowless 297, 1004
UIElement 297, 753 Windows Communication Foundation 101
Unchecked 292 WindowStartingZIndex 992
UnhandledException 26 WindowStateChanged 991
UnselectAll 646 WPF 132
Unselected 572
UploadCanceled
UploadedFiles
1096
1096 -X-
UploadFinished 1096
x:Key 53
UploadPaused 1096
XAML 47
UploadResumed 1096
XCategory 897
UploadServiceUrl 1085
XDocument 83, 86, 1067
UploadSizeExceeded 1089
XElement 941
UploadStarted 1096
XML 86
UrlLoaded 1008, 1012
XMLParseException 53
UseAutoGeneratedItems 861
UseDefaultLayout 861
UserState 692
Using RadContextMenu 297
-Y-
YouTube 86
YValue 861, 889
-V-
ValidFiles
Value
1096
242, 850, 1079
-Z-
ValueChanged 216, 231, 242, 264, 850 zillow.com 940
ValueChanging 231 Z-Order 992
ValueFormat 216, 242
VerticalOffset 297
Video Size and Proportion 1032
VideoHeight 1032
VideoStretch 1032
VideoWidth 1032
View 66
ViewDefinitionBase 740
ViewMode 736, 740
ViewModel 66
ViewsHeaderVisibility 471
VirtualizationMode 591
Visibility 203
VisibleDays 740