lucasg
lucasg

Reputation: 11002

Properly implement a custom grid listview control

I have an application which use three grid listview in a similar way :

enter image description here

(In reality, the "standard label" is a filterbox control, but for the sake of the issue I chosen the simplest visuals elements.)

I figured I can "refactor" the three list views into the same "styled" custom listview by using a ControlTemplate :

<Window x:Class="customlistview.StyledWindow"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
  xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
  xmlns:local="clr-namespace:customlistview"
  mc:Ignorable="d"
  Title="StyledWindow" Height="300" Width="300">
<Window.Resources>
  <ResourceDictionary>
    <ResourceDictionary.MergedDictionaries>
    </ResourceDictionary.MergedDictionaries>

    <Style x:Key="StyledListView" TargetType="{x:Type ListView}">
      <Setter Property="Template">
        <Setter.Value>
          <ControlTemplate TargetType="{x:Type ListView}">

            <Border x:Name="_Border" Background="#ADADAD">
              <Grid x:Name="_Grid" SnapsToDevicePixels="true">

                <Grid.RowDefinitions>
                  <RowDefinition Height="46*"/>
                  <RowDefinition  Height="auto" MaxHeight="25"/>
                </Grid.RowDefinitions>

                <ScrollViewer>
                  <ItemsPresenter x:Name="ContentsItems" Grid.Row="0"></ItemsPresenter>
                </ScrollViewer>
                <Label Grid.Row="1"  Background="#FF5C8F88">Standard Label</Label>
              </Grid>
            </Border>

          </ControlTemplate>
        </Setter.Value>
      </Setter>
    </Style>
  </ResourceDictionary>
</Window.Resources>

<Grid x:Name="MainGrid" Margin="0,0,0,0">
  <Grid.RowDefinitions>
    <RowDefinition Height="Auto"/>
    <RowDefinition Height="Auto"/>
    <RowDefinition Height="Auto"/>
  </Grid.RowDefinitions>

  <ListView x:Name="ModulesList" Style="{StaticResource StyledListView}" ItemsSource="{Binding ModulesItems}" Grid.Row="0">
    <ListView.View>
      <GridView AllowsColumnReorder="true">
        <GridViewColumn Header="Module" Width="300" DisplayMemberBinding="{Binding ModuleName, TargetNullValue='N/A'}"/>
      </GridView>
    </ListView.View>
  </ListView>

  <ListView x:Name="ItemsAList" Style="{StaticResource StyledListView}" Grid.Row="1">
    <ListView.View>
      <GridView AllowsColumnReorder="true">
        <GridViewColumn Header="ItemsA" Width="300" DisplayMemberBinding="{Binding ItemA, TargetNullValue='N/A'}"/>
      </GridView>
    </ListView.View>
  </ListView>

  <ListView x:Name="ItemsBList" Style="{StaticResource StyledListView}" Grid.Row="2">
    <ListView.View>
      <GridView AllowsColumnReorder="true">
        <GridViewColumn Header="ItemsB" Width="300" DisplayMemberBinding="{Binding ItemB, TargetNullValue='N/A'}"/>
        <GridViewColumn Header="SpecificToB" Width="300" DisplayMemberBinding="{Binding SpecificToB, TargetNullValue='N/A'}"/>
      </GridView>
    </ListView.View>
  </ListView>

</Grid>

It work, however I have lost the GridView Column Headers (and the background color has changed too) :

enter image description here

Whats the correct way to add a footer to a listview ?

Here the associated code :

StyledWindow.xaml.cs

using System.Collections.Generic;
using System.Windows;


namespace customlistview
{
   public class ModuleItem
   {
      public ModuleItem(string Name)
      {
         ModuleName = Name;
      }

      public string ModuleName { get; set; }
   }

   public class ItemAItem
   {
      public ItemAItem(string Name)
      {
         ItemA = Name;
      }

      public string ItemA { get; set; }
   }

   public class ItemBItem
   {
      public ItemBItem(string Name, string Specific)
      {
         ItemB = Name;
         SpecificToB = Specific;
      }

      public string ItemB { get; set; }
      public string SpecificToB { get; set; }
   }

   /// <summary>
   /// Interaction logic for MainWindow.xaml
   /// </summary>
   public partial class StyledWindow : Window
   {
      public List<ModuleItem> ModulesItems;
      public List<ItemAItem> ItemsAItems;
      public List<ItemBItem> ItemsBItems;

      public StyledWindow()
      {
         InitializeComponent();
         this.ModulesItems = new List<ModuleItem>();
         this.ItemsAItems = new List<ItemAItem>();
         this.ItemsBItems = new List<ItemBItem>();

         this.ModulesItems.Add(new ModuleItem("Mod1"));
         this.ModulesItems.Add(new ModuleItem("Mod2"));
         this.ModulesItems.Add(new ModuleItem("Mod3"));
         ModulesList.ItemsSource = ModulesItems;

         this.ItemsAItems.Add(new ItemAItem("ItemA1"));
         this.ItemsAItems.Add(new ItemAItem("ItemA2"));
         this.ItemsAItems.Add(new ItemAItem("ItemA3"));
         ItemsAList.ItemsSource = ItemsAItems;

         this.ItemsBItems.Add(new ItemBItem("ItemB1", "SpecificItemB1"));
         this.ItemsBItems.Add(new ItemBItem("ItemB2", "SpecificItemB2"));
         this.ItemsBItems.Add(new ItemBItem("ItemB3", "SpecificItemB3"));
         ItemsBList.ItemsSource = ItemsBItems;
      }
   }
}

Upvotes: 0

Views: 55

Answers (1)

Il Vic
Il Vic

Reputation: 5666

The way you used to add a "footer" to a ListView IMHO is not so wrong. Your issue is that columns' headers are not displayed. The guilty is the ScrollViewer you are using in your ControlTemplate.

In the standard ListView's template, the ScrollViewer is precisely the control which does the trick. Indeed to the ListView's ScrollViewer a peculiar style is applied (a style with a definite template which shows columns' headers).

So the solution is to apply the same style to your ScrollViewer. We are lucky because that style uses a well defined key, i.e. the const value GridView.GridViewScrollViewerStyleKey. So your template will be:

<Border x:Name="_Border" Background="#ADADAD">
    <Grid  x:Name="_Grid" SnapsToDevicePixels="true">

        <Grid.RowDefinitions>
            <RowDefinition Height="*"/>
            <RowDefinition Height="auto" MaxHeight="25"/>
        </Grid.RowDefinitions>

        <ScrollViewer Style="{DynamicResource {x:Static GridView.GridViewScrollViewerStyleKey}}">
            <ItemsPresenter x:Name="ContentsItems" Grid.Row="0" />
        </ScrollViewer>
        <Label Grid.Row="1" Background="#FF5C8F88">Standard Label</Label>
    </Grid>
</Border>

I hope it can help you.

Upvotes: 1

Related Questions