错误 此 PlotModel 已被 OxyPlot 图表中的某些其他 PlotView 控件使用

Posted

技术标签:

【中文标题】错误 此 PlotModel 已被 OxyPlot 图表中的某些其他 PlotView 控件使用【英文标题】:Error This PlotModel is already in use by some other PlotView control in OxyPlot chart 【发布时间】:2022-01-18 22:18:24 【问题描述】:

我正在使用 Xamarin.Forms OxyPlot 图表。我有一个collectionview,每个collectionview项目都有一个扩展器,每个扩展器里面都有一个PlotView

<CollectionView x:Name="Kids">
    <CollectionView.ItemTemplate>
        <DataTemplate>
             <xct:Expander Tapped="Expander_Tapped" ClassId="Binding rowNumber">
            <xct:Expander.Header>
                <Frame Padding="0" CornerRadius="10" Margin="5" BackgroundColor="White" HasShadow="False">
                    <StackLayout>
                        <Grid BackgroundColor="#f8f8f8">
                            <StackLayout Padding="5" Orientation="Horizontal">
                                <Image x:Name="kidProfile" Source="Binding image" WidthRequest="75" HeightRequest="75" HorizontalOptions="Start" Aspect="AspectFill" />
                                <StackLayout Orientation="Vertical">
                                    <Label Text="Binding first_name"></Label>
                                    <StackLayout Orientation="Horizontal">
                                        <Label Text="Grade: " FontSize="Small"></Label>
                                        <Label Text="Binding grade" FontSize="Small"></Label>
                                    </StackLayout>
                                </StackLayout>
                            </StackLayout>
                            <Image Margin="20" HorizontalOptions="End" Source="arrowDown.png" HeightRequest="15"></Image>
                        </Grid>
                    </StackLayout>
                </Frame>
             </xct:Expander.Header>
             <oxy:PlotView Model="Binding chart" HeightRequest="200" WidthRequest="100" />
        </DataTemplate>
    </CollectionView.ItemTemplate>
</CollectionView>

我在课堂上分配了 PlotModel

public class ReportsClass

    public PlotModel chart
    
        get
        
            PlotModel model = new PlotModel();

            CategoryAxis xaxis = new CategoryAxis();
            xaxis.Position = AxisPosition.Bottom;
            xaxis.MajorGridlineStyle = LineStyle.None;
            xaxis.MinorGridlineStyle = LineStyle.None;
            xaxis.MinorTickSize = 0;
            xaxis.MajorTickSize = 0;
            xaxis.TextColor = OxyColors.Gray;
            xaxis.FontSize = 10.0;
            xaxis.Labels.Add("S");
            xaxis.Labels.Add("M");
            xaxis.Labels.Add("T");
            xaxis.Labels.Add("W");
            xaxis.Labels.Add("T");
            xaxis.Labels.Add("F");
            xaxis.Labels.Add("S");
            xaxis.GapWidth = 10.0;
            xaxis.IsPanEnabled = false;
            xaxis.IsZoomEnabled = false;


            LinearAxis yaxis = new LinearAxis();
            yaxis.Position = AxisPosition.Left;
            yaxis.MajorGridlineStyle = LineStyle.None;
            xaxis.MinorGridlineStyle = LineStyle.None;
            yaxis.MinorTickSize = 0;
            yaxis.MajorTickSize = 0;
            yaxis.TextColor = OxyColors.Gray;
            yaxis.FontSize = 10.0;
            yaxis.FontWeight = FontWeights.Bold;
            yaxis.IsZoomEnabled = false;
            yaxis.IsPanEnabled = false;


            ColumnSeries s2 = new ColumnSeries();
            s2.TextColor = OxyColors.White;

            s2.Items.Add(new ColumnItem
            
                Value = Sunday,
                Color = OxyColor.Parse("#02cc9d")
            );
            s2.Items.Add(new ColumnItem
            
                Value = Monday,
                Color = OxyColor.Parse("#02cc9d")
            );
            s2.Items.Add(new ColumnItem
            
                Value = Tuesday,
                Color = OxyColor.Parse("#02cc9d")
            );
            s2.Items.Add(new ColumnItem
            
                Value = Wednesday,
                Color = OxyColor.Parse("#02cc9d")
            );
            s2.Items.Add(new ColumnItem
            
                Value = Thursday,
                Color = OxyColor.Parse("#02cc9d")

            );
            s2.Items.Add(new ColumnItem
            
                Value = Friday,
                Color = OxyColor.Parse("#02cc9d")
            );
            s2.Items.Add(new ColumnItem
            
                Value = Saturday,
                Color = OxyColor.Parse("#02cc9d")
            );

            model.Axes.Add(xaxis);
            model.Axes.Add(yaxis);
            model.Series.Add(s2);
            model.PlotAreaBorderColor = OxyColors.Transparent;

            return model;
        
    
    

