gpt4 book ai didi

wpf - 根据子对象类型选择 DataTemplate

转载 作者:行者123 更新时间:2023-12-04 02:01:00 28 4
gpt4 key购买 nike

我想对 ItemsCollection 进行数据绑定(bind),但不是渲染集合项,而是渲染通过集合项上的属性到达的子对象。

更具体地说:这将是游戏的 2D map 查看器(尽管在当前状态下它还不是 2D)。我将 ItemsControl 数据绑定(bind)到 ObservableCollection ,其中 Square 有一个名为 Terrain(Terrain 类型)的属性。地形是一个基类,并有各种后代。

我想要的是 ItemsControl 从每个集合元素呈现 Terrain 属性,而不是集合元素本身。

我已经可以完成这项工作,但需要一些不必要的开销。我想知道是否有消除不必要开销的好方法。

我目前拥有的是以下类(简化):

public class Terrain {}
public class Dirt : Terrain {}
public class SteelPlate : Terrain {}
public class Square
{
public Square(Terrain terrain)
{
Terrain = terrain;
}
public Terrain Terrain { get; private set; }
// additional properties not relevant here
}

还有一个名为 MapView 的 UserControl,包含以下内容:
<UserControl.Resources>
<DataTemplate DataType="{x:Type TerrainDataModels:Square}">
<ContentControl Content="{Binding Path=Terrain}"/>
</DataTemplate>
<DataTemplate DataType="{x:Type TerrainDataModels:Dirt}">
<Canvas Width="40" Height="40" Background="Tan"/>
</DataTemplate>
<DataTemplate DataType="{x:Type TerrainDataModels:SteelPlate}">
<Canvas Width="40" Height="40" Background="Silver"/>
</DataTemplate>
</UserControl.Resources>
<ItemsControl ItemsSource="{Binding}"/>

鉴于此代码,如果我这样做:
mapView.DataContext = new ObservableCollection<Square> {
new Square(new Dirt()),
new Square(new SteelPlate())
};

我得到的东西看起来与我期望的完全一样:一个 StackPanel,其中包含一个棕褐色盒子(用于 Dirt)和一个银色盒子(用于 SteelPlate)。但我得到了不必要的开销。

我特别关心的是我的 Square 数据模板:
<DataTemplate DataType="{x:Type TerrainDataModels:Square}">
<ContentControl Content="{Binding Path=Terrain}"/>
</DataTemplate>

我真正想说的是“不,不要费心渲染 Square 本身,而是渲染它的 Terrain 属性”。这接近于这个,但是这为每个 Square 的可视化树添加了两个额外的控件:一个 ContentControl,如上面 XAML 中明确编码的那样,以及它的 ContentPresenter。我在这里并不特别想要 ContentControl;我真的很想短路并将 Terrain 属性的 DataTemplate 直接插入到控件树中。

但是我如何告诉 ItemsControl 渲染 collectionitem.Terrain (从而查找上述地形对象的 DataTemplate 之一)而不是渲染 collectionitem (并为 Square 对象寻找 DataTemplate)?

我想将 DataTemplates 用于地形,但完全不一定用于 Square——这只是我发现的第一种有效的方法。事实上,我真正想做的是完全不同的事情——我真的想将 ItemsControl 的 DisplayMemberPath 设置为“Terrain”。这会直接呈现正确的对象(Dirt 或 SteelPlate 对象),而无需添加额外的 ContentControl 或 ContentPresenter。不幸的是,DisplayMemberPath 总是呈现一个字符串,而忽略了地形的 DataTemplates。所以它有正确的想法,但对我来说没用。

这整个事情可能是过早的优化,如果没有简单的方法来获得我想要的,我会接受我所拥有的。但是,如果有一种“WPF 方式”我还不知道要绑定(bind)到属性而不是整个集合项,它将增加我对 WPF 的理解,这正是我所追求的。

最佳答案

我不确定你的模型是什么样的,但你总是可以使用 .绑定(bind)到对象属性。例如:

<DataTemplate DataType="TerrainModels:Square">
<StackPanel>
<TextBlock Content="{Binding Path=Feature.Name}"/>
<TextBlock Content="{Binding Path=Feature.Type}"/>
</StackPanel>
</DataTemplate>

更新

虽然,如果您正在寻找一种方法来绑定(bind)集合中的两个不同对象,您可能需要查看 ItemTemplateSelector 属性(property)。

在您的场景中,它将是这样的(未经测试):
public class TerrainSelector : DataTemplateSelector
{
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
var square = item as Square;
if (square == null)
return null;
if (square.Terrain is Dirt)
{
return Application.Resources["DirtTemplate"] as DataTemplate;
}
if (square.Terrain is Steel)
{
return Application.Resources["SteelTemplate"] as DataTemplate;
}
return null;
}
}

然后要使用它,您将拥有:

应用程序.xaml
<Application ..>
<Application.Resources>
<DataTemplate x:Key="DirtTemplate">
<!-- template here -->
</DataTemplate>
<DataTemplate x:Key="SteelTemplate">
<!-- template here -->
</DataTemplate>
</Application.Resources>
</Application>

Window.xaml
<Window  ..>
<Window.Resources>
<local:TerrainSelector x:Key="templateSelector" />
</Window.Resources>
<ItemsControl ItemSource="{Binding Path=Terrain}" ItemTemplateSelector="{StaticResource templateSelector}" />
</Window>

关于wpf - 根据子对象类型选择 DataTemplate,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/790896/

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com