This is a migrated thread and some comments may be shown as answers.

Connections: Positioning Text below Shape

2 Answers 172 Views
Diagram
This is a migrated thread and some comments may be shown as answers.
Sebastian
Top achievements
Rank 1
Sebastian asked on 02 Apr 2020, 07:05 AM
Hello there,

we are using the RadDiagram to create a kind of hierarchical view, where the connections should show some information about the connection itself.

I've tried to use the Horizontal-/VerticalContentAlignment for this, but since the different directions of the connections I haven't found a solution that worked for all connections yet.

I've attached a picture of our momentary existing diagram. The text for the connections is correctly placed on connections to the left, but not to the right.

Here is the style we are currently using:
<Style x:Key="EdgeStyle" TargetType="telerik:RadDiagramConnection">
                        <Setter Property="SourceCapType" Value="Arrow1" />
                        <Setter Property="VerticalContentAlignment" Value="Center"/>
                        <Setter Property="HorizontalContentAlignment" Value="Left"/>
                        <Setter Property="ContentTemplate">
                            <Setter.Value>
                                <DataTemplate DataType="diagramStuff:DiagramEdge">
                                    <TextBlock HorizontalAlignment="Left" Margin="-20,10,0,0" Background="#F3FFFFFF" Text="{Binding Description}" />
                                </DataTemplate>
                            </Setter.Value>
                        </Setter>
                    </Style>

It would be perfect, if the text could be positioned right beneath where the connection "meets" the Shape (I've marked the spot on the screenshot in yellow), but I have no idea how to accomplish this.

Could you please help me out how we could accomplish this?


2 Answers, 1 is accepted

Sort by
0
Martin Ivanov
Telerik team
answered on 06 Apr 2020, 09:21 PM

Hello Sebastian,

Will get back to you tomorrow with some information on the topic.

Regards,
Martin Ivanov
Progress Telerik

Progress is here for your business, like always. Read more about the measures we are taking to ensure business continuity and help fight the COVID-19 pandemic.
Our thoughts here at Progress are with those affected by the outbreak.
0
Petar Mladenov
Telerik team
answered on 07 Apr 2020, 07:18 AM

Hello Sebastian,

I think this is a duplication of the question from your other diagram thread. I hope my answer there gives you a good start. However, I will post the idea for solution here as well, in case other developer from community need it.

As you have noticed the HorizontalContentAlignment / VerticalContentAlignment control the content position over the bounding rectangle of the connection. For more precise positioning of the content, you will need to inherit the RadDiagramConnection and perform custom logic. I will try to list the steps as detailed as possible. I will skip the steps needed if you need runtime editing of the labels, so if you need this please let me know.

1. Use custom connection. No need to change the default template, but you need to inherit the RadDiagram too. Check last section in this article.

Diagram Custom Shape

2. Override on ApplyTemplate, get the element which holds the content in a local variable like so:

 

   public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();
            if (this.edittingElement != null) this.edittingElement.SizeChanged -= this.OnEdittingElementSizeChanged;
            this.edittingElement = this.GetTemplateChild("EdittingElement") as FrameworkElement;
            if (this.edittingElement != null) this.edittingElement.SizeChanged += this.OnEdittingElementSizeChanged;

 

3. Override the following method

        /// <summary>
        /// When overridden, positions the editing element for the <see cref="RadDiagramConnection"/>.
        /// </summary>
        protected virtual void PositionEditElementOverride()
        {
            if (this.edittingElement != null) this.edittingElement.SetLocation(this.EdittingPoint);
        }

This is basically called then the internal property EditingPoint is changed. I will list the function which calculates it. Keep in mind many functions in the snippets are public and are extension methods from Utils(Telerik.Windows.Controls.Diagrams.Utils) or ConnectionUtilities (Telerik.Windows.Diagrams.Core.ConnectionUtilities).

        /// <summary>
        /// Calculates the point where the editing of the label occurs.
        /// </summary>
        private void CalculateEdittingPoint()
        {
            // the endpoints in local connection coordinates
            var endPoints = this.GetConnectionEndPoints(true);

            // the intermediate points in local coordinates
            var points = new List<Point>();
            if (this.ConnectionType == ConnectionType.Bezier)
            {
                var bezierCurve = Utils.ApproximateBezierCurve(new[] { this.StartPoint, Utils.Lerp(this.StartPoint, this.ConnectionPoints[0], this.BezierTension), Utils.Lerp(this.EndPoint, this.ConnectionPoints[1], this.BezierTension), this.EndPoint }, 10);
                points = bezierCurve.Select(x => x = x.Substract(this.Bounds.TopLeft())).ToList();
            }
            else
            {
                points = this.ConnectionPoints.Select(x => x = x.Substract(this.Bounds.TopLeft())).ToList();
            }

            // the middlepoint in local coordinates; note that the Canvas wherein the label sits will move with the Bounds of this connection
            var defaultEdittingPoint = ConnectionUtilities.CalculateMiddlePointOfLine(endPoints, points);

            if (this.edittingElement != null)
	        {
                defaultEdittingPoint = new Point(
                    defaultEdittingPoint.X - (this.edittingElement.ActualWidth / 2),
                    defaultEdittingPoint.Y - (this.edittingElement.ActualHeight / 2));
 	        }

            var bounds = this.Bounds;
            switch (this.HorizontalContentAlignment)
            {
                case HorizontalAlignment.Center:
                     defaultEdittingPoint.X = defaultEdittingPoint.X;
                     break;
                case HorizontalAlignment.Left:
                     defaultEdittingPoint.X = 0;
                     break;
                case HorizontalAlignment.Right:
                     defaultEdittingPoint.X = bounds.Width;
                     break;
            }

            bounds = this.Bounds;
            switch (this.VerticalContentAlignment)
            {
                case VerticalAlignment.Bottom:
                     defaultEdittingPoint.Y = bounds.Height;
                     break;
                case VerticalAlignment.Center:
                     defaultEdittingPoint.Y = defaultEdittingPoint.Y;
                     break;
                case VerticalAlignment.Top:
                     defaultEdittingPoint.Y = 0;
                     break;
            }

            this.EdittingPoint = defaultEdittingPoint.Add(this.Bounds.TopLeft().Substract(this.Position));
        }

This code is to give you an idea of the current positioning logic. You can create your own similar function and store the result in private field (for example "calculatedContentPosition". Then use this field and set tje position of the editing element. 

        protected virtual void PositionEditElementOverride()
        {
            this.CalculateContentPosition() // your logic in this method
            if (this.edittingElement != null) this.edittingElement.SetLocation(this.calculatedContentPosition);
        }

This setting of position is actually Canvas Position, Canvas holds the Content in the ControlTemplate of the connection:

<Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="diagramscontrols:RadDiagramConnection">
                    <Grid x:Name="RootTemplate">
....
 <Path x:Name="DeferredPath"
<Path x:Name="SelectedInGroupPath"
  <Path Stroke="{TemplateBinding Stroke}" Fill="{TemplateBinding Background}" StrokeThickness="{TemplateBinding StrokeThickness}" x:Name="GeometryPath">
        <Canvas>
                            <Grid x:Name="EdittingElement">
                                <Border Background="#00FFFFFF" />
                                <ContentPresenter x:Name="NormalContent" />
                                <ContentPresenter x:Name="EditContent" Visibility="Collapsed" Content="{TemplateBinding Content}" 
												  ContentTemplate="{TemplateBinding EditTemplate}" />
                                <TextBox x:Name="EditTextBox" Visibility="Collapsed"
										  Style="{StaticResource EditTextBoxStyle}">

Regards,
Petar Mladenov
Progress Telerik

Progress is here for your business, like always. Read more about the measures we are taking to ensure business continuity and help fight the COVID-19 pandemic.
Our thoughts here at Progress are with those affected by the outbreak.
Tags
Diagram
Asked by
Sebastian
Top achievements
Rank 1
Answers by
Martin Ivanov
Telerik team
Petar Mladenov
Telerik team
Share this question
or