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

.NET Maui自定义RadioButton无法横向填充可用空间的问题排查

.NET Maui 9自定义RadioButton边框无法填充容器问题

我正在为.NET Maui 9应用创建一个类模态窗口视图,使用ControlTemplate自定义了RadioButton的外观。虽然设置了HorizontalOptions="Fill",但边框并未视觉上填充剩余空间,不过XAML实时预览显示已填充。请问我的布局存在什么错误?

页面XAML代码

<Border StrokeThickness="2"
        WidthRequest="600"
        VerticalOptions="Center"
        HorizontalOptions="Center">
    <Grid RowDefinitions="Auto,*,Auto"
          Padding="20">
        <Label FontSize="36"
               x:Name="ItemTitle"
               FontAttributes="Bold"
               HorizontalOptions="Center"
               Grid.Row="0"/>
        <ScrollView Grid.Row="1"
                    HorizontalOptions="Fill">
            <VerticalStackLayout RadioButtonGroup.GroupName="RadioGroup"
                                 x:Name="ItemStack"
                                 HorizontalOptions="Fill"
                                 Spacing="5"/>
        </ScrollView>
        <Grid Grid.Row="2"
              Padding="0,10,0,0"
          ColumnSpacing="10"
          ColumnDefinitions="*,*">
            <Button Text="{x:Static local:Lang.cancel}"
                CornerRadius="0"
                    Clicked="OnCancel"/>
            <Button Text="{x:Static local:Lang.confirm}"
                CornerRadius="0"
                Grid.Column="1"
                    Clicked="OnConfirm"/>
        </Grid>
    </Grid>
</Border>

ControlTemplate资源代码

<ContentPage.Resources>
    <ControlTemplate x:Key="SelectorRadioButton">
        <Border Stroke="#F3F2F1"
                StrokeThickness="2"
                HorizontalOptions="Fill">
            <VisualStateManager.VisualStateGroups>
                <VisualStateGroupList>
                    <VisualStateGroup x:Name="CheckedStates">
                        <VisualState x:Name="Checked">
                            <VisualState.Setters>
                                <Setter Property="Stroke"
                                        Value="{StaticResource Primary}" />
                                <Setter TargetName="check"
                                        Property="Opacity"
                                        Value="1" />
                            </VisualState.Setters>
                        </VisualState>
                        <VisualState x:Name="Unchecked">
                            <VisualState.Setters>
                                <Setter Property="BackgroundColor"
                                        Value="{AppThemeBinding Light={StaticResource Gray100}, Dark={StaticResource Gray900}}" />
                                <Setter Property="Stroke"
                                        Value="{AppThemeBinding Light={StaticResource Gray200}, Dark={StaticResource Gray800}}" />
                                <Setter TargetName="check"
                                        Property="Opacity"
                                        Value="0" />
                            </VisualState.Setters>
                        </VisualState>
                    </VisualStateGroup>
                </VisualStateGroupList>
            </VisualStateManager.VisualStateGroups>
            <Grid Margin="5"
                  HorizontalOptions="Fill"
                  ColumnDefinitions="Auto,*">
                <Grid WidthRequest="24"
                      Margin="5,0"
                      HeightRequest="24"
                      HorizontalOptions="Center"
                      VerticalOptions="Center">
                    <Ellipse Stroke="{StaticResource Primary}"
                             WidthRequest="16"
                             HeightRequest="16"
                             HorizontalOptions="Center"
                             VerticalOptions="Center" />
                    <Ellipse x:Name="check"
                             Fill="{StaticResource Primary}"
                             WidthRequest="8"
                             HeightRequest="8"
                             HorizontalOptions="Center"
                             VerticalOptions="Center" />
                </Grid>
                <ContentPresenter Margin="5,0" Grid.Column="1" HorizontalOptions="Fill" VerticalOptions="Fill"/>
            </Grid>
        </Border>
    </ControlTemplate>

    <Style TargetType="RadioButton">
        <Setter Property="ControlTemplate"
                Value="{StaticResource SelectorRadioButton}" />
        <Setter Property="HorizontalOptions"
                Value="Fill" />
    </Style>
</ContentPage.Resources>

后台C#构造函数代码

public Selector(string Title, List<(string, string, string)> Content)
{ 
    InitializeComponent();
    ItemTitle.Text = Title;
    ControlTemplate? style = Resources["SelectorRadioButton"] as ControlTemplate;
    for (int i = 0; i < Content.Count; i++)
    {
        var (icon, title, desc) = Content[i];
        Grid tile = new()
        {
            ColumnDefinitions =
            {
                new ColumnDefinition() { Width = GridLength.Auto },
                new ColumnDefinition() { Width = GridLength.Star }
            },
            RowDefinitions =
            {
                new RowDefinition() { Height = GridLength.Auto },
                new RowDefinition() { Height = GridLength.Auto }
            },
            Margin = new Thickness(5)
        };
        Label titleLabel = new()
        {
            Text = title,
            FontSize = 20,
            FontAttributes = FontAttributes.Bold
        };
        tile.Add(titleLabel,1,0);
        Label descriptionLabel = new()
        {
            Text = desc,
            FontSize = 16,
        };
        tile.Add(descriptionLabel,1,1);
        Image iconImage = new()
        {
            Source = icon,
            WidthRequest = 56,
            HeightRequest = 56,
            Margin = new Thickness(5)
        };
        iconImage.Behaviors.Add(new IconTintColorBehavior { TintColor = GSettings.DarkMode ? Colors.White : Colors.Black });
        tile.Add(iconImage,0,0);
        tile.SetRowSpan(iconImage,2);
        SelectorRadio radio = new()
        {
            Margin = new Thickness(0,5),
            Padding = new Thickness(10,5),
            Content = tile,
            HorizontalOptions = LayoutOptions.Fill,
            ControlTemplate = style,
            Current = i
        };
        ItemStack.Add(radio);
    }
}

问题分析与解决方法

核心问题是RadioButton的内容容器未强制填充宽度,以及ControlTemplate内的Border未指定子元素对齐方式。

具体修复步骤:

  1. 给ControlTemplate中的Border添加HorizontalContentAlignment="Fill"
    确保Border的子元素能填满整个Border宽度,修改后的Border代码:

    <Border Stroke="#F3F2F1"
            StrokeThickness="2"
            HorizontalOptions="Fill"
            HorizontalContentAlignment="Fill">
    
  2. 给后台创建的tile Grid设置HorizontalOptions="Fill"
    让内容容器自身填满RadioButton的可用宽度,修改tile的初始化代码:

    Grid tile = new()
    {
        ColumnDefinitions =
        {
            new ColumnDefinition() { Width = GridLength.Auto },
            new ColumnDefinition() { Width = GridLength.Star }
        },
        RowDefinitions =
        {
            new RowDefinition() { Height = GridLength.Auto },
            new RowDefinition() { Height = GridLength.Auto }
        },
        Margin = new Thickness(5),
        HorizontalOptions = LayoutOptions.Fill // 添加这一行
    };
    

原理说明:

  • HorizontalContentAlignment="Fill"会强制Border的子元素(内部Grid)填充Border的全部宽度,避免子元素仅自适应内容宽度。
  • 后台创建的tile Grid默认是HorizontalOptions="Start",即使ContentPresenter设置了Fill,也需要tile本身设置Fill才能让整个RadioButton的边框视觉上填满容器。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.01 13:19:55