Measurement Studio for .NET Languages

cancel
Showing results for 
Search instead for 
Did you mean: 

Wpf graph can't auto refresh

Solved!
Go to solution

Hi,all.

 

I'm learning the new WPF Graph and meet a strange problem. I put 9 plots in a WPF graph and hoped to refresh the Plots[0] at every tick, but the graph just kept in its initial state. Is there any property I have to set to let the graph keep up with the datasource or any other suggestions ?

 

I'm using Microsoft Visual Studio 2010 with Measurement Studio.

 

herr is the xaml file:

<Window x:Class="NIGraphTest.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:ni="http://schemas.ni.com/controls/2009/xaml/presentation"
        Title="MainWindow" Height="480" Width="640" >
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="400*" />
            <RowDefinition Height="41*" />
        </Grid.RowDefinitions>
        <ni:Graph  Name="m_Graph" 
              Grid.Row="0"   Grid.RowSpan="1"  Interactions="Pan, Zoom, ZoomIn, ZoomOut"  DataContext="{Binding}">
            <ni:Graph.Axes >
                <ni:AxisDouble x:Name="xAxis" Orientation="Horizontal" />
                <ni:AxisDouble x:Name="yAxis" Orientation="Vertical"  Range="0,2048"/>
            </ni:Graph.Axes>
            <ni:Graph.Plots>
                <ni:Plot Label="Plots[0]" >
                    <ni:LinePlotRenderer Stroke="Blue" />
                </ni:Plot>
                <ni:Plot Label="Plots[1]">
                    <ni:LinePlotRenderer Stroke="Green"/>
                </ni:Plot>

                <ni:Plot Label="Plot 2" />
                <ni:Plot Label="Plot 3" />
                <ni:Plot Label="Plot 4" />
                <ni:Plot Label="Plot 5" />
                <ni:Plot Label="Plot 6" />
                <ni:Plot Label="Plot 7" />
                <ni:Plot Label="Plot 8" />
            </ni:Graph.Plots>
        </ni:Graph>
        <Button Content="Button" Grid.Row="1" Name="button1" Width="75" Click="button1_Click" />
    </Grid>
</Window>

 and here is the  code behind:

namespace NIGraphTest
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        private readonly DispatcherTimer timer = new DispatcherTimer();
        Random ra = new Random();
        public MainWindow()
        {
            InitializeComponent();

            timer.Tick += OnTimerTick;
            timer.Interval = TimeSpan.FromMilliseconds(1000);
            timer.Start();

            _dataList = new ObservableCollection<Point>[9];
            int k;
            for (int i = 0; i <9; i++)
            {
                _dataList[i] = new ObservableCollection<Point>();
                for (int j = 0; j < 200; j++)
                {
                    k = i * 10 + j;
                    _dataList[i].Add(new Point(j, k));
                }
            }
            m_Graph.DataSource = _dataList;
        }

        private ObservableCollection<Point>[] _dataList;
        public ObservableCollection<Point>[] DataList
        {
            get
            {

                return this._dataList;
            }
            set
            {
                if (this._dataList != value)
                {
                    this._dataList = value;

                }
            }
        }


        void OnTimerTick(object sender, EventArgs e)
        {
            ObservableCollection<Point> data = new ObservableCollection<Point>();
            
            int i = ra.Next(8);
            int k;
            for (int j = 0; j < 200; j++)
            {
                k = i * 10 - j;
                data.Add(new Point(j, k));
            }
            _dataList[0] = data;
        }

        private void button1_Click(object sender, RoutedEventArgs e)
        {
            if (!timer.IsEnabled)
                timer.Start();
            else
                timer.Stop();
        }
    }
}

 Thanks!

 

0 Kudos
Message 1 of 5
(9,690 Views)
Solution
Accepted by darkeria

The main issue is that the data given to the graph is not changing; instead, you are creating new data and not notifying the graph to re-check its source. There are a few ways to solve this:


1) You can add a call to m_Graph.Refresh() to the end of the OnTimerTick method.
— This will tell the graph to re-examine the array assigned to its DataSource property, where it will find a new observable collection at element zero.


2) You can also change _dataList itself from an array to an observable collection, so that it will automatically notify the graph when a new data collection is replaced.
— Unlike arrays, observable collections provide a data change event that graphs can listen to, so changes to the list of data collections will be displayed automatically.


3) You can use the existing collection already referenced by the graph. To do this, remove the data = new ObservableCollection<Point>() in OnTimerTick and add the lines var data = this._dataList[i]; data.Clear(); after retrieving the index i.
— Similar to option 2, but allows you to edit individual plots without having to re-allocate a new data collection each time.




