Search code examples
c#wpfvisual-studio-2010data-bindingdynamic-data-display

Dynamic Data Display LineGraph Not Updating Correctly when using Binding


This Problem has been fixed. I have added the fixes to the code below, commented out sections are from the old code.

Original Question: I am attempting to use a Dynamic Data Display LineGraph but am having trouble getting it to update (D3 Link: http://dynamicdatadisplay.codeplex.com/)

The Code compiles and runs and will display the Line in the constructor but when I kick off one of the display messages the graph does not update. I have verified that the display message is getting there and that the list of values is updated, If the Graph is clicked on or the zoom feature is used the data will then update on the screen.

XXX_Display1_VM.cs:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections.ObjectModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using XXX.BaseTypes;
using XXX.BaseClasses.BaseViewModels;
using XXX.InterfaceLibrary.Interfaces.Mediator;
using XXX.InterfaceLibrary.Interfaces.SystemController;
using System.Web;
using System.ComponentModel;
using Microsoft.Research.DynamicDataDisplay.DataSources;

namespace SystemController
{

    public class ModelData
    {
        public double XValue { get; set; }
        public double YValue { get; set; }
        public ModelData(double x, double y)
        {
            XValue = x;
            YValue = y;
        }
    }

    class XXX_Display1_VM : ViewModelBase, INotifyPropertyChanged, IXXX_Display1_VM
    {
        IXXX_Driver _Driver;
        IMediator _Messenger;


        public ObservableCollection<ModelData> Values { get; private set; }


        CompositeDataSource _Display1;
        public CompositeDataSource Display1
        {
            get
            {
                //Removed this and moved it to the areas where the values are being set
                //if (_Display1 == null)
                //{
                    //var xData = new EnumerableDataSource<double>(Values.Select(v => v.YValue));
                    //xData.SetXMapping(x => x);
                    //var yData = new EnumerableDataSource<double>(Values.Select(v => v.XValue));

                    //yData.SetYMapping(y => y);
                    //_Display1 = xData.Join(yData);              

                //}

                return _Display1;
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;

        //protected void RaisePropertyChanged(string propertyName)
        //{
        //    if (PropertyChanged != null)
        //    {
        //        PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        //    }
        //}

        protected void OnPropertyChanged(PropertyChangedEventArgs e)
        {
            PropertyChangedEventHandler handler = PropertyChanged;
            if (handler != null)
            {
               handler(this, e);
            }
        }

        protected void OnPropertyChanged(string propertyName)
        {
            OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
        }



        public XXX_Display1_VM(IXXX_Driver InDriver, IMediator InMessenger)
        {
            _Driver = InDriver;
            _Messenger = InMessenger;

            _Messenger.Register<DisplayMsgData>(ShellMessage.DISPLAY_MESSAGE_EVENT, OnReceiveDisplayMessage);


            Values = new ObservableCollection<ModelData>();
            _Display1 = new CompositeDataSource();

            Values.Add(new ModelData(1.0,1.0));
            Values.Add(new ModelData(1.5, 2.0));
            Values.Add(new ModelData(2.0, 2.5));

            var xData = new EnumerableDataSource<double>(Values.Select(v => v.YValue));
            xData.SetXMapping(x => x);
            var yData = new EnumerableDataSource<double>(Values.Select(v => v.XValue));
            yData.SetYMapping(y => y);

            _Display1 = xData.Join(yData);


        }

        private void OnReceiveDisplayMessage(DisplayMsgData Data)
        {
            if (Data.MsgType == "SetMode1")
            {
                Values.Clear();
                Values.Add(new ModelData(2.0, 1.0));
                Values.Add(new ModelData(1.5, 2.0));
                Values.Add(new ModelData(2.0, 2.5));

                var xData = new EnumerableDataSource<double>(Values.Select(v => v.YValue));
                xData.SetXMapping(x => x);
                var yData = new EnumerableDataSource<double>(Values.Select(v => v.XValue));

                yData.SetYMapping(y => y);
                _Display1 = xData.Join(yData);

                OnPropertyChanged("Display1");                    
            }
            else if (Data.MsgType == "SetMode2")
            {
                Values.Clear();
                Values.Add(new ModelData(3.0, 1.0));
                Values.Add(new ModelData(2.5, 2.0));
                Values.Add(new ModelData(5.0, 2.5));

                var xData = new EnumerableDataSource<double>(Values.Select(v => v.YValue));
                xData.SetXMapping(x => x);
                var yData = new EnumerableDataSource<double>(Values.Select(v => v.XValue));

                yData.SetYMapping(y => y);
                _Display1 = xData.Join(yData);

                OnPropertyChanged("Display1");
            }
            else if (Data.MsgType == "SetMode3")
            {
                Values.Clear();
                Values.Add(new ModelData(1.0, 3.0));
                Values.Add(new ModelData(2.5, 2.0));
                Values.Add(new ModelData(3.0, 1.5));

                var xData = new EnumerableDataSource<double>(Values.Select(v => v.YValue));
                xData.SetXMapping(x => x);
                var yData = new EnumerableDataSource<double>(Values.Select(v => v.XValue));

                yData.SetYMapping(y => y);
                _Display1 = xData.Join(yData);

                OnPropertyChanged("Display1");
            }
        }

        public string GetClassName()
        {
            return "XXX_DisplayController_VM";
        }


    }
}

XXX_Display1.xaml.cs:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using XXX.InterfaceLibrary.Interfaces.SystemController;

namespace SystemController
{
    /// <summary>
    /// Interaction logic for XXX_Display1.xaml
    /// </summary>
    public partial class XXX_Display1 : UserControl, IXXX_Display1
    {        
        IXXX_Display1_VM _VM;
        IXXX_Driver _Driver;


        public XXX_Display1(IXXX_Driver InDriver, IXXX_Display1_VM InVM)
        {
            _VM = InVM;
            _Driver = InDriver;
            DataContext = InVM; //new XXX_Display1_VM(_Driver);
            InitializeComponent();


        }

        public string GetClassName()
        {
            return "XXX_Display1";
        }  
    }
}

XXX_Display1.xaml:

<UserControl x:Class="SystemController.XXX_Display1"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             xmlns:d3="http://research.microsoft.com/DynamicDataDisplay/1.0" 
             mc:Ignorable="d" 
             d:DesignHeight="550" d:DesignWidth="550">
    <Grid>
        <d3:ChartPlotter Name="Display1" Height="400" Width="400">
            <d3:Header>
                <TextBlock HorizontalAlignment="Center" FontSize="20">Very simple line plot</TextBlock>
            </d3:Header>
            <d3:VerticalAxisTitle>This is vertical axis</d3:VerticalAxisTitle>
            <d3:HorizontalAxisTitle>This is horizontal axis</d3:HorizontalAxisTitle>
            <d3:LineGraph DataSource="{Binding Path=Display1}" Stroke="Black"/>

        </d3:ChartPlotter>
    </Grid>
</UserControl>

Thank You.


Solution

  • The best way that I have found to do this, is to have a Property in your code behind that represents the DataSource and bind the chart's DataSource to that property. Have your code behind implement INotifyPropertyChanged and call OnPropertyChanged every time you update or re-assign your data source. This will force the plotter to observe the binding and redraw your graph.

    Example:

    EnumerableDataSource<Point> m_d3DataSource;
    public EnumerableDataSource<Point> D3DataSource {
        get {
            return m_d3DataSource;
        }
        set {                
            //you can set your mapping inside the set block as well             
            m_d3DataSource = value;
            OnPropertyChanged("D3DataSource");
        }
    }     
    
    protected void OnPropertyChanged(PropertyChangedEventArgs e) {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null) {
            handler(this, e);
        }
    } 
    
    protected void OnPropertyChanged(string propertyName) {
        OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
    }