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

UWP中GridView自适应最高图片高度的实现问题

UWP ListView 可变高度图片项容器高度适配问题

我正在开发一款包含可变高度图片的UWP应用,使用ContainerContentChanging事件实现先加载文本信息、再加载图片的逻辑。

当前问题:
项容器的高度被固定为第一张加载完成的图片高度,后续加载的更高图片无法撑开容器。

期望效果:
项容器高度应自动匹配对应项中图片的实际高度(每个容器高度随自身图片高度变化)


现有代码

XAML代码

<ListView ContainerContentChanging="GridView_ContainerContentChanging" SelectionChanged="GridView_SelectionChanged">
    <ListView.ItemTemplate>
        <DataTemplate>
            <Grid CornerRadius="4,4,4,4" VerticalAlignment="Center">
                <Grid.RowDefinitions>
                    <RowDefinition Height="Auto" />
                </Grid.RowDefinitions>
                <Button Click="Button_Click" GotFocus="Button_GotFocus"
                        LostFocus="Button_LostFocus" CornerRadius="1,1,1,1" HorizontalAlignment="Center" VerticalAlignment="Center" Width="116"
                        Margin="0,0,0,0" Padding="0,0,0,0" HorizontalContentAlignment="Stretch"
                 VerticalContentAlignment="Stretch"
                 BorderThickness="0"
                 Background="Transparent">
                    <RelativePanel CornerRadius="4,4,4,4" Background="Transparent">
                        
                        <Border
                        Background="Transparent"
                        CornerRadius="4,4,4,4"
                        RelativePanel.AlignLeftWithPanel="True"
                        RelativePanel.AlignRightWithPanel="True"
                        RelativePanel.AlignVerticalCenterWithPanel="True"
                        Visibility="Visible">
                            <TextBlock
                            x:Name="TextTitleEmpty"
                            Style="{StaticResource BaseTextBlockStyle}"
                            Text="{x:Bind Title}"
                            TextAlignment="center" 
                            FontSize="10"
                            Padding="10,0,10,0"/>
                        </Border>
                        <Image
                        x:Name="ItemImage"
                        Width="116"
                        HorizontalAlignment="Center"
                        VerticalAlignment="Center" RelativePanel.AlignTopWithPanel="True"
                        Stretch="Uniform" />

                        <Border
                        Background="#BF000000"
                        RelativePanel.AlignBottomWithPanel="True"
                        RelativePanel.AlignLeftWithPanel="True"
                        RelativePanel.AlignRightWithPanel="True"
                        Visibility="Collapsed">
                            <TextBlock
                            x:Name="TextTitle"
                            Padding="0,0,0,1"
                            MaxLines="2"
                            Style="{StaticResource BaseTextBlockStyle}"
                            Text="{x:Bind Title}"
                            FontSize="10"
                            Foreground="White"
                            TextAlignment="center"
                            TextTrimming="WordEllipsis"
                            TextWrapping="Wrap" />
                        </Border>
                    </RelativePanel>
                </Button>
            </Grid>
        </DataTemplate>
    </ListView.ItemTemplate>
    <ListView.ItemContainerStyle>
        <Style TargetType="GridViewItem">
            <Setter Property="Width" Value="116" />
            <Setter Property="Background" Value="Transparent" />
            <Setter Property="Margin" Value="4,4,4,14" />
            <Setter Property="Padding" Value="0,0,0,0" />
            <Setter Property="Margin" Value="4,4,4,4" />
            <Setter Property="HorizontalContentAlignment" Value="Stretch" />
            <Setter Property="VerticalContentAlignment" Value="Stretch" />
            <Setter Property="IsTabStop" Value="False" />
            <Setter Property="Height" Value="Auto"/>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="GridViewItem">
                        <ListViewItemPresenter
                        CornerRadius="4,4,4,4"
                        SelectedBackground="Transparent"
                        SelectedPointerOverBackground="Transparent"
                        PressedBackground="Transparent"
                        SelectedPressedBackground="Transparent" />
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </ListView.ItemContainerStyle>
    <ListView.ItemsPanel>
        <ItemsPanelTemplate>
            <ItemsWrapGrid Margin="23,60,0,40" Orientation="Horizontal"
                           BringIntoViewRequested="ItemsWrapGrid_BringIntoViewRequested"
                       HorizontalAlignment="Left"/>
        </ItemsPanelTemplate>
    </ListView.ItemsPanel>
