Serialization
RadDiagram
allows you to serialize (save) your current diagram in an XML string and later deserialize it (load). You can achieve this with the RadDiagram.Save()
and RadDiagram.Load(string serializationString)
methods. You can also use the DiagramCommands Save
and Open
.
Save And Load A RadDiagram
In the code below you can see how to save and load a RadDiagram using Commands. Please note that before saving the current diagramming structure, it is best to check whether there are any items to save:
Please note that the examples in this tutorial are showcasing Telerik Windows8 theme. In the Setting a Theme article you can find more information on how to set an application-wide theme.
RadDiagram defined in Xaml
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<telerik:RadDiagram x:Name="diagram" Grid.Row="1">
<telerik:RadDiagramShape />
<telerik:CommandManager.CommandBindings>
<telerik:CommandBindingCollection>
<telerik:CommandBinding Command="telerik:DiagramCommands.Save"
Executed="CommandBinding_Executed_Save"
CanExecute="CommandBinding_CanExecute" />
<telerik:CommandBinding Command="telerik:DiagramCommands.Open"
Executed="CommandBinding_Executed_Open" />
</telerik:CommandBindingCollection>
</telerik:CommandManager.CommandBindings>
</telerik:RadDiagram>
<StackPanel Orientation="Horizontal">
<telerik:RadButton Width="125" Command="telerik:DiagramCommands.Save"
CommandTarget="{Binding ElementName=diagram}" Content="Save" />
<telerik:RadButton Width="125" Command="telerik:DiagramCommands.Copy"
CommandTarget="{Binding ElementName=diagram}" Content="Copy" />
<telerik:RadButton Width="125" Command="telerik:DiagramCommands.Paste"
CommandTarget="{Binding ElementName=diagram}" Content="Paste" />
<telerik:RadButton Width="125" Command="telerik:DiagramCommands.Open"
CommandTarget="{Binding ElementName=diagram}" Content="Load" />
</StackPanel>
</Grid>
Save-Load logic
private string diagramXMLString;
public MainWindow()
{
StyleManager.ApplicationTheme = new Windows8Theme();
InitializeComponent();
}
private void CommandBinding_Executed_Save(object sender, ExecutedRoutedEventArgs e)
{
diagramXMLString = this.diagram.Save();
}
private void CommandBinding_Executed_Open(object sender, ExecutedRoutedEventArgs e)
{
if (diagramXMLString != null)
{
this.diagram.Load(diagramXMLString);
}
}
private void CommandBinding_CanExecute(object sender, CanExecuteRoutedEventArgs e)
{
e.CanExecute = this.diagram.Items.Count > 0;
}
Private diagramXMLString As String
Public MainWindow()
StyleManager.ApplicationTheme = New Windows8Theme()
InitializeComponent()
End Sub
Private Sub CommandBinding_Executed_Save(ByVal sender As Object, ByVal e As ExecutedRoutedEventArgs)
diagramXMLString = Me.diagram.Save()
End Sub
Private Sub CommandBinding_Executed_Open(ByVal sender As Object, ByVal e As ExecutedRoutedEventArgs)
If diagramXMLString IsNot Nothing Then
Me.diagram.Load(diagramXMLString)
End If
End Sub
Private Sub CommandBinding_CanExecute(ByVal sender As Object, ByVal e As CanExecuteRoutedEventArgs)
e.CanExecute = Me.diagram.Items.Count > 0
End Sub
RadDiagram Serialization
Please note that the Content of the Shapes and the Connections is serialized as String. This means that if you have control like ListBox or TreeView as a Content , it will be not serialized successfully.
Save/Load Diagram ViewPort
When zooming with a mouse or panning the RadDiagram, the ViewPort position will be changed. The viewPort is not saved/load during the serialization mechanism, thus you will have to manually restore it. The BringIntoView(position, zoom level) method can be used for this purpose. The following example demonstrates how you can preserve and restore the ViewPort position.
Save-Load Diagram ViewPort
private double savedZoomFactor;
Point savedDiagramPosition;
private void CommandBinding_Executed_Save(object sender, ExecutedRoutedEventArgs e)
{
savedZoomFactor = this.diagram.Zoom;
savedDiagramPosition = this.diagram.Viewport.TopLeft;
diagramXMLString = this.diagram.Save();
}
private void CommandBinding_Executed_Open(object sender, ExecutedRoutedEventArgs e)
{
if (diagramXMLString != null)
{
this.diagram.Load(diagramXMLString);
this.diagram.BringIntoView(savedDiagramPosition, savedZoomFactor);
}
}
Extending RadDiagram Serialization
By default, not every property of the RadDiagramItem is serialized. Below is the list of the properties that are automatically serialized:
These properties are serialized in scenarios where data bindings are not involved. If you are using Binding in styles targeting any of the diagram items, you need to manually serialise and deserialize all the properties that are bound. This is demonstrated in the Manual Serialization/Deserialization section of this article.
-
RadDiagram
- AllowCopy
- AllowCut
- AllowDelete
- AllowPaste
- Background
- BackgroundGrid.CellSize
- BackgroundGrid.LineStroke
- BackgroundGrid.LineStrokeDashArray
- BackgroundGrid.LineStrokeThickness
- ConnectionBridges
- ConnectionRoundedCorners
- IsBackgroundSurfaceVisible
- IsConnectorsManipulationEnabled
- IsDraggingEnabled
- IsEditable
- IsManipulationAdornerVisible
- IsPanEnabled
- IsResizingEnabled
- IsRotationEnabled
- IsSnapEnabled
- IsZoomEnabled
- Metadata
- Position
- RouteConnections
- SelectionMode
- SnapX
- SnapY
- Zoom
-
RadDiagramShape
- AllowCopy
- AllowDelete
- AllowPaste
- Background
- BorderBrush
- BorderThickness
- Content
- FontFamily
- FontSize
- FontWeight
- Foreground
- Geometry
- GlidingStyle
- Id
- IsConnectorsManipulationEnabled
- IsDraggingEnabled
- IsEditable
- IsResizingEnabled
- IsRotationEnabled
- MaxHeight
- MaxWidth
- MinHeight
- MinWidth
- ParentGroup
- Position
- RotationAngle
- Size
- Stroke
- StrokeDashArray
- StrokeThickness
- UseDefaultConnectors
- ZIndex
-
RadDiagramConnection
- AllowCopy
- AllowDelete
- AllowPaste
- Background
- BezierEndPoint
- BezierStartPoint
- BezierTention
- BorderBrush
- BorderThickness
- ConnectionPoints
- ConnectionType
- Content
- EndPoint
- FontFamily
- FontSize
- FontWeight
- Foreground
- Id
- IsConnectorsManipulationEnabled
- IsDraggingEnabled
- IsEditable
- IsModified
- IsResizingEnabled
- IsRotationEnabled
- ParentGroup
- Position
- Source
- SourceCapSize
- SourceCapType
- SourceConnectorPosition
- StartPoint
- Stroke
- StrokeDashArray
- StrokeThickness
- Target
- TargetCapSize
- TargetCapType
- TargetConnectorPosition
- ZIndex
- UseFreeConnectors
For every other property that you need to be part of the Serialization/Deserialization process, you have to use the RadDiagram.ShapeDeserialized
and RadDiagram.ShapeSerialized
or RadDiagram.ConnectionDeserialized
and RadDiagram.ConnectionSerialized
events. Below is shown how you can serialize the Opacity
property of the shapes:
Manual Shape Serialization/Deserialization
If you are using MVVM approach and need to serialize databound properties you can take a look at our Serialize a Databound Diagram article.
Handling the ShapeSerialized and ShapeDeserialized events
void diagram_ShapeDeserialized(object sender, ShapeSerializationRoutedEventArgs e)
{
// load the saved property
(e.Shape as RadDiagramShape).Opacity = Convert.ToDouble(e.SerializationInfo["Opacity"], CultureInfo.InvariantCulture);
}
void diagram_ShapeSerialized(object sender, ShapeSerializationRoutedEventArgs e)
{
// save custom or other property
e.SerializationInfo["Opacity"] = (e.Shape as RadDiagramShape).Opacity.ToInvariant();
}
Private Sub diagram_ShapeDeserialized(ByVal sender As Object, ByVal e As ShapeSerializationRoutedEventArgs)
' load the saved property'
TryCast(e.Shape, RadDiagramShape).Opacity = Convert.ToDouble(e.SerializationInfo("Opacity"), CultureInfo.InvariantCulture)
End Sub
Private Sub diagram_ShapeSerialized(ByVal sender As Object, ByVal e As ShapeSerializationRoutedEventArgs)
' save custom or other property'
e.SerializationInfo("Opacity") = (TryCast(e.Shape, RadDiagramShape)).Opacity.ToInvariant()
End Sub
Please note that the
Double.ToInvariant()
extension method is defined in the Telerik.Windows.Diagrams.Core namespace and in order to use it in your application, you'll have to add a using statement: using Telerik.Windows.Diagrams.Core;
If you decide to manually save one of the properties automatically saved by the diagram, make sure to give it a custom key (different from the default one used for saving). Also, implement both, the saving and loading logic in the ShapeSerialize and ShapeDeserialized event handlers. For example, if you want to manually save the FontWeight property of a RadDiagramShape, you can use a key different than "FontWeight" in the SerializationInfo (example: "MyFontWeight"). We recommend this as a precaution, because internally the diagram parses the value given in the SerializationInfo and if it doesn't match a specific format an exception will be thrown.
Serialize/Deserialize RadDiagram Properties
To include additional information in the generated XML when the RadDiagram control is being serialized, you can handle its MetadataSerialized
event.
Handling the MetadataSerialized event
private void OnMetadataSerialized(object sender, DiagramSerializationRoutedEventArgs e)
{
RadDiagram diagram = (RadDiagram)sender;
e.SerializationInfo["Opacity"] = diagram.Opacity.ToInvariant();
}
Private Sub OnMetadataSerialized(ByVal sender As Object, ByVal e As DiagramSerializationRoutedEventArgs)
Dim diagram As RadDiagram = CType(sender, RadDiagram)
e.SerializationInfo("Opacity") = diagram.Opacity.ToInvariant()
End Sub
To load the serialized property of the RadDiagram control, handle its MetadataDeserialized
event.
Handling the MetadataDeserialized event
private void OnMetadataDeserialized(object sender, Telerik.Windows.Controls.Diagrams.DiagramSerializationRoutedEventArgs e)
{
RadDiagram diagram = (RadDiagram)sender;
diagram.Opacity = Convert.ToDouble(e.SerializationInfo["Opacity"], CultureInfo.InvariantCulture);
}
Private Sub OnMetadataDeserialized(ByVal sender As Object, ByVal e As Telerik.Windows.Controls.Diagrams.DiagramSerializationRoutedEventArgs)
Dim diagram As RadDiagram = CType(sender, RadDiagram)
diagram.Opacity = Convert.ToDouble(e.SerializationInfo("Opacity"), CultureInfo.InvariantCulture)
End Sub
Preserve Bindings to the Automatically Serialized Properties of the RadDiagram's Items
If you have a binding between a RadDiagramItem property that is automatically serialized and a property from the item’s view model the binding won’t work after the deserialization. This is because the serialization/deserialization logic of the diagram sets those properties locally which has bigger priority than a binding. You can read more about the value setting precedence in the Dependency Property Value Precedence MSDN article.
In order to preserve the binding you can remove the value of the property from the serialization info and use another key to serialize/deserialize it.
For example, if you have a binding to the Position property you can use the following approach for saving the binding:
-
Override the
SerializeNode()
method of the diagram’sGraphSource
and set the value of the bound property to null in theSerializationInfo
. Then add the value with a new key in the serialization info.SerializeNode override
public override void SerializeNode(NodeViewModelBase node, SerializationInfo info) { var position = info["Position"]; info["Position"] = null; info["MyPosition"] = position; base.SerializeNode(node, info); }
public Overrides Sub SerializeNode(node As NodeViewModelBase, info As SerializationInfo) Dim position = info("Position") info("Position") = Nothing info("MyPosition") = position MyBase.SerializeNode(node, info) End Sub
-
Override the
DeserializeNode()
method of the diagram’sGraphSource
and get the value of the bound property. Then assign it to the property of the view model.DeserializeNode override
public override NodeViewModelBase DeserializeNode(IShape shape, Telerik.Windows.Diagrams.Core.SerializationInfo info) { var node = base.DeserializeNode(shape, info); if (info["MyPosition"] != null) { var position = Utils.ToPoint(info["MyPosition"].ToString()); node.Position = position.Value; } return node; }
Public Overrides Function DeserializeNode(shape As IShape, info As Telerik.Windows.Diagrams.Core.SerializationInfo) As NodeViewModelBase Dim node = MyBase.DeserializeNode(shape, info) If info("MyPosition") IsNot Nothing Then Dim position = Utils.ToPoint(info("MyPosition").ToString()) node.Position = position.Value End If Return node End Function
The code snippets above demonstrate how to preserve the bindings in an MVVM scenario with a custom GraphSource. If you have statically declared shapes you can use the
ShapeSerialized
andShapeDeserialized
events of the diagram.
If you are using the RadDiagramToolbox in order to drag and drop shapes and you have bindings declared in a style, make sure that the style is added in the Application's Resources as in the above example.
Style containing Position binding in Application's Resources
<Application.Resources>
<Style TargetType="telerik:RadDiagramShape">
<Setter Property="Position" Value="{Binding Position, Mode=TwoWay}" />
</Style>
</Application.Resources>
Preserve Bindings of the Automatically Serialized Properties of the RadDiagram's Metadata
To preserve the bindings of the automatically serialized properties of RadDiagram's metadata, you can derive from it and override the Deserialize
method. Through the info parameter, you can retrieve the property that is used in a data-binding scenario and set its value to null. This will avoid the local setting of a value for it in the base implementation of the Deserialize
method.
Extending the RadDiagram control and overriding the Deserialize method
public class CustomDiagram : RadDiagram
{
public override void Deserialize(SerializationInfo info)
{
info[SerializationConstants.BackgroundGridLineStroke] = null;
base.Deserialize(info);
}
}
Public Class CustomDiagram
Inherits RadDiagram
Public Overrides Sub Deserialize(ByVal info As SerializationInfo)
info(SerializationConstants.BackgroundGridLineStroke) = Nothing
MyBase.Deserialize(info)
End Sub
End Class