Darcara
Darcara

Reputation: 1608

Binding dependant on item type

How can I change the bound value depending on the type of the bound item.

I'm using the TreeListView from Christian Ricciolo although it works just the same with the standard TreeView.

I have two classes

class Group
{
  public String Name {get;}
  public Int32 MaxItems {get;}
  public ObservableList<Item> Items {get;}
}

class Item
{
  public String Name {get;}
  public String Status {get;}
}

The XAML

<Grid>
  <r:TreeListView ItemsSource="{Binding}">
    <r:TreeListView.Resources>
      <HierarchicalDataTemplate DataType="{x:Type local:Group}" ItemsSource="{Binding Items}" />
      <DataTemplate DataType="{x:Type local:Item}" />
    </r:TreeListView.Resources>
    <r:TreeListView.Columns>
      <GridViewColumn Header="Name" DisplayMemberBinding="{Binding Path=Name}"  />
      <GridViewColumn Header="Status" DisplayMemberBinding="???" />
    </r:TreeListView.Columns>
  </r:TreeListView>
</Grid>

That works fine and I can see all groups and their items.

In the status column I want to display Item.Status and Group.Items.Count +"/"+ MaxItems depending on what is actually displayed in that row.

Currently I'm using {Binding Converter={StaticResource testConverter}} where testConverter generates the output depending on the type, but thats cumbersome and I hope there is a cleaner way.

I cannot/don't want to create a Group.Status property. I think I cannot use a in the data templates either, since DisplayMemberBinding overwrites them and I want to be able to do that with multiple columns/bindings.

Upvotes: 0

Views: 268

Answers (1)

ChrisWue
ChrisWue

Reputation: 19020

The only other way I came across so far is to use the CellTemplateSelector property of the GridViewColumn. You will need to implement a DataTemplateSelector to select the proper DataTemplate. It's a little bit cleaner in the sense that you still define the display layout/content in the xaml and just choose the appropriate template.

Edit: Yes, you can't use DisplayMemberBinding in this case, however DisplayMemberBinding is a simple shortcut for a TextBlock data template bound to the path. However when you use the datatemplate you can do all the binding you need in there - you don't need the DisplayMemberBinding in this case.

Quick example:

<Grid>
    <Grid.Resources>
        <r:NameCellTemplateSelector x:Key="NameTemplateSelector"/>
        <r:StatusCellTemplateSelector x:Key="StatusTemplateSelector"/>
    <Grid.Resources>

    ...

    <GridViewColumn Header="Name" CellTemplateSelector={StaticResource NameTemplateSelector}  />
    <GridViewColumn Header="Status" CellTemplateSelector={StaticResource StatusTemplateSelector} />
</Grid>

And and implement the selectors:

public class NameCellTemplateSelector : DataTemplateSelector
{
    public override DataTemplate SelectTemplate(object item, DependencyObject container)
    {
        FrameworkElement element = container as FrameworkElement;

        // find the data template with a specific x:Key
        return element.FindResource("someNameTemplate") as DataTemplate;
    }
}

public class StatusCellTemplateSelector : DataTemplateSelector
{
    public override DataTemplate SelectTemplate(object item, DependencyObject container)
    {
        FrameworkElement element = container as FrameworkElement;

        // find the data template with a specific x:Key
        return element.FindResource("someStatusCellTemplate") as DataTemplate;
    }
}

You could probably also get away with just writing one selector as the container parameter is the UI element and you can walk up the visual tree to find out which column the selector is being called for and choose appropriately based on that. See also the answers to this question: Pass Data to Data Template Selector

Upvotes: 2

Related Questions