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):
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:
Please refer to the attachment sample and let us know if you need further assistance.
Regards,
Nanthini M.
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.
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:
Please let us know if you require further assistance.
Regards,
Nanthini.
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.
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?
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>
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.
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.
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.
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.
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.
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.
Using your CalculateSmartWidth method, I get something that looks like this.
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.
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.