如何使用WPF Toolkit将多个图表绑定到多个系列?

时间:2013-10-11 21:46:29

标签: wpf xaml mvvm charts wpftoolkit

我正在制作一个执行仪表板,它应该能够包含任意数量的图表,每个图表都包含任意数量的系列。我正在使用WPF工具包。

我遇到的第一个问题是将多个系列绑定到图表。我发现Beat Kiener's excellent blogpost on binding multiple series to a chart效果很好,直到我把它放在物品控件中,我必须这样做以满足我的“任意数量的图表”要求。

在我看来,下面的代码应该可行,但事实并非如此。任何人都可以解释为什么下面的代码不起作用,或提供另一种方法来使用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
}

1 个答案:

答案 0 :(得分:1)

我终于明白了。

当您将MultiChart放在ItemsControl内时,SeriesSource属性设置在SeriesTemplate属性之前。这不起作用,因为OnSeriesSourceChanged方法需要知道SeriesTemplate是什么。我的解决方法是只要在OnSeriesSourceChanged发生更改时调用SeriesTemplate方法。

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)
    )
);