Measurement Studio for .NET Languages

cancel
Showing results for 
Search instead for 
Did you mean: 

Binding Graph Datasource and Axes Label to a Collection

I currently have an ObservableCollection<T> containing several signal objects each with properties defining xy data, axis labels etc. This observable collection has been defined as a dependency property of the window to allow me to perform all my bindings in the xaml code.

 

I am finding the graph datasource binding only works if i specify a single signal in the collection to display, but i would like to display the xy data for all the signals. Is there a way of doing this?

 

For example

DataContext="{Binding ElementName=Window1, Path=GraphCollection}" DataSource="{Binding Path=[0].DataXY}"         This works

DataContext="{Binding ElementName=Window1, Path=GraphCollection}" DataSource="{Binding Path=DataXY}"   Does not work

 

FYI the DataXY for each signal is defined as an array of points.

 

I am also having problems binding the graph axis to the same collection. I am finding this binding does not work at all.

 

Label="{Binding ElementName=Window1.GraphCollection, Path=[0].XLabel}"


If i apply the same binding but in the code behind it works perfectly!!

            Binding bTemp = new Binding();
            bTemp.Source = this.GraphCollection;
            bTemp.Path = new PropertyPath("[0].XLabel");
            AxisDouble temp = graph1.Axes[0] as AxisDouble;
            BindingOperations.SetBinding(temp,AxisDouble.LabelProperty,bTemp);

0 Kudos
Message 1 of 7
(6,116 Views)

To make sure I understand your scenario, here is the outline of my test code:


    public class Signal {
        public string XLabel { get; set; }
        public Point[] DataXY { get; set; }
        ...
    }

    public partial class MainWindow : Window {
        public static readonly DependencyProperty GraphCollectionProperty =
            DependencyProperty.Register( ... );
        public ObservableCollection<Signal> GraphCollection {
            get { ... }
            set { ... }
        }

        public MainWindow( ) {
            GraphCollection = ...;
            InitializeComponent( );
        }
    }



For the binding on DataSource, the object in the binding is the ObservableCollection<Signal>:

  • In the first example, Path=[0].DataXY explicitly indexes into the collection and retrieves the value from the DataXY property.
  • In the second example, Path=DataXY ends up using the current item pointed to by the associated collection view.

    (You can verify this by calling CollectionViewSource.GetDefaultView( GraphCollection ).MoveCurrentToNext( ), which shows the data on the second signal in the collection instead of the first. This is the same behavior you get when binding ItemsSource on an ItemsControl.)

For the graph, the easiest workaround is to change the Signal type to implement IEnumerable<Point>:


    public class Signal : IEnumerable<Point> {
        ...

        public IEnumerator<Point> GetEnumerator( ) {
            IEnumerable<Point> data = DataXY;
            return data.GetEnumerator( );
        }

        IEnumerator IEnumerable.GetEnumerator( ) {
            return GetEnumerator( );
        }
    }



For the binding on Label, unfortunately this is a known issue with Measurement Studio 2013. Besides setting the binding in code as you are currently doing, you could use the workaround described in How to set bindings of range of axes and store the signals in a static resource. In other words, using Resources["GraphCollection"] = ... instead of GraphCollection = ... in code, and use DataContext="{StaticResource GraphCollection}" or {Binding Source={StaticResource GraphCollection}, ...} in XAML.

~ Paul H
0 Kudos
Message 2 of 7
(6,104 Views)

Thanks for your help. Yes your test scenario does seem to basically replicate my code. However i am still struggling to get a graph bound to the collection to display data from multiple signals. Is it possible for you to post some example code?

0 Kudos
Message 3 of 7
(6,051 Views)

Here is a complete example showing the IEnumerable<Point> version initialized as a static resource:


    Code:
    using System.Collections;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.Windows;

    namespace GraphBinding {

        public class Signal : IEnumerable<Point> {
            public string XLabel { get; set; }
            public Point[] DataXY { get; set; }

            public IEnumerator<Point> GetEnumerator( ) {
                IEnumerable<Point> data = DataXY;
                return data.GetEnumerator( );
            }

            IEnumerator IEnumerable.GetEnumerator( ) {
                return GetEnumerator( );
            }
        }

        public partial class MainWindow : Window {
            public MainWindow( ) {
                Resources["GraphCollection"] = new ObservableCollection<Signal> {
                    new Signal { XLabel = "X1", DataXY = new[] { new Point( 0, 1 ), new Point( 1, 3 ) } },
                    new Signal { XLabel = "X2", DataXY = new[] { new Point( 2, 2 ), new Point( 3, 4 ) } }
                };

                InitializeComponent( );
            }
        }

    }


    XAML:
    <Window x:Class="GraphBinding.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="Graph Binding">
        <Grid>
            <ni:Graph DataSource="{StaticResource GraphCollection}">
                <ni:Graph.Axes>
                    <ni:AxisDouble Orientation="Horizontal"
                        Label="{Binding [0].XLabel, Source={StaticResource GraphCollection}}" />
                </ni:Graph.Axes>
            </ni:Graph>
        </Grid>
    </Window>

~ Paul H
0 Kudos
Message 4 of 7
(6,045 Views)

Thanks that makes everything a bit clearer for me. The issue i am now having is I don't actually have any signals to add to the collection until they are loaded in and selected later on in the code (well after initialisation). I therefore initialise to an empty observablecollection and add signals later. This causes issues if I use the code you gave me as i get an exception when the xaml attempts to set the graph datasource to null.

0 Kudos
Message 5 of 7
(6,033 Views)

I can bind my GraphCollection to the ItemsSource of a listbox and then set the displaymemberpath in order to display a list of xlabels in the collection. This iterates through the collection and displays the xlabels of all the signals in the collection.

Is there no simple way to get the Datasource of the graph to do a similar thing with the XYData?

0 Kudos
Message 6 of 7
(6,028 Views)

I was able to reproduce the issue and traced it back to the fixed method we use to handle any IEnumerable collection. To work around it, just add an additional constructor to the Signal type:


    public Signal( ) { }

    public Signal( IEnumerable<Point> data ) {
        DataXY = Enumerable.ToArray( data );
    }


To answer your other question, right now there is no equivalent to the DisplayMemberPath property on ItemsControl in the graph. I have create a task to look into adding this functionality in a future release.

~ Paul H
0 Kudos
Message 7 of 7
(6,000 Views)