根据 class 属性具体类型(不是 class 类型)创建 WPF DataTemplate

Create WPF DataTemplate depending on a class attribute concrete type (not class type)

我正在使用以下 classes:

public class ConcreteClassFoo
{
    public ObservableCollection<ConcreteComponent> Components { get; set; }
}

public class ConcreteComponent
{
    IAttribute Foo { get; set; }
}

public class ConcreteAttribute1 : IAttribute {}
public class ConcreteAttribute2 : IAttribute {}

映射到我的 XAML:

<ListView ItemsSource={Bindings Components} />

如您所见,我的 ListBoxComponents 属性绑定为 ItemsSource

Components 属性是 ConcreteComponent 的集合,具有抽象类型 IAttributeFoo 属性 Foo

我想根据属性 Foo 的具体 class 更改 ListView 模板。

使用 DataTemplate 可以根据 class 具体类型构建不同的模板,例如:

<DataTemplate DataType="{x:Type ConcreteComponent1}" />
<DataTemplate DataType="{x:Type ConcreteComponent2}" />

但是如何根据具体的class属性类型构建不同的模板?

更新:

我添加了一些关于我的实际问题的更多信息。

我有一个 ListView,其中包含一个名为 ShapesObservableCollection<BaseWindowShape>。对象 BaseWindowShape 有一个属性 AdditionalDataAdditionalData 是抽象的)可以是 DistanceAdditionalDataTermoAdditionalData.

我想根据 AdditionalData 具体类型更改 che ListView 视图(ListView 中的所有 BaseWindowShape 共享相同的具体 AdditionalData).

所以如果第一个形状的 AdditionalDataDistanceAtdditionalData 我想要三列

否则我希望只有两列

MaxDistanceMinAreaPercentDistanceAdditionalData 属性,而 MaxTemperatureTermoAdditionalData 属性。

我尝试通过以下方式实现此行为,但它不起作用。

我该如何解决我的问题?

<ListView ItemsSource="{Binding Shapes}">
  <ListView.Resources>
        <DataTemplate DataType="{x:Type additionalData:TridimensionalAdditionalData}">
            <ListView>
                <ListView.View>
                    <GridView>
                        <GridViewColumn Header="Level">
                            <GridViewColumn.CellTemplate>
                                <DataTemplate>
                                    <wpfControls:DigitInput Value="{Binding Level, Mode=TwoWay}" />
                                </DataTemplate>
                            </GridViewColumn.CellTemplate>
                        </GridViewColumn>
                        <GridViewColumn Header="Max Distance">
                            <GridViewColumn.CellTemplate>
                                <DataTemplate>
                                    <StackPanel>
                                        <xctk:DoubleUpDown Value="{Binding MaxDistance}"/>
                                        <TextBlock Text="mm">
                                    </StackPanel>
                                </DataTemplate>
                            </GridViewColumn.CellTemplate>
                        </GridViewColumn>
                        <GridViewColumn Header="Min Area">
                            <GridViewColumn.CellTemplate>
                                <DataTemplate>
                                    <xctk:DoubleUpDown Value="{Binding MinAreaPercent}" />
                                </DataTemplate>
                            </GridViewColumn.CellTemplate>
                        </GridViewColumn>
                        </GridView>
                </ListView.View>
            </ListView>
        </DataTemplate>
        <DataTemplate DataType="{x:Type additionalData:ThermalAdditionalData}">
            <ListView>
                <ListView.View>
                    <GridView>
                        <GridViewColumn Header="Level">
                            <GridViewColumn.CellTemplate>
                                <DataTemplate>
                                    <wpfControls:DigitInput Value="{Binding Level, Mode=TwoWay}" />
                                </DataTemplate>
                            </GridViewColumn.CellTemplate>
                        </GridViewColumn>
                        <GridViewColumn Header="Max Temperature">
                            <GridViewColumn.CellTemplate>
                                <DataTemplate>
                                    <StackPanel>
                                        <xctk:DoubleUpDown Value="{Binding MaxTemperature}"/>
                                        <TextBlock Text=" °F"/>
                                    </StackPanel>
                                </DataTemplate>
                            </GridViewColumn.CellTemplate>
                        </GridViewColumn>
                    </GridView>
                </ListView.View>
            </ListView>
        </DataTemplate>
    </ListView.Resources>
    <ListView.ItemTemplate>
        <DataTemplate>
            <ContentControl Content="{Binding AdditionalData}" />
        </DataTemplate>
    </ListView.ItemTemplate>
</ListView>

更新 2: 我使用 lokusking 答案更新我的实现,这里是:

