1

I have an ItemsControl where I display a long list of objects. I display it in a wrappanel so the user does not need to scroll. Now i want to add a Button at the end of the list, so the user can add new objects. What would be the best way to do this?

Here is my xaml:

<ItemsControl ItemsSource="{Binding Inventory}">
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <StackPanel>
                <Grid Width="300">
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="200"/>
                        <ColumnDefinition/>
                        <ColumnDefinition Width="30"/>
                    </Grid.ColumnDefinitions>
                    <Button Content="{Binding Name}"                        
                            MouseDoubleClick="CallEdit"/>
                </Grid>
            </StackPanel>
        </DataTemplate>
    </ItemsControl.ItemTemplate>
    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <WrapPanel MaxHeight="{Binding ElementName=window, Path=Height}"
                       Orientation="Vertical"/>
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>                                        
</ItemsControl>
Ireabor
  • 50
  • 1
  • 9
  • Why don't you just put your ItemsControl and the Button inside another WrapPanel? – icebat Feb 18 '16 at 15:01
  • This only works as long as there is only one column of Objects. As soon as the second column is added the button is stuck at the bottom of the wrappanel. I want the button in the second column below the last item – Ireabor Feb 18 '16 at 15:46

3 Answers3

2

one way i can suggest is to have 2 item templates, depending on their purpose.

here I created a class InventoryNewItem, derived from InventoryItem (public class InventoryNewItem : InventoryItem {}, that is all). Type is used as a key for template selection from resourses

<ItemsControl ItemsSource="{Binding Inventory}">
    <ItemsControl.Resources>
        <DataTemplate DataType="{x:Type views:InventoryItem}">
            <StackPanel>
                <Grid >
                    <Button Content="{Binding Path=Name}" Click="ButtonBase_OnClick"/>
                </Grid>
            </StackPanel>
        </DataTemplate>

        <DataTemplate DataType="{x:Type views:InventoryNewItem}">
            <StackPanel>
                <Grid >
                    <Button Content="+" Background="Green" Click="Add_OnClick"/>
                </Grid>
            </StackPanel>
        </DataTemplate>                    
    </ItemsControl.Resources>

    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <WrapPanel MaxHeight="{Binding ElementName=window, Path=Height}" 
                       Orientation="Vertical"/>
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>
</ItemsControl>

pros: separate templates and click handlers (or command bindings) for each template

cons: additional manipulations to keep InventoryNewItem at the last position in ItemsSource (you can't add, have to insert new elements)

my test ItemsSource

var list = Enumerable.Range(1, 20)
              .Select(i => new InventoryItem {Name = i.ToString()})
              .Concat(new List<InventoryItem> {new InventoryNewItem()});
ItemsSource = new ObservableCollection<InventoryItem>(list);
ASh
  • 34,632
  • 9
  • 60
  • 82
0
<Grid>
  <Grid.RowDefinitions>
        <RowDefinition/>
        <RowDefinition Height="Auto"/>
  </Grid.RowDefinitions>
  <ItemsControl Grid.Row="0" ItemsSource="{Binding Inventory}">
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <StackPanel>
                <Grid Width="300">
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="200"/>
                        <ColumnDefinition/>
                        <ColumnDefinition Width="30"/>
                    </Grid.ColumnDefinitions>
                    <Button Content="{Binding Name}"                        
                            MouseDoubleClick="CallEdit"/>
                </Grid>
            </StackPanel>
        </DataTemplate>
    </ItemsControl.ItemTemplate>
    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <WrapPanel MaxHeight="{Binding ElementName=window, Path=Height}"
                       Orientation="Vertical"/>
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>                                        
    </ItemsControl>
    <StackPanel Grid.Row="1" >
        <Button Content="Add"/>
    </StackPanel>
</Grid>
  • Thank you, but this still leads to the problem that my button is stuck under the wrappanel and not after the last item in the panel where i want it... – Ireabor Feb 18 '16 at 14:27
0

As mentioned in this question, you can use CompositeCollection class to combine your collection with other controls or collections.

In this example I've added two buttons after CheckBox items:

<ItemsControl>
  <ItemsControl.ItemsSource>
    <CompositeCollection>
      <CollectionContainer Collection="{Binding Inventory}"/>
      <Button Margin="5,7,5,7" Padding="4,0" Content="CheckAll"/>
      <Button Margin="5,7,5,7" Padding="4,0" Content="UncheckAll"/>
    </CompositeCollection>
  </ItemsControl.ItemsSource>

  <ItemsControl.ItemsPanel>
    <ItemsPanelTemplate>
      <WrapPanel Orientation="Horizontal"/>
    </ItemsPanelTemplate>
  </ItemsControl.ItemsPanel>

  <ItemsControl.ItemTemplate>
    <DataTemplate>
      <CheckBox Content="{Binding Label}"  
                IsChecked="{Binding IsVisible}"  
                IsEnabled="{Binding IsEnabled}" 
                Margin="5,7,5,7"/>
    </DataTemplate>
  </ItemsControl.ItemTemplate>
</ItemsControl>