Не могу понять как отслеживать какой чекбокс рядом с каким ответом выбран
Есть код, который из 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 шт):
Ваша задача решается немного проще. 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# коде, а контрол их сам забирает. Подробнее можно почитать об этом здесь.

