Как настроить группировку RadioButton в DataTemplate

Я разрабатываю программу динамического конструктора тестов. Для динамического создания и изменения элементов дизайнера используются CollectionView и комбинированный DataTemplate, который определяется как ресурс на уровне страницы.

Сначала перейдем к модели

Этот класс представляет ItemsSource CollectionView, в котором IsGrouped="True"

public class Question : ObservableCollection<IItem>
    {
        public Question() { }

        public Question(IEnumerable<IItem> items) : base(items)
        {
            
        }

        private string questionName;
        public string QuestionName
        {
            get => questionName;
            set => questionName = value;
        }     
    }

Эти классы представляют собой объекты, которые хранятся в классе Question, и на их основе создается DataTemplate.

public class CheckBoxItem : IItem
    {
        private string text;
        public string Text
        {
            get => text;
            set => text = value;
        }

        private bool correctAnswer = false;
        public bool CorrectAnswer
        {
            get => correctAnswer;
            set => correctAnswer = value;
        }

        public (string, int) IItem;

        public bool Compare(object obj)
        {
            if (obj is CheckBoxItem cb)
            {
                if (correctAnswer == cb.correctAnswer)
                    return false;
            }
            return true;
        }
    }

public class RadioButtonItem : IItem
    {
        private string text;
        public string Text
        {
            get => text;
            set => text = value;
        }

        private bool correctAnswer = false;
        public bool CorrectAnswer
        {
            get => correctAnswer;
            set => correctAnswer = value;
        }

        public bool Compare(object obj)
        {
            if (obj is RadioButtonItem rb)
            {
                if (correctAnswer == rb.correctAnswer)
                    return false;
            }
            return true;
        }
    }

public class TextItem : IItem
    {
        private string correctAnswer;
        public string CorrectAnswer
        {
            get => correctAnswer;
            set => correctAnswer = value;
        }

        public bool Compare(object obj)
        {
            if (obj is TextItem ti)
            {
                if (correctAnswer == ti.correctAnswer)
                    return false;
            }
            return true;
        }
    }

Сам код представляет собой страницу CreateTestPage, на которой с помощью кнопок можно динамически создавать вопросы класса Question на выбор с помощью CheckBox, RadioButton и Text, а также динамически изменять количество этих элементов.

