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

.NET MAUI中如何基于可绑定属性动态修改Grid.Row?

.NET MAUI中如何基于可绑定属性动态修改Grid.Row?

嗨,我来帮你搞定这个Grid布局的问题!先理清楚你遇到的核心卡点,再给你几个简单靠谱的解决方案。

一、为什么你之前的方法都没生效?

你踩的坑其实是附加属性的使用误区:Grid.Row是附加属性,不是CollectionView自身的可绑定属性,所以直接绑定或简单用DataTrigger时,容易忽略两个关键问题:

  1. 当你把CollectionView设到Row1时,Row1的预约控件已经设为IsVisible="False",而Row1的Height是Auto,这时候Row1的高度会自动收缩为0——相当于Row1“消失”了,CollectionView在Row1的话,就会被挤到Row0的Header下方,甚至因为没有设置RowSpan,只能在高度为0的Row1里显示,视觉上就和Header重叠了。
  2. 你之前的DataTrigger写法语法本身没问题,但没处理RowSpan,导致CollectionView无法占据原来Row2的空间,才会出现位置异常。

二、三个可行的解决方案(从简单到灵活)

方案3:动态调整行高(最推荐,代码最简洁)

其实你根本不需要移动CollectionView的位置!只要让Row1在没有预约时完全收缩,Row2的CollectionView会自动上移,完美符合你的需求:

<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="0.4*"/>
        <RowDefinition x:Name="AppointmentRowDef" Height="Auto"/>
        <RowDefinition Height="*"/>
    </Grid.RowDefinitions>

    <!-- Header (始终可见) -->
    <components:HomeHeader Grid.Row="0" />

    <!-- 预约信息(无预约时隐藏) -->
    <Grid Grid.Row="1">
        <components:HomePageDisplayingAppointmentBorder IsVisible="{Binding HasAppointments}"/>
    </Grid>

    <!-- CollectionView固定在Row2,不用动位置 -->
    <CollectionView Grid.Row="2"
                    ItemsSource="{Binding HomeButton}"
                    VerticalOptions="FillAndExpand">
        <CollectionView.ItemTemplate>
            <DataTemplate>
                <components:HomeButton Data="{Binding .}" 
                                       BackgroundColor="{DynamicResource Company}" 
                                       TextColor="{DynamicResource Company}" 
                                       FrameTapped="OnButtonTapped" />
            </DataTemplate>
        </CollectionView.ItemTemplate>
    </CollectionView>

    <!-- 给Grid加触发器,动态修改Row1的高度 -->
    <Grid.Triggers>
        <DataTrigger TargetType="Grid" Binding="{Binding HasAppointments}" Value="False">
            <Setter TargetName="AppointmentRowDef" Property="Height" Value="0" />
        </DataTrigger>
        <DataTrigger TargetType="Grid" Binding="{Binding HasAppointments}" Value="True">
            <Setter TargetName="AppointmentRowDef" Property="Height" Value="Auto" />
        </DataTrigger>
    </Grid.Triggers>
</Grid>

这个方案的逻辑很简单:有预约时Row1用Auto高度显示内容,无预约时Row1高度设为0,Row2的CollectionView就会自动填充原来Row1+Row2的空间,完全不会和Header重叠。


方案1:修正DataTrigger,处理Row和RowSpan

如果你一定要手动调整CollectionView的Row位置,那得同时设置RowSpan,让它能跨多行显示:

<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="0.4*"/>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="*"/>
    </Grid.RowDefinitions>

    <components:HomeHeader Grid.Row="0" />

    <Grid Grid.Row="1" x:Name="AppointmentRow">
        <components:HomePageDisplayingAppointmentBorder IsVisible="{Binding HasAppointments}"/>
        <!-- 让Row1在无预约时收缩 -->
        <Grid.Triggers>
            <DataTrigger TargetType="Grid" Binding="{Binding HasAppointments}" Value="False">
                <Setter Property="HeightRequest" Value="0" />
            </DataTrigger>
        </Grid.Triggers>
    </Grid>

    <CollectionView x:Name="MyCollectionView"
                    Grid.Row="2"
                    Grid.RowSpan="1"
                    ItemsSource="{Binding HomeButton}"
                    VerticalOptions="FillAndExpand">
        <CollectionView.Triggers>
            <!-- 无预约时,移到Row1并跨Row1+Row2 -->
            <DataTrigger TargetType="CollectionView" Binding="{Binding HasAppointments}" Value="False">
                <Setter Property="Grid.Row" Value="1" />
                <Setter Property="Grid.RowSpan" Value="2" />
            </DataTrigger>
            <!-- 有预约时恢复原位置 -->
            <DataTrigger TargetType="CollectionView" Binding="{Binding HasAppointments}" Value="True">
                <Setter Property="Grid.Row" Value="2" />
                <Setter Property="Grid.RowSpan" Value="1" />
            </DataTrigger>
        </CollectionView.Triggers>
        <CollectionView.ItemTemplate>
            <DataTemplate>
                <components:HomeButton Data="{Binding .}" 
                                       BackgroundColor="{DynamicResource Company}" 
                                       TextColor="{DynamicResource Company}" 
                                       FrameTapped="OnButtonTapped" />
            </DataTemplate>
        </CollectionView.ItemTemplate>
    </CollectionView>
</Grid>

这里的关键是Grid.RowSpan="2",让CollectionView在无预约时同时占据Row1和Row2的空间,这样就不会被压缩到Row1的0高度里了。


方案2:用转换器绑定附加属性

如果你更倾向于用绑定的方式,可以写两个值转换器,把HasAppointments的bool值转成对应的Row和RowSpan:
首先写转换器代码:

// 把bool转成Grid.Row的值
public class BoolToRowConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return (bool)value ? 2 : 1;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

// 把bool转成Grid.RowSpan的值
public class BoolToRowSpanConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return (bool)value ? 1 : 2;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

然后在XAML的资源里注册转换器:

<ContentPage.Resources>
    <local:BoolToRowConverter x:Key="BoolToRowConverter"/>
    <local:BoolToRowSpanConverter x:Key="BoolToRowSpanConverter"/>
</ContentPage.Resources>

最后绑定CollectionView的附加属性:

<CollectionView 
    Grid.Row="{Binding HasAppointments, Converter={StaticResource BoolToRowConverter}}"
    Grid.RowSpan="{Binding HasAppointments, Converter={StaticResource BoolToRowSpanConverter}}"
    ItemsSource="{Binding HomeButton}"
    VerticalOptions="FillAndExpand">
    <!-- 模板代码不变 -->
</CollectionView>

三、回答你的三个具体问题

  1. 动态修改Grid.Row的正确方法:
    优先推荐方案3(调整行高),逻辑最简单且不易出错;如果一定要移动控件位置,用DataTrigger设置Grid.Row+Grid.RowSpan(方案1),或者用转换器绑定附加属性(方案2)。
  2. 为什么SetValue(Grid.RowProperty, ...)没生效?
    你只修改了CollectionView的Row,但没设置Grid.RowSpan,导致它只能在高度为0的Row1里显示,视觉上就和Header重叠了——相当于CollectionView被“挤”到了Header的下方区域,看起来像是跑到了Row0里。另外,记得在OnDisappearing里移除PropertyChanged事件订阅,避免内存泄漏。
  3. 更好的解决方案?
    必须是方案3!不需要修改控件位置,只通过行高的动态调整实现布局变化,代码最简洁,维护成本最低,完全符合你的需求。

备注:内容来源于stack exchange,提问作者Mohammed Khalil

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.14 10:49:51