WPF ListView CellTemplate Binding

1.2k Views Asked by At

Data model:

public class Metric
{
    public string                         Name      { get; set; }
    public bool                           IsEnabled { get; set; }
}

public class FileItem
{
    public string                         FN        { get; set; }
    public ObservableCollection<string>   Values    { get; set; }
}

public class MainViewModel
{
    public ObservableCollection<Metric>   Metrics   { get; set; }
    public ObservableCollection<FileItem> FileItems { get; set; }
}

XAML:

<Window.Resources>
    <local:ValueConverter x:Key="ValueConverter" />
    <DataTemplate x:Key="MetricHeaderTemplate">
        <TextBlock Text="{Binding Name}" />
    </DataTemplate>
</Window.Resources>
...
<ListView ItemsSource="{Binding FileItems}">
    <ListView.View>
        <GridViewColumn Header="Path to file" Width="400">
            <GridViewColumn.CellTemplate>
                <DataTemplate>
                    <TextBlock Text="{Binding FN}" />
                </DataTemplate>
            </GridViewColumn.CellTemplate>
        </GridViewColumn>
        <GridViewColumn HeaderTemplate="{StaticResource MetricHeaderTemplate}" Header="{Binding Metrics[0]}">
            <GridViewColumn.CellTemplate>
                <DataTemplate>
                    <TextBox Text="{Binding Path=Values[0], Converter={StaticResource ValueConverter}}"/>
                </DataTemplate>
            </GridViewColumn.CellTemplate>
        </GridViewColumn>
        <GridViewColumn HeaderTemplate="{StaticResource MetricHeaderTemplate}" Header="{Binding Metrics[1]}">
            <GridViewColumn.CellTemplate>
                <DataTemplate>
                    <TextBox Text="{Binding Path=Values[1], Converter={StaticResource ValueConverter}}"/>
                </DataTemplate>
            </GridViewColumn.CellTemplate>
        </GridViewColumn>
    </ListView.View>
</ListView>

All works as expected. I'd like to move the CellTemplate to StaticResources (to re-use and not duplicate the logic) and trying this:

<Window.Resources>
    <local:ValueConverter x:Key="ValueConverter" />
    <DataTemplate x:Key="MetricHeaderTemplate">
        <TextBlock Text="{Binding Name}" />
    </DataTemplate>
    <DataTemplate x:Key="MetricCellTemplate">
        <TextBox Text="{Binding Path=., Converter={StaticResource ValueConverter}}"/>
    </DataTemplate>
</Window.Resources>
...
<ListView ItemsSource="{Binding FileItems}">
    <ListView.View>
        <GridViewColumn Header="Path to file" Width="400">
            <GridViewColumn.CellTemplate>
                <DataTemplate>
                    <TextBlock Text="{Binding FN}" />
                </DataTemplate>
            </GridViewColumn.CellTemplate>
        </GridViewColumn>
        <GridViewColumn
            HeaderTemplate="{StaticResource MetricHeaderTemplate}"
            Header="{Binding Metrics[0]}"
            CellTemplate="{StaticResource MetricCellTemplate}"
            DisplayMemberBinding="{Binding Values[0]}"
        />
        <GridViewColumn
            HeaderTemplate="{StaticResource MetricHeaderTemplate}"
            Header="{Binding Metrics[1]}"
            CellTemplate="{StaticResource MetricCellTemplate}"
            DisplayMemberBinding="{Binding Values[1]}"
        />
    </ListView.View>
</ListView>

Unfortunately, no luck: the cell's value not displayed. What am I missing?

Thanks.

P.S. This is simplified version, in fact both header template and cell template are more complicated and have different elements and logic.

2

There are 2 best solutions below

2
On

Incorect binding path (Path=.) on MyCellTemplate

<DataTemplate x:Key="MyCellTemplate">
    <TextBox Text="{Binding Path=., Converter={StaticResource MyConverter}}"/>
</DataTemplate>
10
On

According to the code you said it is working you are using the wrong property path.
Just because the template is defined as a resource, the Binding.Path doesn't change. This is because the binding you set is relative to the DataContext of the DataTemplate. Once the externally defined template is being applied, the DataContext is the same.
So the Binding.Path remains the same:

<Window.Resources>
  <local:MyConverter x:Key="MyConverter" />
  <DataTemplate x:Key="MyCellTemplate">
    <TextBox Text="{Binding Path=Values[0], Converter={StaticResource MyConverter}}"/>
  </DataTemplate>
</Window.Resources>