RadzenChart creates deadlock #222

Closed
opened 2026-01-29 17:33:38 +00:00 by claunia · 6 comments
Owner

Originally created by @MarvinKlein1508 on GitHub (Oct 6, 2021).

Describe the bug
The RadzenChart will create a deadlock OnMouseOver when you have not specified Tooltips or disabled them.

To Reproduce
Consider this example code:

<RadzenChart Style="width: 100%"
            MouseEnter="@(async () => { await  jsRuntime.ShowToast(ToastType.warning, "Test Enter"); })"
            MouseLeave="@(async () => { await  jsRuntime.ShowToast(ToastType.warning, "Test Leave"); })">
    @foreach (KeyValuePair<int, Jahresumsatz> umsatz in Verbrauchsdaten)
    {
        <RadzenLineSeries Smooth="true" Data="@umsatz.Value.ToDataItem(umsatz.Key)" Title="@String.Format(LabelText, umsatz.Key)" CategoryProperty="Month" LineType="LineType.Solid" ValueProperty="Revenue">
            <RadzenMarkers MarkerType="MarkerType.Square" />
        </RadzenLineSeries>
    }
    <Radzen.Blazor.RadzenCategoryAxis FormatString="{0:MMM}" Step="1" Formatter="@FormatAsMonth" />
    <RadzenValueAxis FormatString="@ValueFormat">
        <RadzenGridLines Visible="true" />
        <RadzenAxisTitle />
    </RadzenValueAxis>
</RadzenChart>

This chart shows revenue on a monthly base. When I trigger the MouseOver event, the custom EventCallback is being executed just fine. After it has finished the application is frozen. When I add this line to my RadzenChart
<RadzenChartTooltipOptions Visible="false" /> everything works as expected then.

Desktop (please complete the following information):

  • OS: Windows 10 Pro 21H1
  • Tested in Chrome and Firefox
  • All up-to-date

Additional context
This can be fixed by reloading the entire webpage. However once you mouse with the mouse over the chart again, it results in another deadlock.

Originally created by @MarvinKlein1508 on GitHub (Oct 6, 2021). **Describe the bug** The RadzenChart will create a deadlock OnMouseOver when you have not specified Tooltips or disabled them. **To Reproduce** Consider this example code: ``` <RadzenChart Style="width: 100%" MouseEnter="@(async () => { await jsRuntime.ShowToast(ToastType.warning, "Test Enter"); })" MouseLeave="@(async () => { await jsRuntime.ShowToast(ToastType.warning, "Test Leave"); })"> @foreach (KeyValuePair<int, Jahresumsatz> umsatz in Verbrauchsdaten) { <RadzenLineSeries Smooth="true" Data="@umsatz.Value.ToDataItem(umsatz.Key)" Title="@String.Format(LabelText, umsatz.Key)" CategoryProperty="Month" LineType="LineType.Solid" ValueProperty="Revenue"> <RadzenMarkers MarkerType="MarkerType.Square" /> </RadzenLineSeries> } <Radzen.Blazor.RadzenCategoryAxis FormatString="{0:MMM}" Step="1" Formatter="@FormatAsMonth" /> <RadzenValueAxis FormatString="@ValueFormat"> <RadzenGridLines Visible="true" /> <RadzenAxisTitle /> </RadzenValueAxis> </RadzenChart> ``` This chart shows revenue on a monthly base. When I trigger the MouseOver event, the custom EventCallback is being executed just fine. After it has finished the application is frozen. When I add this line to my RadzenChart `<RadzenChartTooltipOptions Visible="false" />` everything works as expected then. **Desktop (please complete the following information):** - OS: Windows 10 Pro 21H1 - Tested in Chrome and Firefox - All up-to-date **Additional context** This can be fixed by reloading the entire webpage. However once you mouse with the mouse over the chart again, it results in another deadlock.
Author
Owner

@akorchev commented on GitHub (Oct 7, 2021):

I don't understand what the problem is based on the provided information. Can you provide a reproduction?

@akorchev commented on GitHub (Oct 7, 2021): I don't understand what the problem is based on the provided information. Can you provide a reproduction?
Author
Owner

@brcaswell commented on GitHub (Oct 9, 2021):

I attempted to reproduce this in LineChartPage.razor in Demo -- replacing jsRuntime.ShowToast with dialog.Confirm. I cannot reproduce this.

