Binding problem with RadCartesianChart

1 Answer 220 Views
Chart
Tinus
Top achievements
Rank 2
Tinus asked on 29 Apr 2022, 04:14 PM

Hi,

I'm working on a MAUI application for Android (VS 2022 17.2 Update 5, Telerik Maui 0.8).

I'm trying to use a RadCartesianChart. The data I'm binding uses child objects and that throws an NullReferenceExeption on Android but works fine on Windows. I'm not sure if this a MAUI issue or a Telerik issue.

To replicate, create a new MAUI app. Then replace MainPage.xaml with this code (slightly modified from your documentation):

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:telerikDataControls="clr-namespace:Telerik.XamarinForms.DataControls;assembly=Telerik.Maui.Controls.Compatibility"
             xmlns:telerikChart="clr-namespace:Telerik.XamarinForms.Chart;assembly=Telerik.Maui.Controls.Compatibility"
             xmlns:telerikListView="clr-namespace:Telerik.XamarinForms.DataControls.ListView;assembly=Telerik.Maui.Controls.Compatibility"
             x:Class="MauiApp1.MainPage">

    <telerikChart:RadCartesianChart MinimumHeightRequest="300">

        <telerikChart:RadCartesianChart.HorizontalAxis>
            <telerikChart:NumericalAxis LabelFitMode="MultiLine" />
        </telerikChart:RadCartesianChart.HorizontalAxis>

        <telerikChart:RadCartesianChart.VerticalAxis>
            <telerikChart:NumericalAxis />
        </telerikChart:RadCartesianChart.VerticalAxis>

        <telerikChart:RadCartesianChart.Series>
            <telerikChart:ScatterSplineSeries XValueBinding="NumericalData.XData" YValueBinding="NumericalData.YData" ItemsSource="{Binding Data1}" />

            <telerikChart:ScatterSplineSeries XValueBinding="XData" YValueBinding="YData" ItemsSource="{Binding Data2}" />
        </telerikChart:RadCartesianChart.Series>
    </telerikChart:RadCartesianChart>
</ContentPage>

And MainPage.xaml.cs with this code:

namespace MauiApp1;

using System.Collections.ObjectModel;

public partial class MainPage : ContentPage
{
	public MainPage()
	{
		InitializeComponent();
		Data1 = GetNumericData1();
		Data2 = GetNumericData2();
		BindingContext = this;
	}

	public ObservableCollection<Item> Data1 { get; set; }
	public ObservableCollection<NumericalData> Data2 { get; set; }


	public static ObservableCollection<Item> GetNumericData1()
	{
		var data = new ObservableCollection<Item>
	{
		new Item { NumericalData = new NumericalData { XData = 2, YData = 13 } },
		new Item { NumericalData = new NumericalData { XData = 19, YData = 31 } },
		new Item { NumericalData = new NumericalData { XData = 22, YData = 33 } },
		new Item { NumericalData = new NumericalData { XData = 28, YData = 35 } },
		new Item { NumericalData = new NumericalData { XData = 33, YData = 46 } },
		new Item { NumericalData = new NumericalData { XData = 38, YData = 34 } },
		new Item { NumericalData = new NumericalData { XData = 49, YData = 66 } },
		new Item { NumericalData = new NumericalData { XData = 55, YData = 24 } },
		new Item { NumericalData = new NumericalData { XData = 62, YData = 41 } },
	};
		return data;
	}
	public static ObservableCollection<NumericalData> GetNumericData2()
	{
		var data = new ObservableCollection<NumericalData>
	{
		new NumericalData { XData = 7, YData = 13 },
		new NumericalData { XData = 19, YData = 17 },
		new NumericalData { XData = 22, YData = 19 },
		new NumericalData { XData = 28, YData = 21 },
		new NumericalData { XData = 33, YData = 35 },
		new NumericalData { XData = 38, YData = 43 },
		new NumericalData { XData = 49, YData = 15 },
		new NumericalData { XData = 55, YData = 21 },
		new NumericalData { XData = 62, YData = 47 },
	};
		return data;
	}
}


public class NumericalData
{
	public double XData { get; set; }
	public double YData { get; set; }
}

public class Item
{
	public NumericalData NumericalData { get; set; }
}

 

Regards
Martin

Lance | Manager Technical Support
Telerik team
commented on 29 Apr 2022, 04:26 PM

Thank you for the repo code, I'm investigating now. While I do, can you try a quick test that adds a delay to the loading of data until the view is done? For example, something like this instead of using the page constructor

async override OnAppearing()
{
    base.OnAppearing();
    
    await Task.Delay(1000);

    Data1 = GetNumericData1();
	Data2 = GetNumericData2();
	BindingContext = this;
}

I have this suspicion because App.Current.Main sometimes isn't ready for Content interaction until 200-300 milliseconds after on appearing is not ready (this is something that has affecting our Popup control and threw the same exception).

I will reply back again as soon as I've reproduced this locally and taken a closer look.

Tinus
Top achievements
Rank 2
commented on 29 Apr 2022, 04:31 PM

