Where are the margins/padding set on a wpf ListView GridView?

Many features of the GridView are hard-coded into the class. In particular the GridViewRowPresenter creates either a hard-coded TextBlock or ContentPresenter container for each of the cells and forces the margins to 6,0,6,0 whether you like it or not.

Normally I would discourage you from anything that involves hard-coding a negative margin to compensate as it's a hack layered on another hack. However, after disassembling the GridViewRowPresenter, there are no other options.

My first attempt was to override the implicit style for the container control that was created in the GridViewRowPresenter class, but that control is a TextBox or a ContentPresenter which has no template to override.

My second attempt was to disassemble the code and build a new GridViewRowPresenter. I'm afraid there are just too many incestuous 'internal' calls in the class to make this a workable solution.

A smarter architect would have provided a DependencyProperty that allowed the user of the class to override the container that was created for each cell. For some reason they allowed this with the headers but not the actual cell contents.

So we are left with the fact that the cells in a GridRowPresenter can neither be overridden, reverse engineered, nor templated. I'm sorry, but the negative margins is the best that can be done with this class.

We need a better ListView: the GridView is not suitable for a lookless interface though I appreciate what they original architects were trying to do by divorcing the ItemsPresenter from the presentation.


I developed a solution based on Ian Griffiths's hack ( http://www.interact-sw.co.uk/iangblog/2007/05/30/wpf-listview-column-margins ). The control does the following:

  • sets hard-coded Margin for TextBlock or ContentPresenter created by GridViewRowPresenter to this control's Padding dependency property
  • propagates this control's HorizontalAlignment and VerticalAlignment to its containers
  • resets Margin/Padding for ListViewItem/GridViewRowPresenter containers.

So it gives you a kind of control of values which are hard-coded.

Code:

using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;

namespace Put.Your.Namespace.Here
{
   /// <summary>
   /// Class allows for reseting hard coded ListViewItem margins and paddings
   /// </summary>
   public class ListViewCustomizableCellPresenter : Decorator
   {
      protected override void OnVisualParentChanged(DependencyObject oldParent)
      {
         base.OnVisualParentChanged(oldParent);

         var cp = VisualTreeHelper.GetParent(this) as FrameworkElement;
         if (cp != null)
         {
            cp.Margin = Padding;
            cp.VerticalAlignment = VerticalAlignment;
            cp.HorizontalAlignment = HorizontalAlignment;
         }

         ResetGridViewRowPresenterMargin();
         ResetListViewItemPadding();
      }

      private T FindInVisualTreeUp<T>() where T : class
      {
         DependencyObject result = this;
         do
         {
            result = VisualTreeHelper.GetParent(result);
         }
         while (result != null && !(result is T));
         return result as T;
      }

      private void ResetGridViewRowPresenterMargin()
      {
         var gvrp = FindInVisualTreeUp<GridViewRowPresenter>();
         if (gvrp != null)
            gvrp.Margin = new Thickness(0);
      }

      private void ResetListViewItemPadding()
      {
         var lvi = FindInVisualTreeUp<ListViewItem>();
         if (lvi != null)
            lvi.Padding = new Thickness(0);
      }

      /// <summary>
      /// Padding dependency property registration
      /// </summary>
      public static readonly DependencyProperty PaddingProperty =
         DependencyProperty.Register("Padding", typeof(Thickness), typeof(ListViewCustomizableCellPresenter), new PropertyMetadata(default(Thickness)));

      /// <summary>
      /// Padding dependency property
      /// </summary>
      public Thickness Padding
      {
         get { return (Thickness)GetValue(PaddingProperty); }
         set { SetValue(PaddingProperty, value); }
      }
   }

}

Sample usage in xaml (inside definition of GridViewColumn):

<GridViewColumn.CellTemplate>
    <DataTemplate>
        <yourxamlnamespacehere:ListViewCustomizableCellPresenter Padding="0"
                                                        VerticalAlignment="Center">
            <YourControlOrPanelHere />
        </yourxamlnamespacehere:ListViewCustomizableCellPresenter>
    </DataTemplate>
</GridViewColumn.CellTemplate>

Use Snoop to figure out which element is responsible for imposing the extra spacing. Mouse over the space and hold down control and shift. The element will then be highlighted in the visual tree.


by default, each cell has a hardcoded margin of 6,0. Probably that's your problem. You can override this behaviour by setting a margin of -6,0 in your celltemplate