Xamarin.Forms中CollectionView内嵌Expander的OxyPlot不显示问题求解
问题根因
该问题的核心原因是Xamarin Community Toolkit的Expander控件默认采用懒加载逻辑,收起状态下不会对内部内容进行测量和渲染,OxyPlot的PlotView在初始化时父容器处于不可见状态,因此不会触发绘图逻辑,导致展开后空白。
可行解决方案
方案1:局部刷新PlotView(最低改动,推荐)
不要在Expander的点击事件中重新加载整个CollectionView数据源,仅触发当前点击条目内PlotView的重绘即可,完全不会影响Expander的展开状态。
代码示例:
首先给XAML中的PlotView添加名称标识:
<oxy:PlotView x:Name="KidChart" HeightRequest="200" WidthRequest="200" Model="{Binding chart}" />
然后修改Expander的点击事件逻辑:
private void Expander_Tapped(object sender, EventArgs e) { var expander = sender as Expander; if (expander == null || !expander.IsExpanded) return; // 查找当前Expander内的PlotView var plotView = expander.FindByName<PlotView>("KidChart"); if (plotView?.Model != null) { // 强制重绘图表 plotView.Model.InvalidatePlot(true); plotView.InvalidateMeasure(); } }
方案2:预渲染Expander内容
给ReportsClass添加IsExpanded属性并实现INotifyPropertyChanged接口,和Expander的IsExpanded属性双向绑定,初始化时短暂设置为展开状态触发内容渲染后再收起,后续展开就能直接显示图表。
代码示例:
修改ReportsClass定义:
public class ReportsClass : INotifyPropertyChanged { private bool _isExpanded; public bool IsExpanded { get => _isExpanded; set { _isExpanded = value; OnPropertyChanged(); } } public PlotModel chart { get; set; } // 其他原有属性省略 public event PropertyChangedEventHandler PropertyChanged; protected void OnPropertyChanged([CallerMemberName] string propertyName = null) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } }
绑定Expander的IsExpanded属性:
<xct:Expander IsExpanded="{Binding IsExpanded}">
初始化数据源时添加预渲染逻辑:
// 先把所有条目设为展开状态触发渲染 foreach (var item in newKidList) { item.IsExpanded = true; } Kids.ItemsSource = newKidList; // 100ms后自动收起所有条目 Device.StartTimer(TimeSpan.FromMilliseconds(100), () => { foreach (var item in newKidList) { item.IsExpanded = false; } return false; });
方案3:替换控件规避问题
如果不想针对Expander做适配,可以选择替换相关控件彻底规避该类渲染问题:
- 替换Expander:自定义折叠控件,用
StackLayout的IsVisible属性或高度动画实现展开/收起效果,内部内容始终处于可视树中,OxyPlot初始化时就能正常渲染。 - 替换CollectionView:如果条目数量不多(少于50条),可以直接用
BindableLayout绑定普通StackLayout,完全规避CollectionView虚拟化带来的各类渲染异常问题,示例:
<ScrollView> <StackLayout x:Name="KidsContainer"> <BindableLayout.ItemTemplate> <DataTemplate> <!-- 原有Expander和内部内容直接放在这里即可 --> </DataTemplate> </BindableLayout.ItemTemplate> </StackLayout> </ScrollView>
后台赋值逻辑直接给BindableLayout.SetItemsSource(KidsContainer, newKidList)即可。
内容的提问来源于stack exchange,提问作者user979331
相关产品推荐
相关产品推荐

