Search code examples
c#wpfchartslivecharts

Min and max values on livecharts cartesian chart wpf


I want to force the Y axis to be centered on 0 and I have to ensure that min and max values are the same.

I use the MinValue and MaxValue properties of the Axis but it doesn't work (note that there is usually a binding on those values but i changed them for this post).

<lvc:CartesianChart.AxisY>
            <lvc:Axis Title="{Binding SelectedLine.Unit}"
                      LabelFormatter="{Binding Formatter}"
                      FontSize="14"
                      MinValue="-390000"
                      MaxValue="390000"
                      Foreground="{StaticResource GeneralForegroundColor}">
                <lvc:Axis.Separator>
                    <lvc:Separator Stroke="LightGray"

                                   StrokeThickness="1">
                        <lvc:Separator.StrokeDashArray>
                            <DoubleCollection>
                                <sys:Double>6</sys:Double>
                            </DoubleCollection>
                        </lvc:Separator.StrokeDashArray>
                    </lvc:Separator>
                </lvc:Axis.Separator>
                <lvc:Axis.Sections>
                    <lvc:AxisSection Value="0" StrokeThickness="1"  Stroke="Black">
                    </lvc:AxisSection>
                </lvc:Axis.Sections>
            </lvc:Axis>
        </lvc:CartesianChart.AxisY>

The max value is not 390000 and I don't know how to do to force it

Max value not the one i'm expecting


Solution

  • You have to calculate min/max when you add values to the chart.
    It's best to add corresponding binding sources to your data model.

    In case you are adding values dynamically you have to recalculate min/max. An most important, the data model must implement INotifyPropertyChanged and the properties MaxAxisValue and MinAxisValue must raise the INotifyPropertyChanged.PropertyChanged event:

    class ChartModel
    {
      public double MaxAxisValue { get; set; }
      public double MinAxisValue { get; set; }
    
      public ChartModel()
      {
        var chartValues = new ChartValues<double> { 10, 50, -3, -25 };
        this.MaxAxisValue = chartValues
          .Select(value => Math.Abs(value))
          .Max();
        this.MinAxisValue = this.MaxAxisValue * -1;
    
        this.SeriesCollection = new SeriesCollection
        {
          new ColumnSeries()
          {
            Title = "Series 1",                    
            Values = chartValues
          }
        };
      }
    }
    

    View

    <Grid>
      <Grid.DataContext>
        <ChartModel />
      </Grid.DataContext>
    
      <wpf:CartesianChart Series="{Binding SeriesCollection}">
        <wpf:CartesianChart.AxisY>
          <wpf:Axis MaxValue="{Binding MaxAxisValue}"
                    MinValue="{Binding MinAxisValue}">
    
            ...
    
          </wpf:Axis>
        </wpf:CartesianChart.AxisY>
      </wpf:CartesianChart>
    </Grid>
    

    Update

    It seems that your problem is related to the separators of the axis or their distribution.

    First note that although the distribution is not even, the values of Axis.MinValue and Axis.MaxValue are still respected by the chart. It's just a visual discrepancy, which is introduced by the interval of the divisions.

    By default the interval is calculated automatically. I guess the algorithm takes the decimal position of values into account. If max absolute value is 39, then the interval will be either 10^1 or (10^1)/2. In your case you have 390,000 which uses position 10^5. So the interval is set to either 100,000 or 50,000 depending on the range. The divisions start from the lower border e.g. -390,000. Applying the interval of 100,000 will get the highest possible division, which is lesser than or equal to max value (390,000), of 310,000.
    This explains why you get the divisions ranging from -390,000 to 310,000.

    To "fix" this, you have to decide the number of steps (or divisions) you want to have and set Separator.Step explicitly. Separator.Step is set to 0 by default, which enables the automatic interval calculations.

    The basic formula would be
    step = value_range / desired_divisions_count

    I find the terminology used by LiveCharts quite irritating. I naturally would call the steps interval and the number of divisions steps. But apparently the designers had a different perception.

    Let's say you want to have 10 divisions or grid lines. You would have to set Separator.Step="78000" because 78000 = (390,000 - (-390,000)) / 10. This will distribute the divisions evenly, where min/max value will each fall on a division.

    <wpf:CartesianChart.AxisY>
      <wpf:Axis MaxValue="390000"
                MinValue="-390000">
        <wpf:Axis.Separator>
          <wpf:Separator Step="78000" />
        </wpf:Axis.Separator>
      </wpf:Axis>
    </wpf:CartesianChart.AxisY>