Measurement Studio for .NET Languages

cancel
Showing results for 
Search instead for 
Did you mean: 

How to data bind a Waveform Class to a WPF Datagrid?

I have an AnalogWaveform object with my data in it. Is it possible to databind this to the Microsoft WPF Datagrid control?

0 Kudos
Message 1 of 9
(8,229 Views)

Hi cymrieg,

 

It doesn't seem like there's an obvious, traditional way to bind an AnalogWaveform object to a DataGrid control. Can I ask why you're trying to do this? Traditionally DataGrids are used to make "table" type UI's with mostly text and occasional pictures:

 

http://wpftutorial.net/DataGrid.html#selection

 

 

Kelsey W.
National Instruments
Applications Engineer
0 Kudos
Message 2 of 9
(8,197 Views)

I want to use the graph to plot the data and I also want to be able to show the data in a "spreadsheet" (a.k.a. Datagrid). 1 column for the Timestamps and 1 colunm for the measurements.

 

I mean you are dead right the WPF data grid seems to be designed for showing contact info out of a database.

 

0 Kudos
Message 3 of 9
(8,184 Views)

Hi cymrieg,

 

Thanks for the clarification. I was initially thinking you wanted to display the data as a waveform within the DataGrid. But now I see you want more of a table control with timestamps and numeric values for the measurements. 

 

Can you use the Timing and Samples properties or the GetTimeStamps and GetRawData methods to programmatically extract the AnalogWaveform data?

 

http://zone.ni.com/reference/en-XX/help/372636F-01/mstudiowebhelp/html/b3168f32/

 

 

Kelsey W.
National Instruments
Applications Engineer
0 Kudos
Message 4 of 9
(8,167 Views)

Expanding on Kelsey's suggestion, here is an example using the Samples of an analog waveform exposed through a "Waveform" property on the data context:


    <DataGrid ItemsSource="{Binding Waveform.Samples}" AutoGenerateColumns="False">
        <DataGrid.Columns>
            <DataGridTextColumn Header="Index" Binding="{Binding Index}" />
            <DataGridTextColumn Header="TimeStamp" Binding="{Binding TimeStamp}" />
            <DataGridTextColumn Header="Value" Binding="{Binding Value}" />
        </DataGrid.Columns>
    </DataGrid>


It uses manually defined data grid columns to show the index, time stamp, and value of each AnalogWaveformSample in the Samples collection.

~ Paul H
0 Kudos
Message 5 of 9
(8,160 Views)

Paul,

 

That looks good, thanks.

 

How about when I have a series of waveforms (say one is current values and the others are voltage and resistance and so on)? I would like to show in this case 5 columns (Index, Timestamp, V, I and R. How can I bind then to the DataGrid?

 

Do I go down the route of an array of AnalogWaveforms or do I try and use the AnalogWaveformCollection. For the latter I don't see how to add the waveforms into the colection there is no Add method.

 

 

0 Kudos
Message 6 of 9
(7,982 Views)

To get the data into a format the DataGrid can accept, you can either 1) create a custom object for each sample, or 2) bind back to the parent data context to access the other waveforms.



For option 1, you would change the binding from Waveform.Samples to your custom object collection, and add more columns binding to the Voltage, Current, etc properties on the custom object:


    <DataGrid ItemsSource="{Binding Custom}" AutoGenerateColumns="False">
        <DataGrid.Columns>
            <DataGridTextColumn Header="Index" Binding="{Binding Index}" />
            <DataGridTextColumn Header="TimeStamp" Binding="{Binding TimeStamp}" />
            <DataGridTextColumn Header="V" Binding="{Binding Voltage}" />
            <DataGridTextColumn Header="I" Binding="{Binding Current}" />
            <DataGridTextColumn Header="R" Binding="{Binding Resistance}" />
        </DataGrid.Columns>
    </DataGrid>