Hi Lance,

thanks for looking into the issue.

Does not help. I already tried a similar approach with a button click handler.

Regards
Martin

Lance | Manager Technical Support
Telerik team
commented on 29 Apr 2022, 05:12 PM

On top of my answer below, I've also opened this Feedback Portal item on your behalf. This will allow the devs to review and add it to their backlog.

1 Answer, 1 is accepted

Sort by
0
Lance | Manager Technical Support
Telerik team
answered on 29 Apr 2022, 05:01 PM

Hello Martin,

This appears to be happening because of using nested properties for value bindings. To verify this, comment out the ScatterSplineSeries that is attempting to use a nested property in the value bindings (e.g. XValueBinding="NumericalData.XData")

I do not believe this is supported on Android, but I will need to get confirmation from the development team (you get away with it on WinUI because it happenstance that it's supported by the native chart and the Xamarin.Forms shim's renderers passes that down correctly).

My workaround for right now is to add two top-level, read-only, properties that can be bound to:

public class Item
{
    // I changed the property name so that it is not the same as the type name
    public NumericalData NumData { get; set; }

    // Two top-level, read-only, properties so they can be databound using reflection
    public double XData => this.NumData.XData;
    public double YData => this.NumData.YData;
}

Then, update the property name bindings accordingly:

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:telerikChart="clr-namespace:Telerik.XamarinForms.Chart;assembly=Telerik.Maui.Controls.Compatibility"
             x:Class="RC2Tests.MainPage">

    <telerikChart:RadCartesianChart MinimumHeightRequest="300">
        <telerikChart:RadCartesianChart.HorizontalAxis>
            <telerikChart:NumericalAxis LabelFitMode="MultiLine" />
        </telerikChart:RadCartesianChart.HorizontalAxis>

        <telerikChart:RadCartesianChart.VerticalAxis>
            <telerikChart:NumericalAxis />
        </telerikChart:RadCartesianChart.VerticalAxis>

        <telerikChart:RadCartesianChart.Series>
            <telerikChart:ScatterSplineSeries XValueBinding="XData"
                                              YValueBinding="YData"
                                              ItemsSource="{Binding Data1}" />

            <telerikChart:ScatterSplineSeries XValueBinding="XData"
                                              YValueBinding="YData"
                                              ItemsSource="{Binding Data2}" />
        </telerikChart:RadCartesianChart.Series>
    </telerikChart:RadCartesianChart>
</ContentPage>

 

Here's the result at runtime, and I've attached the test project:

Regards,
Lance | Manager Technical Support
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.

Tinus
Top achievements
Rank 2
commented on 02 May 2022, 07:35 AM

Hi Lance,

thanks for looking into the issue. I know that the nested properties trigger the issue, that's why I made a simple repro.

I expect that binding to nested properties should work on Android. I have MAUI labels bound to nestest properties and that's working on Android.

I tried the suggested workaround and that seems to work.

Coming from a native WinUI3 app with Telerik controls I noticed two differences which affect my app:

1. RadCartesianChart can only have one x and y axis, while RadChart on WinuI3 supports multiple axes. Can you please add a feature request for multiple axes? Thanks.

2. I want my user to select the series he wants to see. On WinUI3, RadChartSeries have a Visibility property which I use to enable/disable the series. On MAUI this seems to be missing. 

Thanks
Martin

 

 

Tinus
Top achievements
Rank 2
commented on 02 May 2022, 12:45 PM

Hi Lance,

I tried two approaches to show/hide series dynamically, both failed.

1. Change Stroke to Color.Transparent. Problem: changing Stroke property dynamically is not reflected at runtime, color does not change.

2. Change StrokeThickness to 0. Works on Windows but not on Android. There's still a very thin line visible.

Do you have any other idea/workaround to make that work?

Thanks
Martin

Lance | Manager Technical Support
Telerik team
commented on 03 May 2022, 03:48 PM

Hi Martin,

I've escalated this to the development team, they're reviewing it over the next few days. Just so that you're aware, it is a holiday week in Sofia, so it may take several days to get a confirmation.

I will do what I can to get you options in those items in the mean time. For now, I do have one recommendation. Don't try to play with visibility. Instead, use the Series collection to dynamically show and hide series (i.e. use Series.Add and Series.Remove). This is the only way reliably have the downstream charts handle their respective series collections and perform all necessary axis recalculations. This is because of the collection changed notification, each platform's renderer takes the necessary steps to handle that in the plot area.

Research tip - The charts you are using right now come from our Telerik UI for Xamarin controls (they're renderers rather than native MAUI handlers). This means you can search the many years of conversations in the UI for Xamarin forums for the same questions, and use the same code in answers in the MAUI project.

Tinus
Top achievements
Rank 2
commented on 03 May 2022, 03:56 PM

Hi Lance,

thanks for the tips, will give them a try.

Regards
Martin

Tags
Chart
Asked by
Tinus
Top achievements
Rank 2
Answers by
Lance | Manager Technical Support
Telerik team
Share this question
or