Search code examples
wpfxamlcanvasitemscontrol

Canvas with static content and items


I have a Canvas which contains static elements - those elements use binding to draw it in the right places. Now I need to draw other elements depending on items in a collection. I want to use ItemsControl, but I don't know how to do it correctly. My current pseudo-code:

<UserControl.Resources>
    <RectangleGeometry x:Key="MyGeometry1">
        <RectangleGeometry.Rect>
            <MultiBinding Converter="{StaticResource RectConverter}">
                <Binding Path="ActualWidth" ElementName="m_Canvas" />
                <Binding Path="ActualHeight" ElementName="m_Canvas" />
            </MultiBinding>
        </RectangleGeometry.Rect>
    </RectangleGeometry>
</UserControl.Resources>

<Canvas x:Name="m_Canvas">
    <!-- "static" content -->
    <Line x:Name="Line1" X1="{Binding Line1X1}" X2="{Binding Line1X2}" Y1="{Binding Line1Y1}" Y2="{Binding Line1Y2}"/>
    <Line X1="0" X2="{Binding ActualWidth, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Canvas}}}" Y1="0" Y2="0">
    <Path Fill="#35A500"
          Opacity="0.15">
        <Path.Data>
            <GeometryGroup FillRule="EvenOdd">
                <StaticResource ResourceKey="MyGeometry1" />
            </GeometryGroup>
        </Path.Data>
    </Path>
    
    <Line X1="{Binding Items[0].X1}" X2="{Binding Items[0].X2}" Y1="{Binding Items[0].Y1}" Y2="{Binding Items[0].Y2}"/>
    <Line X1="{Binding Items[1].X1}" X2="{Binding Items[1].X2}" Y1="{Binding Items[1].Y1}" Y2="{Binding Items[1].Y2}"/>
    <Line X1="{Binding Items[2].X1}" X2="{Binding Items[2].X2}" Y1="{Binding Items[2].Y1}" Y2="{Binding Items[2].Y2}"/>
</Canvas>

I tried something like this:

<ItemsControl ItemsSource="{Binding Items}">
    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <Canvas>
                <!-- "static" content -->
                <Line x:Name="Line1" X1="{Binding Line1X1}" X2="{Binding Line1X2}" Y1="{Binding Line1Y1}" Y2="{Binding Line1Y2}"/>
                <Line X1="0" X2="{Binding ActualWidth, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Canvas}}}" Y1="0" Y2="0">
            </Canvas>
        </ItemsPanelTemplate>
<!-- (...) other code -->
</ItemsControl>

But I have an error:

Error XDG0062 Cannot explicitly modify Children collection of Panel used as ItemsPanel for ItemsControl. ItemsControl generates child elements for Panel.

I understand that is because I put Line1 inside Canvas but how to make such Canvas with some kind static content and also as a container for items?


Solution

  • The "static content" could be put into another Canvas above or below the ItemsPresenter in the ControlTemplate of the ItemsControl:

    <ItemsControl ItemsSource="{Binding Items}">
        <ItemsControl.Template>
            <ControlTemplate TargetType="ItemsControl">
                <Grid>
                    <Canvas>
                        <!-- "static" content -->
                    </Canvas>
                    <ItemsPresenter/>
                </Grid>
            </ControlTemplate>
        </ItemsControl.Template>
    
        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <Canvas/>
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>
    
    </ItemsControl>