For option 2, instead of having one Waveform property on the data context, you would either have separate Voltage, Current, etc properties for each waveform, or a collection of those waveforms. The example below illustrates both of these cases using a data context with Voltage, Current, Resistance, and Collection properties:


    Code:
    // Custom converter to index corresponding samples in parallel waveforms.
    public sealed class WaveformIndexer : IMultiValueConverter {
        public object Convert( object[] values, Type targetType, object parameter, CultureInfo culture ) {
            var waveform = (AnalogWaveform<double>)values[0];
            int index = (int)values[1];

            var sample = waveform.Samples[index];
            return sample.Value;
        }
        ...
    }

    XAML:
    <!-- separate waveform properties -->
    <DataGrid ItemsSource="{Binding Voltage.Samples}" AutoGenerateColumns="False">
    <!-- or collection of waveforms -->
    <DataGrid ItemsSource="{Binding Collection[0].Samples}" AutoGenerateColumns="False">
        <DataGrid.Resources>
            <local:WaveformIndexer x:Key="Indexer" />
        </DataGrid.Resources>
        <DataGrid.Columns>
            <DataGridTextColumn Header="Index" Binding="{Binding Index}" />
            <DataGridTextColumn Header="TimeStamp" Binding="{Binding TimeStamp}" />
            <DataGridTextColumn Header="V" Binding="{Binding Value}" />
            <DataGridTextColumn Header="I">
                <DataGridTextColumn.Binding>
                    <MultiBinding Converter="{StaticResource Indexer}" StringFormat="G">
                        <!-- separate waveform properties -->
                        <Binding Path="DataContext.Current" RelativeSource="{RelativeSource FindAncestor, AncestorType=DataGrid}" />
                        <Binding Path="Index" />
                    </MultiBinding>
                </DataGridTextColumn.Binding>
            </DataGridTextColumn>
            <DataGridTextColumn Header="R">
                <DataGridTextColumn.Binding>
                    <MultiBinding Converter="{StaticResource Indexer}" StringFormat="G">
                        <!-- collection of waveforms -->
                        <Binding Path="DataContext.Collection[2]" RelativeSource="{RelativeSource FindAncestor, AncestorType=DataGrid}" />
                        <Binding Path="Index" />
                    </MultiBinding>
                </DataGridTextColumn.Binding>
            </DataGridTextColumn>
        </DataGrid.Columns>
    </DataGrid>



(The AnalogWaveformCollection is used to represent a fixed set of data read by hardware, so it does not support modification.)

~ Paul H
0 Kudos
Message 7 of 9
(7,976 Views)

I only showed an example of having V, I and R but we are writing some generic code that can take data fron a Data Logger so there could be between 1 and 300 channels of data that we would like on the Datagrid. A column for each channel.

 

SO, that rules out the property per channel option.

 

I like the collection approach so what is the property "Collection" in the XAML? Is it a Collection of AnalogWaveform's or AnalogWaveformSamples?

 

Then I suppose it should also be an ObservableCollection so if we append data to the already existing channels (waveforms) the the DataGrid will see the additions.

 

0 Kudos
Message 8 of 9
(7,974 Views)

What is the property "Collection" in the XAML?


For the example, I just used an array of AnalogWaveform<double>, but any IList would work.



Collection could be an ObservableCollection so the DataGrid will see when we append channels.


From what I know of the data grid, it is geard towards a fixed set of columns and a variable set of rows. Appending data to the example Collection property will not automatically add more columns.


Instead, I think you will need to create the columns for the data grid in code. This should make the bindings a lot simpler: you will still want one reference collection in the data grid for indexing, but all of the value columns can use a simple binding, changing the custom multi-value converter to an IValueConverter that takes the collection as a parameter. For example:


    var converter = new WaveformIndexer( );
    ...
    var bindingA = new Binding( "Index" ) { Converter = converter, ConverterParameter = waveformA };
    var columnA = new DataGridTextColumn { Header = "Value A", Binding = bindingA };
    dataGrid.Columns.Add( columnA );

~ Paul H
0 Kudos
Message 9 of 9
(7,964 Views)