0
votes

I want to display a large collection of points as a chart (at least 300 000 points), using wpf toolkit chart.

I have the following XAML

<chartingToolkit:Chart Name="chartHistory">
    <chartingToolkit:Chart.Axes>
        <chartingToolkit:LinearAxis x:Name="horizontalAxis" Orientation="X" Title="Time [s]"  ShowGridLines="True"/>
        <chartingToolkit:LinearAxis x:Name="verticalAxis" Orientation="Y" Title="Value [mm]"  ShowGridLines="True"/>
    </chartingToolkit:Chart.Axes>
    <chartingToolkit:AreaSeries x:Name="chartSeries" DataPointStyle="{StaticResource chartDataPoint}"
        IndependentValuePath="TimeInSeconds"
        DependentValuePath="Value">
    </chartingToolkit:AreaSeries>
</chartingToolkit:Chart>

And in code behind:

public class PointData
{
    public double TimeInSeconds { get; set; }
    public double Value { get; set; }
}

private List<PointData> points;

private void Screen_Loaded(object sender, RoutedEventArgs e)
{
    // this is a large collection of points (minimum 300 000)
    points = LoadPointsFromFile();

    // and it takes a lot of time to read from the file and load in the UI
    chartSeries.ItemsSource = points;

    // additional chart display properties (setting min/max on the axes etc.)
}

So, I have 2 time consuming operations that block my UI. What I want is to display a "please load dialog" while the time consuming operations take place, so that the user knows the application is still doing something.

The time consuming operations are:

  1. reading the points from the file (this operation could be done on a separate thread, but since the next operation (loading the points in the chart) depends on it and is a UI operation, I didn't put it in a separate thread)
  2. loading the points as ItemsSource in the chart - this is a UI operation and should be done on the UI thread. But how can I still make the application responsive since I do not have any control on how the points are displayed - this is the chart's own logic?

So, any ideas? Did you have any similar problems? Thank you, Nadia

1

1 Answers

0
votes

Actually, what I did was to create a separate thread that displays a different "loading dialog" that shows up while the data is loading. There still is about one second from the moment the dialog is closed until the UI gets fully responsive, but it's still better than looking at a unresponsive UI for 5-10 seconds.

public class PointData
{
    public double TimeInSeconds { get; set; }
    public double Value { get; set; }
}

#region Wait Dialog Thread
private class MainWindowSize
{
    public double Left;
    public double Top;
    public double Width;
    public double Height;
}

private Thread newWindowThread = null;

private void ThreadStartingPoint(object obj)
{
    // WaitDialog is a new window from your project - you can place a animation or message inside it
    WaitDialog tempWindow = new WaitDialog();

    // since we don't have an owner for the window, we need
    // to compute the location of the popup dialog, so that
    // its centered inside the main window
    MainWindowSize wndSize = obj as MainWindowSize;
    if (wndSize != null)
    {
        tempWindow.Left = wndSize.Left + wndSize.Width / 2 - tempWindow.Width / 2;
        tempWindow.Top = wndSize.Top + wndSize.Height / 2 - tempWindow.Height / 2;
    }

    // it's very important not to set the owner of this dialog
    // otherwise it won't work in a separate thread
    tempWindow.Owner = null;

    // it shouldn't be a modal dialog
    tempWindow.Show();
    tempWindow.Closed += (sender1, e1) => tempWindow.Dispatcher.InvokeShutdown();
    System.Windows.Threading.Dispatcher.Run();
}

private void CreateAndStartWaitWindowThread()
{
    // create a thread only if you don't have an active one 
    if (newWindowThread == null)
    {
        // use ParameterizedThreadStart instead of ThreadStart
        // in order to send a parameter to the thread start method
        newWindowThread = new Thread(new ParameterizedThreadStart(ThreadStartingPoint));
        newWindowThread.SetApartmentState(ApartmentState.STA);
        newWindowThread.IsBackground = true;

        // get the properties of the window, in order to compute the location of the new dialog
        Window mainWnd = App.CurrentApp.MainWindow;     
        MainWindowSize threadParams = new MainWindowSize { Left = mainWnd.Left, Top = mainWnd.Top, Width = mainWnd.ActualWidth, Height = mainWnd.ActualHeight };

        // start thread with parameters
        newWindowThread.Start(threadParams);
    }
}

private void AbortAndDeleteWaitWindowThread()
{
    // abort a thread only if you have an active one 
    if (newWindowThread != null)
    {
        newWindowThread.Abort();
        newWindowThread = null;
    }
}
#endregion

private List<PointData> points;

private void Screen_Loaded(object sender, RoutedEventArgs e)
{
    try
    {
        // call this before long operation
        this.Cursor = Cursors.Wait;
        CreateAndStartWaitWindowThread();

        // this is a large collection of points (minimum 300 000)
        points = LoadPointsFromFile();

        // and it takes a lot of time to read from the file and load in the UI
        chartSeries.ItemsSource = points;

        // additional chart display properties (setting min/max on the axes etc.)
    }
    catch(Exception ex)
    {
        // do something with the exception
    }
    finally
    {
        // call this after long operation - and make sure it's getting called
        // so put it in the finally block - to call it even if an exception is raised
        this.Cursor = Cursors.Arrow;
        AbortAndDeleteWaitWindowThread();
    }
}

Source: http://www.c-sharpcorner.com/uploadfile/suchit_84/creating-wpf-windows-on-dedicated-threads/