2017-06-16 6 views
0

アンケートを表現するためにUserControlを作成したいと思っています。以下のようなものです(スタイリングの欠如を無視してください)。私は、だから私はQuestionnaireControl.xamlQAMLでアンケートのUserControlを作成するにはどうすればよいでしょうか?

<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

を以下しているように

<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> 

として、XAMLでの重要な内容を指定できるようにしたい

QuestionnaireControl

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からの回答が追加された後にAnswersのコピーを取得するにはどうすればよいですか?私はそのコードをLoadedコールバックに入れようとしましたが、うまくいきませんでした。 –

答えて

0

あなたはObservableCollection<Answer>にコントロールのAnswersプロパティの種類を変更する場合は、そのCollectionChangedイベントを処理し、各質問への回答をコピーすることができます:

0

各質問の回答オプションをコピーするコードが正しく動作しません。一度それはコンストラクタで一度だけ実行され、回答が追加された後ではありません。さらに、Answerオブジェクトの新しいインスタンスを作成せず、結果としてすべての質問が同じ参照を保持し、1つの質問に対して最初のオプションが選択された場合、それは他の質問に対して直ちに選択されます。各質問には、回答の独自のセットが必要になります。

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(マークアップの制限)にジェネリックリストを作成することはできません。しかし、既知の回避策は、ジェネリックコレクションから派生した特殊なコレクションを作成することです。ここでは完全な例である(あなたがAnswerQuestionAndAnswersクラスにINotifyPropertyChnagedの実装を追加したい場合があります):

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'で与えられたので、私は答えとして彼を選びました。 GroupNameバインディングに気づいて修正するための+1! –

+1

@MichaelRepucci、 'Answers.CollectionChanged + = Answers_CollectionChanged;'コンストラクタは信頼できません:ユーザが新しいコレクションをpublic 'Answers'プロパティに割り当てると、質問は更新されず、' Answers_CollectionChanged'はもはやトリガされません – ASh

関連する問題