radio-buttondatatemplatemauicontroltemplate

MAUI RadioButton with ControlTemplate and DataTemplate doesn't render content


I am trying to create RadioButtons that bind to a list of content using a DataTemplate, and render based on a ControlTemplate. The data binding is working - I get a button for each element in the list. The VisualStateManager is also working - I get the requested state changes as I click on the buttons.

However, the ContentPresenter is not working - my buttons are empty. I'm also getting an InvalidCastException each time I click on a button. Here is my code.

<ContentPage.BindingContext>
    <pages:ActivitiesViewModel/>
</ContentPage.BindingContext>

<ContentPage.Resources>
    <ControlTemplate x:Key="MuscleGroupButtonsTemplate">
        <Border
        Stroke="{StaticResource SecondaryBrush}"
        StrokeThickness="1"
        Background="Transparent"
        >
            <Border.StrokeShape>
                <RoundRectangle CornerRadius="8"/>
            </Border.StrokeShape>
            <VisualStateManager.VisualStateGroups>
                <VisualStateGroupList>
                    <VisualStateGroup x:Name="CheckedStates">
                        <VisualState x:Name="Checked">
                            <VisualState.Setters>
                                <Setter
                                    Property="Background"
                                    Value="{StaticResource AccentBrush}"/>
                                <Setter
                                    Property="Stroke"
                                    Value="{StaticResource SecondaryBrush}"/>
                            </VisualState.Setters>
                        </VisualState>
                        <VisualState x:Name="Unchecked">
                            <VisualState.Setters>
                                <Setter
                                    Property="Background"
                                    Value="{StaticResource NeutralBrush}"/>
                                <Setter
                                    Property="Stroke"
                                    Value="{StaticResource SecondaryBrush}"/>
                            </VisualState.Setters>
                        </VisualState>
                    </VisualStateGroup>
                </VisualStateGroupList>
            </VisualStateManager.VisualStateGroups>
            <ContentPresenter HeightRequest="64"/>
        </Border>
    </ControlTemplate>
</ContentPage.Resources>

<VerticalStackLayout Spacing="12" 
    RadioButtonGroup.GroupName="MuscleGroups"
    RadioButtonGroup.SelectedValue="{Binding SelectedMuscleGroup}"
    BindableLayout.ItemsSource="{Binding MuscleGroupList}">

    <VerticalStackLayout.Resources>
        <Style TargetType="RadioButton">
            <Setter Property="ControlTemplate" Value="{StaticResource MuscleGroupButtonsTemplate}"/>
        </Style>
    </VerticalStackLayout.Resources>

    <BindableLayout.ItemTemplate>
        <DataTemplate x:DataType="m:MuscleGroup">
            <RadioButton Value="{Binding Id}" CheckedChanged="OnMuscleGroupChanged">
                <RadioButton.Content>
                    <HorizontalStackLayout Margin="12,6,0,0" Spacing="8">
                        <Image WidthRequest="64"
                            Aspect="AspectFit"
                            Source="{Binding Icon}"/>
                        <Label Style="{StaticResource Headline}" VerticalOptions="Center">
                            <Label.FormattedText>
                                <FormattedString>
                                    <Span Text="{Binding Name}"/>
                                </FormattedString>
                            </Label.FormattedText>
                        </Label>
                    </HorizontalStackLayout>
                </RadioButton.Content>
            </RadioButton>
        </DataTemplate>
    </BindableLayout.ItemTemplate>
</VerticalStackLayout>

I'm also puzzled because the Visual Tree shows the expected control hierarchy. enter image description here

Oh, and BTW - my RadioButtonGroup.SelectedValue is not working either.

I would appreciate any assistance. Thanks.

Chuck


Solution

  • With Content part of ControlTemplate and use of TemplateBinding Content.<property>:

    <ControlTemplate x:Key="RadioButtonControlTemplate">
      <Border>
        <VisualStateManager.VisualStateGroups>
          <VisualStateGroupList>
            <VisualStateGroup x:Name="CheckedStates">
              <VisualState x:Name="Checked">
                <VisualState.Setters>
                  <Setter Property="Stroke" Value="Red"/>
                </VisualState.Setters>
              </VisualState>
              <VisualState x:Name="Unchecked">
                <VisualState.Setters>
                  <Setter Property="Stroke" Value="Green"/>
                </VisualState.Setters>
              </VisualState>
            </VisualStateGroup>
          </VisualStateGroupList>
        </VisualStateManager.VisualStateGroups>
    
        <VerticalStackLayout x:DataType="m:NameId">
          <Label Text="{TemplateBinding Content.Name}"/>
          <Label Text="{TemplateBinding Content.Id}"/>
        </VerticalStackLayout>
    
      </Border>
    </ControlTemplate>
    

    VerticalStackLayout:

    <VerticalStackLayout
      RadioButtonGroup.GroupName="NameIdGroup"
      BindableLayout.ItemsSource="{Binding NameIdList}">
    
      <VerticalStackLayout.Resources>
        <Style TargetType="RadioButton">
          <Setter Property="ControlTemplate" 
                  Value="{StaticResource RadioButtonControlTemplate}"/>
        </Style>
      </VerticalStackLayout.Resources>
    
      <BindableLayout.ItemTemplate>
        <DataTemplate>
          <RadioButton Content="{Binding .}">
            <RadioButton.GestureRecognizers>
              <TapGestureRecognizer 
                Tapped="RadioButton_Tapped" 
                CommandParameter="{Binding .}" />
            </RadioButton.GestureRecognizers>
          </RadioButton>
        </DataTemplate>
      </BindableLayout.ItemTemplate>
    
    </VerticalStackLayout>
    

    NameId class and ViewModel:

    public class NameId
    {
      public string Name { get; set; }
      public string Id { get; set; }
    }
    
    public class ViewModel
    {
      public ObservableCollection<NameId> NameIdList { get; set; }
    
      public ViewModel()
      {
        NameIdList = new ObservableCollection<NameId>
        {
            new NameId { Id = "Id A", Name = "Name A" },
            new NameId { Id = "Id B", Name = "Name B" },
            new NameId { Id = "Id C", Name = "Name C" }
        };
      }
    }