@inject DialogService dialog
    <RadzenChart Style="width: 100%"
            MouseEnter="@(async () => { await dialog.Confirm("Test Enter"); })"
            MouseLeave="@(async () => { await dialog.Confirm("Test Leave"); })">
        <RadzenLineSeries Smooth="@smooth" Data="@revenue2019" CategoryProperty="Date" Title="2019" LineType="LineType.Dashed" ValueProperty="Revenue">
            <RadzenMarkers MarkerType="MarkerType.Square" />
        </RadzenLineSeries>
        <RadzenLineSeries Smooth="@smooth" Data="@revenue2020" CategoryProperty="Date" Title="2020" ValueProperty="Revenue">
            <RadzenMarkers MarkerType="MarkerType.Circle" />
        </RadzenLineSeries>
        <Radzen.Blazor.RadzenCategoryAxis FormatString="{0:MMM}" Step="1" />
        <RadzenValueAxis Formatter="@FormatAsUSD">
            <RadzenGridLines Visible="true" />
            <RadzenAxisTitle />
        </RadzenValueAxis>
    </RadzenChart>

As such, the issue you're observing seems to relate to jsRuntime.ShowToast usage.

Note: there is no ShowToast on IJSRuntime, so I'm not exactly sure what jsRuntime type is here -- and how this ShowToast method is defined. Is it an extension on IJSRuntime that is calling to a custom ToastService?

@brcaswell commented on GitHub (Oct 9, 2021): I attempted to reproduce this in `LineChartPage.razor` in Demo -- replacing `jsRuntime.ShowToast` with `dialog.Confirm`. I cannot reproduce this. ``` razor @inject DialogService dialog ``` ``` razor <RadzenChart Style="width: 100%" MouseEnter="@(async () => { await dialog.Confirm("Test Enter"); })" MouseLeave="@(async () => { await dialog.Confirm("Test Leave"); })"> <RadzenLineSeries Smooth="@smooth" Data="@revenue2019" CategoryProperty="Date" Title="2019" LineType="LineType.Dashed" ValueProperty="Revenue"> <RadzenMarkers MarkerType="MarkerType.Square" /> </RadzenLineSeries> <RadzenLineSeries Smooth="@smooth" Data="@revenue2020" CategoryProperty="Date" Title="2020" ValueProperty="Revenue"> <RadzenMarkers MarkerType="MarkerType.Circle" /> </RadzenLineSeries> <Radzen.Blazor.RadzenCategoryAxis FormatString="{0:MMM}" Step="1" /> <RadzenValueAxis Formatter="@FormatAsUSD"> <RadzenGridLines Visible="true" /> <RadzenAxisTitle /> </RadzenValueAxis> </RadzenChart> ``` As such, the issue you're observing seems to relate to `jsRuntime.ShowToast` usage. Note: there is no `ShowToast` on `IJSRuntime`, so I'm not exactly sure what `jsRuntime` type is here -- and how this `ShowToast` method is defined. Is it an extension on `IJSRuntime` that is calling to a custom `ToastService`?
Author
Owner

@MarvinKlein1508 commented on GitHub (Oct 12, 2021):

As such, the issue you're observing seems to relate to jsRuntime.ShowToast usage.
No I just used this extension method to test the MouseEvents.

Is it an extension on IJSRuntime that is calling to a custom ToastService?
Yes. I tjust creates a new toastr toast via JS.

I think it has somethine todo with my foreach within the RadzenChart component. The class Jahresumsatz is defined as:

