I have a WPF project that draws several things in a panel. For the next release I need to add another type of thing to draw in addition to the existing things. Currently I hav
Thank your for the improved code example. From that, it appears to me that you are going about your goal the wrong way entirely.
That is, you are trying to have a single ItemsControl
object render all of your lines. But that's not how your data model is organized. Your data model has two completely different kinds of objects: LineArt
objects, and the ObstacleArt
object that contains LineArt
objects.
Given that, it seems to me that a more appropriate approach would be to simply compose the MainWindowResource.Lines
and MainWindowResource.Obstacles
collections, and then use data templates to appropriately display these collections together.
Here is a new version of your XAML that shows what I mean:
<Window x:Class="POC_WPF_nestedDrawingObjects.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:c="clr-namespace:POC_WPF_nestedDrawingObjects"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<c:MainWindowResource x:Key="MainWindowResource"/>
<Style x:Key="ContentCanvasStyle" TargetType="Canvas">
<Setter Property="RenderTransformOrigin" Value="0,0"/>
</Style>
<DataTemplate DataType="{x:Type c:LineArt}">
<Line
X1="{Binding Path=AX}"
Y1="{Binding Path=AY}"
X2="{Binding Path=BX}"
Y2="{Binding Path=BY}"
Stroke="{Binding Path=LineColor}"
StrokeThickness="{Binding Path=ScaledWeight}"
StrokeEndLineCap="Round"
StrokeStartLineCap="Round">
</Line>
</DataTemplate>
<DataTemplate DataType="{x:Type c:ObstacleArt}">
<ItemsControl ItemsSource="{Binding Lines, Mode=OneWay}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas x:Name="ContentCanvas"
Style="{StaticResource ContentCanvasStyle}">
</Canvas>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.RenderTransform>
<TransformGroup>
<RotateTransform Angle="{Binding RotateAngle}"/>
<TranslateTransform X="{Binding TranslateX}" Y="{Binding TranslateY}"/>
</TransformGroup>
</ItemsControl.RenderTransform>
</ItemsControl>
</DataTemplate>
</Window.Resources>
<Grid>
<ItemsControl>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas x:Name="ContentCanvas"
Style="{StaticResource ContentCanvasStyle}">
</Canvas>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemsSource>
<CompositeCollection>
<CollectionContainer
Collection="{Binding
Source={StaticResource MainWindowResource},
Path=Lines,
Mode=OneWay}"/>
<CollectionContainer
Collection="{Binding
Source={StaticResource MainWindowResource},
Path=Obstacles,
Mode=OneWay}"/>
</CompositeCollection>
</ItemsControl.ItemsSource>
</ItemsControl>
</Grid>
</Window>
The key here is the second DataTemplate
, with a target type of ObstacleArt
. This allows the main ItemsControl
to display the individual ObstacleArt
elements in the composite collection. Via the second data template, it does so by nesting an entirely new ItemsControl
within for each ObstacleArt
object, where that ItemsControl
handles all of the rendering for the ObstacleArt
object. Note that since the actual items in that nested ItemsControl
object are themselves LineArt
items, this winds up referring back to the DataTemplate
for the LineArt
type.
The above works without any changes at all to your code-behind. That said, it is my opinion that you would be better off making your classes inherit DependencyObject
, and then make the bindable properties dependency properties. WPF does support INotifyPropertyChanged
of course, but you have a lot of explicit property notification code in there that would just go away if you were using the dependency-property paradigm.