根据 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} />
如您所见,我的 ListBox
将 Components
属性绑定为 ItemsSource
。
Components
属性是 ConcreteComponent
的集合,具有抽象类型 IAttribute
的 Foo
属性 Foo
。
我想根据属性 Foo
的具体 class 更改 ListView
模板。
使用 DataTemplate
可以根据 class 具体类型构建不同的模板,例如:
<DataTemplate DataType="{x:Type ConcreteComponent1}" />
<DataTemplate DataType="{x:Type ConcreteComponent2}" />
但是如何根据具体的class属性类型构建不同的模板?
更新:
我添加了一些关于我的实际问题的更多信息。
我有一个 ListView
,其中包含一个名为 Shapes
的 ObservableCollection<BaseWindowShape>
。对象 BaseWindowShape
有一个属性 AdditionalData
(AdditionalData
是抽象的)可以是 DistanceAdditionalData
或 TermoAdditionalData
.
我想根据 AdditionalData
具体类型更改 che ListView
视图(ListView
中的所有 BaseWindowShape
共享相同的具体 AdditionalData
).
所以如果第一个形状的 AdditionalData
是 DistanceAtdditionalData
我想要三列
否则我希望只有两列
MaxDistance
和 MinAreaPercent
是 DistanceAdditionalData
属性,而 MaxTemperature
是 TermoAdditionalData
属性。
我尝试通过以下方式实现此行为,但它不起作用。
我该如何解决我的问题?
<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() };
并将其绑定为我第一个 ListView
的 ItemsSource
。
作为 FirstComponent
的 DataTemplate
,我声明了另一个 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 异常
我正在使用以下 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} />
如您所见,我的 ListBox
将 Components
属性绑定为 ItemsSource
。
Components
属性是 ConcreteComponent
的集合,具有抽象类型 IAttribute
的 Foo
属性 Foo
。
我想根据属性 Foo
的具体 class 更改 ListView
模板。
使用 DataTemplate
可以根据 class 具体类型构建不同的模板,例如:
<DataTemplate DataType="{x:Type ConcreteComponent1}" />
<DataTemplate DataType="{x:Type ConcreteComponent2}" />
但是如何根据具体的class属性类型构建不同的模板?
更新:
我添加了一些关于我的实际问题的更多信息。
我有一个 ListView
,其中包含一个名为 Shapes
的 ObservableCollection<BaseWindowShape>
。对象 BaseWindowShape
有一个属性 AdditionalData
(AdditionalData
是抽象的)可以是 DistanceAdditionalData
或 TermoAdditionalData
.
我想根据 AdditionalData
具体类型更改 che ListView
视图(ListView
中的所有 BaseWindowShape
共享相同的具体 AdditionalData
).
所以如果第一个形状的 AdditionalData
是 DistanceAtdditionalData
我想要三列
否则我希望只有两列
MaxDistance
和 MinAreaPercent
是 DistanceAdditionalData
属性,而 MaxTemperature
是 TermoAdditionalData
属性。
我尝试通过以下方式实现此行为,但它不起作用。
我该如何解决我的问题?
<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() };
并将其绑定为我第一个 ListView
的 ItemsSource
。
作为 FirstComponent
的 DataTemplate
,我声明了另一个 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 异常