【问题标题】:Why my 2 instances of Livecharts cartesian not working?为什么我的 2 个 Livecharts 笛卡尔实例不起作用?
【发布时间】:2020-09-04 02:53:38
【问题描述】:

我的代码目前有问题。

基本上我需要在我的应用程序的一个网格中显示 2 个图表,但它似乎不起作用。 问题是一张图会显示,而第二张图不会绘制。

下面是使用的代码:

XAML:

                   <Grid>
                        <lvc:CartesianChart x:Name="cartchartdb" Series="{Binding SeriesCollection}" LegendLocation="Right" Margin="10,249,578.4,218.2" >
                            <lvc:CartesianChart.AxisY>
                                <lvc:Axis Title="Average Gap (Meter)" LabelFormatter="{Binding YFormatter}"></lvc:Axis>
                            </lvc:CartesianChart.AxisY>
                            <lvc:CartesianChart.AxisX>
                                <lvc:Axis Title="Time" Labels="{Binding Labels}"></lvc:Axis>
                            </lvc:CartesianChart.AxisX>
                        </lvc:CartesianChart>
                    </Grid>

                    <Grid>
                        <lvc:CartesianChart Series="{Binding SeriesCollection2}" LegendLocation="Right" Margin="792,160,9.8,238" >
                            <lvc:CartesianChart.AxisY>
                                <lvc:Axis Title="Sales" LabelFormatter="{Binding YFormatter2}"></lvc:Axis>
                            </lvc:CartesianChart.AxisY>
                            <lvc:CartesianChart.AxisX>
                                <lvc:Axis Title="Month" Labels="{Binding Labels2}"></lvc:Axis>
                            </lvc:CartesianChart.AxisX>
                        </lvc:CartesianChart>
                    </Grid>

C#:

public MainWindow(){
cartchartinit();
cartchartinit2();
}

private void cartchartinit2()
    {
        SeriesCollection2 = new SeriesCollection
        {
            new LineSeries
            {
                Title = "Series 1",
                Values = new ChartValues<double> { 4, 6, 5, 2 ,7 }
            },
            new LineSeries
            {
                Title = "Series 2",
                Values = new ChartValues<double> { 6, 7, 3, 4 ,6 }
            }
        };

        Labels2 = new[] { "Jan", "Feb", "Mar", "Apr", "May" };
        YFormatter2 = value => value.ToString("C");

        //modifying the series collection will animate and update the chart
        SeriesCollection2.Add(new LineSeries
        {
            Values = new ChartValues<double> { 5, 3, 2, 4 },
            LineSmoothness = 0 //straight lines, 1 really smooth lines
        });

        //modifying any series values will also animate and update the chart
        SeriesCollection2[2].Values.Add(5d);

        DataContext = this;
    }

    public SeriesCollection SeriesCollection2 { get; set; }
    public string[] Labels2 { get; set; }
    public Func<double, string> YFormatter2 { get; set; }

private void cartchartinit()
    {

        SeriesCollection = new SeriesCollection
        {
            new LineSeries
            {
                Title = "Average Vehicles Gap",
                Values = null
            },
           /* new LineSeries
            {
                Title = "Avg Gap (Metre)",
                Values = null



            },*/

            /*new LineSeries
            {
                Title = "Series 3",
                Values = new ChartValues<double> { 4,2,7,2,7 },
                PointGeometry = DefaultGeometries.Square,
                PointGeometrySize = 15
            }*/
        };

        Labels = null;
        YFormatter = value => value.ToString("");

        

        DataContext = this;

    }
    public SeriesCollection SeriesCollection { get; set; }
    public string[] Labels { get; set; }
    public Func<double, string> YFormatter { get; set; }

当我只使用 cartchartinit() 方法时,它可以工作。但是当我添加 cartchartinit2() 时,它只为后面的图表绘制图形。我做错了吗?

我们将不胜感激。

谢谢

【问题讨论】:

  • 您可能已经注意到自己,但是在 MainWindow 类的其他相同属性的名称中添加数字后缀是一种不好的方法。如果您必须显示 10 个图表怎么办?您应该使用这三个属性创建一个单独的类,例如称之为 ChartViewModel。然后,您将创建此类的多个实例并将它们分配给多个 CartesianChart 控件的 DataContext。
  • 这最好(并且几乎是自动地)通过将 CartesianChart 控件放在 ItemsControl 的 ItemTemplate 中来完成,它的 ItemsSource 属性绑定到 ChartViewModel 类的集合。见Data Templating Overview
  • @Clemens 在摆弄并测试了您的建议之后,我发现问题出在 datacontext = 这在代码中设置了不止一次,这可能导致两个图表没有同时初始化。尽管如此,感谢您对数据模板方式的解释,这对我来说是新的,它实际上使我的代码更干净。

标签: asp.net wpf livecharts


【解决方案1】:

第一个图表是空的,因为绑定源也是。
您注释了SeriesCollection 属性的初始化,并将唯一系列项目的LineSeries.Values 属性设置为null ==> 此处没有数据集显示。您还遇到了未向绑定引擎报告属性更改的问题。

