Не могу понять как отслеживать какой чекбокс рядом с каким ответом выбран

Есть код, который из json файла достаёт вопрос и ответы, и это всё отображается в окошке, проблема в том, что я не могу понять как мне отслеживать какие чекбоксы нажаты.

namespace TestSuit
{
    /// <summary>
    /// Логика взаимодействия для TestWindow.xaml
    /// </summary>
    public partial class TestWindow : Window
    {
        Grid mainGrid = new Grid();
        public TestWindow(List<Questions> json)
        {
            Dictionary<string, bool> questAnsw = new();
            List<Dictionary<string, bool>> questAnswCompleteForm = new();
            InitializeComponent();
            Grid.SetIsSharedSizeScope(mainGrid, false);
            for(int i=0; i<json.Count; i++)
            {
                questAnsw.Clear();
                questAnsw.Add(json[i].TrueAnswer, true);
                for(int f = 0; f<json[i].FalseAnswer.Count; f++)
                {
                    questAnsw.Add(json[i].FalseAnswer[f], false);
                }
                questAnswCompleteForm.Add(questAnsw);
                mainPanel.Children.Add(AddGrid(json[i], questAnswCompleteForm[i]));
            }
            Button complete = new();
            mainPanel.Children.Add(complete);
            this.Show();
        }
        public Grid AddGrid(Questions question, Dictionary<string, bool> questAnsw)
        {
            // Create the Grid
            Grid myGrid = new Grid();
            myGrid.Width = 350;
            myGrid.Height = 200;
            myGrid.HorizontalAlignment = HorizontalAlignment.Left;
            myGrid.VerticalAlignment = VerticalAlignment.Top;
            myGrid.ShowGridLines = false;
            Grid.SetIsSharedSizeScope(myGrid, false);
            mainGrid.RowDefinitions.Add(new RowDefinition());
            

            // Define the Columnsw
            myGrid.ColumnDefinitions.Add(new ColumnDefinition());
            myGrid.ColumnDefinitions.Add(new ColumnDefinition());

            // Define the Rows
            myGrid.RowDefinitions.Add(new RowDefinition());
            myGrid.RowDefinitions.Add(new RowDefinition());
            myGrid.RowDefinitions.Add(new RowDefinition());
            myGrid.RowDefinitions.Add(new RowDefinition());
            myGrid.RowDefinitions.Add(new RowDefinition());

            // Add the first text cell to the Grid
            Label lQuestion = new Label();
            lQuestion.Content = question.Question;
            lQuestion.FontSize = 13;
            lQuestion.FontWeight = FontWeights.Bold;
            Grid.SetColumnSpan(lQuestion, 2);
            Grid.SetRow(lQuestion, 0);

            // Add the second text cell to the Grid
            TextBlock answer1 = new TextBlock();
            answer1.Text = questAnsw.ElementAt(0).Key;
            answer1.FontSize = 12;
            answer1.FontWeight = FontWeights.Bold;
            Grid.SetRow(answer1, 1);
            Grid.SetColumn(answer1, 0);

            // Add the third text cell to the Grid
            TextBlock answer2 = new TextBlock();
            answer2.Text = questAnsw.ElementAt(1).Key;
            answer2.FontSize = 12;
            answer2.FontWeight = FontWeights.Bold;
            Grid.SetRow(answer2, 2);
            Grid.SetColumn(answer2, 0);

            // Add the fourth text cell to the Grid
            TextBlock answer3 = new TextBlock();
            answer3.Text = questAnsw.ElementAt(2).Key;
            answer3.FontSize = 12;
            answer3.FontWeight = FontWeights.Bold;
            Grid.SetRow(answer3, 3);
            Grid.SetColumn(answer3, 0);

            // Add the fourth text cell to the Grid
            TextBlock answer4 = new TextBlock();
            answer4.Text = questAnsw.ElementAt(3).Key;
            answer4.FontSize = 12;
            answer4.FontWeight = FontWeights.Bold;
            Grid.SetRow(answer4, 4);
            Grid.SetColumn(answer4, 0);

            RadioButton an1 = new();
            Grid.SetRow(an1, 1);
            Grid.SetColumn(an1, 1);

            RadioButton an2 = new();
            Grid.SetRow(an2, 2);
            Grid.SetColumn(an2, 1);

            RadioButton an3 = new();
            Grid.SetRow(an3, 3);
            Grid.SetColumn(an3, 1);

            RadioButton an4 = new();
            Grid.SetRow(an4, 4);
            Grid.SetColumn(an4, 1);

            // Add the TextBlock elements to the Grid Children collection
            myGrid.Children.Add(lQuestion);
            myGrid.Children.Add(answer1);
            myGrid.Children.Add(answer2);
            myGrid.Children.Add(answer3);
            myGrid.Children.Add(answer4);
            myGrid.Children.Add(an1);
            myGrid.Children.Add(an2);
            myGrid.Children.Add(an3);
            myGrid.Children.Add(an4);

            // Add the Grid as the Content of the Parent Window Object
            //return myGrid;
            
            return myGrid;
        }
    }
}

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

