» Full source code of the demo application here
The new RadDiagram framework brings, together with a host of new exciting things in Telerik's 2012 Q1 release, a new world of possibilities to the WPF and Silverlight community. This article attempts to show you that Telerik's RadDiagram is easy-to-use and that it differs from other Telerik controls in that it more closely represents a framework than a control. It’s an invitation to augment and customize the RadDiagram toolset in function of your application and business context. That said, we have to tell you straight away that RadDiagram, as released with Q1, is just the beginning. We have at this point put down the foundations for lots of exciting things to come and you can expect to see this RadDiagram branch off in various directions in coming releases. Great care and much energy were put in making it a flexible, robust, and broad framework for future developments. We have tried to make your (and our) task of customizing things easy while retaining the possibility to make the complex possible.
The dashboard designer described in this article aims to highlight various development tasks related to RadDiagram and how to integrate it into a real-world LOB application. It also tries to underline the fact that the diagramming world is a very broad subject on many levels. In its most basic form a diagram (graph, network) is just a set of shapes and lines connecting these shapes, but diagramming frameworks are also used for a wild spectrum of other tasks:
|
[Picture above] Typical dashboard in our sample application representing an overview of stock-related data coming from Yahoo Finance. |
Yet another context wherein diagrams come in handy is when you need a flexible surface on which you need to drop elements to design a document or data dashboard. While graphs are intrinsically related to links and connections, nothing prevents you from using a diagram framework without them and thus reduce it to a layout or design engine (a form designer anyone?). Our financial dashboard designer uses the RadDiagram surface in both ways; to design data dashboards (no connections here) and to represent the hierarchical structure of these dashboards (relationship graph).
|
[Picture above] The very same RadDiagram surface which presents a digital dashboard is re-used to display the relationships contained within different dashboards by inspecting the hyperlinks between the screens. |
How does the application gets its relational structure? Put simply, the hyperlinks you add on a dashboard can be internal; you can create a link which jumps to another dashboard page much like hyperlinks in a web site.
|
[Picture above] Example of an internal link in a dashboard page. |
| [Picture left]The link editor (which pops up on double-clicking a link) allows you to add an internal link to another dashboard page or to an arbitrary (external) address. |
The important bit here is the fact that across the application there is only one RadDiagram instance used in multiple ways and that all the dynamics occur through the (de)serialization. In fact, the generic flow in the application is as follows:
|
[Picture above] A dashboard page in design mode shows some additional elements in the UI: the toolbox and the toolbar. Through these you can achieve a very diverse set of data pages. In addition, it’s really easy to add your own toolbox items. |
This flow demonstrates the following aspects of RadDiagram:
Now that you have an overview of what the demo application does and its various features let’s dig a bit more into the technical details.
The dashboard designer uses realtime financial information from the Yahoo Finance API:
The code to access the Yahoo Finance API is quite straightforward and one can, of course, reuse these controls and the underlying code for other data sources.
| [Picture left] Sample output of the Yahoo API which lets you query the stock details of an arbitrary symbol (here show is ‘MSFT’). |
The RadDiagram framework comes with a standard set of flowcharting shapes (geometry shapes) but this shouldn't prevent you from enlarging it with your own set. In fact, diagramming is such a vast domain and its integration within the business context is so tight that any diagramming framework would fail to deliver a complete set of shapes. Rather than trying to offer lots of shapes RadDiagram offers you a flexible and straightforward route to designing custom shapes and embedding (Telerik or third-party) controls.
| [Picture left] Sample custom RadDiagram shape hosting a RadChart control,which displays the latest trade price as well as the procentual change. |
The generic recipe for creating a custom shape goes as follows:
Let’s take, as an example, the GaugeShape from the sample, which (see adjacent picture) displays the latest price and price change of a stock. The shape inherits from RadDiagramShape
public
sealed
GaugeShape :RadDiagramShape
and defines a custom Symbol dependency property
public
static
readonly
DependencyProperty SymbolProperty = DependencyProperty.Register(
"Symbol"
,
typeof
(
string
),
typeof
(GaugeShape),
new
FrameworkPropertyMetadata(
null
, OnSymbolChanged));
Whether it’s a standard or a dependency property depends really on what you want to do with it, see the MSDN documentation on this.
Next, you need to define the visual appearance of the control. Here is an important point you need to consider; do you need the adorning elements (resizing adorner, connectors, rotation handle) of the RadDiagram framework to appear? If so, the easiest way is to base your XAML style on the base style (using the BasedOn property) from the framework;
<
Style
TargetType
=
"{x:Type local:GaugeShape}"
BasedOn
=
"{StaticResource {x:Type telerik:RadDiagramShape}}"
>
As a result, all the adorning diagramming elements are already present and your custom shape will take part in the internal mechanics of RadDiagram. If you omit the directive, then the shape will still be part of the diagram, but you will not see or be able to use the resizer, rotation handle, and so on.
From this point on you can define inside the style all you wish, but there are some gotchas:
In the content template of the GaugeShape you will see the following bits:
<
TextBlock
Text
=
"{Binding Path=Name}"
Foreground
=
"{StaticResource MetroAccentBrush}"
FontWeight
=
"Bold"
HorizontalAlignment
=
"Center"
/> <
telerik:RadRadialGauge
OuterBorderBrush
=
"#FF08FF00"
Grid.Row
=
"1"
Background
=
"WhiteSmoke"
>
The TextBlock accesses the Name property because the Content contains the Quote information:
var quote =
new
Quote(Symbol);
YahooEngine.Fetch(
new
List<Quote> { quote });
this
.Content = quote;
On the other hand, if you want your shape to be stretched and resized according to the outer resizing adorners, you should set the following properties:
<
Setter
Property
=
"HorizontalContentAlignment"
Value
=
"Stretch"
/> <
Setter
Property
=
"VerticalContentAlignment"
Value
=
"Stretch"
/>
Finally, in order to have the Symbol property persistent, you need to tell the framework how to serialize it. While this might sound complicated we really made it simple for you to perform this step. The serialization of a property (say, the Symbol property) amounts to adding an entry in a dictionary:
public
override
SerializationInfo Serialize() {
var info =
base
.Serialize();
info[“Symbol”] =
this
.Symbol ??
""
;
return
info; }
Conversely, if you want to pick up the saved value of the Symbol property you just ask the dictionary for the value:
public
override
void
Deserialize(SerializationInfo info) {
base
.Deserialize(info);
if
(info[“Symbol”] !=
null
)
this
.Symbol = info[SymbolTag].ToString(); }
There is more under the hood than this straightforward serialization alone, please consult the RadDiagram documentation for more serialization goodies.
Once all this is in place you can add the custom shape to the diagram and bind it to the application data like so:
var shape =
new
GaugeShape
{
Position =
new
Point(10, 120)
};
Diagram.AddShape(shape);
Many more possibilities and extension points are at your disposal, several of which are demonstrated in the custom shapes inside the sample application. This elementary overview, however,should get you going and we have made the documentation as complete as possible to guide you through the ins and outs of the RadDiagram framework.
The RadDiagram framework does not provide a Toolbox control out-of-the-box. It is too specific a part of the story and could just be used for some diagramming applications. That said, it is not part of the core framework, but we wrote several lines of code that can be used and easily customized for your needs. There is an Assembly that we called Features (Telerik.Windows.Diagrams.Features.dll), which is an extension of the core RadDiagram framework, and contains some nice features. For example, there is a ToolBox control there. It is a very simple Hierarchical ItemsControl that you can populate with a hierarchical data source or manually with ToolBoxGroups and items. On the other hand, the items are not much more than ContentControl, so you can effectively add any UI Elements you would like to the toolbox(i.e. you can even add regular shapes from the RadDiagram framework)
<
expandertoolbox:DiagramToolbox
> <
expandertoolbox:ToolboxGroup
Header
=
"Shapes"
> <
expandertoolbox:ToolboxItem
Header
=
"Rectangle"
Description
=
"A simple rectangle"
> <
local:ShapeToolboxItem
Geometry
=
"{telerik:CommonShape ShapeType=RoundedRectangleShape}"
/> </
expandertoolbox:ToolboxItem
> <
expandertoolbox:ToolboxItem
Header
=
"Ellipse"
Description
=
"A simple rectangle"
> <
local:ShapeToolboxItem
Geometry
=
"{telerik:CommonShape ShapeType=EllipseShape}"
/> </
expandertoolbox:ToolboxItem
> </
expandertoolbox:ToolboxGroup
> </
expandertoolbox:DiagramToolbox
>
But the interesting part here is how to handle the drop from the Toolbox to the diagram surface. To make our lives easier, we’re using the Telerik DragDropManager, which automatically handles the start-drag operation of the toolbox item. Thus we need to handle the drop operation at the other side, on the diagram surface.
this.diagram.Drop += this.OnDropToolboxItem;
Let’s take a look at the handler as well.
var item = (e.Data
as
DataObject).GetData(
"Telerik.Windows.Diagrams.Features.ExpanderToolbox.ToolboxItem"
)
as
ToolboxItem;
if
(item !=
null
) {
IToolboxItem toolboxItem = item.Content
as
IToolboxItem;
RadDiagramShape shape =
null
;
var position = e.GetPosition(
this
.diagram);
if
(toolboxItem !=
null
) {
shape = toolboxItem.CreateShape(position); }
if
(shape !=
null
) {
this
.diagram.AddShape(shape);
e.Handled =
true
; } }
The trickier part is that each type of ToolboxItem implements the CreateShape factory method (from the IToolboxItem interface), which creates a new, configured shape for this type of toolbox item.
public
override
RadDiagramShape CreateShape(Point position) {
var shape =
new
ImageShape { Location =
this
.ImageLocation, Address =
this
.Address, Position = position, NavigateUri =
this
.NavigateUri };
return
shape; }
There is one more interesting story in this application: How to switch between Edit and Design surface modes. The RadDiagram framework provides you with plenty of properties that can control the read-only state in different aspects. Mentioning these properties here affects the run-time through UI behavior and does not enforce changes through the APIs.
They are:
|
[Picture above] Please visit the documentation on RadDiagram for much more info on other aspects not touched by this overview article. |
While this demo application shows off some of the more juicy features of RadDiagram it is by no means the end of the story. You are invited to continue reading on:
Miro Miroslavov is XAML enthusiast. You can follow him on Twitter at @mmiroslavov.