如何在 XAML 中构建问卷 UserControl,其问题具有相同的答案?
How can I build a questionnaire UserControl in XAML whose questions share the same answers?
我想构建一个 UserControl
来表示问卷,如下图所示(忽略缺少样式)。
我希望能够指定XAML中的重要内容,比如
<local:QuestionnaireControl>
<local:QuestionnaireControl.Questions>
<local:QuestionAndAnswers Number="1" Question="Is this working?" />
<local:QuestionAndAnswers Number="2" Question="Are these questions sharing answers?" />
</local:QuestionnaireControl.Questions>
<local:QuestionnaireControl.Answers>
<local:Answer Value="0" Text="Yes" />
<local:Answer Value="1" Text="No" />
<local:Answer Value="2" Text="Help Me Please" />
</local:QuestionnaireControl.Answers>
</local:QuestionnaireControl>
所以我有以下QuestionnaireControl.xaml
<UserControl x:Class="MyProject.QuestionnaireControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
xmlns:local="clr-namespace:MyProject"
DataContext="{Binding RelativeSource={RelativeSource Self}}"
d:DataContext="{d:DesignInstance Type=local:QuestionnaireControl, IsDesignTimeCreatable=True}">
<ItemsControl ItemsSource="{Binding Questions}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Vertical"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Number, StringFormat='{}{0}.'}" Margin="0,0,10,0" />
<TextBlock Text="{Binding Question}" Width="220"/>
<ItemsControl ItemsSource="{Binding Answers}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<RadioButton
Content="{Binding Text}"
IsChecked="{Binding IsSelected}"
GroupName="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=local:QuestionAndAnswers}, Path=Question}"
Margin="0,0,10,0"
/>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</UserControl>
以及以下QuestionnaireControl.xaml.cs
public partial class QuestionnaireControl : UserControl
{
public QuestionnaireControl()
{
InitializeComponent();
if (System.ComponentModel.DesignerProperties.GetIsInDesignMode(this))
{
Questions = new List<QuestionAndAnswers> {
new QuestionAndAnswers() { Number=1, Question="Do you like pizza?" },
new QuestionAndAnswers() { Number=2, Question="Can you surf?" },
new QuestionAndAnswers() { Number=3, Question="Are you funny?" },
new QuestionAndAnswers() { Number=4, Question="Is Monday your favorite day of the week?" },
new QuestionAndAnswers() { Number=5, Question="Have you been to Paris?" },
new QuestionAndAnswers() { Number=6, Question="When sleeping, do you snore?" },
new QuestionAndAnswers() { Number=7, Question="Could you be living in a dream?" }
};
Answers = new List<Answer> {
new Answer() { Value=1, Text="Yes", IsSelected=false },
new Answer() { Value=2, Text="No", IsSelected=false },
new Answer() { Value=3, Text="Sort Of", IsSelected=false },
};
}
else
{
Questions = new List<QuestionAndAnswers>();
Answers = new List<Answer>();
}
// Copy Answers to each QuestionAndAnswers.
foreach (QuestionAndAnswers qa in Questions)
{
qa.Answers = new List<Answer>(Answers);
}
}
public List<QuestionAndAnswers> Questions
{
get { return (List<QuestionAndAnswers>)GetValue(QuestionsProperty); }
set { SetValue(QuestionsProperty, value); }
}
public static readonly DependencyProperty QuestionsProperty =
DependencyProperty.Register("Questions", typeof(List<QuestionAndAnswers>), typeof(QuestionnaireControl), new FrameworkPropertyMetadata(new List<QuestionAndAnswers>()));
public List<Answer> Answers
{
get { return (List<Answer>)GetValue(AnswersProperty); }
set { SetValue(AnswersProperty, value); }
}
public static readonly DependencyProperty AnswersProperty =
DependencyProperty.Register("Answers", typeof(List<Answer>), typeof(QuestionnaireControl), new FrameworkPropertyMetadata(new List<Answer>()));
}
public class QuestionAndAnswers
{
public int Number { get; set; }
public string Question { get; set; }
public List<Answer> Answers { get; set; }
}
public class Answer
{
public string Text { get; set; }
public int Value { get; set; }
public bool IsSelected { get; set; }
}
使用上面的代码,我可以在 Visual Studio 设计器中生成上面 QuestionnaireControl
的图像。但是当我实际使用 QuestionnaireControl
时,根据上面的例子,问题被呈现而不是答案。有谁知道我需要调整什么?
为每个问题复制答案选项的代码无法正常工作。 For once 它只在构造函数中运行一次,而不是在添加答案之后运行。此外,它不会创建 Answer
对象的新实例,因此所有问题都保持相同的引用,并且当为一个问题选择第一个选项时,它会立即为所有其他问题选择。每个问题都需要自己的一组答案:
public class QuestionAndAnswers
{
public QuestionAndAnswers()
{
Answers = new List<Answer>();
}
public int Number { get; set; }
public string Question { get; set; }
public List<Answer> Answers { get; set; }
}
<local:QuestionnaireControl>
<local:QuestionnaireControl.Questions>
<local:QuestionAndAnswers Number="1" Question="Is this working?">
<local:QuestionAndAnswers.Answers>
<local:Answer Value="0" Text="Yes" />
<local:Answer Value="1" Text="No" IsSelected="true"/>
<local:Answer Value="2" Text="Help Me Please" />
</local:QuestionAndAnswers.Answers>
</local:QuestionAndAnswers>
<local:QuestionAndAnswers Number="2" Question="Are these questions sharing answers?">
<local:QuestionAndAnswers.Answers>
<local:Answer Value="0" Text="Yes" IsSelected="true"/>
<local:Answer Value="1" Text="No" />
<local:Answer Value="2" Text="Help Me Please" />
</local:QuestionAndAnswers.Answers>
</local:QuestionAndAnswers>
</local:QuestionnaireControl.Questions>
</local:QuestionnaireControl>
如果您将控件的 Answers
属性 类型更改为 ObservableCollection<Answer>
您可以处理它的 CollectionChanged
事件并复制每个问题的答案:
public partial class QuestionnaireControl : UserControl
{
public QuestionnaireControl()
{
InitializeComponent();
Questions = new List<QuestionAndAnswers>();
Answers = new ObservableCollection<Answer>();
Answers.CollectionChanged += Answers_CollectionChanged;
}
private void Answers_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
foreach (var question in Questions)
{
question.Answers = new List<Answer>();
foreach (var answer in Answers)
{
question.Answers.Add(new Answer() { Text = answer.Text, Value = answer.Value, IsSelected = answer.IsSelected });
}
}
}
public List<QuestionAndAnswers> Questions
{
get { return (List<QuestionAndAnswers>)GetValue(QuestionsProperty); }
set { SetValue(QuestionsProperty, value); }
}
public static readonly DependencyProperty QuestionsProperty =
DependencyProperty.Register("Questions", typeof(List<QuestionAndAnswers>), typeof(QuestionnaireControl));
public ObservableCollection<Answer> Answers
{
get { return (ObservableCollection<Answer>)GetValue(AnswersProperty); }
set { SetValue(AnswersProperty, value); }
}
public static readonly DependencyProperty AnswersProperty =
DependencyProperty.Register("Answers", typeof(ObservableCollection<Answer>), typeof(QuestionnaireControl), new FrameworkPropertyMetadata(null));
}
复制 Answer
对象 (new Answer() {...}
) 很容易,但是检测它应该发生的时刻是很棘手的。 AnswersProperty
仅更改 1 次(当分配新的 List<Answer>
时),然后项目被添加到该列表中,我们无法获得通知。而且我们不能在 xaml 中创建通用列表(标记限制)。然而,一个已知的解决方法是创建从通用集合派生的专门集合。这是一个完整的示例(您可能希望将 INotifyPropertyChnaged 实现添加到 Answer
和 QuestionAndAnswers
类):
public class QuestionAndAnswers
{
public QuestionAndAnswers()
{
Answers = new ObservableCollection<Answer>();
}
public int Number { get; set; }
public string Question { get; set; }
public ObservableCollection<Answer> Answers { get; private set; }
}
public class Answer : ICloneable
{
public string Text { get; set; }
public int Value { get; set; }
public bool IsSelected { get; set; }
public object Clone()
{
return MemberwiseClone();
}
}
public class QuestionCollection : List<QuestionAndAnswers>
{
}
public class AnswerCollection : List<Answer>
{
}
public partial class QuestionnaireControl : UserControl
{
public QuestionnaireControl()
{
InitializeComponent();
}
public List<QuestionAndAnswers> Questions
{
get { return (List<QuestionAndAnswers>) GetValue(QuestionsProperty); }
set { SetValue(QuestionsProperty, value); }
}
public static readonly DependencyProperty QuestionsProperty =
DependencyProperty.Register("Questions", typeof (List<QuestionAndAnswers>), typeof (QuestionnaireControl),
new PropertyMetadata(new List<QuestionAndAnswers>(), QuestionsChangedCallback));
private static void QuestionsChangedCallback(DependencyObject o, DependencyPropertyChangedEventArgs e)
{
var q = o as QuestionnaireControl;
if (q == null)
return;
CopyAnswers(q);
}
public List<Answer> Answers
{
get { return (List<Answer>) GetValue(AnswersProperty); }
set { SetValue(AnswersProperty, value); }
}
public static readonly DependencyProperty AnswersProperty =
DependencyProperty.Register("Answers", typeof(List<Answer>), typeof(QuestionnaireControl),
new PropertyMetadata(new List<Answer>(), AnswersChangedCallback));
private static void AnswersChangedCallback(DependencyObject o, DependencyPropertyChangedEventArgs e)
{
var q = o as QuestionnaireControl;
if (q == null)
return;
CopyAnswers(q);
}
private static void CopyAnswers(QuestionnaireControl q)
{
if (q.Answers == null || q.Questions == null)
return;
foreach (var question in q.Questions)
{
// remove old Answers
question.Answers.Clear();
// adding new Answers to each question
foreach (var answer in q.Answers)
question.Answers.Add((Answer) answer.Clone());
}
}
}
<UserControl x:Class="WpfDemos.QuestionnaireControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
DataContext="{Binding RelativeSource={RelativeSource Self}}">
<ItemsControl ItemsSource="{Binding Questions}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Vertical"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Number, StringFormat='{}{0}.'}" Margin="0,0,10,0" />
<TextBlock Text="{Binding Question}" Width="220"/>
<ItemsControl ItemsSource="{Binding Answers}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<RadioButton
Content="{Binding Path=Text}"
IsChecked="{Binding Path=IsSelected}"
GroupName="{Binding Path=DataContext.Question, RelativeSource={RelativeSource AncestorType=ItemsControl}}"
Margin="0,0,10,0"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</UserControl>
<Window x:Class="WpfDemos.MyWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:wpfDemos="clr-namespace:WpfDemos"
Title="Questionnaire"
Height="300" Width="480">
<wpfDemos:QuestionnaireControl>
<wpfDemos:QuestionnaireControl.Questions>
<wpfDemos:QuestionCollection>
<wpfDemos:QuestionAndAnswers Number="1" Question="Is this working?" />
<wpfDemos:QuestionAndAnswers Number="2" Question="Are these questions sharing answers?" />
</wpfDemos:QuestionCollection>
</wpfDemos:QuestionnaireControl.Questions>
<wpfDemos:QuestionnaireControl.Answers>
<wpfDemos:AnswerCollection>
<wpfDemos:Answer Value="0" Text="Yes" />
<wpfDemos:Answer Value="1" Text="No" />
<wpfDemos:Answer Value="2" Text="Help Me Please" />
</wpfDemos:AnswerCollection>
</wpfDemos:QuestionnaireControl.Answers>
</wpfDemos:QuestionnaireControl>
</Window>
工作原理:AnswersProperty
和 QuestionsProperty
属性 更改了我们复制问题答案的回调。 And 触发回调是因为我们创建了新集合 (<wpfDemos:QuestionCollection>
,<wpfDemos:AnswerCollection>
) :)
我想构建一个 UserControl
来表示问卷,如下图所示(忽略缺少样式)。
我希望能够指定XAML中的重要内容,比如
<local:QuestionnaireControl>
<local:QuestionnaireControl.Questions>
<local:QuestionAndAnswers Number="1" Question="Is this working?" />
<local:QuestionAndAnswers Number="2" Question="Are these questions sharing answers?" />
</local:QuestionnaireControl.Questions>
<local:QuestionnaireControl.Answers>
<local:Answer Value="0" Text="Yes" />
<local:Answer Value="1" Text="No" />
<local:Answer Value="2" Text="Help Me Please" />
</local:QuestionnaireControl.Answers>
</local:QuestionnaireControl>
所以我有以下QuestionnaireControl.xaml
<UserControl x:Class="MyProject.QuestionnaireControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
xmlns:local="clr-namespace:MyProject"
DataContext="{Binding RelativeSource={RelativeSource Self}}"
d:DataContext="{d:DesignInstance Type=local:QuestionnaireControl, IsDesignTimeCreatable=True}">
<ItemsControl ItemsSource="{Binding Questions}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Vertical"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Number, StringFormat='{}{0}.'}" Margin="0,0,10,0" />
<TextBlock Text="{Binding Question}" Width="220"/>
<ItemsControl ItemsSource="{Binding Answers}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<RadioButton
Content="{Binding Text}"
IsChecked="{Binding IsSelected}"
GroupName="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=local:QuestionAndAnswers}, Path=Question}"
Margin="0,0,10,0"
/>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</UserControl>
以及以下QuestionnaireControl.xaml.cs
public partial class QuestionnaireControl : UserControl
{
public QuestionnaireControl()
{
InitializeComponent();
if (System.ComponentModel.DesignerProperties.GetIsInDesignMode(this))
{
Questions = new List<QuestionAndAnswers> {
new QuestionAndAnswers() { Number=1, Question="Do you like pizza?" },
new QuestionAndAnswers() { Number=2, Question="Can you surf?" },
new QuestionAndAnswers() { Number=3, Question="Are you funny?" },
new QuestionAndAnswers() { Number=4, Question="Is Monday your favorite day of the week?" },
new QuestionAndAnswers() { Number=5, Question="Have you been to Paris?" },
new QuestionAndAnswers() { Number=6, Question="When sleeping, do you snore?" },
new QuestionAndAnswers() { Number=7, Question="Could you be living in a dream?" }
};
Answers = new List<Answer> {
new Answer() { Value=1, Text="Yes", IsSelected=false },
new Answer() { Value=2, Text="No", IsSelected=false },
new Answer() { Value=3, Text="Sort Of", IsSelected=false },
};
}
else
{
Questions = new List<QuestionAndAnswers>();
Answers = new List<Answer>();
}
// Copy Answers to each QuestionAndAnswers.
foreach (QuestionAndAnswers qa in Questions)
{
qa.Answers = new List<Answer>(Answers);
}
}
public List<QuestionAndAnswers> Questions
{
get { return (List<QuestionAndAnswers>)GetValue(QuestionsProperty); }
set { SetValue(QuestionsProperty, value); }
}
public static readonly DependencyProperty QuestionsProperty =
DependencyProperty.Register("Questions", typeof(List<QuestionAndAnswers>), typeof(QuestionnaireControl), new FrameworkPropertyMetadata(new List<QuestionAndAnswers>()));
public List<Answer> Answers
{
get { return (List<Answer>)GetValue(AnswersProperty); }
set { SetValue(AnswersProperty, value); }
}
public static readonly DependencyProperty AnswersProperty =
DependencyProperty.Register("Answers", typeof(List<Answer>), typeof(QuestionnaireControl), new FrameworkPropertyMetadata(new List<Answer>()));
}
public class QuestionAndAnswers
{
public int Number { get; set; }
public string Question { get; set; }
public List<Answer> Answers { get; set; }
}
public class Answer
{
public string Text { get; set; }
public int Value { get; set; }
public bool IsSelected { get; set; }
}
使用上面的代码,我可以在 Visual Studio 设计器中生成上面 QuestionnaireControl
的图像。但是当我实际使用 QuestionnaireControl
时,根据上面的例子,问题被呈现而不是答案。有谁知道我需要调整什么?
为每个问题复制答案选项的代码无法正常工作。 For once 它只在构造函数中运行一次,而不是在添加答案之后运行。此外,它不会创建 Answer
对象的新实例,因此所有问题都保持相同的引用,并且当为一个问题选择第一个选项时,它会立即为所有其他问题选择。每个问题都需要自己的一组答案:
public class QuestionAndAnswers
{
public QuestionAndAnswers()
{
Answers = new List<Answer>();
}
public int Number { get; set; }
public string Question { get; set; }
public List<Answer> Answers { get; set; }
}
<local:QuestionnaireControl>
<local:QuestionnaireControl.Questions>
<local:QuestionAndAnswers Number="1" Question="Is this working?">
<local:QuestionAndAnswers.Answers>
<local:Answer Value="0" Text="Yes" />
<local:Answer Value="1" Text="No" IsSelected="true"/>
<local:Answer Value="2" Text="Help Me Please" />
</local:QuestionAndAnswers.Answers>
</local:QuestionAndAnswers>
<local:QuestionAndAnswers Number="2" Question="Are these questions sharing answers?">
<local:QuestionAndAnswers.Answers>
<local:Answer Value="0" Text="Yes" IsSelected="true"/>
<local:Answer Value="1" Text="No" />
<local:Answer Value="2" Text="Help Me Please" />
</local:QuestionAndAnswers.Answers>
</local:QuestionAndAnswers>
</local:QuestionnaireControl.Questions>
</local:QuestionnaireControl>
如果您将控件的 Answers
属性 类型更改为 ObservableCollection<Answer>
您可以处理它的 CollectionChanged
事件并复制每个问题的答案:
public partial class QuestionnaireControl : UserControl
{
public QuestionnaireControl()
{
InitializeComponent();
Questions = new List<QuestionAndAnswers>();
Answers = new ObservableCollection<Answer>();
Answers.CollectionChanged += Answers_CollectionChanged;
}
private void Answers_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
foreach (var question in Questions)
{
question.Answers = new List<Answer>();
foreach (var answer in Answers)
{
question.Answers.Add(new Answer() { Text = answer.Text, Value = answer.Value, IsSelected = answer.IsSelected });
}
}
}
public List<QuestionAndAnswers> Questions
{
get { return (List<QuestionAndAnswers>)GetValue(QuestionsProperty); }
set { SetValue(QuestionsProperty, value); }
}
public static readonly DependencyProperty QuestionsProperty =
DependencyProperty.Register("Questions", typeof(List<QuestionAndAnswers>), typeof(QuestionnaireControl));
public ObservableCollection<Answer> Answers
{
get { return (ObservableCollection<Answer>)GetValue(AnswersProperty); }
set { SetValue(AnswersProperty, value); }
}
public static readonly DependencyProperty AnswersProperty =
DependencyProperty.Register("Answers", typeof(ObservableCollection<Answer>), typeof(QuestionnaireControl), new FrameworkPropertyMetadata(null));
}
复制 Answer
对象 (new Answer() {...}
) 很容易,但是检测它应该发生的时刻是很棘手的。 AnswersProperty
仅更改 1 次(当分配新的 List<Answer>
时),然后项目被添加到该列表中,我们无法获得通知。而且我们不能在 xaml 中创建通用列表(标记限制)。然而,一个已知的解决方法是创建从通用集合派生的专门集合。这是一个完整的示例(您可能希望将 INotifyPropertyChnaged 实现添加到 Answer
和 QuestionAndAnswers
类):
public class QuestionAndAnswers
{
public QuestionAndAnswers()
{
Answers = new ObservableCollection<Answer>();
}
public int Number { get; set; }
public string Question { get; set; }
public ObservableCollection<Answer> Answers { get; private set; }
}
public class Answer : ICloneable
{
public string Text { get; set; }
public int Value { get; set; }
public bool IsSelected { get; set; }
public object Clone()
{
return MemberwiseClone();
}
}
public class QuestionCollection : List<QuestionAndAnswers>
{
}
public class AnswerCollection : List<Answer>
{
}
public partial class QuestionnaireControl : UserControl
{
public QuestionnaireControl()
{
InitializeComponent();
}
public List<QuestionAndAnswers> Questions
{
get { return (List<QuestionAndAnswers>) GetValue(QuestionsProperty); }
set { SetValue(QuestionsProperty, value); }
}
public static readonly DependencyProperty QuestionsProperty =
DependencyProperty.Register("Questions", typeof (List<QuestionAndAnswers>), typeof (QuestionnaireControl),
new PropertyMetadata(new List<QuestionAndAnswers>(), QuestionsChangedCallback));
private static void QuestionsChangedCallback(DependencyObject o, DependencyPropertyChangedEventArgs e)
{
var q = o as QuestionnaireControl;
if (q == null)
return;
CopyAnswers(q);
}
public List<Answer> Answers
{
get { return (List<Answer>) GetValue(AnswersProperty); }
set { SetValue(AnswersProperty, value); }
}
public static readonly DependencyProperty AnswersProperty =
DependencyProperty.Register("Answers", typeof(List<Answer>), typeof(QuestionnaireControl),
new PropertyMetadata(new List<Answer>(), AnswersChangedCallback));
private static void AnswersChangedCallback(DependencyObject o, DependencyPropertyChangedEventArgs e)
{
var q = o as QuestionnaireControl;
if (q == null)
return;
CopyAnswers(q);
}
private static void CopyAnswers(QuestionnaireControl q)
{
if (q.Answers == null || q.Questions == null)
return;
foreach (var question in q.Questions)
{
// remove old Answers
question.Answers.Clear();
// adding new Answers to each question
foreach (var answer in q.Answers)
question.Answers.Add((Answer) answer.Clone());
}
}
}
<UserControl x:Class="WpfDemos.QuestionnaireControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
DataContext="{Binding RelativeSource={RelativeSource Self}}">
<ItemsControl ItemsSource="{Binding Questions}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Vertical"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Number, StringFormat='{}{0}.'}" Margin="0,0,10,0" />
<TextBlock Text="{Binding Question}" Width="220"/>
<ItemsControl ItemsSource="{Binding Answers}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<RadioButton
Content="{Binding Path=Text}"
IsChecked="{Binding Path=IsSelected}"
GroupName="{Binding Path=DataContext.Question, RelativeSource={RelativeSource AncestorType=ItemsControl}}"
Margin="0,0,10,0"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</UserControl>
<Window x:Class="WpfDemos.MyWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:wpfDemos="clr-namespace:WpfDemos"
Title="Questionnaire"
Height="300" Width="480">
<wpfDemos:QuestionnaireControl>
<wpfDemos:QuestionnaireControl.Questions>
<wpfDemos:QuestionCollection>
<wpfDemos:QuestionAndAnswers Number="1" Question="Is this working?" />
<wpfDemos:QuestionAndAnswers Number="2" Question="Are these questions sharing answers?" />
</wpfDemos:QuestionCollection>
</wpfDemos:QuestionnaireControl.Questions>
<wpfDemos:QuestionnaireControl.Answers>
<wpfDemos:AnswerCollection>
<wpfDemos:Answer Value="0" Text="Yes" />
<wpfDemos:Answer Value="1" Text="No" />
<wpfDemos:Answer Value="2" Text="Help Me Please" />
</wpfDemos:AnswerCollection>
</wpfDemos:QuestionnaireControl.Answers>
</wpfDemos:QuestionnaireControl>
</Window>
工作原理:AnswersProperty
和 QuestionsProperty
属性 更改了我们复制问题答案的回调。 And 触发回调是因为我们创建了新集合 (<wpfDemos:QuestionCollection>
,<wpfDemos:AnswerCollection>
) :)