title | page_title | description | type | slug | position | tags | ticketid | res_type | category |
---|---|---|---|---|---|---|---|---|---|
Custom RadDiagramConnection With Additional Caps |
Custom RadDiagramConnection |
Extend the base implementation of the RadDiagramConnection element, by adding additional Path elements for displaying Caps, to its ControlTemplate. |
how-to |
kb-diagram-custom-connection-cap |
0 |
diagram, connection, cap |
1542456 |
kb |
knowledge-base |
Product Version | 2021.1.325 |
Product | Diagram for WPF |
With the current implementation of the RadDiagram control applying properties to a RadDiagramConnection element will also apply them to the Connection Cap. This is because the ConnectionLine and ConnectionCap elements are visualized by a single native Path control. To further customize the separate elements, the default implementation of the RadDiagramConnection control, would need to be extended.
To extend the base implementation of the RadDiagramConnection element, extract its default control template, and add two additional Path controls.
The following example shows the extracted and modified control template of the RadDiagramConnection element, from the Office2016 theme. The used version of the assemblies for this example is NoXaml, which allows for easier customization of the controls' templates.
{{region kb-diagram-custom-connection-cap_0}} <VisualStateManager.VisualStateGroups> <DiscreteObjectKeyFrame.Value> Visible </DiscreteObjectKeyFrame.Value> <DiscreteObjectKeyFrame.Value> Collapsed </DiscreteObjectKeyFrame.Value> <DiscreteObjectKeyFrame.Value> Visible </DiscreteObjectKeyFrame.Value> <DiscreteObjectKeyFrame.Value> Collapsed </DiscreteObjectKeyFrame.Value> <DiscreteObjectKeyFrame.Value> Visible </DiscreteObjectKeyFrame.Value> </VisualStateManager.VisualStateGroups> <TextBox.InputBindings> </TextBox.InputBindings> {{endregion}}
Create a class that derives from the RadDiagramConnection class and implement the following logic for handling the two additional path elements.
{{region kb-diagram-custom-connection-cap_1}} public class CustomConnection : RadDiagramConnection { private Path sourceConnectionCap; private Path targetConnectionCap;
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
this.sourceConnectionCap = (Path)this.GetTemplateChild("SourceConnectionCap");
this.targetConnectionCap = (Path)this.GetTemplateChild("TargetConnectionCap");
this.UpdateGeometryOverride();
}
protected override Geometry CreateGeometry(BridgeType bridgeType, bool roundedCorners)
{
this.AddConnectionCaps();
return base.CreateGeometry(bridgeType, roundedCorners);
}
private void AddConnectionCaps()
{
if (this.sourceConnectionCap != null && this.targetConnectionCap != null)
{
var sourcePoint = this.StartPoint.Substract(this.Position);
var targetPoint = this.EndPoint.Substract(this.Position);
var transformedPoints = this.TranslateConnectionPoints(false).ToList();
Point sourceCapSecondPoint;
Point targetCapSecondPoint;
if (this.ConnectionType == ConnectionType.Spline)
{
var points = new List<Point> { sourcePoint };
points.AddRange(transformedPoints);
points.Add(targetPoint);
GeometryExtensions.GetSplineFigureTangents(points, out sourceCapSecondPoint, out targetCapSecondPoint);
}
else
{
sourceCapSecondPoint = transformedPoints.Count == 0 ? targetPoint : transformedPoints[0];
targetCapSecondPoint = transformedPoints.Count == 0 ? sourcePoint : transformedPoints[transformedPoints.Count - 1];
}
if (this.SourceCapType != CapType.None)
{
this.sourceConnectionCap.Data = CreateSourceCapGeometryData(sourcePoint, sourceCapSecondPoint, ref sourcePoint);
}
if (this.TargetCapType != CapType.None)
{
this.targetConnectionCap.Data = CreateTargetCapGeometryData(targetPoint, targetCapSecondPoint, ref targetPoint);
}
}
}
private Geometry CreateSourceCapGeometryData(Point startPoint, Point endPoint, ref Point baseLineStart)
{
var geometry = new PathGeometry();
geometry.Figures.Add(this.CreateSourceCapGeometry(startPoint, endPoint, ref baseLineStart));
return geometry;
}
private Geometry CreateTargetCapGeometryData(Point startPoint, Point endPoint, ref Point baseLineStart)
{
var geometry = new PathGeometry();
geometry.Figures.Add(this.CreateTargetCapGeometry(startPoint, endPoint, ref baseLineStart));
return geometry;
}
}
{{endregion}}
{{region kb-diagram-custom-connection-cap_2}} Public Class CustomConnection Inherits RadDiagramConnection
Private sourceConnectionCap As Path
Private targetConnectionCap As Path
Public Overrides Sub OnApplyTemplate()
MyBase.OnApplyTemplate()
Me.sourceConnectionCap = CType(Me.GetTemplateChild("SourceConnectionCap"), Path)
Me.targetConnectionCap = CType(Me.GetTemplateChild("TargetConnectionCap"), Path)
Me.UpdateGeometryOverride()
End Sub
Protected Overrides Function CreateGeometry(ByVal bridgeType As BridgeType, ByVal roundedCorners As Boolean) As Geometry
Me.AddConnectionCaps()
Return MyBase.CreateGeometry(bridgeType, roundedCorners)
End Function
Private Sub AddConnectionCaps()
If Me.sourceConnectionCap IsNot Nothing AndAlso Me.targetConnectionCap IsNot Nothing Then
Dim sourcePoint = Me.StartPoint.Substract(Me.Position)
Dim targetPoint = Me.EndPoint.Substract(Me.Position)
Dim transformedPoints = Me.TranslateConnectionPoints(False).ToList()
Dim sourceCapSecondPoint As Point
Dim targetCapSecondPoint As Point
If Me.ConnectionType = ConnectionType.Spline Then
Dim points = New List(Of Point) From {
sourcePoint
}
points.AddRange(transformedPoints)
points.Add(targetPoint)
GeometryExtensions.GetSplineFigureTangents(points, sourceCapSecondPoint, targetCapSecondPoint)
Else
sourceCapSecondPoint = If(transformedPoints.Count = 0, targetPoint, transformedPoints(0))
targetCapSecondPoint = If(transformedPoints.Count = 0, sourcePoint, transformedPoints(transformedPoints.Count - 1))
End If
If Me.SourceCapType <> CapType.None Then
Me.sourceConnectionCap.Data = CreateSourceCapGeometryData(sourcePoint, sourceCapSecondPoint, sourcePoint)
End If
If Me.TargetCapType <> CapType.None Then
Me.targetConnectionCap.Data = CreateTargetCapGeometryData(targetPoint, targetCapSecondPoint, targetPoint)
End If
End If
End Sub
Private Function CreateSourceCapGeometryData(ByVal startPoint As Point, ByVal endPoint As Point, ByRef baseLineStart As Point) As Geometry
Dim geometry = New PathGeometry()
geometry.Figures.Add(Me.CreateSourceCapGeometry(startPoint, endPoint, baseLineStart))
Return geometry
End Function
Private Function CreateTargetCapGeometryData(ByVal startPoint As Point, ByVal endPoint As Point, ByRef baseLineStart As Point) As Geometry
Dim geometry = New PathGeometry()
geometry.Figures.Add(Me.CreateTargetCapGeometry(startPoint, endPoint, baseLineStart))
Return geometry
End Function
End Class
{{endregion}}
The result is a custom connection element, which can be used both in Xaml and in code.
{{region kb-diagram-custom-connection-cap_3}}
telerik:RadDiagram
<local:CustomConnection StartPoint="400, 100"
EndPoint="200, 100"
SourceCapType="Arrow2"
TargetCapType="Arrow4Filled"
SourceCapSize="6, 6"
TargetCapSize="6, 6"/>
</telerik:RadDiagram>
{{endregion}}
{{region kb-diagram-custom-connection-cap_4}} var connection = new CustomConnection() { SourceCapType = Telerik.Windows.Diagrams.Core.CapType.Arrow1, TargetCapType = Telerik.Windows.Diagrams.Core.CapType.Arrow4Filled, StartPoint = new Point(400, 100), EndPoint = new Point(200, 100) };
this.diagram.Items.Add(connection);
{{endregion}}
{{region kb-diagram-custom-connection-cap_5}} Dim connection = New CustomConnection() With { .SourceCapType = Telerik.Windows.Diagrams.Core.CapType.Arrow1, .TargetCapType = Telerik.Windows.Diagrams.Core.CapType.Arrow4Filled, .StartPoint = New Point(400, 100), .EndPoint = New Point(200, 100) }
diagram.Items.Add(connection)
{{endregion}}