2017-06-16 95 views
0

我想創建一個UserControl來表示問卷,如下圖所示(忽略缺少樣式)。如何在XAML中構建問卷共享相同答案的調查問卷UserControl?

QuestionnaireControl

我希望能夠以指定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時,根據上面的示例,提供的問題不是答案。有誰知道我需要調整什麼?

+0

'//答案複製到每個QuestionAndAnswers.'一部分觸發回調只能一次。在設計模式下它有一些數據要複製。它在啓動應用程序後添加''的答案後不運行。 – ASh

+0

@ASh如何在添加XAML的答案之後獲得答案的複製以運行?我試圖將該代碼放入Loaded回調函數中,但這沒有奏效。 –

回答

0

如果更改了控制的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)); 

} 
0

爲每個問題複製答案選項的代碼工作不正確。一次,它只在構造函數中運行一次,而不是在添加回答後運行。此外,它不會創建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> 
+0

我明確地想避免XAML中每個問題上的答案重複,因爲這很容易導致錯誤。 –

+0

@MichaelRepucci,看到另一個答案。請注意,我沒有在.ctor中設置設計時間數據,因爲設計師應該從問題和答案列表中顯示實際數據。還請注意控制xaml(特別是'GroupName'綁定)的更改 – ASh

+0

@MichaelRepucci,可否請您提供任何反饋重新調整我發佈的替代解決方案? – ASh

1

這是很容易複製Answer對象(new Answer() {...}),但檢測到的時刻,它應該發生的是棘手的。 AnswersProperty只更改1次(當分配新的List<Answer>時),然後將項目添加到該列表中,並且我們無法獲取通知。我們不能在xaml中創建通用列表(標記限制)。然而,已知的解決方法是創建從泛型集合派生的專用集合。這是一個完整的示例(您可能要INotifyPropertyChnaged實現增加AnswerQuestionAndAnswers類):

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> 

它是如何工作的:AnswersPropertyQuestionsProperty有財產改變了回調,我們在那裏複製問題的答案。 因爲我們創建新的集合(<wpfDemos:QuestionCollection><wpfDemos:AnswerCollection>):)

+0

我很欣賞額外的代碼和解釋,但是必要的答案早先由'mm8'給出,所以我選擇他作爲答案。 +1注意並修復GroupName綁定! –

+1

@MichaelRepucci,'Answers.CollectionChanged + = Answers_CollectionChanged;'在構造函數中不可靠:如果用戶將新集合分配給公共'Answers'屬性,則問題將不會更新並且'Answers_CollectionChanged'不會再觸發 – ASh