public class Jahresumsatz
    {
        private readonly Dictionary<int, decimal> _umsatzWerte;

        public int Jahr { get; set; }
        public decimal Januar => GetValueAt(1);
        public decimal Februar => GetValueAt(2);
        public decimal März => GetValueAt(3);
        public decimal April => GetValueAt(4);
        public decimal Mai => GetValueAt(5);
        public decimal Juni => GetValueAt(6);
        public decimal Juli => GetValueAt(7);
        public decimal August => GetValueAt(8);
        public decimal September => GetValueAt(9);
        public decimal Oktober => GetValueAt(10);
        public decimal November => GetValueAt(11);
        public decimal Dezember => GetValueAt(12);

        public decimal Total => Januar + Februar + März + April + Mai + Juni + Juli + August + September + Oktober + November + Dezember;

        private decimal this[int i]
        {
            get
            {
                return GetValueAt(i);
            }
        }

        public decimal GetValueAt(int index)
        {
            if (_umsatzWerte.ContainsKey(index))
            {
                return _umsatzWerte[index];
            }
            return 0.0m;
        }

        private void SetValueAt(int index, decimal value)
        {
            if (_umsatzWerte.ContainsKey(index))
            {
                _umsatzWerte[index] = value;
            }
            else
            {
                _umsatzWerte.Add(index, value);
            }
        }

        public Jahresumsatz(Dictionary<int, decimal> werte)
        {
            _umsatzWerte = werte;
        }

        private Jahresumsatz()
        {
            _umsatzWerte = new Dictionary<int, decimal>();
        }

        public static Jahresumsatz FromList(List<Jahresumsatz> umsätze)
        {
            Jahresumsatz tmp = new Jahresumsatz();
            foreach (var umsatz in umsätze)
            {
                tmp += umsatz;
            }
            return tmp;
        }

        public IEnumerable<decimal> ToIEnumerable()
        {
            List<decimal> values = new List<decimal>();
            for (int i = 1; i <= 12; i++)
            {
                values.Add(GetValueAt(i));
            }
            return values.AsEnumerable();
        }
        public List<JahresumsatzDataItem> ToDataItem(int year)
        {
            List<JahresumsatzDataItem> data = new List<JahresumsatzDataItem>();
            for (int i = 1; i <= 12; i++)
            {
                data.Add(new JahresumsatzDataItem
                {
                    Month = i,
                    Revenue = GetValueAt(i)
                });
            }
            return data;
        }

        public static Jahresumsatz operator +(Jahresumsatz a, Jahresumsatz b)
        {
            Jahresumsatz tmp = new Jahresumsatz() { Jahr = a.Jahr == b.Jahr ? a.Jahr : 0 };
            for (int i = 1; i <= 12; i++)
            {
                tmp.SetValueAt(i, a.GetValueAt(i) + b.GetValueAt(i));
            }
            return tmp;
        }

        public static Jahresumsatz operator -(Jahresumsatz a, Jahresumsatz b)
        {
            Jahresumsatz tmp = new Jahresumsatz() { Jahr = a.Jahr == b.Jahr ? a.Jahr : 0 };
            for (int i = 1; i <= 12; i++)
            {
                tmp.SetValueAt(i, a.GetValueAt(i) - b.GetValueAt(i));
            }
            return tmp;
        }
    }

    public class JahresumsatzDataItem
    {
        public int Month { get; set; }
        public decimal Revenue { get; set; }
    }

i = 1 is the revenue for January
i = 2 is the revenue for February and so on.

Dictionary<int, Jahresumsatz> Verbrauchsdaten holds the data for the last 4 years. Which gets casted into a JahresumsatzDataItem which is being used by the RadzenChart.

@foreach (KeyValuePair<int, Jahresumsatz> umsatz in Verbrauchsdaten)
                {
                    <RadzenLineSeries Smooth="true" Data="@umsatz.Value.ToDataItem(umsatz.Key)" Title="@String.Format(LabelText, umsatz.Key)" CategoryProperty="Month" LineType="LineType.Solid" ValueProperty="Revenue">
                        <RadzenMarkers MarkerType="MarkerType.Square" />
                    </RadzenLineSeries>
                }