<ListView ItemsSource="{Binding FirstComponent}">
<ListView.Resources>
    <DataTemplate DataType="{x:Type local:ConcreteAttribute1}">
        <ListView ItemsSource="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, Path=Components}">
            <ListView.View>
                <GridView>
                    <GridViewColumn Header="1 column">
                        <GridViewColumn.CellTemplate>
                            <DataTemplate>
                                    <TextBlock Text="{Binding Foo.Name, Mode=OneWay}" />
                            </DataTemplate>
                        </GridViewColumn.CellTemplate>
                    </GridViewColumn>
                    <GridViewColumn Header="2 column">
                        <GridViewColumn.CellTemplate>
                            <DataTemplate>
                                <TextBox Text="{Binding Foo.Number, Mode=OneWay}"/>
                            </DataTemplate>
                        </GridViewColumn.CellTemplate>
                    </GridViewColumn>
                </GridView>
            </ListView.View>
        </ListView>
    </DataTemplate>
    <DataTemplate DataType="{x:Type local:ConcreteAttribute2}">
        <ListView ItemsSource="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, Path=Components}">
            <ListView.View>
                <GridView>
                    <GridViewColumn Header="3 column">
                        <GridViewColumn.CellTemplate>
                            <DataTemplate>
                                <TextBlock Text="{Binding Foo.Name, Mode=OneWay}"/>
                            </DataTemplate>
                        </GridViewColumn.CellTemplate>
                    </GridViewColumn>
                    <GridViewColumn Header="4 column">
                        <GridViewColumn.CellTemplate>
                            <DataTemplate>
                                <CheckBox IsChecked="{Binding Foo.B, Mode=OneWay}" />
                            </DataTemplate>
                        </GridViewColumn.CellTemplate>
                    </GridViewColumn>
                </GridView>
            </ListView.View>
        </ListView>
    </DataTemplate>
</ListView.Resources>
<ListView.ItemTemplate>
    <DataTemplate>
        <ContentControl Content="{Binding Foo}" />
    </DataTemplate>
</ListView.ItemTemplate>
</ListView>

我添加了一个名为 FirstComponent 的 属性 声明为:

public ObservableCollection<ConcreteComponent> FirstComponent { get; } = new ObservableCollection<ConcreteComponent> { Components.First() };

并将其绑定为我第一个 ListViewItemsSource。 作为 FirstComponentDataTemplate,我声明了另一个 ListView,绑定到集合 Components 以便 有不同的列取决于它 . 如果我的集合的第一个元素具有 ConcreteAttribute1 的属性,我将得到以下结果:

否则如下:

可能这不是实现此结果的最佳方法(我必须声明只有一个元素的 ObservableCollection 才能检查它)但它有效。

因为我没有真正得到你想要实现的目标,所以我只能猜测,这就是你想要做的:

<ListView ItemsSource="{Binding Components}">
            <ListView.Resources>
                <DataTemplate DataType="{x:Type ConcreteAttribute1}">
                    <TextBlock Text="Con1"></TextBlock>
                </DataTemplate>
                <DataTemplate DataType="{x:Type ConcreteAttribute2}">
                    <TextBlock Text="Con2"></TextBlock>
                </DataTemplate>
            </ListView.Resources>
            <ListView.ItemTemplate>
                <DataTemplate>
                    <ContentControl Content="{Binding Foo}"></ContentControl>
                </DataTemplate>
            </ListView.ItemTemplate>
        </ListView>

如果这不能解决您的问题,请再解释一下

编辑

这是我的演示实现和结果:

public class ConcreteComponent {
    public IAttribute Foo {
      get; set;
    }
  }

  public class ConcreteAttribute1 : IAttribute {
    public string Name => "ConAtt 1";
    public int Number => 999;

  }
  public class ConcreteAttribute2 : IAttribute {
    public string Name => "ConAtt 2";
    public bool B => true;

  }

  public interface IAttribute {
    string Name {
      get;
    }
  }

XAML

<ListView ItemsSource="{Binding Components}">
    <ListView.Resources>
        <DataTemplate DataType="{x:Type ConcreteAttribute1}">
            <StackPanel>
                <TextBlock Text="{Binding Name, Mode=OneWay}"/>
                <TextBox Text="{Binding Number, Mode=OneWay}"></TextBox>
            </StackPanel>
        </DataTemplate>
        <DataTemplate DataType="{x:Type ConcreteAttribute2}">
            <StackPanel>
                <TextBlock Text="{Binding Name, Mode=OneWay}"/>
                <CheckBox IsChecked="{Binding B, Mode=OneWay}"></CheckBox>
            </StackPanel>
        </DataTemplate>
    </ListView.Resources>
    <ListView.ItemTemplate>
        <DataTemplate>
            <ContentControl Content="{Binding Foo}" />
        </DataTemplate>
    </ListView.ItemTemplate>
</ListView>

用法

public MainWindow() {
      InitializeComponent();
      this.Components = new ObservableCollection<ConcreteComponent>();
      this.Components.Add(new ConcreteComponent { Foo = new ConcreteAttribute1() });

      this.Components.Add(new ConcreteComponent { Foo = new ConcreteAttribute2() });


      this.DataContext = this;
    }

    public ObservableCollection<ConcreteComponent> Components {
      get;
    }

结果

备注

确保您没有错别字。 如果仍然无法正常工作,可能 post 屏幕错误输出 and/or 异常