views:

240

answers:

1

So, I have a ListBox which is bound to a list of business objects, using a DataTemplate:

<DataTemplate x:Key="msgListTemplate">
    <Grid Height="17">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="{Binding MaxWidth}" />
            <ColumnDefinition Width="*" />
         </Grid.ColumnDefinitions>
         <TextBlock Grid.Column="0" Foreground="Silver" Text="{Binding SequenceNo}" />
         <TextBlock Grid.Column="1" Text="{Binding MessageName}" />
    </Grid>
</DataTemplate>


<ListBox Name="msgList" 
    Grid.Column="0"
    ItemTemplate="{StaticResource msgListTemplate}"
    SelectionChanged="msgList_SelectionChanged"
    VirtualizingStackPanel.IsVirtualizing="True"
    ScrollViewer.HorizontalScrollBarVisibility="Hidden">
</ListBox>

Sometime after binding, I want to colour certain items in the list to distinguish them from the others. I do this on a background thread:

if(someCondition)
{
    msgList.Dispatcher.BeginInvoke(new Fader(FadeListItem), DispatcherPriority.Render, request);
}

delegate void Fader(GMIRequest request);
void FadeListItem(GMIRequest request)
{
    ListBoxItem item =
           msgList.ItemContainerGenerator.ContainerFromItem(request) as ListBoxItem;

    if(item!=null)
            item.Foreground = new SolidColorBrush(Colors.Silver);
}

This all works fine, and some list items are greyed out as expected. However, if I scroll such that the greyed items are no longer shown, then scroll back again to where they were, they are no longer silver, and have returned to the default black foreground.

Any idea why this is, or how to fix it? Is it because I have set IsVirtualizing to true? The listbox typically contains many items (20,000 is not uncommon).

+3  A: 

Is it because I have set IsVirtualizing to true? The listbox typically contains many items (20,000 is not uncommon).

You nailed it - the item you set the foreground color on is getting trashed once the user scrolls away.

While you've got the right general idea, the way you're going about this is a very un-WPFy way to do this - one better way to do this is to have a bool DP in your business object class (or have the BO implement INotifyPropertyChanged), then bind the bool to the Foreground color via a custom IValueConverter that returns (isTrue ? whiteBrush : greyBrush).

Since you may not want to / may not be able to modify your business object to support INotifyPropChanged, this is the reason for the M-V-VM pattern - create a class that wraps the object that is a DependencyObject and exposes just the properties you're interested in displaying.

Paul Betts
Polluting the domain model with switches to control presentation reeks of bad design to me.Will going the dependency object route actually fix my problem, or was that just an aside? It seems an awfully verbose way to achieve something simple.
Winston Smith
There is nothing inherent in my domain object that determines its display colour. I'm basing it on whether this object has a match in another set of data. Won't the dependency objects themselves be trashed once out of view, thus leaving us with the same problem?
Winston Smith
@joe: You're not mixing domain with presentation if that switch has a meaning *in* the domain (remember, at first we had no idea what the Grey actually signified) - if it doesn't really fit, this is where the ViewModel part comes in. @joe: No, the ViewModel objects will be in the collection you've bound the ItemsSource to, and they'll always exist.
Paul Betts
Since it's only an internal tool, I went with the easy option - added a property to my domain object and implemented INotifyPropertyChanged. Works just fine, thanks.
Winston Smith