<ContentPage.Resources>
        <ResourceDictionary>
            <DataTemplate x:Key="ListTemplateCheckBox" x:DataType="model:CheckBoxItem">
                <HorizontalStackLayout>
                    <CheckBox x:Name="ch" IsChecked="{Binding CorrectAnswer, Mode=TwoWay}"/>
                    <Editor x:Name="ed" IsTextPredictionEnabled="True" Background="Transparent" HorizontalOptions="Fill" IsSpellCheckEnabled="True"  Placeholder="Enter your answer" Text="{Binding Text, Mode=TwoWay}"/>
                    <ImageButton x:Name="RemoveItem" Background="White" Source="/Images/del.png"
                         Command="{Binding Source={RelativeSource AncestorType={x:Type vm:CreateTestPageViewModel}}, Path=RemoveItemCommand}"
                         CommandParameter="{Binding}"/>
                </HorizontalStackLayout>
            </DataTemplate>

            <DataTemplate x:Key="ListTemplateRadioButton" x:DataType="model:RadioButtonItem">
                <HorizontalStackLayout RadioButtonGroup.GroupName="{Binding Source={RelativeSource TemplatedParent}, Path=Context}">
                    <RadioButton IsChecked="{Binding CorrectAnswer, Mode=TwoWay}"/>
                    <Editor IsTextPredictionEnabled="True" Background="Transparent" HorizontalOptions="Fill" IsSpellCheckEnabled="True"  Placeholder="Enter your answer" Text="{Binding Text, Mode=TwoWay}"/>
                    <ImageButton x:Name="RemoveItem" Background="White" Source="/Images/del.png"
                             Command="{Binding Source={RelativeSource AncestorType={x:Type vm:CreateTestPageViewModel}}, Path=RemoveItemCommand}"
                             CommandParameter="{Binding}"/>
                </HorizontalStackLayout>
            </DataTemplate>

            <DataTemplate x:Key="ListTemplateText" x:DataType="model:TextItem">
                <HorizontalStackLayout>
                    <Editor IsTextPredictionEnabled="True" Background="Transparent" HorizontalOptions="Fill" IsSpellCheckEnabled="True"  Placeholder="Enter your answer" Text="{Binding CorrectAnswer, Mode=TwoWay}"/>
                    <ImageButton BackgroundColor="White" x:Name="RemoveItem" Source="/Images/del.png"
                             Command="{Binding Source={RelativeSource AncestorType={x:Type vm:CreateTestPageViewModel}}, Path= RemoveItemCommand}"
                             CommandParameter="{Binding}"/>
                </HorizontalStackLayout>
            </DataTemplate>

            <templates:CreateTestTemplate x:Key="ItemSelector" CheckBox="{StaticResource ListTemplateCheckBox}"
                                      RadioButton="{StaticResource ListTemplateRadioButton}"
                                      Text="{StaticResource ListTemplateText}"/>
        </ResourceDictionary>
    </ContentPage.Resources>
    
    <Grid>
            <Grid.RowDefinitions>
                <RowDefinition Height="0.15*" />
                <RowDefinition Height="*" />
            </Grid.RowDefinitions>
            <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*"/>
        </Grid.ColumnDefinitions>
            <VerticalStackLayout Grid.Row="0" HorizontalOptions="Fill">
                <Button Text="CreateTest" Command="{Binding CreateTestCommand}"/>
                <Editor Text="{Binding Test.Name, Mode=TwoWay}"/>
            </VerticalStackLayout>

        <CollectionView x:Name="Qestions" Grid.Row="1" ItemsSource="{Binding Test.Questions}"                     
                        SelectionMode="None" VerticalOptions="Fill" HorizontalOptions="StartAndExpand" RadioButtonGroup.GroupName="1" IsGrouped="True" ItemTemplate="{StaticResource ItemSelector}">

            <CollectionView.GroupHeaderTemplate>
                <DataTemplate x:DataType="model:Question">
                    <HorizontalStackLayout HorizontalOptions="Fill">
                        <Editor Placeholder="Enter a question title" Text="{Binding QuestionName}" HorizontalOptions="FillAndExpand"/>
                        <ImageButton  Source="/Images/del.png" Command="{Binding Source={RelativeSource AncestorType={x:Type vm:CreateTestPageViewModel}}, Path= RemoveQuestionCommand}" CommandParameter="{Binding}"/>
                    </HorizontalStackLayout>
                </DataTemplate>
            </CollectionView.GroupHeaderTemplate>

            <CollectionView.GroupFooterTemplate>
                <DataTemplate x:DataType="vm:CreateTestPageViewModel">
                    <ImageButton HorizontalOptions="Center" Source="/Images/plus.png" Command="{Binding Source={RelativeSource AncestorType={x:Type vm:CreateTestPageViewModel}}, Path= AddItemCommand}" CommandParameter="{Binding}"/>
                </DataTemplate>
            </CollectionView.GroupFooterTemplate>

            <CollectionView.ItemsLayout>
                <LinearItemsLayout RadioButtonGroup.GroupName="1" Orientation="Vertical" BindingContext="{x:Reference Qestions}"/>
            </CollectionView.ItemsLayout>
        </CollectionView>

        <Border Background="White" Grid.Row="{Binding Test.Questions.Count}" StrokeShape="RoundRectangle 25,0,25,0" HorizontalOptions="End" VerticalOptions="Start">
            <VerticalStackLayout>
                <Button BackgroundColor="Red" Command="{Binding CreateQuestionCheckBoxItemsCommand}"/>
                <Button BackgroundColor="Red" Command="{Binding CreateQuestionRadioButtomItemsCommand}"/>
                <Button BackgroundColor="Red" Command="{Binding CreateQuestionTextItemsCommand}"/>
            </VerticalStackLayout>
        </Border>
    </Grid>

github

Я попытался настроить RadioButtonGroup, переопределив

<CollectionView.ItemsLayout>
                <LinearItemsLayout RadioButtonGroup.GroupName="1" Orientation="Vertical" BindingContext="{x:Reference Qestions}"/>
            </CollectionView.ItemsLayout>

1 был написан в качестве эксперимента, чтобы понять, сгруппированы ли элементы DataTemplate или нет. Но это не помогло, поскольку элементы DataTemplate не видят элементы страницы, хотя они определены на уровне страницы. Также попробовал x:Reference и RelativeSource для доступа к элементам извне DataTemplate и использовал их {Binding} для привязки группировки непосредственно к родительскому классу вопроса к контейнеру внутри DataTemplate, но не смог получить к ним доступ.


Ответы (1 шт):

Автор решения: Kowari
<RadioButton IsChecked="{Binding CorrectAnswer, Mode=TwoWay}">
                    <RadioButton.GestureRecognizers>
                        <TapGestureRecognizer Command="{Binding Source={RelativeSource AncestorType={x:Type vm:CreateTestPageViewModel}}, Path=RadioButtonChekcedCommand}"
                             CommandParameter="{Binding}"/>
                    </RadioButton.GestureRecognizers>
                </RadioButton>
[RelayCommand]
        private void RadioButtonChekced(RadioButtonItem item)
        {
            var items = Test.Questions.Single(x => x.Contains(item));

            foreach (RadioButtonItem item2 in items)
            {
                 item2.CorrectAnswer = false;
            }            
            item.CorrectAnswer = true;
        }
→ Ссылка