How to set exact bar width in Column series(Bar chart)

It seems the bar width of the SFCartesian chart column series are set on based on number of data point. Is there any way I can set exact width for bars?

Current scenario(Demo project is attached):

Image_2139_1707755900083


Attachment: MauiBarChartWidth_a14d33c7.zip

11 Replies 1 reply marked as answer

NM Nanthini Mahalingam Syncfusion Team February 13, 2024 03:30 PM UTC

Hi Md. Shahadat,


We have validated your query and would like to inform you that we suggest using the category axis and setting the width property in the column series to specify the exact width for each data point to the desired column width, as shown in the following code snippet and images.

Chart 2:


<chart:SfCartesianChart>

    <chart:SfCartesianChart.XAxes>

        <chart:CategoryAxis >

            <chart:CategoryAxis.Title>

                <chart:ChartAxisTitle Text="Time" />

            </chart:CategoryAxis.Title>

            <chart:CategoryAxis.LabelStyle>

                <chart:ChartAxisLabelStyle LabelFormat="hh:mm:ss" />

            </chart:CategoryAxis.LabelStyle>

        </chart:CategoryAxis>

    </chart:SfCartesianChart.XAxes>

    <chart:SfCartesianChart.YAxes>

        <chart:NumericalAxis>

            <chart:NumericalAxis.Title>

                <chart:ChartAxisTitle Text="Value" />

            </chart:NumericalAxis.Title>

        </chart:NumericalAxis>

    </chart:SfCartesianChart.YAxes>

 

    <chart:ColumnSeries x:Name="series2"

                    XBindingPath="Time"

                    YBindingPath="Value"

                    Width="0.8"/>

</chart:SfCartesianChart>


Output:


A screenshot of a graph

Description automatically generated


Please refer to the attachment sample and let us know if you need further assistance.


Regards,

Nanthini M.


Attachment: F_186690_ce614d44.zip


MS Md. Shahadat Hossen Shakil replied to Nanthini Mahalingam February 14, 2024 01:11 PM UTC

Hello! Thanks for the quick response.

Sorry for not being clear enough about my context. What I meant, I want both charts to have bars with the same bar width.

To clarify, different charts with different amounts of datapoints, but constant bar width.




NM Nanthini Mahalingam Syncfusion Team February 15, 2024 04:23 PM UTC

Hi Md. Shahadat,


We have validated your query, and you can set the fixed bar width using a custom series and custom segment, which are inherited from the column series and column segment, respectively.

Here, you can set the fixed width and spacing for the bar segment, as shown the following code snippet.


While drawing the custom segment, we were unable to place the axis label and tick line near the segment due to the internal accessibility restriction to set the axis label and tick positions.


Custom series


public class ColumnSeriesExt : ColumnSeries

{

    private double width;

    public double FixedWidth

    {

        get { return width; }

        set { width = value; }

 

    }

 

    private double spacing;

    public double FixedSpacing

    {

        get { return spacing; }

        set { spacing = value; }

 

    }

 

 

    protected override void DrawSeries(ICanvas canvas, ReadOnlyObservableCollection<ChartSegment> segments, RectF clipRect)

    {

    

        for(int index = 0;index < segments.Count; index++)

        {

            if (segments[index] is ColumnSegmentExt segmentExt)

            {

                segmentExt.Index = index;

            }

        }

     

        base.DrawSeries (canvas, segments, clipRect);  

    }

 

   

 

    protected override ChartSegment CreateSegment()

    {

        return new ColumnSegmentExt();

    }

}



Custom segment

public class ColumnSegmentExt : ColumnSegment

{

    private int index;

    public int Index

    {

        get { return index; }

        set

        {

            index = value;

        }

    }

 

    public RectF rect { get; set; }

 

    protected override void Draw(ICanvas canvas)

    {

        if (this.Series is ColumnSeriesExt series)

        {

            PathF path = new PathF();

 

 

            if (Index == 0)

            {

                rect = new RectF((float)series.FixedSpacing * Index, Top, (float)series.FixedWidth, Bottom - Top);

            }

            else

            {

                rect = new RectF((float)((series.FixedSpacing  + series.FixedWidth) *(Index)), Top, (float)series.FixedWidth, Bottom - Top);

            }

         

 

            path.AppendRectangle(rect);

         

 

            var color = (Fill is SolidColorBrush brush) ? brush.Color : Colors.Transparent;

            canvas.FillColor = color;

            canvas.FillPath(path);

        }

    }

}