A few other notes:


  • For options 1 and 2, the data collections do not need to be observable collections, since the collections are never changed once they are initialized.
     
  • Adding plots to the Plots collection is completely optional. Auto-generated plots will automatically have their Label property initialized with a readable value like "Plot 6". Since only the first two plots in the collection have a custom renderer and the other plots are not referenced elsewhere, you could leave the other plots unspecified and the graph would behave the same.
     
  • The argument to Random.Next(int) is an exclusive upper bound, so ra.Next(8) will return a value in the range [0,7]. Since you have nine data collections in the _dataList array, you would want ra.Next(9) to cover the whole array.
     
  • The DispatcherTimer.IsEnabled property is mutable, so you can change the body of button1_Click to timer.IsEnabled = !timer.IsEnabled to toggle the timer on and off.
~ Paul H
Message 2 of 5
(9,676 Views)

Thanks for your precious reply,Paul.

 

I used solution 3 and the graph can refresh now. But another problem came out. When I changed the length of _dataList[0] from 200 to 2000 and the timer.Interval from 1000ms to 100ms, I found the refresh interval is about 1000ms. How can I improve the refresh frequency?

 

The modified code behind file is :

    public partial class MainWindow : Window
    {
        private readonly DispatcherTimer timer = new DispatcherTimer();
        Random ra = new Random();
        public MainWindow()
        {
            
            InitializeComponent();
           
            timer.Tick += OnTimerTick;
            timer.Interval = TimeSpan.FromMilliseconds(1000);
            timer.Start();

            int k;
            for (int i = 0; i <9; i++)
            {
                _dataList[i] = new ObservableCollection<Point>();
                for (int j = 0; j < 2000; j++)
                {
                     k = i * 10 + j;
                    _dataList[i].Add(new Point(j, k));
                }
            }
            m_Graph.DataSource = _dataList;

        }

        private ObservableCollection<Point>[] _dataList=new ObservableCollection<Point>[9];
        public ObservableCollection<Point>[] DataList
        {
            get
            {

                return this._dataList;
            }
            set
            {
                if (this._dataList != value)
                {
                    this._dataList = value;

                }
            }
        }


        void OnTimerTick(object sender, EventArgs e)
        {
            int i = ra.Next(9);
            var data = this._dataList[0];
            data.Clear();

            int k;
            for (int j = 0; j < 2000; j++)
            {            
                k = i * 10 - j;
                data.Add(new Point(j, k));
            }
            _dataList[0] = data;
        }

        private void button1_Click(object sender, RoutedEventArgs e)
        {
            timer.IsEnabled = !timer.IsEnabled;
        }
    }
}

 

0 Kudos
Message 3 of 5
(9,663 Views)

I believe the issue here is the use of observable collections. Since ObservableCollection<T> raises an event for every single Add operation, the graph is receiving 2 000 events every timer tick. To fix this, we want to reduce the number of events the graph observes.


A) We could go with option 2 from before, but make the data list an ObservableCollection<Point[]>, creating a new Point[] for every tick:


    const int Size = 2000;
    ...

    // (in OnTimerTick method)
    int i = ra.Next(9);
    var points = new Point[Size];
    for( int j = 0; j < Size; ++j ) {
        int k = i * 10 - j;
        points[j] = new Point(j, k);
    }

    _dataList[i] = points;


Here the graph will see a single replace event when the array of points is assigned to the data list observable collection.


B) Another option would be to use a ChartCollection<int> instead of an ObservableCollection<Point> for each element in the data list:


    // (in MainWindow constructor)
    _dataList[i] = new ChartCollection<int>(capacity: Size);
    ...

    // (in OnTimerTick method)
    int i = ra.Next(9);
    var data = this._dataList[i];
    data.Clear();

    var ks = new int[Size];
    for( int j = 0; j < Size; ++j )
        ks[j] = i * 10 - j;
    data.Append(ks);


Here the graph receives only two events (one for Clear, and one for Append). However, this does have the additional constraint that the index values must be increasing (since your code was using the index variable j, I used the simpler chart collection that automatically assigns the index values; you could also use ChartCollection<int,int> to provide your own index values, but it will not allow the scatter data that is possible with Point).

~ Paul H
0 Kudos
Message 4 of 5
(9,654 Views)

I tried both solutions and the first one seems to response more quickly. 

 

Thank you Paul, my problem was solved.

0 Kudos
Message 5 of 5
(9,647 Views)