C# 基于绑定集合创建控件组

C# 基于绑定集合创建控件组,c#,wpf,C#,Wpf,因此,我希望有一个大致如下的数据结构: List<Question> questions 在我的WPF表单上,我希望能够将这个列表绑定到一个堆栈框,并让它为列表中的每个问题添加一个新的网格,该网格的确切布局在每个派生类的方法中定义。这些Question对象将基于JSON文件生成,以生成问题集 例如,OneTenQuestion将添加一个滑块和一个标签,而freecomentquestion将添加一个标签和一个文本框。还有其他几种类型的问题,但我认为这些例子应该足够了 我从哪里开始呢

因此,我希望有一个大致如下的数据结构:

List<Question> questions
在我的WPF表单上,我希望能够将这个
列表
绑定到一个堆栈框,并让它为列表中的每个
问题
添加一个新的
网格
,该网格的确切布局在每个派生类的方法中定义。这些
Question
对象将基于JSON文件生成,以生成问题集

例如,
OneTenQuestion
将添加一个滑块和一个标签,而
freecomentquestion
将添加一个标签和一个文本框。还有其他几种类型的问题,但我认为这些例子应该足够了


我从哪里开始呢?我正在从WinForms过渡到WPF,因此,如果有一个指针指向正确的概念或表明这不是最好的方法,我将不胜感激。

在这种情况下,您通常使用数据模板(请参阅)。当您需要动态创建控件时,使用
DataTemplate
是一个非常强大的解决方案。
不带键(
x:key
)的
DataTemplate
是一个隐式模板,因为它将自动应用于与
DataTemplate.DataType
匹配的类型,即不显式引用。您应该为每种类型的
问题定义一个隐式
数据模板

或者,如果选择
DataTemplate
取决于更多的条件而不仅仅是数据类型,则可以使用

使用
ItemsControl
而不是
StackPanel
,如
ListView

您可以修改
ItemsControl.ItemsPanel
以垂直(默认)或水平堆叠项目:

ViewModel.cs

public class ViewModel : INotifyPropertyChanged
{
  public ViewModel()
  {
    this.Questions = new ObservableCollection<Question>() 
    {
      new OneTenQuestion(), 
      new FreeCommentQuestion
    };
  }

  private ObservableCollection<Question> questions;
  public ObservableCollection<Question> Questions
  {
    get => this.questions;
    set
    {
      this.questions = value;
      OnPropertyChanged();
    }
  }

  public event PropertyChangedEventHandler PropertyChanged;
  protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
  {
    this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
  }
}
公共类视图模型:INotifyPropertyChanged
{
公共视图模型()
{
this.Questions=新的ObservableCollection()
{
新一题(),
新问题
};
}
私人收集问题;
公开收集问题
{
get=>this.questions;
设置
{
这个问题=价值;
OnPropertyChanged();
}
}
公共事件属性更改事件处理程序属性更改;
受保护的虚拟void OnPropertyChanged([CallerMemberName]字符串propertyName=null)
{
this.PropertyChanged?.Invoke(this,newpropertychangedeventargs(propertyName));
}
}
main window.xaml

<Window>
  <Window.DataContext>
    <ViewModel />
  </Window.DataContext>
  <Window.Resources>
    <DataTemplate DataType="{x:Type OneTenQuestion}">

      <!-- The DataContext is the OneTenQuestion item. You can bind to it directly -->
      <Slider />
    </DataTemplate>

    <DataTemplate DataType="{x:Type FreeCommentQuestion}">
      <StackPanel>

        <!-- 
          The DataContext is the FreeCommentQuestion item.  
          You can bind to it directly.
          If FreeCommentQuestion had a property Comment, then you could bind the TextBox to it.
        -->
        <Label />
        <TextBox Text="{Binding Comment}" />
      </StackPanel>
    </DataTemplate>
  </Window.Resources>

  <ListView ItemsSource="{Binding Questions}" />
</Window>

<Window>
  <Window.DataContext>
    <ViewModel />
  </Window.DataContext>
  <Window.Resources>
    <DataTemplate DataType="{x:Type OneTenQuestion}">

      <!-- The DataContext is the OneTenQuestion item. You can bind to it directly -->
      <Slider />
    </DataTemplate>

    <DataTemplate DataType="{x:Type FreeCommentQuestion}">
      <StackPanel>

        <!-- 
          The DataContext is the FreeCommentQuestion item.  
          You can bind to it directly.
          If FreeCommentQuestion had a property Comment, then you could bind the TextBox to it.
        -->
        <Label />
        <TextBox Text="{Binding Comment}" />
      </StackPanel>
    </DataTemplate>
  </Window.Resources>

  <ListView ItemsSource="{Binding Questions}" />
</Window>