</ListView>

C#代码

private void GridView_ContainerContentChanging(ListViewBase sender, ContainerContentChangingEventArgs args)
{
    if (args.InRecycleQueue)
    {
        var templateRoot = args.ItemContainer.ContentTemplateRoot as FrameworkElement;
        var image = (Image)templateRoot.FindName("ItemImage");

        image.Source = null;
        image.Opacity = 0;
        args.Handled = true;
    }

    if (args.Phase == 0)
    {
        var templateRoot = args.ItemContainer.ContentTemplateRoot as FrameworkElement;
        var image = (Image)templateRoot.FindName("ItemImage");
        image.Source = null;
        image.Opacity = 0;

        args.RegisterUpdateCallback(ShowImage);
        args.Handled = true;
    }
}

private async void ShowImage(ListViewBase sender, ContainerContentChangingEventArgs args)
{
    if (args.Phase == 1)
    {
        var item = args.Item as ListItem;
        var bitmapImage = await item.GetImageThumbnailAsync();

        var templateRoot = args.ItemContainer.ContentTemplateRoot as FrameworkElement;
        var image = (Image)templateRoot.FindName("ItemImage");

        image.Opacity = 0;
        image.Source = bitmapImage;
        if (image.Source == null)
        {
            bitmapImage = new BitmapImage();
            bitmapImage.UriSource = new Uri(image.BaseUri, "Assets/empty.png");
            image.Source = bitmapImage;
        }
        
        image.Opacity = 100;
        args.Handled = true;
    }
}

注意事项

  • 当ItemsPanel被替换为ItemsStackPanel或ItemsWrapGrid以外的面板类型时,ContainerContentChanging事件将不会被触发。

解决方案

  1. 修正布局约束
    移除DataTemplate根Grid的VerticalAlignment="Center"属性,改为VerticalAlignment="Stretch",让Grid能随内容高度自动调整:

    <Grid CornerRadius="4,4,4,4" VerticalAlignment="Stretch">
    
  2. 监听图片加载完成,强制更新布局
    在ShowImage方法中,给Image添加ImageOpened事件,图片加载完成后触发容器和面板的布局更新,确保容器高度适配图片实际尺寸:

    private async void ShowImage(ListViewBase sender, ContainerContentChangingEventArgs args)
    {
        if (args.Phase == 1)
        {
            var item = args.Item as ListItem;
            var bitmapImage = await item.GetImageThumbnailAsync();
    
            var templateRoot = args.ItemContainer.ContentTemplateRoot as FrameworkElement;
            var image = (Image)templateRoot.FindName("ItemImage");
    
            image.Opacity = 0;
            // 简化空图赋值逻辑
            image.Source = bitmapImage ?? new BitmapImage(new Uri(image.BaseUri, "Assets/empty.png"));
            
            // 图片加载完成后更新布局
            image.ImageOpened += (s, e) =>
            {
                image.Opacity = 1;
                // 强制容器重新计算高度
                args.ItemContainer.UpdateLayout();
                // 通知ItemsWrapGrid重新排列项
                if (sender.ItemsPanelRoot is ItemsWrapGrid wrapGrid)
                {
                    wrapGrid.InvalidateArrange();
                }
            };
            
            args.Handled = true;
        }
    }
    
  3. 确认容器样式设置
    保留ItemContainerStyle中的Height="Auto"和VerticalContentAlignment="Stretch",确保容器没有固定高度限制,能完全填充内容高度。


关键原理

  • ContainerContentChanging的异步加载流程会让容器先基于空图片状态计算高度,图片加载完成后必须手动触发布局更新,才能让容器重新适配图片的实际高度。
  • ItemsWrapGrid需要调用InvalidateArrange来重新排列所有项,避免因单个容器高度变化导致的布局错位。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.26 00:48:10