@MarvinKlein1508 commented on GitHub (Oct 12, 2021): `As such, the issue you're observing seems to relate to jsRuntime.ShowToast usage.` No I just used this extension method to test the MouseEvents. ` Is it an extension on IJSRuntime that is calling to a custom ToastService?` Yes. I tjust creates a new toastr toast via JS. I think it has somethine todo with my foreach within the RadzenChart component. The class `Jahresumsatz` is defined as: ``` public class Jahresumsatz { private readonly Dictionary<int, decimal> _umsatzWerte; public int Jahr { get; set; } public decimal Januar => GetValueAt(1); public decimal Februar => GetValueAt(2); public decimal März => GetValueAt(3); public decimal April => GetValueAt(4); public decimal Mai => GetValueAt(5); public decimal Juni => GetValueAt(6); public decimal Juli => GetValueAt(7); public decimal August => GetValueAt(8); public decimal September => GetValueAt(9); public decimal Oktober => GetValueAt(10); public decimal November => GetValueAt(11); public decimal Dezember => GetValueAt(12); public decimal Total => Januar + Februar + März + April + Mai + Juni + Juli + August + September + Oktober + November + Dezember; private decimal this[int i] { get { return GetValueAt(i); } } public decimal GetValueAt(int index) { if (_umsatzWerte.ContainsKey(index)) { return _umsatzWerte[index]; } return 0.0m; } private void SetValueAt(int index, decimal value) { if (_umsatzWerte.ContainsKey(index)) { _umsatzWerte[index] = value; } else { _umsatzWerte.Add(index, value); } } public Jahresumsatz(Dictionary<int, decimal> werte) { _umsatzWerte = werte; } private Jahresumsatz() { _umsatzWerte = new Dictionary<int, decimal>(); } public static Jahresumsatz FromList(List<Jahresumsatz> umsätze) { Jahresumsatz tmp = new Jahresumsatz(); foreach (var umsatz in umsätze) { tmp += umsatz; } return tmp; } public IEnumerable<decimal> ToIEnumerable() { List<decimal> values = new List<decimal>(); for (int i = 1; i <= 12; i++) { values.Add(GetValueAt(i)); } return values.AsEnumerable(); } public List<JahresumsatzDataItem> ToDataItem(int year) { List<JahresumsatzDataItem> data = new List<JahresumsatzDataItem>(); for (int i = 1; i <= 12; i++) { data.Add(new JahresumsatzDataItem { Month = i, Revenue = GetValueAt(i) }); } return data; } public static Jahresumsatz operator +(Jahresumsatz a, Jahresumsatz b) { Jahresumsatz tmp = new Jahresumsatz() { Jahr = a.Jahr == b.Jahr ? a.Jahr : 0 }; for (int i = 1; i <= 12; i++) { tmp.SetValueAt(i, a.GetValueAt(i) + b.GetValueAt(i)); } return tmp; } public static Jahresumsatz operator -(Jahresumsatz a, Jahresumsatz b) { Jahresumsatz tmp = new Jahresumsatz() { Jahr = a.Jahr == b.Jahr ? a.Jahr : 0 }; for (int i = 1; i <= 12; i++) { tmp.SetValueAt(i, a.GetValueAt(i) - b.GetValueAt(i)); } return tmp; } } public class JahresumsatzDataItem { public int Month { get; set; } public decimal Revenue { get; set; } } ``` i = 1 is the revenue for January i = 2 is the revenue for February and so on. `Dictionary<int, Jahresumsatz> Verbrauchsdaten` holds the data for the last 4 years. Which gets casted into a `JahresumsatzDataItem` which is being used by the RadzenChart. ``` @foreach (KeyValuePair<int, Jahresumsatz> umsatz in Verbrauchsdaten) { <RadzenLineSeries Smooth="true" Data="@umsatz.Value.ToDataItem(umsatz.Key)" Title="@String.Format(LabelText, umsatz.Key)" CategoryProperty="Month" LineType="LineType.Solid" ValueProperty="Revenue"> <RadzenMarkers MarkerType="MarkerType.Square" /> </RadzenLineSeries> } ```
Author
Owner

@brcaswell commented on GitHub (Oct 13, 2021):

I am able to reproduce using this class you provided.

    Dictionary<int, Jahresumsatz> Verbrauchsdaten => new Dictionary<int, Jahresumsatz>
    {
        { 1, new LineChartPage.Jahresumsatz(new Dictionary<int, decimal> { {1, 0.1m }, {2, 0.5m } }) },
        { 2, new LineChartPage.Jahresumsatz(new Dictionary<int, decimal> { {1, 0.4m }, {2, 1.5m } }) },
        { 3, new LineChartPage.Jahresumsatz(new Dictionary<int, decimal> { {1, 0.2m }, {2, 1.3m } }) },
        { 4, new LineChartPage.Jahresumsatz(new Dictionary<int, decimal> { {1, 0.7m }, {2, 2.1m } }) }
    };

Though I'm uncertain as to the underline cause, it seems there is an infinite loop created with CartesianSeries<TItem> conditional call to Chart.DisplayTooltip() in SetParametersAsync.

Commenting out that Chart.DisplayTooltip() in that scope resolves properly and displays the tooltip.