现在这可行,但是在 Expander 中,当我展开一个项目时,PlotView 根本不会显示。所以我改变了我的班级以使用 INotifyPropertyChanged

public class ReportsClass : INotifyPropertyChanged

    public event PropertyChangedEventHandler PropertyChanged;

    public PlotModel chart
    
        get => _chart;
        set
        
            _chart = value;
            if(_chart.PlotView == null && value.PlotView == null)
            
                OnPropertyChanged("chart");
            

        
    
    public PlotModel _chart;

    protected void OnPropertyChanged(string propertyName)
    
        var handler = PropertyChanged;
        if (handler != null)
            if(this.chart.PlotView == null)
            
                handler(this, new PropertyChangedEventArgs(propertyName));
            
    

在我后面的代码中,我使用扩展器的 tapped 方法来填充 PlotView:

void Expander_Tapped(System.Object sender, System.EventArgs e)

    if(expander != null)
    
        expander.IsExpanded = false;
    
    
    expander = sender as Expander;

    int id = Convert.ToInt32(expander.ClassId);

    ReportsClass item = newKidList[id];

    Device.StartTimer(TimeSpan.FromSeconds(1), () =>
    
        if (item.chart == null)
        
            PlotModel model = new PlotModel();

            CategoryAxis xaxis = new CategoryAxis();
            xaxis.Position = AxisPosition.Bottom;
            xaxis.MajorGridlineStyle = LineStyle.None;
            xaxis.MinorGridlineStyle = LineStyle.None;
            xaxis.MinorTickSize = 0;
            xaxis.MajorTickSize = 0;
            xaxis.TextColor = OxyColors.Gray;
            xaxis.FontSize = 10.0;
            xaxis.Labels.Add("S");
            xaxis.Labels.Add("M");
            xaxis.Labels.Add("T");
            xaxis.Labels.Add("W");
            xaxis.Labels.Add("T");
            xaxis.Labels.Add("F");
            xaxis.Labels.Add("S");
            xaxis.GapWidth = 10.0;
            xaxis.IsPanEnabled = false;
            xaxis.IsZoomEnabled = false;


            LinearAxis yaxis = new LinearAxis();
            yaxis.Position = AxisPosition.Left;
            yaxis.MajorGridlineStyle = LineStyle.None;
            xaxis.MinorGridlineStyle = LineStyle.None;
            yaxis.MinorTickSize = 0;
            yaxis.MajorTickSize = 0;
            yaxis.TextColor = OxyColors.Gray;
            yaxis.FontSize = 10.0;
            yaxis.FontWeight = FontWeights.Bold;
            yaxis.IsZoomEnabled = false;
            yaxis.IsPanEnabled = false;


            ColumnSeries s2 = new ColumnSeries();
            s2.TextColor = OxyColors.White;

            s2.Items.Add(new ColumnItem
            
                Value = item.Sunday,
                Color = OxyColor.Parse("#02cc9d")
            );
            s2.Items.Add(new ColumnItem
            
                Value = item.Monday,
                Color = OxyColor.Parse("#02cc9d")
            );
            s2.Items.Add(new ColumnItem
            
                Value = item.Tuesday,
                Color = OxyColor.Parse("#02cc9d")
            );
            s2.Items.Add(new ColumnItem
            
                Value = item.Wednesday,
                Color = OxyColor.Parse("#02cc9d")
            );
            s2.Items.Add(new ColumnItem
            
                Value = item.Thursday,
                Color = OxyColor.Parse("#02cc9d")

            );
            s2.Items.Add(new ColumnItem
            
                Value = item.Friday,
                Color = OxyColor.Parse("#02cc9d")
            );
            s2.Items.Add(new ColumnItem
            
                Value = item.Saturday,
                Color = OxyColor.Parse("#02cc9d")
            );

            model.Axes.Add(xaxis);
            model.Axes.Add(yaxis);
            model.Series.Add(s2);
            model.PlotAreaBorderColor = OxyColors.Transparent;

            item.chart = model;
        

        return false;
    );

但是,最终我会得到这个错误:

此 PlotModel 已被其他一些 PlotView 控件使用

现在我知道 PlotView 与其 PlotModel 之间存在一对一的关系,这给了我们错误,所以我试图检查 PlotModel 是否有 PlotView,但我仍然得到这个错误。

我找到了这个解决方案:

如果您将 OxyPlot 的父视图设置为 DataTemplate 每次都创建一个新的 OxyPlot,然后无法缓存 OxyPlot。一种 每次都会创建新的 PlotModel 和 PlotView,这个错误是 避免(至少这似乎对我有用,我正在使用 CarouselView)

https://github.com/oxyplot/oxyplot-xamarin/issues/17

但我不知道如何为集合视图执行此操作,任何帮助都会非常感激。

我还发现了这个:

