Ich möchte eine ItemsCollection erstellen, aber anstatt die Sammlungselemente zu rendern, möchte ich Unterobjekte rendern, die über eine Eigenschaft für das Sammlungselement erreicht werden.Auswählen von DataTemplate auf Basis des Unterobjekttyps
Um genauer zu sein: Dies wird ein 2D-Karten-Viewer für ein Spiel (obwohl in seinem aktuellen Zustand ist es noch nicht 2D). Ich databind ein ItemsControl zu einer ObservableCollection <Square>, wo Square eine Eigenschaft namens Terrain (vom Typ Terrain) hat. Terrain ist eine Basisklasse und hat verschiedene Nachkommen.
Ich möchte, dass das ItemsControl die Terrain-Eigenschaft von jedem Auflistungselement darstellt, nicht das Auflistungselement selbst.
Ich kann das schon machen, aber mit einigen unnötigen Overhead. Ich möchte wissen, ob es einen guten Weg gibt, den unnötigen Overhead zu beseitigen.
Was ich zur Zeit haben, sind die folgenden Klassen (vereinfacht):
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
}
Und ein Usercontrol genannt MapView, mit folgendem Inhalt:
<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}"/>
diesen Code gegeben, wenn ich tun:
mapView.DataContext = new ObservableCollection<Square> {
new Square(new Dirt()),
new Square(new SteelPlate())
};
Ich bekomme etwas, das genau so aussieht, wie ich es erwarte: ein StackPanel mit einer Bräunung Box (für den Schmutz) und eine silberne Box (für die SteelPlate). Aber ich bekomme es mit unnötigen Overhead.
Mein besonderes Anliegen ist mit meinem Datatemplate für Square:
<DataTemplate DataType="{x:Type TerrainDataModels:Square}">
<ContentControl Content="{Binding Path=Terrain}"/>
</DataTemplate>
Was ich eigentlich sagen will ist „nein, nicht die Mühe der Platz selbst zu machen, dessen Terrain Eigenschaft machen statt“. Dies nähert sich dem an, aber dies fügt dem visuellen Baum für jedes Quadrat zwei zusätzliche Steuerelemente hinzu: ein ContentControl, wie es im obigen XAML explizit codiert ist, und seinen ContentPresenter. Ich möchte hier nicht unbedingt ein ContentControl; Ich möchte die DataTemplate der Terrain-Eigenschaft kurzschließen und direkt in den Steuerungsbaum einfügen.
Aber wie sage ich dem ItemsControl, um collectionitem.Terrain zu rendern (also eines der obigen DataTemplates für das Terrain-Objekt nachzuschlagen), anstatt collectionitem zu rendern (und nach einem DataTemplate für das Square-Objekt zu suchen)?
Ich möchte DataTemplates für die Terrains verwenden, aber nicht unbedingt für den Square - das war nur der erste Ansatz, den ich fand, der angemessen funktionierte. Eigentlich möchte ich etwas komplett anderes machen - ich möchte den DisplayMemberPath von ItemsControl wirklich auf "Terrain" setzen. Dadurch wird das richtige Objekt (das Dirt- oder SteelPlate-Objekt) direkt gerendert, ohne dass ein zusätzliches ContentControl oder ContentPresenter hinzugefügt wird. Leider rendert DisplayMemberPath immer einen String und ignoriert die DataTemplates für die Terrains. Es hat also die richtige Idee, aber es ist nutzlos für mich.
Diese ganze Sache kann vorzeitige Optimierung sein, und wenn es keine einfache Weise gibt, was ich will, werde ich mit dem leben, was ich habe. Aber wenn es einen "WPF-Weg" gibt, den ich noch nicht kenne, um mich an eine Eigenschaft zu binden, statt an den gesamten Sammlungsgegenstand, wird es zu meinem Verständnis von WPF beitragen, was wirklich mein Ziel ist.
I hinzugefügt eine zweite Antwort. Werfen Sie einen Blick und lassen Sie mich wissen, wenn das hilft. – bendewey