You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.NET MAUI中CollectionView发布模式仅显示白块问题求助

.NET MAUI CollectionView Release模式仅显示白色矩形问题排查与解决

问题描述

.NET MAUI应用中,CollectionView在Debug模式下能正常展示图片和标签,但切换到Release模式后,仅显示白色矩形,不过点击功能仍正常。

相关代码

XAML代码

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:vm="clr-namespace:VegaMaui"
             xmlns:model="clr-namespace:VegaMaui.Model"
             x:Class="VegaMaui.MenuDetails"   x:DataType="vm:MenuDetailsViewModel"        
             Title="Ապրանքներ"
             BackgroundColor="#f9f9f9">   
    <CollectionView 
            RemainingItemsThreshold="5"
            RemainingItemsThresholdReachedCommand="{Binding LoadMoreS}"
            SelectionMode="Single"
            SelectionChanged="OnProdSelectionChanged"
            ItemsSource="{Binding ProdListS}"
            x:Name="prodcollect">
        <CollectionView.ItemsLayout>
            <GridItemsLayout Orientation="Vertical"
                        Span="2" />
        </CollectionView.ItemsLayout>       
        <CollectionView.ItemTemplate>            
            <DataTemplate x:DataType="model:ProductImg">                    
                <Grid Margin="5,5,5,5" Padding="1" >
                    <Grid.RowDefinitions>
                        <RowDefinition Height="Auto" />
                        <RowDefinition Height="Auto" />
                        <RowDefinition Height="Auto" />
                        <RowDefinition Height="Auto" />
                    </Grid.RowDefinitions>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="*" />                           
                    </Grid.ColumnDefinitions>
                    <Image
                        Grid.Row="0"                           
                        Grid.Column="0"                         
                        Source= "{Binding firstPicture}"                        
                        Aspect="AspectFill"/>                    
                    <Label Grid.Row="1"
                        Grid.Column="0"                       
                        FontAttributes="Bold"
                        BackgroundColor="White"
                        LineBreakMode="WordWrap"
                        Text="{Binding product_Name}"
                        VerticalOptions="Start" />
                    <Label Grid.Row="2"
                        Grid.Column="0"                       
                        FontAttributes="Bold"
                        LineBreakMode="WordWrap"
                        BackgroundColor="White"
                        Text="{Binding product_DisPrice, StringFormat='{0:F0} դր.'}"
                        TextColor ="Red" FontSize="Default"
                        VerticalOptions="Start" />
                    <Label Grid.Row="3"
                        Grid.Column="0"                                   
                        LineBreakMode="WordWrap"
                        BackgroundColor="White"
                        IsVisible="{Binding IsShowMode}"
                        Text="{Binding product_Price, StringFormat='{0:F0} դր.'}"
                        TextColor="#40414a"  FontSize="Default" 
                        TextDecorations="Strikethrough"
                        VerticalOptions="Start" />

                </Grid>                    
            </DataTemplate>
        </CollectionView.ItemTemplate>
    </CollectionView>   
</ContentPage>

ViewModel代码

namespace VegaMaui
{
    public  class MenuDetailsViewModel : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
        private int startindex = 1;
        private int endindex = 10;        
        private string SubGrInd = "";
        private readonly VegaApi ApiControl;
        private void OnPropertyChanged(string propertyName) =>
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        public MenuDetailsViewModel(string SubGroup)
        {
            SubGrInd = SubGroup;    
             ApiControl = new();
            ProdListS = new ObservableCollection<Model.Product>();
            new Thread(() =>
            {
                LoadDataS();
            }).Start();
            
        }        
        public async Task LoadDataS()
        {           
            var data =  await ApiControl.GetMenuDetAsync(SubGrInd, startindex,endindex);
            ProdListS = new ObservableCollection<Model.Product>(data);
            PropertyChanged.Invoke(this,new PropertyChangedEventArgs(nameof(ProdListS)));
        }
        public ICommand LoadMoreS => new Command(async () =>
        {           
            startindex += 16;
            endindex += 16;
                ObservableCollection<Model.Product> data = null;
                new Thread(() =>
                {
                     data=  ApiControl.GetMenuDetAsync(SubGrInd, startindex, endindex).Result;          
                    foreach (var item in data)
                    {
                        ProdListS.Add(item);
                        PropertyChanged.Invoke(this, new PropertyChangedEventArgs(nameof(ProdListS)));
                    }               
                }).Start();            
        });       
        public ObservableCollection<Model.Product> ProdListS { get; set; } 
    }
}

页面后台代码

