The World’s Leading Microsoft .NET Magazine
   
 
The .NET Addict's Blog

My Top Tags

                                                           

My RSS Feeds








Latest Diggs - Programming

Internet Blogs - Blog Top Sites

Site Hits

Total: 2,502,635
since: 19 Jan 2005

Building a Ledger Style for WPF Grids

posted Tue 21 Aug 07

When working on a demonstration application for CLINQ, I decided that I was going to use an alternating background color (commonly known as "ledger style") for a list of items. This is usually no big deal and I've seen a truckload of samples for doing it. One of the simplest and most common ways of doing it is to create an Item Container Style where the background property is data bound:

 <Style x:Key="alternatingStyle" TargetType="{x:Type ListBoxItem}">
      <Setter Property="Background">
        <Setter.Value>
          <Binding RelativeSource="{RelativeSource Self}"
Converter="{StaticResource myConverter}"/>
        </Setter.Value>
      </Setter>
</Style>

In the code for the binding (the static resource called "myConverter" which is an IValueConverter , you then simply use code like this:

listBox.ItemsContainerGenerator.IndexFromContainer(item);

To generate the index. Then all you need to do is see if the index is odd or even and return the appropriate resource/brush accordingly. This is typically where a lot of the samples stop. What they don't tell you is that this only works on the first shot through. Once your data source becomes volatile (let's say your list box is bound to something like a ContinuousCollection), your results are going to be erratic. It will result in multiple adjacent rows with the same color because the background color of previously inserted rows is not being modified.

I personally prefer to use an ItemContainerStyleSelector instead of setting up the fixed container style with a databound property. That looks something like this:

 <ListBox ... ItemContainerStyleSelector="{StaticResource ledgerStyleSelector}"/>

The code for the ledger style selector is very simple, and looks nearly identical to the code for a bound value converter that does the same task:

public class LedgerStyleSelector : StyleSelector
{
    private int _rowCount = 0;

    public override Style SelectStyle(object item, DependencyObject container)
    {
            string styleKey;
            ItemsControl ic = ItemsControl.ItemsControlFromItemContainer(container);
         
            if (item == ic.Items[0])
            {
                _rowCount = 0;
            }            

            if (_rowCount % 2 == 0)
            {
                styleKey = "whiteBackgroundStyle";
            }
            else
            {
                styleKey = "paleGoldenrodBackgroundStyle";
            }
            _rowCount++;

           return (Style)(ic.FindResource(styleKey));
        }
}

Again, this approach suffers from a similar problem as the first approach. Once the list to which your view/grid/listbox is bound starts churning with adds and removes, the existing rows are going to maintain their previous background style, screwing up your ledger style completely. The only way around this is to somehow refresh the style selector so that it will be re-run against the list when the collection changes.

Happily enough for us, ContinuousCollection is completely compliant with publishing collection changes because it derives from ObservableCollection<T>, so I was able to subscribe to the collection changed event (thanks for the tip from Bea Costa's blog! If you want anything data-binding related, check out Bea's Blog). Here's the code that runs every time the bound collection changes:

private void Restyle(object sender, NotifyCollectionChangedEventArgs args)
{
    LedgerStyleSelector selector = askList.ItemContainerStyleSelector as LedgerStyleSelector;
    selector.ResetRowCount(); 
    askList.ItemContainerStyleSelector = null;
    askList.ItemContainerStyleSelector = selector;

    ...
}

By setting the container style selector to null and then setting it back to the existing selector, it forces a re-evaluation of the style container for each item in the list. I also have a call to ResetRowCount() because there is a chance that this application could be running all day long with constantly changing data and if the row counter isn't reset to 0, at some point it will overflow.

So that's what I discovered when binding a continuously-updating data source to a WPF listbox - if you're going to do a ledger view there's a couple of details to remember, but it's not all that difficult at all.

Enjoy!

tags:    

links: digg this    del.icio.us    technorati    reddit




Tag Related Posts

CLINQ v1.1.0.0 Released!

Fri 02 May 08 5:38 P GMT-05
tags:          

CLINQ v1 Demo - Network Message Filtering

Wed 09 Jan 08 7:47 P GMT-05
tags:        

Continuous LINQ v1.0.0.0 Released

Tue 08 Jan 08 4:53 P GMT-05

Building a Ledger Style for WPF Grids

Tue 21 Aug 07 3:30 P GMT-05
tags:    

Continuous LINQ - Can I write games with it?

Mon 13 Aug 07 3:09 P GMT-05
tags:        

Continuous LINQ

Mon 06 Aug 07 1:21 P GMT-05
tags:    

My first "Acropolis" Application

Mon 04 Jun 07 1:40 P GMT-05
tags:      

Exploring the Delegate Design Pattern

Mon 14 May 07 6:30 P GMT-05

Will Silverlight be DOA?

Mon 16 Apr 07 8:02 P GMT-05

Exploring the MVC Pattern in WPF

Tue 10 Apr 07 12:51 P GMT-05
tags:                      

WPF Bindings == WTF Bindings?

Mon 12 Mar 07 6:31 P GMT-05

On MUDs

Thu 08 Mar 07 5:00 A GMT-05
tags:                    

Cocoa Programming vs. WPF : NIB vs XAML

Tue 20 Feb 07 2:09 P GMT-05

Cocoa Bindings vs. WPF Binding

Thu 15 Feb 07 5:41 P GMT-05
tags:                

Ulysses Agenda Makes Redmond Developer News

Wed 29 Nov 06 7:10 P GMT-05
tags:                

Ulysses Agenda - Network Engine Test 1

Mon 09 Oct 06 2:26 A GMT-05
tags:              

Ulysses Agenda : First Cut Networking Design

Thu 14 Sep 06 12:46 A GMT-05
tags:                  

First Impressions of Windows Vista RC1

Thu 07 Sep 06 1:30 P GMT-05
tags:                      

Localizing a WPF Application

Tue 22 Aug 06 11:39 A GMT-05
tags:            

WPF Slide Show and Photo Album

Fri 18 Aug 06 6:48 P GMT-05

.NET Framework 3.0 June CTP is out!

Fri 23 Jun 06 6:23 P GMT-05
tags:                

Tech-Ed 2006 - Session Reviews

Tue 13 Jun 06 6:22 P GMT-05
tags:                    

Tech-Ed 2006 Day 1 - Registration Day

Sun 11 Jun 06 7:17 P GMT-05

WPF/XAML and LINQ - A match made in heaven

Tue 06 Jun 06 11:32 A GMT-05
tags: