Change Color of a Diagram Shape without recalculating all shape positions?

1 Answer 15 Views
Diagram
Scott
Top achievements
Rank 1
Scott asked on 13 Nov 2025, 07:42 PM | edited on 13 Nov 2025, 08:08 PM

I'm writing an OnShapeClick method that (among other things) should change the Color of the selected shape. I am defining shapes as such: 

<DiagramShapes>
    @foreach (Node node in Nodes)
    {
        <DiagramShape Id="@node.LinkID" >
            <DiagramShapeContent Text="@node.GetDisplayName()" />
            <DiagramShapeFill Color="@node.GetColor()"/>
        </DiagramShape>
    }
</DiagramShapes>

In OnShapeClick, I am updating the Color by changing a field for the specified node that results in node.GetColor() returning a new Value, and this works. 

async Task OnPersonClick(DiagramShapeClickEventArgs Args)
{
    foreach (Node node in Nodes)
    {
        if (node.LinkID.Equals(Args.Id))
            node.State = SelectedState.True;
        else
            node.State = SelectedState.False;
    }
}

The issue is that when the Diagram is re-rendered, the positions of all the shapes are recalculated. resulting in any Shapes that the user has dragged out of position to revert to their original position, or in the case of the Force Diagram, a complete reshuffle of the Shapes in the Diagram. This is undesired behavior. 

In short, how do I update the Color (or any property) of a Diagram Shape while maintaining the current positions of all shapes within the Diagram?

1 Answer, 1 is accepted

Sort by
0
Dimo
Telerik team
answered on 18 Nov 2025, 11:08 AM | edited on 18 Nov 2025, 11:14 AM

Hello Scott,

In this scenario, you need to use the Diagram state and persist the shape X and Y property values. Use the following example as a reference. Note that the serialized Diagram state contains a lot more properties, but you can persist only the ones you need. You can also directly configure the initial Diagram state from JSON if you like.

https://blazorrepl.telerik.com/GflvvMvl11Wn8WXY57 

@using System.Text.Json
@using System.Text.Json.Serialization

<TelerikDiagram @ref="@DiagramRef" OnShapeClick="@OnDiagramShapeClick">
    <DiagramLayout Type="@DiagramLayoutType.Tree" />

    <DiagramShapes>
        <DiagramShape Id="shape1">
            <DiagramShapeContent Text="Shape 1" />
        </DiagramShape>
        <DiagramShape Id="shape2">
            <DiagramShapeContent Text="Shape 2" />
        </DiagramShape>
        <DiagramShape Id="shape3">
            <DiagramShapeContent Text="Shape 3" />
        </DiagramShape>
        <DiagramShape Id="shape4">
            <DiagramShapeContent Text="Shape 4" />
        </DiagramShape>
        <DiagramShape Id="shape5">
            <DiagramShapeContent Text="Shape 5" />
        </DiagramShape>
        <DiagramShape Id="shape6">
            <DiagramShapeContent Text="Shape 6" />
        </DiagramShape>
    </DiagramShapes>

    <DiagramConnections>
        <DiagramConnection FromId="shape1" ToId="shape2" />
        <DiagramConnection FromId="shape1" ToId="shape3" />
        <DiagramConnection FromId="shape2" ToId="shape4" />
        <DiagramConnection FromId="shape2" ToId="shape5" />
        <DiagramConnection FromId="shape3" ToId="shape6" />
    </DiagramConnections>
</TelerikDiagram>

@code {
    #nullable enable

    private TelerikDiagram? DiagramRef { get; set; }

    private async Task OnDiagramShapeClick(DiagramShapeClickEventArgs args)
    {
        DiagramJson = await DiagramRef!.SaveAsJsonAsync();
        JsonSerializerOptions jsonOptions = new JsonSerializerOptions()
        {
            PropertyNamingPolicy = JsonNamingPolicy.CamelCase
        };

        DiagramState? diagramStateObject = JsonSerializer.Deserialize<DiagramState>(DiagramJson, jsonOptions);

        if (diagramStateObject is not null && diagramStateObject.Shapes is not null)
        {
            var rnd = Random.Shared;
            string newFillColor = $"rgb({rnd.Next(0, 127)},{rnd.Next(0, 127)},{rnd.Next(0, 127)})";

            diagramStateObject.Shapes.First(x => x.Id == args.Id).Fill.Color = newFillColor;

            DiagramJson = JsonSerializer.Serialize(diagramStateObject, jsonOptions);
            await DiagramRef!.LoadFromJsonAsync(DiagramJson);
        }
    }

    private string DiagramJson { get; set; } = string.Empty;

    public class DiagramState
    {
        public IEnumerable<ShapeDescriptor>? Shapes { get; set; }
        public IEnumerable<ConnectionDescriptor>? Connections { get; set; }
    }

    public class ShapeDescriptor
    {
        public string Id { get; set; } = Guid.NewGuid().ToString();

        // Needs a custom converter, because the serialized value is a string
        //public DiagramShapeType Type { get; set; } = DiagramShapeType.Rectangle;
        public double? X { get; set; }
        public double? Y { get; set; }

        public double? Height { get; set; } = 100;
        public double? Width { get; set; } = 100;

        public ShapeDescriptorContentFill Fill { get; set; } = new();

        public ShapeDescriptorContent Content { get; set; } = new();
    }

    public class ShapeDescriptorContent
    {
        public string Text { get; set; } = string.Empty;
    }

    public class ShapeDescriptorContentFill
    {
        public string? Color { get; set; }
    }

    public class ConnectionDescriptor
    {
        public string Id { get; set; } = Guid.NewGuid().ToString();

        public ConnectionDescriptorFromTo From { get; set; } = new();
        public ConnectionDescriptorFromTo To { get; set; } = new();
    }

    public class ConnectionDescriptorFromTo
    {
        public string ShapeId { get; set; } = string.Empty;
    }
}

Regards,
Dimo
Progress Telerik

Love the Telerik and Kendo UI products and believe more people should try them? Invite a fellow developer to become a Progress customer and each of you can get a $50 Amazon gift voucher.

Tags
Diagram
Asked by
Scott
Top achievements
Rank 1
Answers by
Dimo
Telerik team
Share this question
or