Xaml

<chart:SfCartesianChart.Series>

                    <local:ColumnSeriesExt FixedWidth="80" FixedSpacing="10" x:Name="series1"

                                    XBindingPath="Time"

                                    YBindingPath="Value"

                                    />

                </chart:SfCartesianChart.Series>


Output:


A graph of a bar chart

Description automatically generated with medium confidence


Please let us know if you require further assistance.


Regards,

Nanthini.


Attachment: F_186690(1)_88a0b943.zip


BH Ben Holtsclaw February 15, 2024 05:17 PM UTC

While drawing the custom segment, we were unable to place the axis label and tick line near the segment due to the internal accessibility restriction to set the axis label and tick positions.

Does this mean the segment is not displayed in the correct spot on the axis? When I use your sample code, I can indeed specify the width of the segment. However, the segments are just drawn consecutively and not placed accordingly on the axis. This doesn't seem to be a workable solution.


I'm facing somewhat of a different issue than the original poster but looking for essentially the same solution. If I have a graph with one data point, the segment is properly displayed over the correct date.

Image_4239_1708017021843

If I add a second data point, things get really weird. I don't understand how this width is calculated. Why does adding a second data point cause the segment to get so wide?

Image_3228_1708017146535

Specifying the width property on the ColumnSeries doesn't help here. I want the segment to always be "one day" wide.


<chart:ChartSeriesCollection>
    <chart:ColumnSeries ItemsSource="{Binding SfColumnChartAverageDailyReadings}"
        XBindingPath="DateTime"
        YBindingPath="Systolic"
        EnableAnimation="True"
        Label="Avg Daily Systolic"
        Fill="{Binding SfColumnChartSystolicColor}"
        CornerRadius="5"
        EnableTooltip="True"
        />


    <chart:ColumnSeries ItemsSource="{Binding SfColumnChartAverageDailyReadings}"
        XBindingPath="DateTime"
        YBindingPath="Diastolic"
        EnableAnimation="True"
        Label="Avg Daily Diastolic"
        Fill="{Binding SfColumnChartDiastolicColor}"
        CornerRadius="5"
        EnableTooltip="True"
        />
</chart:ChartSeriesCollection>
<chart:SfCartesianChart.XAxes>
    <chart:DateTimeAxis Interval="1"
                        IntervalType="Days"
                        ShowMajorGridLines="False"
                        LabelRotation="-45"
                        Maximum="{Binding SelectedEndDate}"
                        Minimum="{Binding SelectedStartDate}"
                        EdgeLabelsDrawingMode="Center"
                        >
        <chart:DateTimeAxis.LabelStyle>
            <chart:ChartAxisLabelStyle TextColor="{Binding LabelsColor}" LabelFormat="MM/dd" />
        </chart:DateTimeAxis.LabelStyle>
    </chart:DateTimeAxis>
</chart:SfCartesianChart.XAxes>



NM Nanthini Mahalingam Syncfusion Team February 16, 2024 04:04 PM UTC

Hi Ben,

 

We apologize for the inconvenience caused.

The data labels and tooltips cannot be positioned using a fixed bar width in the custom series.

 

The axis labels and tick lines are automatically positioned based on the axis interval, regardless of the custom series used. Please disregard the previous update regarding the axis and tick positions.

 

We are validating the segment width calculation when adding a second data point in the date-time axis at our end and will update the status within two business days (20/02/2024).

 

Regards,

Nanthini M.




NM Nanthini Mahalingam Syncfusion Team February 20, 2024 06:27 PM UTC

Hi Ben,


We have validated your query and would like to inform you that the column width is updated based on the available visible range only when we dynamically add the second data. Dynamically, we can only retrieve the visible minimum and maximum values. Therefore, currently, we are unable to set the fixed column width based on the available visible range of the axis.


We will check the possibility of providing fixed column width support as a feature request with our development team and will update the status on or before two business days (22/02/2024).


Please let us know if you need further updates.


Regards,

Nanthini M.



NM Nanthini Mahalingam Syncfusion Team February 23, 2024 03:11 PM UTC

