Mick Posted December 29, 2009 Report Share Posted December 29, 2009 I created a chart and a button hooked up to an event that would repopulate the chart with random data. The entire chart randomization takes place in the code behind. Depending on the speed of the machine I was running on and how fast I clicked this button, the series for the chart would occasionally end up empty. I am not clearing the series manually, so I'm wondering if there's anything going on behind the scenes in the render pass that isn't thread safe (i.e. a thread that clears the series is executing AFTER a thread that sets the data). I also have a sample project that recreates this that's 26 lines of .xaml and 92 lines of .xaml.cs. Would this be the best place to post it? Quote Link to comment Share on other sites More sharing options...
JuanC Posted December 29, 2009 Report Share Posted December 29, 2009 This would be the best place to post a sample. It would also be helpful if you could describe the final behavior you are looking for related to the chart. JuanC Quote Link to comment Share on other sites More sharing options...
Mick Posted December 29, 2009 Author Report Share Posted December 29, 2009 The following was built with ChartFX version 8.0.3309.21029. Here is my sample project. It has 3 buttons and comments throughout the code: The first button generates a new random chart The second button generates data for 1000 new charts, but reuses the same chart control. The third button checks the status of the series for the chart that is currently shown. To reproduce the error: Click the first button to generate a single chart Click the third button to verify that the chart has 2 series Click the second button to generate 1000 charts worth of data.On my computer, the chart shows the legend but no data Click the third button to verify that the chart has dataThe chart should have 2 series, but it does not, so I throw an error Desired behavior: series does not get cleared. ----- XAML: ----- <Window x:Class="ChartFX.Window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:cfx="clr-namespace:ChartFX.WPF;assembly=ChartFX.WPF" Title="Window1" Height="600" Width="800" > <Grid> <Grid.ColumnDefinitions> <ColumnDefinition /> <ColumnDefinition /> <ColumnDefinition /> </Grid.ColumnDefinitions> <Grid.RowDefinitions> <RowDefinition Height="19*" /> <RowDefinition /> </Grid.RowDefinitions> <cfx:Chart x:Name="myChart" Grid.Row="0" Grid.ColumnSpan="3" /> <Button Grid.Row="1" Content="Generate 1 chart" Margin="2" Click="Button1_Click" /> <Button Grid.Row="1" Grid.Column="1" Content="Generate 1000 charts" Margin="2" Click="Button2_Click" /> <Button Grid.Row="1" Grid.Column="2" Margin="2" Content="Verify chart has 2 series" Click="Button3_Click" /> </Grid></Window> ----- Code: ----- using System;using System.Windows;using ChartFX.WPF;namespace ChartFX{ // Note: I have put locks in my code to make sure that I am not causing the undesired clearing of the series public partial class Window1 : Window { private object LOCKOBJECT = new object(); public Window1() { InitializeComponent(); } /// <summary> /// Randomize the chart /// </summary> private void RandomizeChart() { myChart.Series.Clear(); label_series_cleared: // Create the series and add them to the chart SeriesAttributes sa1 = new SeriesAttributes { Gallery = Gallery.Bar }; SeriesAttributes sa2 = new SeriesAttributes { Gallery = Gallery.Line }; myChart.Series.Add(sa1); myChart.Series.Add(sa2); Random random = new Random(); // Set the data for the two series for (int i = 0; i < 10; i++) { myChart.Data[0, i] = random.Next(1, 25); myChart.Data[1, i] = random.Next(1, 25); } } /// <summary> /// Check that the chart has series - this should never fail because I only call myChart.Series.Clear() once and immediately repopulate the series (relevant code is locked to be 100% sure) /// </summary> private void Verify2SeriesExist() { // Lock this check to make sure that it isn't interfering with our chart generation // - i.e. the code at location "label_series_cleared" can NEVER be called until the code at "label_1000_charts" is COMPLETE lock (LOCKOBJECT) { if (myChart.Data.Series == 0) { // This should never happen, but it does for some reason throw new ApplicationException("Series collection has been cleared unexpectedly."); } } } /// <summary> /// Randomize the chart once - there is USUALLY not a problem here, but myChart.Series is randomly cleared (between 1/10 - 1-100 times in my experience) /// </summary> private void Button1_Click(object sender, RoutedEventArgs e) { RandomizeChart(); } /// <summary> /// Randomize the chart 1000 times - this reliably creates a problem for me where the series is cleared before the chart renders (creating an empty chart that has legend entries but no visible data) /// </summary> private void Button2_Click(object sender, RoutedEventArgs e) { label_1000_charts: // Lock the generation of all of the charts lock (LOCKOBJECT) { for (int i = 0; i < 1000; i++) RandomizeChart(); // Verify that we have 2 series immediately after our charts are generated Verify2SeriesExist(); } } /// <summary> /// Check the status of the series collection manually /// </summary> private void Button3_Click(object sender, RoutedEventArgs e) { Verify2SeriesExist(); } }} ---------------------------- Thank you, Mick AjaxTelerikChartFX.zip Quote Link to comment Share on other sites More sharing options...
Mick Posted January 8, 2010 Author Report Share Posted January 8, 2010 Is this issue reproducible with the posted sample (I'm running 8.0.3309.21029)? Thank you, Mick Quote Link to comment Share on other sites More sharing options...
JuanC Posted January 8, 2010 Report Share Posted January 8, 2010 Yes, we were able to reproduce the issue on our latest build, there is a workaround which involves using binding instead of passing data manually, e.g. in RandomizeChart // Set the data for the two series /* for (int i = 0; i < 10; i++) { chart1.Data[0, i] = random.Next(1, 25); chart1.Data[1, i] = random.Next(1, 25); } */ List<MyData> data = new List<MyData>(); for (int i = 0; i < 10; i++) { int n0 = random.Next(1, 25); int n1 = random.Next(1, 25); data.Add(new MyData() { V0 = n0, V1 = n1 }); } sa1.BindingPath = "V0"; sa2.BindingPath = "V1"; chart1.ItemsSource = data; And this class for your data internal class MyData { public double V0 { get; set; } public double V1 { get; set; } } I am still unsure of what scenario you are trying to test, one thing about this workaround, because of the way we process the data, even though you are setting the ItemsSource collection in a loop that executes one thousand times, we will only process the data once as we delay this for performance reasons JuanC Quote Link to comment Share on other sites More sharing options...
Mick Posted January 8, 2010 Author Report Share Posted January 8, 2010 Thank you, I am not actually trying to generate 1000 charts in rapid succession - I only made that example to pinpoint my issue. I have a screen where I dynamically add/remove charts based on available screen real estate (i.e. "is there room for 1 chart, 2 charts, ... n charts?"). I do this processing in a separate thread that I created - meaning that my thread and the ChartFX rendering thread (or whatever thread was clearing the series in the example) need to talk to each other. The sample posted is a result of my narrowing down the scope of the issue to the ChartFX render routine (after single-threading all of my code). Thank you, Mick Quote Link to comment Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.