@akorchev from git history I can't determine the merit to that line to call DisplayTooltip -- The file changes were all introduced in a broader push. Note: MouseMove event calls to DisplayTooltip in this scenario/control.


note: that I wasn't able to reproduce this based simply on this being a foreach.

I foreach looped over this property with no issues (binding where applicable)

    IEnumerable<(string Title, MarkerType MarkerType, LineType LineType, DataItem[] DataItems)> revenues => new List<(string Title, MarkerType markerType, LineType LineType, DataItem[] DataItems)>
    {
        ("2019", MarkerType.Square, LineType.Dashed, revenue2019),
        ("2020", MarkerType.Circle, LineType.Solid, revenue2020),
    };
@brcaswell commented on GitHub (Oct 13, 2021): I **am able** to reproduce using this class you provided. ``` csharp Dictionary<int, Jahresumsatz> Verbrauchsdaten => new Dictionary<int, Jahresumsatz> { { 1, new LineChartPage.Jahresumsatz(new Dictionary<int, decimal> { {1, 0.1m }, {2, 0.5m } }) }, { 2, new LineChartPage.Jahresumsatz(new Dictionary<int, decimal> { {1, 0.4m }, {2, 1.5m } }) }, { 3, new LineChartPage.Jahresumsatz(new Dictionary<int, decimal> { {1, 0.2m }, {2, 1.3m } }) }, { 4, new LineChartPage.Jahresumsatz(new Dictionary<int, decimal> { {1, 0.7m }, {2, 2.1m } }) } }; ``` Though I'm uncertain as to the underline cause, it seems there is an infinite loop created with `CartesianSeries<TItem>` conditional call to `Chart.DisplayTooltip()` in `SetParametersAsync`. Commenting out that `Chart.DisplayTooltip()` in that scope resolves properly and displays the tooltip. @akorchev from git history I can't determine the merit to that line to call `DisplayTooltip` -- The file changes were all introduced in a broader push. Note: `MouseMove` event calls to DisplayTooltip in this scenario/control. --- note: that I wasn't able to reproduce this based simply on this being a `foreach`. I `foreach` looped over this property with no issues (binding where applicable) ``` csharp IEnumerable<(string Title, MarkerType MarkerType, LineType LineType, DataItem[] DataItems)> revenues => new List<(string Title, MarkerType markerType, LineType LineType, DataItem[] DataItems)> { ("2019", MarkerType.Square, LineType.Dashed, revenue2019), ("2020", MarkerType.Circle, LineType.Solid, revenue2020), }; ```
Author
Owner

@akorchev commented on GitHub (Oct 13, 2021):

I think this could happen if the Data of the series changes on every render. Here is a forum thread describing a similar sounding issue: https://forum.radzen.com/t/pie-chart-tool-tip-problem/8590

The tooltitp should redisplay if the Data of the series changes intentionally.

@akorchev commented on GitHub (Oct 13, 2021): I think this could happen if the Data of the series changes on every render. Here is a forum thread describing a similar sounding issue: https://forum.radzen.com/t/pie-chart-tool-tip-problem/8590 The tooltitp should redisplay if the Data of the series changes intentionally.
Author
Owner

@brcaswell commented on GitHub (Oct 13, 2021):

thanks for the insight @akorchev

in regards to

The problem is the definition of the processedData property - it returns completely new data every time it is accessed.

that does seem to be the case here as well; and as a workaround that is good advice;

However, the observed issue there and here is one that can and probably should be addressed in Razden -- as I don't think this is an issue inherent to Blazor.

EDIT: merged
I created the PR above; but if this is a matter that needs more investigation on, feel free to close/abandon it and we'll just advise using this convention until the matter can be fully determined on.

@brcaswell commented on GitHub (Oct 13, 2021): thanks for the insight @akorchev in regards to > The problem is the definition of the processedData property - it returns completely new data every time it is accessed. that does seem to be the case here as well; and as a workaround that is good advice; However, the observed issue there and here is one that can and probably should be addressed in Razden -- as I don't think this is an issue inherent to Blazor. EDIT: merged ~~I created the PR above; but if this is a matter that needs more investigation on, feel free to close/abandon it and we'll just advise using this convention until the matter can be fully determined on.~~
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: starred/radzen-blazor#222