您的代码存在几个问题。解决最重要的问题:

布局

不要将每个控件包装成Grid
这只会增加渲染性能成本。包装网格是绝对多余的。 Grid 是一个布局 Panel,但目前您没有将它用于元素排列。

不要使用Margin 来定位您的网格。
这不允许您的应用程序扩展,并且基于边距的布局实施起来很麻烦。
如果您需要绝对定位,请使用Canvas。如果您需要相对定位,请使用 Panel,例如 Grid,例如用于基于列和行的布局或StackPanel 用于简单的垂直或水平排列的元素。

Margin 仅用于小的调整,最好是相对屏幕位置。

从您的页边距看来,您希望图表显示为两行两列(从左上角到右下角的对角线)。在这种情况下使用Grid:

<Grid>
  <Grid.RowDefinitions>
    <RowDefinition Height="*" />
    <RowDefinition Height="*" />
  </Grid.RowDefinitions>
  <Grid.ColumnDefinitions>
    <ColumnDefinition Width="*" />
    <ColumnDefinition Width="*" />
  </Grid.ColumnDefinitions>

  <lvc:CartesianChart Grid.Row="0" Grid.Column="0"
                      Series="{Binding SeriesCollection}" 
                      LegendLocation="Right">
    ...
  </lvc:CartesianChart>

  <lvc:CartesianChart Grid.Row="1" Grid.Column="1"
                      Series="{Binding SeriesCollection2}" 
                      LegendLocation="Right">
    ...
  </lvc:CartesianChart>
</Grid>

如果您想将图表彼此对齐,只需使用StackPanel

<StackPanel Orientation="Horizontal">    
  <lvc:CartesianChart Series="{Binding SeriesCollection}" 
                      LegendLocation="Right">
    ...
  </lvc:CartesianChart>

  <lvc:CartesianChart Series="{Binding SeriesCollection2}" 
                      LegendLocation="Right">
    ...
  </lvc:CartesianChart>
</StackPanel>

数据绑定

您使用数据绑定将图表数据分配给图表。那很好。 但是您正在设置绑定源 XAML 绑定表达式被初始化。由于您的所有源属性,例如 SeriesCollectionSeriesCollection2 既不是 DependencyProperty 也不是引发 INotifyPropertyChanged.PropertyChanged 事件,绑定引擎不会拾取这些属性的更改/分配。在您的情况下,它可以工作,但仅仅是因为您在每次属性更改后通过重新分配 this.DataContext = this 来刷新 Binding.Source
这将强制您视图中的每个绑定再次初始化。

在更复杂的应用程序中,这会显着增加启动时间或导致 UI 迟缓。
默认情况下,WPF 沿可视树向下继承 DataContext 属性值(有例外,例如 DataTemplate):每个子元素都隐含地共享其父元素的相同 DataContext
这意味着刷新此属性会影响完整的可视化树! ==> 不要仅仅因为Binding.Path 已更新而刷新Binding.Source(尤其是DataContext)。

让绑定引擎处理这些更改:

只要绑定的源属性预计会动态更改,源对象就必须将这些属性实现为DependencyProperty,或者如果该对象不是DependencyObject,则必须实现INotifyPropertyChanged

Data binding overview in WPFDependency properties overview

WindowDependencyObject,因此MainWindow 应将所有用作绑定源或绑定目标的属性实现为DependencyProerty

partial class MainWindow : Window
{
  public static readonly DependencyProperty SeriesCollectionProperty = DependencyProperty.Register(
    "SeriesCollection",
    typeof(SeriesCollection),
    typeof(MainWindow),
    new PropertyMetadata(default(SeriesCollection)));

  public SeriesCollection SeriesCollection
  {
    get => (SeriesCollection) GetValue(MainWindow.SeriesCollectionProperty);
    set => SetValue(MainWindow.SeriesCollectionProperty, value);
  }

  public static readonly DependencyProperty SeriesCollection2Property = DependencyProperty.Register(
    "SeriesCollection2",
    typeof(SeriesCollection),
    typeof(MainWindow),
    new PropertyMetadata(default(SeriesCollection)));

  public SeriesCollection SeriesCollection2
  {
    get => (SeriesCollection) GetValue(MainWindow.SeriesCollection2Property);
    set => SetValue(MainWindow.SeriesCollection2Property, value);
  }

  ... // Do this for all properties that serve as binding source or target

  public MainwWindow()
  {
    InitializeComponent();
    this.DataContext = this;
  }

  private void cartchartinit()
  {
    // Aside from their slightly clumsy definition, 
    // dependency properties are used like common CLR properties
    SeriesCollection = new SeriesCollection();
  }
}

【讨论】:

  • 啊抱歉。我忘了包括我用于 Cartchartinit() 值的数组输入,这些值已在其他函数中初始化。感谢您的解释和更正 datacontext = 这实际上解决了问题。对于数据绑定等,我想我将从现在开始应用它。非常感谢
猜你喜欢
  • 2017-12-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-06-07
  • 2011-05-06
  • 1970-01-01
相关资源
最近更新 更多