Question

I'm attempting to create a chart using the WPF Toolkit where the Y Axis is being updated by values in a List(). I'm trying access the value via a specific index. Currently, binding to the index of the List() rather than an int creates a "No suitable axis is available for plotting the dependent value." exception.

Here's what I have so far, note my attempt to get the DependentValuePath to access an index:

<Charting:LineSeries VerticalAlignment="Stretch" 
                     HorizontalAlignment="Stretch" 
                     ItemsSource="{Binding Path=MemoryStats}" 
                     IndependentValuePath="Timestamp" 
                     DependentValuePath="ByteCount[0]"
                     Title="Data Points">

This is what MemoryStats values consist of in the code behind:

public List<int> ByteCount { get; set; }
public DateTime Timestamp { get; set; }

The chart works fine when the LineSeries in the XAML has the property DependentValuePath="ByteCount" and when the codebehind uses a simple int:

public int ByteCount { get; set; }
public DateTime Timestamp { get; set; }

How do I get it to bind to an index of a List() rather than an int?

EDIT

I have been able to grab the specific value in the list from the code behind by naming its index, but there will be multiple LineSeries generated dynamically when the chart is created. I would like to bind each one to an index of a List<int>(), which I recreate every second or so.

Here is the full method that MemoryStats uses to update the UI. It is working by updating all of the LineSeries Y values to the single ByteCount int, so currently all of the lines look the same.

    public class MemorySample
    {
        public static MemorySample Generate(List<int> dataPoints)
        {

            return new MemorySample
            {
                ByteCount = dataPoints[0],
                Timestamp = DateTime.Now
            };
        }

        public int ByteCount { get; set; }
        public DateTime Timestamp { get; set; }
    }

Of course, I'd like all of the LineSeries to be different. I would like to have the X axis of each LineSeries of the chart be the TimeStamp (so they all have the same TimeStamp) and the various LineSeries have their Y axis values be updated by a List() of integers, each using a separate index of the List()

I'll try to implement a type converter, but I'm not entirely sure when/where to do that.

EDIT 2

I got it to work the way I wanted it to! I found a lot of help from this S.O. question regarding using multiple series in a line chart.

It appears as if a type converter would work as well, so Shimrod has got the question answered. However, what I ended up doing is set the binding of the LineSeries' ItemSource to an index and then retrieved the data of the content of that index.

So, here's what the LineSeries looks like:

        <Charting:LineSeries VerticalAlignment="Stretch" 
                            HorizontalAlignment="Stretch" 
                            ItemsSource="{Binding [0]}" 
                            IndependentValuePath="X" 
                            DependentValuePath="Y"
                            Title="Data Points">
        </Charting:LineSeries>

Note the index in the ItemSource binding. In the code behind, I set the DataContext of the control to an ObservableCollection, which holds an object that inherits from 'IList' (which you could use any object that does so), and that object holds the object that contains the properties X and Y properties.

   public ObservableCollection<InheritsFromIList<ObjectWithXandYProperties>> VariableDataContextIsSetTo { get; set; }

Accessing a specific index of the ObservableCollection will return the list. Then, the items in that list display on the chart.

Was it helpful?

Solution

The easiest way I think to do this would be to add a property in your class which would be the int value of the List.

e.g.

int val { get { return ByteCount[0]; } }

Or you could also create a converter which would take the list as binding and the index as parameter, and return the desired value.

For example (I haven't tried this):

public class ElementOfListConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        if (value is IList && parameter is int)
        {
            var lst = value as IList;
            int pos = (int)parameter;

            if (lst.Count >= pos)
            {
                return lst[pos];
            }
        }

        return null;
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

Edit

These are the steps to use the converter:

  1. Create the class (ElementOfListConverter), as shown before.
  2. Add a new xml namespace pointing towards the location of your converter. E.g. xmlns:local="clr-namespace:WpfApplication2"
  3. In the resources of your Window (or UserControl), add a reference to your converter, like this:

    <Window.Resources>
        <local:ElementOfListConverter x:Key="ElemOfList" />
    </Window.Resources>
    
  4. In your binding, specify the converter to use (using its key) {Binding YourElement, Converter={StaticResource ElemOfList}, ConverterParameter=0}

The pro for this method is that you can specify the index of the element directly in the xaml. You could also bind this value to some other value using a MultiBinding and a MultiValueConverter. (see this question on S.O. if you need more information.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top