Search code examples
c#wpfstylesitemscontrol

Style me an ItemsControl subclass please - stuch with ItemsPresenter


I have an control that is subclassed from ItemsControl, called WorkSheet:

public sealed class WorkSheet : ItemsControl {

Its elements are forced to be WorkTiles:

    /// <inheritdoc />
    protected override bool IsItemItsOwnContainerOverride(object item) {
        return (item is WorkTile);
    }

    /// <inheritdoc />
    protected override DependencyObject GetContainerForItemOverride() {
        return new WorkTile();
    }

So far - so good. I want the WorkSheet to use a Canvas for presenting the WorkItems, position being determined by overriing ArrangeOverride, which is called and positions properly determined. The exact psosition is being determined in overrides. I am pretty lost in the styling, though. I simply can not get the items to appear. In The Generic.xaml, I have defined the styles. They work, but not as they should:

<Style TargetType="{x:Type local:WorkSheet}">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate>
                <Border>
                    <ItemsPresenter />
                </Border>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
    <Setter Property="ItemsPanel">
        <Setter.Value>
            <ItemsPanelTemplate>
                <Canvas />
            </ItemsPanelTemplate>
        </Setter.Value>
    </Setter>
    <Setter Property="ItemTemplate">
        <Setter.Value>
            <DataTemplate>
                <ContentPresenter />
            </DataTemplate>
        </Setter.Value>
    </Setter>
</Style>

Here,pretty much, in the presentation tree, the ItemsPresenter is the lowest element. The subelements of ItemsPresenter never appear. I also tried putting a Canvas into the ControlTemplate with IsItemsHost="true" - again, no items. Anyone any idea what I am doing wrong here?

Again, as explanation - I put a breakpoint into a button handler on the form and use the Visualizer to see the visual tree. The hierarchy I get is:

WorkSheet -> Border -> ItemsPresenter ... and nothing below.

Obviously this means the ControlTemplate is used, but the ItemsPanel is never invoked.


Solution

  • Place a TargetType in your ControlTemplate as well:

    <ControlTemplate TargetType="{x:Type local:WorkSheet}">
    

    Does not work ;)

    Update 2:

    I replicated your things in a side project and you have two problems:

    First problem is the ItemTemplate setter in your Style which will trigger a StackOverflow exception (how ironic ;)). Remove the ContentPresenter, remove the whole template or use keys.

    Second problem is the GetContainerForItemOverride method. Removing this method will give me stuff on screen!

    Here is my code:

    public sealed class WorkSheet : ItemsControl
    {
        /// <inheritdoc />
        protected override bool IsItemItsOwnContainerOverride(object item)
        {
            return (item is WorkTile);
        }
    
    }
    

    And the xaml of Window with the Style:

    <Window x:Class="WpfApplication8.Window1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:WpfApplication8="clr-namespace:WpfApplication8"
        Title="Window1" Height="300" Width="300">
    
        <Window.Resources>
            <Style TargetType="{x:Type WpfApplication8:WorkSheet}">
                <Setter Property="Template">
                    <Setter.Value>
                        <ControlTemplate >
                            <Border>
                                <ItemsPresenter />
                            </Border>
                        </ControlTemplate>
                    </Setter.Value>
                </Setter>
                <Setter Property="ItemsPanel">
                    <Setter.Value>
                        <ItemsPanelTemplate>
                            <Canvas />
                        </ItemsPanelTemplate>
                    </Setter.Value>
                </Setter>
            </Style>
    
        </Window.Resources>
    
        <Grid>
            <WpfApplication8:WorkSheet x:Name="sheet" />
        </Grid>
    </Window>