سؤال

I am working on an executive dashboard that should be able to have any number of charts each with any number of series. I am using the WPF Toolkit.

The first problem I had was binding multiple series to a chart. I found Beat Kiener's excellent blogpost on binding multiple series to a chart which worked great until I put it in an items control, which I have to do to meet my "any number of charts" requirement.

It seems to me that the below code should work, but it does not. Can anyone explain why the below code does not work, or offer another way to do it using MVVM?

MainWindow.xaml.cs

public partial class MainWindow : Window
{
    public ChartData ChartData { get; set; }
    public List<ChartData> ChartDataList { get; set; }

    public MainWindow()
    {
        var dataSeries = new Dictionary<string, int>();
        dataSeries.Add("Jan", 5);
        dataSeries.Add("Feb", 7);
        dataSeries.Add("Mar", 3);

        ChartData = new ChartData();
        ChartData.Title = "Chart Title";
        ChartData.DataSeriesList = new List<Dictionary<string, int>>();
        ChartData.DataSeriesList.Add(dataSeries);

        ChartDataList = new List<ChartData>();
        ChartDataList.Add(ChartData);

        InitializeComponent();

        this.DataContext = this;
    }
}

public class ChartData
{
    public string Title { get; set; }
    public List<Dictionary<string, int>> DataSeriesList { get; set; }
}

MainWindow.xaml

<UniformGrid
    Rows="1">

    <!-- These charts do not work -->
    <ItemsControl
        x:Name="itemsControl"
        ItemsSource="{Binding ChartDataList}">
        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <local:MultiChart
                    Title="{Binding Title}"
                    SeriesSource="{Binding DataSeriesList}">
                    <local:MultiChart.SeriesTemplate>
                        <DataTemplate >
                            <chartingToolkit:ColumnSeries
                                Title="Series Title"
                                ItemsSource="{Binding}"
                                IndependentValueBinding="{Binding Key}"
                                DependentValueBinding="{Binding Value}"/>
                        </DataTemplate>
                    </local:MultiChart.SeriesTemplate>
                </local:MultiChart>
            </DataTemplate>
        </ItemsControl.ItemTemplate>
    </ItemsControl>
    <!-- End of not working charts -->

    <!-- This chart works -->
    <local:MultiChart
        Title="{Binding ChartData.Title}"
        SeriesSource="{Binding ChartData.DataSeriesList}">
        <local:MultiChart.SeriesTemplate>
            <DataTemplate>
                <chartingToolkit:ColumnSeries
                    Title="Series Title"
                    ItemsSource="{Binding}"
                    IndependentValueBinding="{Binding Key}"
                    DependentValueBinding="{Binding Value}" />
            </DataTemplate>
        </local:MultiChart.SeriesTemplate>
    </local:MultiChart>
    <!-- End of working chart -->

</UniformGrid>

MultiChart.cs

public class MultiChart : System.Windows.Controls.DataVisualization.Charting.Chart
{
    #region SeriesSource (DependencyProperty)

    public IEnumerable SeriesSource
    {
        get
        {
            return (IEnumerable)GetValue(SeriesSourceProperty);
        }
        set
        {
            SetValue(SeriesSourceProperty, value);
        }
    }

    public static readonly DependencyProperty SeriesSourceProperty = DependencyProperty.Register(
        name: "SeriesSource",
        propertyType: typeof(IEnumerable),
        ownerType: typeof(MultiChart),
        typeMetadata: new PropertyMetadata(
            defaultValue: default(IEnumerable),
            propertyChangedCallback: new PropertyChangedCallback(OnSeriesSourceChanged)
        )
    );

    private static void OnSeriesSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        IEnumerable oldValue = (IEnumerable)e.OldValue;
        IEnumerable newValue = (IEnumerable)e.NewValue;
        MultiChart source = (MultiChart)d;
        source.OnSeriesSourceChanged(oldValue, newValue);
    }

    protected virtual void OnSeriesSourceChanged(IEnumerable oldValue, IEnumerable newValue)
    {
        this.Series.Clear();

        if (newValue != null)
        {
            foreach (object item in newValue)
            {
                DataTemplate dataTemplate = null;

                if (this.SeriesTemplate != null)
                {
                    dataTemplate = this.SeriesTemplate;
                }

                // load data template content
                if (dataTemplate != null)
                {
                    Series series = dataTemplate.LoadContent() as Series;

                    if (series != null)
                    {
                        // set data context
                        series.DataContext = item;

                        this.Series.Add(series);
                    }
                }
            }
        }
    }

    #endregion

    #region SeriesTemplate (DependencyProperty)

    public DataTemplate SeriesTemplate
    {
        get
        {
            return (DataTemplate)GetValue(SeriesTemplateProperty);
        }
        set
        {
            SetValue(SeriesTemplateProperty, value);
        }
    }

    public static readonly DependencyProperty SeriesTemplateProperty = DependencyProperty.Register(
        name: "SeriesTemplate",
        propertyType: typeof(DataTemplate),
        ownerType: typeof(MultiChart),
        typeMetadata: new PropertyMetadata(default(DataTemplate))
    );

    #endregion
}
هل كانت مفيدة؟

المحلول

I finally figured it out.

When you put the MultiChart inside of an ItemsControl the SeriesSource property is set BEFORE the SeriesTemplate property. This does not work because the OnSeriesSourceChanged method needs to know what the SeriesTemplate is. My workaround is to just call the OnSeriesSourceChanged method whenever the SeriesTemplate is changed.

private static void OnSeriesTemplateChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
    DataTemplate oldValue = (DataTemplate)e.OldValue;
    DataTemplate newValue = (DataTemplate)e.NewValue;
    MultiChart source = (MultiChart)d;
    source.OnSeriesTemplateChanged(oldValue, newValue);
}

protected virtual void OnSeriesTemplateChanged(DataTemplate oldValue, DataTemplate newValue)
{
    this.SeriesTemplate = newValue;
    OnSeriesSourceChanged(SeriesSource, SeriesSource);
}

public static readonly DependencyProperty SeriesTemplateProperty = DependencyProperty.Register(
    name: "SeriesTemplate",
    propertyType: typeof(DataTemplate),
    ownerType: typeof(MultiChart),
    typeMetadata: new PropertyMetadata(
        defaultValue: default(DataTemplate),
        propertyChangedCallback: new PropertyChangedCallback(OnSeriesTemplateChanged)
    )
);
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top