tags:

views:

92

answers:

2

Is it possible to continue the alteration styles in a gridview even when there are no items?

alt text

As you can see, after the last item, the pattern stops.

A: 

I can think of a very hacky way to achieve this. Just add empty rows to fill the screen space. All you would have to do is calculate how much space you have left on the screen and then translate that into how many rows you need (again this is very hacky).

It seems like a lot of work though to get the look you want.

Lucas McCoy
+1  A: 

Yes, WPF provides a rather elegant way to implement this because its templating mechanism allows you to fill the unused area in a GridView with whatever you like.

All you need to do is modify the ListView template to paint the unused section of the with a VisualBrush that typically consists of two GridViewItems stacked vertically (in the general case it will be AlternationCount GridViewItems).

The only complexity is choosing which color to start with when painting the unused section of the ScrollViewer. This is calculated as Items.Count modulo AlternationCount. The solution is to create a simple Control that does this calculation and use it in our ListView template. For the sake of my explanation I will call the control "ContinueAlternation".

The ListView template which would be mostly the default template with a local:ContinueAlternation control added below the ScrollViewer using a DockPanel, like this:

<ControlTemplate TargetType="{x:Type ListView}">
  <Border BorderThickness="{TemplateBinding BorderThickness}"
          BorderBrush="{TemplateBinding BorderBrush}"
          Background="{TemplateBinding Background}"
          SnapsToDevicePixels="True">
    <DockPanel>
      <ScrollViewer DockPanel.Dock="Top"
                    Style="{DynamicResource {x:Static GridView.GridViewScrollViewerStyleKey}}"
                    Padding="{TemplateBinding Padding}">
        <ItemsPresenter SnapsToDevicePixels="True" />
      </ScrollViewer>

      <local:ContinueAlternation
        ItemContainerStyle="{TemplateBinding ItemContainerStyle}"
        AlternationCount="{TemplateBinding AlternationCount}"
        ItemsCount="{Binding Items.Count,
                          RelativeSource={RelativeSource TemplatedParent}}" />

    </DockPanel>
  </Border>
</ControlTemplate>

The ContinueAlternation control will be displayed as a Rectangle painted with a tiled VisualBrush containing an ItemsControl that shows dummy rows, as follows:

<ControlTemplate TargetType="{x:Type local:ContinueAlternation}">
  <Rectangle>
    <Rectangle.Fill>

      <VisualBrush TileMode="Tile" Stretch="None"
                   ViewPortUnits="Absolute"
                   ViewPort="{TemplateBinding ViewportSize}">

        <ItemsControl x:Name="PART_ItemsControl"
                      ItemsSource="{Binding}" />
      </VisualBrush>
    </Rectangle.Fill>
  </Rectangle>
</ControlTemplate>

The DataContext here will be an array of dummy ListViewItem generated in code-behind from the given AlternationCount and ItemsCount:

public class ContinueAlternation
{
  public Style ItemsContainerStyle ... // Declare as DependencyProperty using propdp snippet
  public int AlternationCount ... // Declare as DependencyProperty using propdp snippet
  public int ItemsCount ... // Declare as DependencyProperty using propdp snippet

  protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e)
  {
    if(e.Property==ItemsContainerStyleProperty ||
       e.Property==AlternationCountProperty ||
       e.Property==ItemsCountProperty)
    {
      // Here is where we build the items for display
      DataContext =
        from index in Enumerable.Range(ItemsCount,
                                       ItemsCount + AlternationCount)
        select BuildItem( index % AlternationCount);
    }
  }
  ListViewItem BuildItem(int alternationIndex)
  {
    var item = new ListViewItem { Style = ItemsContainerStyle };
    ItemsControl.SetAlternationIndex(item, alternationIndex);
    return item;
  }

  protected override Size MeasureOverride(Size desiredSize)
  {
    var ic = (ItemsControl)GetTemplateChild("PART_ItemsControl");
    ic.Width = desiredSize.Width;
    Size result = base.MeasureOverride(desiredSize);
    ViewportSize = new Size(ic.DesiredSize);
    return result;
  }
  public Size ViewportSize ... // Declare as DependencyProperty using propdp snippet
}

Note that this same code could be written with PropertyChangedCallback instead of OnPropertyChanged.

You also need to do something to make sure the blank rows are the desired height. The easiest way to do this is to set either MinHeight or Content in your ItemsContainerStyle. Alternatively ContinueAlternation could set the height when it constructs each ListViewItem.

I typed all this code off the top of my head, but it is similar to code I've written and used before so it ought to work basically as-is.

Ray Burns