Search code examples
silverlightsilverlight-4.0chartssilverlight-toolkitstackedbarseries

Group StackedBarSeries and BarSeries on same axis


I want to display 3 series on my Silverlight toolkit horizontal bar chart...2 normal bar series and 1 stacked bar series (consisting of 2 SeriesDefinition). When I add the first 2 normal bar series, they show up next to each other as expected. But when I add the stacked series, it takes up the height of the whole row. Is there any way to combine the 2 types of series so that it displays as, from top to bottom - stacked, bar, bar?

Here is how I'm setting it up currently:

<charts:Chart Title="Manufacturer Overview" LegendTitle="Legend" Style="{StaticResource ZChartNoBackground}">
<charts:Chart.Series>
    <charts:StackedBarSeries>
        <charts:SeriesDefinition Title="Oppotunities" 
              ItemsSource="{Binding Path=TotalValueInFunnelByVendor}" 
              IndependentValueBinding="{Binding IndependentValue}" 
              DependentValueBinding="{Binding DependentValue}">
        </charts:SeriesDefinition>
        <!--SAMPLE DATA UNTIL REAL DATA IS IN-->
        <charts:SeriesDefinition Title="Flow Business" 
              ItemsSource="{Binding Path=TotalValueInFunnelByVendor}" 
              IndependentValueBinding="{Binding IndependentValue}" 
              DependentValueBinding="{Binding DependentValue}">
        </charts:SeriesDefinition>
    </charts:StackedBarSeries>
    <charts:BarSeries Title="Sales to date" 
              ItemsSource="{Binding Path=SalesToDateByVendor}" 
              IndependentValueBinding="{Binding IndependentValue}" 
              DependentValueBinding="{Binding DependentValue}">
    </charts:BarSeries>
    <charts:BarSeries Title="Forecasted" 
              ItemsSource="{Binding Path=ForecastedSalesByVendor}" 
              IndependentValueBinding="{Binding IndependentValue}" 
              DependentValueBinding="{Binding DependentValue}">
    </charts:BarSeries>
</charts:Chart.Series>
</charts:Chart>

Here is an image of the chart. Notice the GREEN and RED bars are correctly placed but the stacked bar is the height of the row and in the "back" of the other 2 series:

Chart snapshot


Solution

  • So after a whole bunch of looking around, I've come to the conclusion that this behavior is "by design" or a bug. I looked at the source code for the StackedBarSeries. I created a new class that inherited from StackedBarSeries and added code to the "UpdateDataItemPlacement" method that divides the height of the bar by the number of series in the SeriesHost:

    protected override void UpdateDataItemPlacement(IEnumerable<DefinitionSeries.DataItem> dataItems)
    {
        /* A BUNCH OF CODE FROM TOOLKIT */
    
        double sum = IsStacked100 ?
           group.DataItems.Sum(di =>Math.Abs(ValueHelper.ToDouble(di.DataPoint.ActualDependentValue))) : 
           1;
        if (0 == sum)
        {
            sum = 1;
        }
    
        // MY ADDITION        
        var numSeries = this.SeriesHost.Series.Count;
        int index = this.SeriesHost.Series.IndexOf(this);
        // END ADDITION
    
        double ceiling = 0;
        double floor = 0;
        foreach (DataItem dataItem in group.DataItems)
        {
            DataPoint dataPoint = dataItem.DataPoint;
            double value = IsStacked100 ? (ValueHelper.ToDouble(dataPoint.ActualDependentValue) * (100 / sum)) : ValueHelper.ToDouble(dataPoint.ActualDependentValue);
            if (ValueHelper.CanGraph(value))
            {
                double valueCoordinate = ActualDependentAxis.GetPlotAreaCoordinate(value).Value;
                double fillerCoordinate = (0 <= value) ? ceiling : floor;
    
                double topCoordinate = 0, leftCoordinate = 0, height = 0, width = 0, deltaCoordinate = 0;
                if (AxisOrientation.Y == ActualDependentAxis.Orientation)
                {
                    topCoordinate = plotAreaMaximumDependentCoordinate - Math.Max(valueCoordinate + fillerCoordinate, zeroCoordinate + fillerCoordinate);
                    double bottomCoordinate = plotAreaMaximumDependentCoordinate - Math.Min(valueCoordinate + fillerCoordinate, zeroCoordinate + fillerCoordinate);
                    deltaCoordinate = bottomCoordinate - topCoordinate;
                    height = (0 < deltaCoordinate) ? deltaCoordinate + 1 : 0;
                    leftCoordinate = categoryMinimumCoordinate;
                    width = categoryMaximumCoordinate - categoryMinimumCoordinate + 1;
    
                    // MY MODIFICATION
                    //adjusting the width and offset to allow multiple columns to render beside each other instead of overtop
                    width = width / numSeries;
                    leftCoordinate = leftCoordinate + (width * index);
                    //
                }
                else
                {
                    leftCoordinate = Math.Min(valueCoordinate + fillerCoordinate, zeroCoordinate + fillerCoordinate);
                    double rightCoordinate = Math.Max(valueCoordinate + fillerCoordinate, zeroCoordinate + fillerCoordinate);
                    deltaCoordinate = rightCoordinate - leftCoordinate;
                    width = (0 < deltaCoordinate) ? deltaCoordinate + 1 : 0;
                    topCoordinate = categoryMinimumCoordinate;
                    height = categoryMaximumCoordinate - categoryMinimumCoordinate + 1;
    
                    //MY MODIFICATION
                    //adjusting the height and offset to allow multiple columns to render beside each other instead of overtop
                    height = height / numSeries;
                    topCoordinate = topCoordinate + (height * index);
                    //
                }
    
                // THE REST OF THE CODE FROM THE TOOLKIT
           }  
           else
           {
               dataPoint.Visibility = Visibility.Collapsed;
           }
       }
    }