WrapPanel as ItemPanel for ItemsControl
Asked Answered
N

3

42

Still fooling around with WPF and learning as I go. Trying now to build a dynamic grouping of controls (mostly Buttons but might include CheckBoxes and others).

I had no idea what was the best way to do this so I tried creating a ItemsControl style and then add the items into a ItemsPresenter inside a WrapPanel. Soon realized the items wouldn't wrap because they effectively weren't inside the WrapPanel unless I put it as ItemsHost. Like this:

<Style x:Key="ButtonPanelGroup" TargetType="{x:Type ItemsControl}">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type ItemsControl}">
                <Border CornerRadius="5"
                        BorderBrush="{StaticResource DarkColorBrush}"
                        BorderThickness="1"
                        Margin="5">
                    <Grid>
                        <Grid.RowDefinitions>
                            <RowDefinition />
                            <RowDefinition Height="Auto" />
                        </Grid.RowDefinitions>

                        <WrapPanel IsItemsHost="True" FlowDirection="LeftToRight">
                            <ItemsPresenter />
                        </WrapPanel>

                        <ContentPresenter ContentSource="Name" Grid.Row="1" />

                    </Grid>
                </Border>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

Note that this is a work in progress and there are many styling effects I still need to implement. Here I use it:

<UniformGrid Rows="1">
    <ItemsControl Name="Group1" Style="{StaticResource ButtonPanelGroup}">
        <Button>Button1</Button>
        <Button>Button2</Button>
        <CheckBox>TickBox</CheckBox>
    </ItemsControl>

    <ItemsControl Name="Group2" Style="{StaticResource ButtonPanelGroup}">
        <Button>Button3</Button>
        <Button>Button4</Button>
        <Button>Button5</Button>
    </ItemsControl>

    <ItemsControl Name="Group3" Style="{StaticResource ButtonPanelGroup}">
        <Button>Button6</Button>
        <Button>Button7</Button>
        <Button>Button8</Button>
    </ItemsControl>
</UniformGrid>

Also note here that it is still a work in progress as UniformGrid wouldn't be the way to go here and also margins can be a pain (are there any margins that overlap?) so input there would be appreciated.

Now to the real problem. This doesn't work I get a error:

'ItemsPresenter' object cannot be added to 'WrapPanel'. Cannot explicitly modify Children collection of Panel used as ItemsPanel for ItemsControl. ItemsControl generates child elements for Panel. Error at object 'System.Windows.Controls.ItemsPresenter'.

So what's the best way to do something like this ( would love to be able to just throw buttons and other controls into the ItemControl and the line up real nice ). Would it be better to put the Controls into a collection of some kind and iterate.

Would love to get it nicely done but my WPF skills are still lacking. Are there any WPF books that teach beyond the basics and show how pro's would really do it?

Nation answered 28/6, 2010 at 11:26 Comment(0)
E
62

You might want to take a look at the ItemsPanel property:

Gets or sets the template that defines the panel that controls the layout of items.

Example:

<ItemsControl>
    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <WrapPanel />
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>
</ItemsControl>

And you can set it in a Style as follows:

<Style TargetType="ItemsControl">
    <Setter Property="ItemsPanel">
      <Setter.Value>
            <ItemsPanelTemplate>
                <WrapPanel />
            </ItemsPanelTemplate>
        </Setter.Value>
    </Setter>
</Style>
Enswathe answered 28/6, 2010 at 11:35 Comment(7)
That works but I wan't to set it at the style instead of at every ItemsControl. I thought that was possible with the IsItemsHost property but doesn't seem to work. Is this the only way?Lupien
Updated answer with a Style for ItemsControlEnswathe
Ah of course I can also just add into this style, thank you. If you have time you could look at 2 other questions I have that are still unresolved. #3118766 https://mcmap.net/q/391833/-how-to-reuse-layouts-in-wpfLupien
And another questions, all the items I will put into my ItemsControl that uses this Style will inherit from ContentControl, can I somewhere set the default properties of them ( like margin and such ) so I only need to set it once, or better yet say that if it's a button use this style, checkbox use this and so on?Lupien
Sure. Just put styles with the appropriate TargetType into the ItemsControl's resource dictionary (ItemsControl.Resources).Pacificia
It will always look up the most specific style.. So if you define it for ContentControl, it will also be for Button. But if you have both, the Button rules out the ContentControl style. You should also take a look at the BasedOn property of Style: switchonthecode.com/tutorials/wpf-the-basedon-style-propertyEnswathe
Just so you know, your first approach was right using IsItemsHost, except for the part that you don't needed to throw in the ItemsPresenter, as this is managed by the ItemsControl.Ezekielezell
J
7

Don't forget definition of clue property IsItemsHost="True". Otherwise your ItemsControl won't show your items.

<ListBox ItemsSource="{Binding MyItemsSource}">
        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate >
                <WrapPanel IsItemsHost="True"/>
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>
    </ListBox>
Jest answered 8/3, 2016 at 11:6 Comment(3)
IsItemsHost="True" is not necessaryDaphinedaphna
@kux, try to remove this attribute and see what happensJest
I have tried it, otherwise I would not have written it here. removed and it still works. prnt.sc/va06yiDaphinedaphna
L
1

Here's another simple alternative to slow DataGrid / xceed datagrid and WrapPanel solution. Might be useful when a lot of data or whole table is just for editing. Using ItemsControl + Grid.IsSharedSizeScope="True"

More info here: https://wpf.2000things.com/tag/issharedsizescope/ + On ItemsControl virutualization for performance: Virtualizing an ItemsControl?

<Grid Grid.IsSharedSizeScope="True" Margin="0,30,0,0">
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto" />
        <RowDefinition Height="Auto" />
    </Grid.RowDefinitions>

    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition MinWidth="50" Width="Auto" SharedSizeGroup="Id" />
            <ColumnDefinition MinWidth="50" Width="Auto" SharedSizeGroup="Time"  />
        </Grid.ColumnDefinitions>
        <Border Grid.Column="0" >
            <TextBlock VerticalAlignment="Center" TextWrapping="NoWrap" Text="Header1" />
        </Border>
        <Border Grid.Column="1" >
            <TextBlock VerticalAlignment="Center" TextWrapping="NoWrap" Text="Header2" />
        </Border>
    </Grid>

    <ItemsControl Grid.Row="1" ItemsSource="{Binding RunInstance.ConcentrationGradient.Steps}">
        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <Grid>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition MinWidth="50" Width="Auto" SharedSizeGroup="Id" />
                        <ColumnDefinition MinWidth="50" Width="Auto" SharedSizeGroup="Time" />
                    </Grid.ColumnDefinitions>
                    <Border Grid.Column="0">
                        <TextBlock VerticalAlignment="Center" TextWrapping="NoWrap" Text="{Binding Index, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}" />
                    </Border>
                     <Border Grid.Column="1">
                        <TextBlock VerticalAlignment="Center" TextWrapping="NoWrap" Text="{Binding Time, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}" />
                    </Border>
                </Grid>
            </DataTemplate>
        </ItemsControl.ItemTemplate>
    </ItemsControl>
</Grid>
Lutestring answered 26/7, 2018 at 11:22 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.