这是 OxyPlot 在 MVVM 中使用时非常常见的问题。在 OxyPlot,视图和模型是一对一映射的,当第二个视图 正在尝试绑定相同的 PlotModel,您遇到了“PlotModel 已被其他一些 PlotView 控件使用”。在 ios 上,ListView 的 滚动时将重新创建单元格。在这种情况下,会有 是一个新创建的视图,试图绑定相同的 PlotModel,然后 你有问题。在android上,我猜你会遇到同样的问题 也。试着把手机从横屏转竖屏,看看有没有 同样的问题。在 Android 中,视图将完全重新创建 当方向改变时。

一个快速的解决方法是稍微打破这里的 MVVM 设计。别 在单独的地方创建模型,但在 看法。因此,每当 iOS 或 Android 重新创建视图时,都会生成一个新模型 也被重新创建。

https://github.com/oxyplot/oxyplot-xamarin/issues/60

但是我不知道如何应用这部分:

一个快速的解决方法是稍微打破这里的 MVVM 设计。别 在单独的地方创建模型,但在 看法。因此,每当 iOS 或 Android 重新创建视图时,都会生成一个新模型 也被重新创建。

【问题讨论】:

您是否需要同时打开多个扩展器?如果没有,不使用扩展器会更容易,使用弹出窗口。然后它在打开之前不会被实例化。我意识到这不是一个很好的用户体验,而是一个后备解决方案。 不,我一次只打开一个扩展器。我在点击时关闭了之前打开的扩展器....嗨,史蒂夫! 嗨 - 仍在等待那个 oxyplot repo :) 好的,关闭前一个时一定有一些事情需要清除。 我相信我首先点击此链接进行设置:parallelcodes.com/… @ToolmakerSteve 仅供参考,我正在测试我的 collectionview 中的 4 个项目 【参考方案1】:

有关工作版本,请参阅github ToolmakerSteve / repo OxyplotApp1。


“此 PlotModel 已被其他 PlotView 控件使用”

在iOS上经过各种测试,我得出结论,在iOS上使用(CollectionView or ListView) + Expander + Oxyplot 根本不靠谱。

Oxyplot 似乎加剧了 Expander and CollectionView 的已知问题。

因此,最重要的修复是停止使用这些集合视图。将CollectionView 的使用替换为:

<StackLayout Spacing="0" BindableLayout.ItemsSource="Binding KidModels">
    <BindableLayout.ItemTemplate>
        <DataTemplate>

为了获得更好的性能,请进行此更改,以便每个图表仅在用户第一次单击给定项目时创建:

void Expander_Tapped(System.Object sender, System.EventArgs e)

    // --- ADD THESE LINES ---
    if (ReferenceEquals(sender, expander)) 
        // User is tapping the existing expander. Don't do anything special.
        return;
    
    ...

注意:还修复了如果用户连续点击 3 次扩展器会立即再次关闭的问题。


第一次单击每个扩展器时显示速度更快。这里有三种选择。从最快到最慢。使用第一个,但如果出现空白图表或其他问题,请切换到第二个。如果第二个仍然有问题,请切换到第三个 - 这是原始的,但时间延迟稍短:

if (item.Chart == null) 
    PlotModel model = CreateReportChart(item);
    Action action = () => 
        item.Chart = model;
    ;
    if (false) 
        action();
     else if (true) 
        Device.BeginInvokeOnMainThread(() => 
            action();
        );
     else 
        Device.StartTimer(TimeSpan.FromSeconds(0.5), () => 
            action();

            return false;
        );
    


可选:确保扩展动画在与 Oxyplot 一起使用时不会导致问题。

如果出现问题,但只是“有时”,试试这个,看看情况是否有所改善:

<xct:Expander AnimationLength="0" ...>

这应该会删除动画。


您可以从与 OnPropertyChanged 相关的代码中删除测试,例如 if (_chart.PlotView == null &amp;&amp; value.PlotView == null)。也就是说,始终执行 OnPropertyChanged。

原因:将来您可能希望生成修改后的绘图,因为数据发生了变化,而您所做的测试会阻止 UI 看到变化。

【讨论】:

谢谢史蒂夫,等我回到电脑上试试(2 小时) 但听起来我所要做的就是在我的扩展方法中添加这些行并进行更改? 当新的扩展器打开时,我还能保留关闭扩展器的代码吗? @user979331 - 是的,是的。 我让您的代码与我的 API 一起工作。感谢您的帮助,从这里应该很容易。

以上是关于错误 此 PlotModel 已被 OxyPlot 图表中的某些其他 PlotView 控件使用的主要内容,如果未能解决你的问题,请参考以下文章

OxyPlot.Wpf 图表控件使用备忘

覆盖 OxyPlot 默认调色板

InvalidatePlot 上 WPF 中大数据的 OxyPlot 性能问题

以编程方式将系列添加到 OxyPlot 图表:没有显示

在 XAML 中设置 WPF OxyPlot PlotViews 的样式

如何从所有序列的Y值中获取特定的X值(OxyPlot)