Автор решения: aepot

Ваша задача решается немного проще. WPF так устроен, что в нем крайне неудобно работать с контролами напрямую, но очень удобно сделано их поведение, если использовать привязки данных. Я не буду ничего рассказывать про великолепный шаблон проектирования MVVM, но вы поищите и обязательно почитайте про него, если планируете дальше учиться разрабатывать приложения под WPF.

К примеру, пусть будет такая структура данных для вопросов.

public class Question
{
    public string Text { get; set; }
    public List<Answer> Answers { get; set; }
}

public class Answer
{
    public string Text { get; set; }
    public bool Selected { get; set; }
}

Тогда вызвать окно можно будет например вот так

private void Window_Loaded(object sender, RoutedEventArgs e)
{
    List<Question> questions = new List<Question>
    {
        new Question
        {
            Text = "Текст вопроса 1",
            Answers = new List<Answer>
            {
                new Answer { Text = "Ответ 1" },
                new Answer { Text = "Ответ 2" },
                new Answer { Text = "Ответ 3" },
                new Answer { Text = "Ответ 4" },
            }
        },
        new Question
        {
            Text = "Текст вопроса 2",
            Answers = new List<Answer>
            {
                new Answer { Text = "Ответ 1" },
                new Answer { Text = "Ответ 2" },
                new Answer { Text = "Ответ 3" },
                new Answer { Text = "Ответ 4" },
                new Answer { Text = "Ответ 5" },
                new Answer { Text = "Ответ 6" }
            }
        }
    };
    new TestWindow(questions).Show();
}

И если в окне TestWindow реализовать вот такую разметку

<Window x:Class="WpfApp4.TestWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfApp4"
        mc:Ignorable="d"
        Title="TestWindow" Height="450" Width="800">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>
        <ItemsControl ItemsSource="{Binding Questions}" Margin="5">
            <ItemsControl.ItemTemplate>
                <DataTemplate>
                    <StackPanel>
                        <TextBlock Text="{Binding Text}" FontWeight="Bold"/>
                        <ItemsControl ItemsSource="{Binding Answers}" Margin="5">
                            <ItemsControl.ItemTemplate>
                                <DataTemplate>
                                    <RadioButton GroupName="{Binding DataContext.Text, RelativeSource={RelativeSource AncestorType=ItemsControl, AncestorLevel=1}}" 
                                                 Content="{Binding Text}" 
                                                 IsChecked="{Binding Selected}"
                                                 Margin="5"/>
                                </DataTemplate>
                            </ItemsControl.ItemTemplate>
                        </ItemsControl>
                    </StackPanel>
                </DataTemplate>
            </ItemsControl.ItemTemplate>
        </ItemsControl>
        <Button Content="Проверить" Grid.Row="1" Margin="5" Click="Button_Click"/>
    </Grid>
</Window>

И вот такой код-бихайнд

public partial class TestWindow : Window
{
    public List<Question> Questions { get; }

    public TestWindow(List<Question> questions)
    {
        InitializeComponent();
        Questions = questions;
        DataContext = this;
    }

    private void Button_Click(object sender, RoutedEventArgs e)
    {
        StringBuilder sb = new StringBuilder();
        foreach (Question question in Questions)
        {
            sb.Append("Вопрос: ").AppendLine(question.Text);
            sb.Append("Ответ: ").AppendLine(question.Answers.FirstOrDefault(a => a.Selected)?.Text ?? "<не выбран>");
            sb.AppendLine();
        }
        MessageBox.Show(sb.ToString(), "Ответы");
    }
}

Тогда окно быдет выглядеть вот так:

введите сюда описание изображения

А MessageBox, выскакивающий при нажатии на кнопку вот так:

введите сюда описание изображения

Если вам нужна полоса прокрутки, просто в XAML положите внешний ItemsControl в ScrollViewer и настройте параметры полос прокрутки сроллвьюверу.

Собственно, определить, какие именно ответы выбраны можно вообще не вспоминая про то что контрлы существуют, потому что нужные значения уже содержатся в самих данных.

WPF так же возможно организовать и двухстороннюю привязку данных - это когда вы меняете данные в C# коде, а контрол их сам забирает. Подробнее можно почитать об этом здесь.

→ Ссылка