如何从 LiveCharts 轴获取轴大小(以像素为单位)以确定轴上的自定义分隔符步数?

How do I get the Axis size, in pixels, from a LiveCharts Axis in order to determine a custom number of Separator steps on the axis?

我正在使用 LiveCharts 的 WinForms 版本在我的 .NET 应用程序中绘制一系列数据,作为 LineSeries。我希望能够根据轴的长度(以像素为单位)调整 X 轴上显示的 'ticks' 的数量。到目前为止,我无法确定轴的实际长度。在运行时检查时,Axis 对象有一个 Height 和一个 NaNWidth,所以我的方法无法计算正确的滴答数(或步数)。

所以,我的问题是,如何获得屏幕上显示的 Axis 对象的正确大小(以像素为单位)?

到目前为止我的代码:

表单方法调用 Chart class 方法以根据所需的像素间距获取刻度数并在轴上设置刻度间距:

private static void UpdateChainageTickSpacing()
{
    Axis chainageAxis = Chart.GetAxisByName(AxisOrientation.X, "Chainage");
    int numTicks = Chart.GetNumTicksByPixelSpacing(chainageAxis, 40);
    Chart.SetTickSpacing(chainageAxis, numTicks, 0);
}

Chart class:

public static void SetTickSpacing(Axis axis, int numTicks, int precision)
{
    double maxValue = double.MinValue;
    double minValue = double.MaxValue;

    foreach (Series series in AllSeries)
    {
        maxValue = Math.Max(GetMaxValue(series, GetAxisOrientation(axis)), maxValue);
        minValue = Math.Min(GetMinValue(series, GetAxisOrientation(axis)), minValue);
    }

    double range = maxValue - minValue;
    double spacing = range / numTicks;
    spacing = Math.Round(spacing / 10, precision) * 10;
    axis.Separator.Step = spacing;
}

public static int GetNumTicksByPixelSpacing(Axis axis, int pixelSpacing)
{
    double pixelSize;

    if (GetAxisOrientation(axis) == AxisOrientation.X)
    {
        int index = GetAxisIndexByName(GetAxisCollection(AxisOrientation.X), axis.Name);
        pixelSize = cartesianChart.AxisX[index].Width;   // this returns NaN
    }
    else
    {
        int index = GetAxisIndexByName(GetAxisCollection(AxisOrientation.Y), axis.Name);
        pixelSize = cartesianChart.AxisY[index].Height;   // this returns NaN
    }

    return (int)Math.Round(pixelSize / pixelSpacing);
}

该图表有两个 Canvas 元素:父元素 Canvas 包含图例、部分等图表元素,另一个子元素 Canvas 作为实际绘图区域来承载绘制的图表图。

轴长(x 和 y)等于绘图区域的尺寸(宽度和高度)。

您可以通过 CartesianChart.Content 属性:

引用父 Canvas 来访问绘图 canvas

您需要等到所有元素都添加到绘图中 canvas 才能获得最终大小。

图表本身不公开通知内容布局何时完成的事件。所以你要听剧情canvas.

UIElement.LayoutUpdated事件

为此,您需要从 Window.ContentRenderedWindow.Loaded 事件处理程序订阅 Canvas.LayoutUpdated 事件。由于您想尽可能多地忽略冗余布局更新,因此最后引发的 Window.ContentRendered 事件在这种情况下是最好的。

UIElement.LayoutUpdated 在每个布局过程中都会引发,例如调整大小或 Canvas.Children 集合操作,例如 add/move/remove 子元素,这很常见,因此需要一些优化来减少冗余 re-calculations.

MainWindow.xaml

<Window>
  <CartesianChart x:Name="Chart" />
</Window>

MainWindow.xaml.cs

partial class MainWindow: Window
{
  public MainWindow()
  {
    InitializeComponent();
    
    this.ContentRendered += OnContentRendered;
  }

  private void OnContentRendered(object sender, EventArgs e)
  {
    this.ContentRendered -= OnContentRendered;

    var canvas = this.Chart.Content as Canvas;
    var plotCanvas = canvas.Children.OfType<Canvas>().FirstOrDefault();
    plotCanvas.LayoutUpdated += GetAxisXLengthOnLayoutUpdated;
  }

  // Will be called very often 
  // (on every layout pass of the Canvas sender like on resize or add/move/remove child)
  private void GetAxisXLengthOnLayoutUpdated(object sender, EventArgs e)
  {
    var plotCanvas = sender as Canvas;

    // If this is a one time operation, unsubscribe from LayoutUpdated event
    plotCanvas.LayoutUpdated -= GetAxisXLengthOnLayoutUpdated;

    // The length of the x-axis is equal to the final width of the plot area
    var actualAxisXLength = plotCanvas.ActualWidth;
  }
}