Search code examples
wpflistboxitemspaneltemplate

DisplayMemberPath when using ItemsPanelTemplate


I have a list of objects of type SomeObject that is defined like:

public struct SomeObject
{
  public SomeObject(int id, string imgPath)
  {
    Id = id;
    ImagePath = imgPath;
  }

  public int Id;
  public string ImagePath;
}

I want to display these objects in a listbox, that shows one image for each object and arranges them in a tile layout. The ItemsTemplate is just an Image. The ItemsPanelTemplate is a TilePanel.

When I use just strings (image paths) as list items instead of the SombeObject items, everything works and the images are displayed correctly.

<ListBox ScrollViewer.HorizontalScrollBarVisibility="Disabled"
         SelectionMode="Extended"
         SelectionChanged="OnItemSelectionChanged"
         ItemsSource="{Binding RelativeSource={RelativeSource AncestorType={x:Type controls:VerticalTileBox}},
                    Path=SomeObjectsCollectionView, UpdateSourceTrigger=PropertyChanged}"
         >
  <ListBox.ItemTemplate>
  <DataTemplate>
      <Image Source ="{Binding}" 
                 VerticalAlignment="Center" HorizontalAlignment="Center" Stretch="Uniform" >            
      </Image>
   </DataTemplate>
  </ListBox.ItemTemplate>
  <ListBox.ItemsPanel>
    <ItemsPanelTemplate>
      <controls:VirtualizingTilePanel ChildSize="{Binding 
        RelativeSource={RelativeSource AncestorType={x:Type controls:VerticalTileBox}},
                    Path=ItemSize, UpdateSourceTrigger=PropertyChanged, Mode=OneWay}" />
    </ItemsPanelTemplate>
  </ListBox.ItemsPanel>
  <ListBox.ItemContainerStyle>
    <Style TargetType="ListBoxItem">
      <Setter Property="HorizontalContentAlignment" Value="Stretch"/>
      <Setter Property="VerticalContentAlignment" Value="Stretch"/>
      <Setter Property="Padding" Value="0"/>
    </Style>
  </ListBox.ItemContainerStyle>
</ListBox>

When using SomeObject items, this code gives me a list of tiles but the images are not displayed.

System.Windows.Data Error: 40 : BindingExpression path error: 'ImagePath' property not found on 'object' ''SomeObject' (HashCode=1739718653)'. BindingExpression:Path=ImagePath; DataItem='SomeObject' (HashCode=1739718653); target element is 'Image' (Name=''); target property is 'Source' (type 'ImageSource')

This is expected, since I have not told the control, what exactly to display (which property of the SomeObject). Using the DisplayMemberPath in addition to the ItemsPanelTemplate does not work, so how do I set the property to be used for display in the image?

Thanks for your help!

edit: @ nkoniishvt

That really sounds like the right way to do it, but I cannot get it to work properly. What am I doing wrong?

I added this DP to the UserControl code behind:

public static readonly DependencyProperty ItemsTemplateProperty =
  DependencyProperty.Register("ItemsTemplate", 
  typeof(DataTemplate),
  typeof(VerticalTileBox),
  new PropertyMetadata(null));

public DataTemplate ItemsTemplate
{
  get {return (DataTemplate)GetValue(ItemsTemplateProperty);}
  set {SetValue(ItemsTemplateProperty, value);}
}

and changed the xaml like this:

<ListBox 
         ScrollViewer.HorizontalScrollBarVisibility="Disabled"
         SelectionMode="Extended"
         SelectionChanged="OnItemSelectionChanged"
         ItemsSource="{Binding RelativeSource={RelativeSource AncestorType={x:Type controls:VerticalTileBox}},
                    Path=ItemCollection, UpdateSourceTrigger=PropertyChanged}"
       ItemTemplate="{Binding RelativeSource={RelativeSource AncestorType={x:Type controls:VerticalTileBox}},
                    Path=ItemsTemplate, UpdateSourceTrigger=PropertyChanged}"
       >
   <ItemsPanelTemplate>
      <controls:VirtualizingTilePanel ChildSize="{Binding 
        RelativeSource={RelativeSource AncestorType={x:Type controls:VerticalTileBox}},
                    Path=ItemSize, UpdateSourceTrigger=PropertyChanged, Mode=OneWay}" />
  </ItemsPanelTemplate>
  </ListBox.ItemsPanel>
  <ListBox.ItemContainerStyle>
    <Style TargetType="ListBoxItem">
      <Setter Property="HorizontalContentAlignment" Value="Stretch"/>
      <Setter Property="VerticalContentAlignment" Value="Stretch"/>
      <Setter Property="Padding" Value="0"/>
    </Style>
  </ListBox.ItemContainerStyle>
</ListBox>

The data template is defined like this:

  <DataTemplate x:Name="SomeImageItemTemplate"   DataType="utilities:SomeObject">
    <Image Source ="{Binding Path=ImagePath}" 
                 VerticalAlignment="Center" HorizontalAlignment="Center" Stretch="Uniform" />
  </DataTemplate>

And the overall thing is used in my view like this:

  <ctrls:VerticalTileBox Source="{Binding ImageListView, UpdateSourceTrigger=PropertyChanged}" ItemsTemplate="{DynamicResource SomeImageItemTemplate}"/>

The result is that the tiles are displayed but they are all empty except for the objects' class names (SomeObject).


Solution

  • You can't have a generic DataTemplate if the data you put as ItemsSource of your ListBox is unknown and your UserControl must remain generic.

    You should let the consumer of your UserControl provide the DataTemplate instead, either in the UserControl.Resources or in a DataTemplate DP you need to create and bind to the ListBox's ItemTemplate.

    Panels shouldn't say how a children is displayed, only where and how big it should be.