Hi Ben,

We have validated your query and would like to inform you that we can set a fixed width based on your requirement, as shown in the following code snippet:


public class ColumnSeriesExt : ColumnSeries

 {

     private double width;

     

     // Fixed width value should be between 0 to 30 based, otherwise segment over lab with each other

     public double FixedWidth

     {

         get { return width; }

         set { width = value; }

 

     }

 

     protected override void DrawSeries(ICanvas canvas, ReadOnlyObservableCollection<ChartSegment> segments, RectF clipRect)

     {

         // If you want to fix the width directly you can ignore below method

         CalculateSmartWidth(segments, clipRect);

          

         base.DrawSeries(canvas, segments, clipRect);

     }

 

     private void CalculateSmartWidth(ReadOnlyObservableCollection<ChartSegment> segments, RectF clipRect)

     {

         var labels = ActualXAxis?.VisibleLabels;

         if (labels != null && labels.Count > 0)

         {

             int maxVisibleSegments = (int)(clipRect.Width / labels.Count);

 

             // Calculate the fixed width based on the minimum segment width and the available width

             int availableSegments = segments.Count;

             FixedWidth = clipRect.Width / Math.Max(maxVisibleSegments , availableSegments);

         }

 

     }

 

     protected override ChartSegment CreateSegment()

     {

         return new ColumnSegmentExt();

     }

 

 

 }

 

 public class ColumnSegmentExt : ColumnSegment

 {

     public RectF rect { get; set; }

 

     protected override void Draw(ICanvas canvas)

     {

         if (this.Series is ColumnSeriesExt series)

         {

             var width = series.FixedWidth;

             var left = (Left + Right) / 2 - (width / 2);

 

             rect = new RectF((float)left, Top, (float)width, Bottom - Top);

             

             canvas.SetFillPaint(Fill, rect);

             canvas.FillRectangle(rect);

         }

     }

 }


We suggest using the CalculateSmartWidth method, which adjusts the fixed width based on the given value, instead of setting a fixed width value that cause segments to overlap when it exceeds 30.


We hope the above workaround meets your exact requirement.


Please let us know if you need further assistance.


Regards,

Nanthini M.


Attachment: MigrationSample_ad8e96f5.zip

Marked as answer

BH Ben Holtsclaw February 26, 2024 02:19 PM UTC

The code that you provided still didn't accomplish what I needed. I just amended your code to use the following.


private void CalculateSmartWidth(ReadOnlyObservableCollection<ChartSegment> segments, RectF clipRect)
{
    var labels = ActualXAxis?.VisibleLabels;


    if (labels != null && labels.Count > 0)
    {
        FixedWidth = (int)clipRect.Width / labels.Count;
    }
}


I want my segments to always be displayed one interval (one day) wide. This accomplishes that. However, this code doesn't work if the interval is more than one day. That falls outside of my current needs. Thanks for your help and pointing me in the right direction.




NM Nanthini Mahalingam Syncfusion Team February 27, 2024 03:17 PM UTC

Hi Ben,


We have validated your query and tested with various interval values. The columns are arranged based on their interval position, and they do not fall outside the current position. Could you please share the code snippet where you encountered the issue? This will assist us to prepare better stable solution. 


Regards,

Nanthini M.


Attachment: MigrationSample_(2)_4d9015b3.zip


BH Ben Holtsclaw February 29, 2024 06:34 PM UTC

Just to be clear, as I indicated, I found a workable solution to the problem I was facing. I want each segment of the chart to be drawn one day wide. Here's the outcome I want.

Desired Outcome.png


Using your CalculateSmartWidth method, I get something that looks like this.

Sample Code Outcome.png

As you requested, I've attached sample code that shows how I'm using DateTimeAxis and how it doesn't seem to work with your CalculateSmartWidth method.



Attachment: ColumnChart_f66e7bb4.zip


NM Nanthini Mahalingam Syncfusion Team March 1, 2024 01:32 PM UTC

Hi Ben,


Thank you for your update. We have reviewed the code snippet you provided and are glad to see that it meets your exact requirements. As a result, we will mark this thread as resolved.  Please let us know if you need further assistance. As always, we are happy to help you out. 


Regards,

Nanthini M.


Loader.
Up arrow icon