错误 此 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 && value.PlotView == null)
。也就是说,始终执行 OnPropertyChanged。
原因:将来您可能希望生成修改后的绘图,因为数据发生了变化,而您所做的测试会阻止 UI 看到变化。
【讨论】:
谢谢史蒂夫,等我回到电脑上试试(2 小时) 但听起来我所要做的就是在我的扩展方法中添加这些行并进行更改? 当新的扩展器打开时,我还能保留关闭扩展器的代码吗? @user979331 - 是的,是的。 我让您的代码与我的 API 一起工作。感谢您的帮助,从这里应该很容易。以上是关于错误 此 PlotModel 已被 OxyPlot 图表中的某些其他 PlotView 控件使用的主要内容,如果未能解决你的问题,请参考以下文章
InvalidatePlot 上 WPF 中大数据的 OxyPlot 性能问题