public partial class MenuDetails : ContentPage
{
public string[] ImgList;

private MenuDetailsViewModel _mdvm;
public MenuDetails(string subGID)
{        
    BindingContext = _mdvm = new MenuDetailsViewModel(subGID);
    InitializeComponent();    
   
}
async void OnProdSelectionChanged(object sender, SelectionChangedEventArgs e)
{
    
    Model.Product prodItem = this.prodcollect.SelectedItem as Model.Product;
    int id_Param = prodItem.product_Id;
    VegaApi ApiControl = new();
    List<Model.ProdParams> ParamList = await ApiControl.GetParamDetAsync(id_Param.ToString());      
    ProdInformation ProfilePage = new ProdInformation();
    ProfilePage.Title = this.Title;
    ProfilePage.BindingContext = prodItem;        
    VerticalStackLayout PropsStack = ProfilePage.FindByName<VerticalStackLayout>("InfoLayout");
   
    // Adding Images carousel view      
    string[] picarray = prodItem.product_PicturePath.Split('@');
        System.Collections.ObjectModel.ObservableCollection<Promo> Data = new System.Collections.ObjectModel.ObservableCollection<Promo>();      
    Data.Add(new Promo() { ImgSource =prodItem.firstPicture});
    if (picarray.Count() > 1)
    {          
        for (int k = 1; k < picarray.Count(); k++)
        {
            if (picarray[k] != null && picarray[k].Length > 3)
            {
                Data.Add(new Promo() { ImgSource = "https://vega.am/image/"+ picarray[k] });
            }
        }
    }
   
   CarouselView CurCarousel= ProfilePage.FindByName<CarouselView>("pcarousel");
    CurCarousel.ItemsSource = Data;

    List<int> TitleIds = (from el in ParamList select el.Title_Id).Distinct().ToList();
    if (TitleIds != null && TitleIds.Count > 0)
    {
        for (int j = 0; j<TitleIds.Count; j++)
        {
            List<Model.ProdParams> subLst= ParamList.Where(p => p.Title_Id == TitleIds[j]).ToList();
            if(subLst != null && subLst.Count > 0)
            {
                Label ltitle= new Label();
                ltitle.Text = subLst[0].Title_Name;
                ltitle.FontSize = 18;
                ltitle.TextColor = Color.FromRgb(16, 26, 175);
                ltitle.Padding = new Thickness(3, 5, 3, 5);
                ltitle.HorizontalOptions = LayoutOptions.Start;
                PropsStack.Add(ltitle);
                Grid CurGrid = new Grid
                {
                    
                    ColumnDefinitions =
                     {
                        new ColumnDefinition{Width = new GridLength(5, GridUnitType.Star)},
                        new ColumnDefinition{Width = new GridLength(3, GridUnitType.Star)}
                     }
                };                    
                for (int q=0;q< subLst.Count;q++)
                {     
                    CurGrid.AddRowDefinition(new RowDefinition());
                   
                    
                    CurGrid.Add(new Label
                    {
                        
                        Text = subLst[q].Param_Name+" ",
                        LineBreakMode = LineBreakMode.WordWrap,
                        MaxLines =2,
                        HorizontalOptions = LayoutOptions.Start,
                        VerticalOptions = LayoutOptions.Center
                    },0,q);
                    CurGrid.Add(new Label
                     {
                        Text = subLst[q].Param_Value,
                        LineBreakMode = LineBreakMode.WordWrap,
                        MaxLines = 2,
                        HorizontalOptions = LayoutOptions.Start,
                        VerticalOptions = LayoutOptions.Center
                    },1,q);
                }
                PropsStack.Add(CurGrid);
            }
        }
    }        
    await Navigation.PushAsync(ProfilePage);
}
}

问题排查与解决

1. 修正DataTemplate类型匹配问题

XAML中DataTemplate的x:DataType设置为model:ProductImg,但ViewModel中ProdListS是ObservableCollection<Model.Product>,两者类型不匹配。Release模式下XAML编译会进行严格的类型检查,导致绑定失效,视图无法渲染。

修改方法:将DataTemplate的x:DataType改为model:Product,与集合元素类型一致:

<DataTemplate x:DataType="model:Product">

2. 修复线程更新UI的错误

ViewModel中使用Thread来执行异步数据加载,虽然LoadDataS是async方法,但Thread创建的非UI线程更新集合后触发PropertyChanged,会导致Release模式下绑定无法正确同步到UI线程。

修改方法:使用MainThread.BeginInvokeOnMainThread确保UI相关操作在主线程执行,或者直接在构造函数中异步调用加载方法(避免阻塞):

// 构造函数修改
public MenuDetailsViewModel(string SubGroup)
{
    SubGrInd = SubGroup;    
    ApiControl = new();
    ProdListS = new ObservableCollection<Model.Product>();
    // 直接异步调用,不阻塞构造函数
    _ = LoadDataS();
}

// LoadDataS方法修改
public async Task LoadDataS()
{           
    var data = await ApiControl.GetMenuDetAsync(SubGrInd, startindex, endindex);
    // 确保在主线程更新集合
    MainThread.BeginInvokeOnMainThread(() =>
    {
        ProdListS = new ObservableCollection<Model.Product>(data);
        OnPropertyChanged(nameof(ProdListS));
    });
}

// LoadMoreS命令修改
public ICommand LoadMoreS => new Command(async () =>
{           
    startindex += 16;
    endindex += 16;
    var data = await ApiControl.GetMenuDetAsync(SubGrInd, startindex, endindex);
    // 主线程添加元素
    MainThread.BeginInvokeOnMainThread(() =>
    {
        foreach (var item in data)
        {
            ProdListS.Add(item);
        }
        // ObservableCollection添加元素会自动通知集合变化,无需手动触发PropertyChanged
    });            
});

3. 优化PropertyChanged触发逻辑

ObservableCollection本身会在添加、移除元素时自动触发集合变化通知,不需要在每次Add元素时手动触发ProdListS的PropertyChanged,频繁触发反而会导致Release模式下绑定异常。

修改方法:移除LoadMoreS中每次Add后的PropertyChanged.Invoke调用,仅在替换整个集合时触发一次即可。

4. 确保Release模式下图片加载正常

  • 检查firstPicture的路径是否为有效的URI或本地资源路径,确保没有拼写错误。
  • 配置平台权限:
    • Android:在AndroidManifest.xml中添加INTERNET权限(如果加载网络图片):
      <uses-permission android:name="android.permission.INTERNET" />
      
    • iOS:在Info.plist中配置App Transport Security,允许加载HTTP资源(如果图片地址是HTTP):
      <key>NSAppTransportSecurity</key>
      <dict>
          <key>NSAllowsArbitraryLoads</key>
          <true/>
      </dict>
      

内容的提问来源于stack exchange,提问作者Alisa

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.25 18:49:55