WPF ListBox with multiple sources of different types
Asked Answered
T

2

1

I was actually setting up a sample application for something entirely different, but then I was trying this:

I have a collection of Movies. I'll have a list box which displays all the movies. The list box, however, provides them as buttons, so that you can click onto a button and play the movie.

The code is:

<StackPanel DockPanel.Dock="Top">
    <ListBox ItemsSource="{Binding Movies}" SelectedItem="{Binding Path=SelectedMovie}">
        <ListBox.ItemsPanel>
            <ItemsPanelTemplate>
                <WrapPanel IsItemsHost="True"
                           Width="{Binding (FrameworkElement.ActualWidth),
                                   RelativeSource={RelativeSource AncestorType=ScrollContentPresenter}}"/>
            </ItemsPanelTemplate>
        </ListBox.ItemsPanel>
        <ListBox.ItemTemplate>
            <DataTemplate>
                <Button Content="{Binding Title}"
                        Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}},
                                  Path=DataContext.PlayMovieCommand}"
                        CommandParameter="{Binding Id}" />
            </DataTemplate>
        </ListBox.ItemTemplate>
    </ListBox>
</StackPanel>

Then I was thinking of adding a single Button to the end, with the text "Add", and when I click onto that button, I can add a new movie.

I don't find a solution to provide this. While searching the Internet I found HierarchicalDataTemplate and CompositeCollection; both looking promising at first, but I failed to get it working as I want. I was thinking of MultiBinding, too, but again I seem to fail.

So, I guess my question is:
How can I add a single Add-button to my collection of movies?

Or more generic: How can I add several sources/collections of data of different types to a list box?

Tart answered 20/8, 2014 at 19:48 Comment(0)
L
2

Use CompositeCollection and DataTemplate for types.

<Page
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
  <Page.Resources>
    <XmlDataProvider x:Key="MoviesData" XPath="Movies/Movie">
      <x:XData>
      <Movies xmlns="">
        <Movie Title="The Shawshank Redemption" />
        <Movie Title="The Godfather" />
        <Movie Title="The Dark Knight" />
      </Movies>
      </x:XData>
    </XmlDataProvider>
    <XmlDataProvider x:Key="MyButtonsData" XPath="MyButtons/MyButton">
      <x:XData>
      <MyButtons xmlns="">
        <MyButton Title="Add Movie" />
      </MyButtons>
      </x:XData>
    </XmlDataProvider>
    <DataTemplate DataType="Movie">
      <Button Content="{Binding XPath=@Title}"
              Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}},
                        Path=DataContext.PlayMovieCommand}"
              CommandParameter="{Binding Id}" />
  </DataTemplate>
    <DataTemplate DataType="MyButton">
      <Button Content="{Binding XPath=@Title}"
              Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}},
                        Path=DataContext.AddMovieCommand}" />
  </DataTemplate>  

  </Page.Resources>
    <ListBox>
      <ListBox.ItemsSource>
        <CompositeCollection>
        <CollectionContainer Collection="{Binding Source={StaticResource MoviesData}}"/>
        <CollectionContainer Collection="{Binding Source={StaticResource MyButtonsData}}"/>
        </CompositeCollection>
      </ListBox.ItemsSource>
        <ListBox.ItemsPanel>
            <ItemsPanelTemplate>
                <WrapPanel IsItemsHost="True"
                           Width="{Binding (FrameworkElement.ActualWidth),
                                   RelativeSource={RelativeSource AncestorType=ScrollContentPresenter}}"/>
            </ItemsPanelTemplate>
        </ListBox.ItemsPanel>
    </ListBox>
</Page>

Here is the result in Kaxaml

enter image description here

Leucoplast answered 20/8, 2014 at 20:10 Comment(4)
Well, it took me some time to bind the list against the view model, because neither FindAncestor nor ElementName gave me the list. But when I added a CollectionViewSource to my resources and bound against that source, I've got my list of movies. Still, I had to implement a DataTemplateSelector as suggested in the other solution. – I'll accept this answer, because it provides me the basic concept of how to add several sources to my list.Tart
You don't need to implement DataTemplateSelector. You need to investigate the DataType property of the DataTemplate class.Leucoplast
Well, declaring two DataTemplates having the DataType property in the Window.Resources (or ListBox.Resources for that matter) wasn't enough. I didn't see the titles of the movies (I mean I saw the default template which is not satisfying here). When I put the DataTemplate into my List.ItemTemplate, I could only use one of them, but I could see my movies, at least. Using DataTemplateSelector solved the issue.Tart
The example above states another.Leucoplast
U
1

You can create List of objects, add Movies and then add another type for instance "string" object. Then you need to create class deriving from DataTemplateSelector which goes throughout List and determines whether element is Movie object. If so then it returns proper DataTemplate otherwise it return DataTemplate for button.

class DTS : DataTemplateSelector
{
    public DataTemplate MovieTemplate { get; set; }
    public DataTemplate ButtonTemplate { get; set; }

    public override System.Windows.DataTemplate SelectTemplate(object item, System.Windows.DependencyObject container)
    {
        return item is Movie ? MovieTemplate : ButtonTemplate;
    }
}

then in XAML you do

 <local:DTS x:Key="DTS" ButtonTemplate="{StaticResource ButtonTemplate}" MovieTemplate="{StaticResource MovieTemplate}"/>

Bear in mind that ButtonTemplate and MovieTemplate must be created before applying them since StaticResource demands this. MovieTemplate is the same template which you already have, only provide template in case of string for your Button. At the end you set ItemTemplateSelector as follows

   <ListBox ItemsSource="{Binding lista}" ItemTemplateSelector="{StaticResource DTS}"/>

As a result you are supposed to be displayed enter image description here

Unswear answered 20/8, 2014 at 20:2 Comment(1)
Your answer was very helpful to get it working. However, I don't like it to bind against a property of type object which represents all possible data (I guess that's what you were saying, right?). I've chosen the CompositeCollection solution as provided in the other answer and combined it with the DataTemplateSelector. +1 anywayTart

© 2022 - 2024 — McMap. All rights reserved.