Click here to Skip to main content
Click here to Skip to main content

Highlighting Items in a WPF ListView

By , 28 Apr 2007
 
Screenshot - HighlightingItemsInWPFListView.png

Introduction

This article shows how to change the color of items in a ListView, based on values in or associated with the item. The technique used here binds a ListView to an ADO.NET DataTable, and makes use of a custom value converter to determine what color each ListViewItem should be.

Background

Here's a common scenario: you have a DataTable which needs to be displayed in a ListView, and any row which contains a value in some certain range (say, less than zero) should be "highlighted" with a special color. Perhaps the value which determines the ListViewItem's color is not even displayed in the ListView, but only exists in a DataRow. How might one implement that functionality in WPF?

There are four steps involved with this task:

  1. Populate a DataTable and bind it to a ListView.
  2. Specify how the ListView should display the DataTable (i.e. specify where the items come from, configure the columns, etc.).
  3. Write a Style which highlights ListViewItems.
  4. Create a class which helps determine a ListViewItem's color.

This article's demo application creates a simple DataTable, which contains a customer ID, name, and balance. If the customer is owed money (i.e. her balance is negative) then that customer's item is highlighted red. If the customer owes money, then the item is green.

Step one - Populate a DataTable and bind it to a ListView

Let's assume that our Window subclass contains a ListView in it, named 'listView'. First we must create the DataTable and set it as the ListView's DataContext.

public Window1()
{
 InitializeComponent();
 this.listView.DataContext = CreateDataTable();
}

// In a real app the DataTable would be populated from a database
// but in this simple demo the dummy data is created locally.
DataTable CreateDataTable()
{
 DataTable tbl = new DataTable( "Customers" );

 tbl.Columns.Add( "ID", typeof( int ) );
 tbl.Columns.Add( "Name", typeof( string ) );
 tbl.Columns.Add( "Balance", typeof( decimal ) );

 tbl.Rows.Add( 1, "John Doe", 100m );
 tbl.Rows.Add( 2, "Jane Dorkenheimer", -209m );
 tbl.Rows.Add( 3, "Fred Porkroomio", 0m );
 tbl.Rows.Add( 4, "Mike Spike", 550m );
 tbl.Rows.Add( 5, "Doris Yakovakovich", 0m );
 tbl.Rows.Add( 6, "Boris Zinkwolf", -25m );

 return tbl;
}

Step two - Specify how the ListView should display the DataTable

Now that the DataTable is ready and available to be displayed, let's see how to show it in a ListView.

<ListView
  Name="listView"
  ItemContainerStyle="{StaticResource ItemContStyle}"
  ItemsSource="{Binding}"
  >
  <ListView.View>
    <GridView>
      <GridViewColumn Header="ID" DisplayMemberBinding="{Binding ID}" />
      <GridViewColumn Header="Name" DisplayMemberBinding="{Binding Name}" />
      <GridViewColumn Header="Balance" Width="140">
        <GridViewColumn.CellTemplate>
          <DataTemplate>
            <TextBlock Text="{Binding Balance}" TextAlignment="Right" />
          </DataTemplate>
        </GridViewColumn.CellTemplate>
      </GridViewColumn>
    </GridView>
  </ListView.View>
</ListView>

In the Window's constructor we assigned a DataTable to the ListView's DataContext, so setting its ItemsSource property to '{Binding}' means to simply bind against that DataTable. Each column displayed in the ListView is represented by a GridViewColumn. The 'Balance' column's CellTemplate is set (as opposed to using the DisplayMemberBinding) so that the monetary value can be right-aligned, which is typical for displaying numeric values.

The ListView's ItemContainerStyle property is set to a Style, which is yet to be shown. ItemContainerStyle is used because that property affects the Style property of each ListViewItem generated by the ListView. Since we want to highlight an entire ListViewItem that property is the logical place to apply our "highlight style".

Step three - Write a Style which highlights ListViewItems

In the previous section, the ListView's ItemContainerStyle was set to a Style whose key is 'ItemContStyle'. That Style is seen below:

<Style x:Key="ItemContStyle" TargetType="{x:Type ListViewItem}">
  <Style.Resources>
    <!-- Brushes omitted for clarity… -->
    <!-- Reduces a customer's Balance to either -1, 0, or +1 -->
    <local:NumberToPolarValueConverter x:Key="PolarValueConv" />
  </Style.Resources>

  <!-- Stretch the content so that we can right-align values
       in the Balance column. -->
  <Setter Property="HorizontalContentAlignment" Value="Stretch" />

  <Style.Triggers>
    <!-- When a customer owes money, color them green. -->
    <DataTrigger
      Binding="{Binding Balance, Converter={StaticResource PolarValueConv}}"
      Value="+1"
      >
      <Setter Property="Background" Value="{StaticResource ProfitBrush}" />
    </DataTrigger>

    <!-- When a customer is owed money, color them red. -->
    <DataTrigger
      Binding="{Binding Balance, Converter={StaticResource PolarValueConv}}"
      Value="-1"
      >
      <Setter Property="Background" Value="{StaticResource LossBrush}" />
    </DataTrigger>
  </Style.Triggers>
</Style>

The Style sets two properties on each ListViewItem: HorizontalContentAlignment and Background. The former is set to 'Stretch' so that the elements in the ListView's "cells" will occupy the entire surface area of those cells. That allows us to right-align the text in the 'Balance' column.

The Background property of each ListViewItem is conditionally set to a "highlight brush" based on the customer's 'Balance' value. A DataTrigger is used to evaluate a customer's 'Balance' value and then, if the customer either owes money or is owed money, that customer's ListViewItem will have its Background set to the appropriate brush.

Step four - Create a class which helps determine a ListViewItem's color

The DataTriggers seen in the previous section use a custom value converter in their Bindings, called NumberToPolarValueConverter. The purpose of that converter is to take in a customer's balance and return a simple value which indicates if that customer either is owed money, owes money, or has no balance. If the customer owes money (i.e. the customer's balance is more than zero dollars) then it returns +1. If the customer is owed money, it returns -1. If the customer has no balance, zero is returned.

This value converter is necessary because a DataTrigger's Value property cannot express a range, it can only express a distinct value. In other words, there is no way to have the DataTrigger's Value indicate that the trigger should execute when a customer's balance is, say, any number less than zero.

Since Value cannot express a range, we can take the opposite approach and have the DataTrigger's Binding eliminate the range of values which the 'Balance' field can have. If the Binding evaluates to a small, discrete set of values (-1, 0, or +1) then the Value property can easily be used to check for those specific values.

Here is how that value converter is implemented:

[ValueConversion( typeof(object), typeof(int) )]
public class NumberToPolarValueConverter : IValueConverter
{
 public object Convert(
  object value,     Type targetType,
  object parameter, CultureInfo culture )
 {
  double number = (double)System.Convert.ChangeType( value, typeof(double) );

  if( number < 0.0 )
   return -1;

  if( number == 0.0 )
   return 0;

  return +1;
 }

 public object ConvertBack(
  object value,     Type targetType,
  object parameter, CultureInfo culture )
 {
  throw new NotSupportedException( "ConvertBack not supported" );
 }
}

External links

ADO.NET data binding in WPF

Related topics

History

  • April 28, 2007 – Created the article.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Josh Smith
Software Developer (Senior) Cynergy Systems
United States United States
Member
Josh creates software, for iOS and Windows.
 
He works at Cynergy Systems as a Senior Experience Developer.
 
Read his iOS Programming for .NET Developers[^] book to learn how to write iPhone and iPad apps by leveraging your existing .NET skills.
 
Use his Master WPF[^] app on your iPhone to sharpen your WPF skills on the go.
 
Check out his Advanced MVVM[^] book.
 
Visit his WPF blog[^] or stop by his iOS blog[^].

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
GeneralMy vote of 5memberNyrr11 Feb '13 - 1:01 
the only thing that helps!
GeneralMy vote of 4memberMarconte17 Sep '12 - 2:11 
Very clear.
GeneralMy vote of 5memberTarun.K.S8 Nov '10 - 0:05 
Nice article! As a newbie to WPF, i have learnt a new thing from this article.
Thanks a lot! Smile | :)
Generalthanksmembervegeta4ss27 Apr '10 - 6:37 
I was able to use this article to build myself a style and converter which colors objects based on the amount of time elapsed since the event was logged.
 
Thanks! Keep publishing wpf stuff and i'll keep reading Smile | :)
GeneralBinding to DataTable is unclear.memberMember 418359019 Feb '10 - 12:50 
Hi. Could you please explain how you bind DataTable to ListView.
 
You Create DataTable, fill with data , call this.listView.DataContext = CreateDataTable();
So far so good.
In the List View you have
ItemsSource="{Binding}"
So to what collection it's binded? DataTable exposes many public properties.
 
<GridViewColumn Header="ID" DisplayMemberBinding="{Binding ID}"
ID is the property (Path = ID). So what object exposes this property?
Could you please explain the details. Thaznk you, Alexander
 
Could you please send me the reply on
ayanushpolsk@bloomberg.net
Generalcould you help megroupSparkling_ouc6 Jul '09 - 16:34 
Hello,
i have some questions in the databinding.
in the xaml, i defined a listview nested an commbobox and a button.
<Grid Height="Auto" Width="Auto">
<ListView Name="listview1" Margin="0,0,0,29">
<ListView.View>
<GridView x:Name="testGridviw">
<GridViewColumn Header="Index" DisplayMemberBinding="{Binding Path=Index}"/>
 
<GridViewColumn Header="Databse" >
<GridViewColumn.CellTemplate>
<DataTemplate>
<ComboBox Name="testcombobox" Width="200"
ItemsSource="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:Window1}}, Path=EducationTypes}"
SelectionChanged="comboBoxInWnd_SelectionChanged">
</ComboBox>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
</Grid>
<Button Height="23" HorizontalAlignment="Right" Margin="0,0,62,0" Name="button1" VerticalAlignment="Bottom" Width="75" Click="button1_Click">show result</Button>
 
in the xaml.cs, my code writes as:
public Window1()
{
 
InitializeComponent();
InitializeDatabase();
this.listview1.ItemsSource = persons;
 
}
 
public BindingList<Person> persons = new BindingList<Person>();
		public BindingList<Person> Persons
		{
			get
			{
				return persons;
			}
		}
 
public List<String> types = new List<String>();
		public List<String> EducationTypes
		{
			get
			{
				return types;
			}
		}
 
private void InitializeDatabase()
{
types.Add("A");
types.Add("BB");
types.Add("CCC");
types.Add("DDDD");
types.Add("EEEEE");
types.Add("F");
 
for(int i = 0; i <= 6; i++)
{
persons.Add(new Person(i, types[i]));
}
}
 
private void comboBoxInWnd_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
ComboBox box = sender as ComboBox;
String s1 = box.Text;
}
private void button1_Click(object sender, RoutedEventArgs e)
{
MessageBox.Show(persons[0].Name);
MessageBox.Show(persons[1].Name);
}
 
and the class Person writes as:
public class Person : INotifyPropertyChanged
{
public Person(int index, String name)
{
this.index = index;
this.m_name = name;
}
private int index;
 
public int Index
{
get
{
return index;
}
set
{
if (index != value)
{
index = value;
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("Index"));
}
}
}
}
 
private string m_name;
 
public string Name
{
get
{
return m_name;
}
set
{
if (m_name != value)
{
m_name = value;
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("Name"));
}
}
}
}
 
#region INotifyPropertyChanged Members
 
public event PropertyChangedEventHandler PropertyChanged;
 
#endregion
}
 
and when i run the program ,in the right of the list view ,there are a group of comboboxes, and when i change the commbox's text,and click the button , as the "MessageBox.Show(persons[0].Name);
MessageBox.Show(persons[1].Name);" showed.
i thought the Name of the current person would be changed.
but actually,it didn't work as i thought.
so where the key was and how can i solve the problem?
thanks a lot for helping me if any.
GeneralHelp me onmembermohammad sayedi5 Jul '09 - 0:58 
I want capture SelectionChanged event by Binding.
Code: SelectionChanged="{Binding OnSelectionChanged}"
protected void OnSelectionChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e){...}
Do you can help me to solve it?
GeneralWPF=WTFmemberadrian7321 Feb '09 - 4:01 
Can someone explain me why we go back 15 years to program in a html like editor?
Why it takes 4 pages to explain how to change the row color of a list, while you can do this in WinForms with a single line or in designer?
Why can someone release these days a super form designer without a font selector dialog?
Why everything is fuzzy and the text look all blured?
 
Well I thought this technology its gonna be great, but it sucks bad, or I'm missing something. Its not even fast, which might be the only reason I would switch to it.
GeneralRe: WPF=WTFmemberRugbyLeague5 Mar '09 - 3:41 
I feel your pain. I am eventually starting to like WPF but it has taken a long time - articles from Josh, Sacha et al have certainly helped. VS 2008 and WPF apps are still horribly slow on my PC though.
GeneralRe: WPF=WTFmemberTibor Blazko20 Apr '09 - 21:04 
wow, the same feeling
GeneralThanks!memberGary Wheeler3 Dec '08 - 5:22 
I'm just getting started with C#, .NET, and WPF. I used the technique from this article in a little demo application, and the results are very Cool | :cool: .
 
I used the phrase "you native-mode savages" several times when demonstrating it to my coworkers Laugh | :laugh: .
 
Software Zen: delete this;

GeneralRe: Thanks!mvpJosh Smith3 Dec '08 - 5:24 
Hey Gary,
 
I'm glad you liked it. Thanks!
 
:josh:
Try Crack![^]
Sleep is overrated.

Questionuse a function within a class as the Converter?memberbflosabre9113 Aug '08 - 8:20 
great article, but im having one problem with the line of code. i have a function in a class that returns the status of the row based on a value of that row. how would i reference that function in xaml?
AnswerRe: use a function within a class as the Converter?mvpJosh Smith13 Aug '08 - 8:21 
Have a value converter call that function from its Convert method and return that function's return value.
 
:josh:
My WPF Blog[^]
Sleep is overrated.

GeneralthanksmemberAbhijit Jana5 Aug '08 - 1:10 
It's realy helped me !!!!
 
thanks a lot @@@
 
cheers,
Abhijit

GeneralNeed your suggestionmemberMoim Hossain28 Jul '08 - 3:54 
Hi Josh, This is a cool article.
Hey I am struggling a bit with a simple issue- my bad. So thought, let's ask a WPF guru like you for a quick resolution.
 
I have a listbox/listview that is displaying a DataView using Binding. Now I need to have a zic-zac effect onto the listbox/listview. Say one row should be in blue backgroud and the next one is white and the next one is blue and so on...
 
Could you please help me to get a resolution for this..?
 
Thanks in Advance!!
 
Moim Hossain
Senior Software Engineer
KAZ Software

GeneralRe: Need your suggestionmvpJosh Smith28 Jul '08 - 4:09 
This[^] article shows how to do that.
 
:josh:
My WPF Blog[^]
Sleep is overrated.

GeneralRe: Need your suggestionmemberMoim Hossain28 Jul '08 - 4:22 
Thanks a lot for a quick reply...Best regards!
 
Moim Hossain
Senior Software Engineer
KAZ Software

QuestionHow to display multiple colors for different Cells of listviewmemberchanduin4u16 Jul '08 - 8:49 
Hi i have been through your article ..Its good and very helpful.
 
In my scenario i am getting the result set from a Ilist Collection. in my listview i have 3 columns ( FirstName, LastName, Nickname ). the result set is obtained based upon the input given by the user in the textbox. I have to higlight the particular cell which matches with the given user input. If the input given by the user is a single word it searches for all the three (FirstName, LastName, Nickname ) fields in the database and populate it to listview.
 
Now i have to highlight the cells in the first, last, nick names that matches the user input.
 
Note: Complete row should not be highlited ro colored as in the given article, but cell.
 
Thanks,
 
Chandu.....
 
cheers
chandu

AnswerRe: How to display multiple colors for different Cells of listviewmvpJosh Smith16 Jul '08 - 8:55 
I'd use a ViewModel to determine which values contain the input text, and then use some Triggers in the CellTemplate of the ListView that change the color based on whether the ViewModel properties indicate that a match was found.
 
:josh:
My WPF Blog[^]
All of life is just a big rambling blog post.

GeneralRe: How to display multiple colors for different Cells of listviewmemberchanduin4u16 Jul '08 - 9:00 
I did not get you Josh..
 
Chandu
 
cheers
chandu

GeneralRe: How to display multiple colors for different Cells of listviewmvpJosh Smith16 Jul '08 - 9:03 
I suggest you read this[^] article, and then apply what you learn from it to this problem.
 
:josh:
My WPF Blog[^]
All of life is just a big rambling blog post.

GeneralRe: How to display multiple colors for different Cells of listviewmemberTibor Blazko20 Apr '09 - 21:12 
no idea for simple solution?
or any listview example?
GeneralBrilliant [modified]memberKyle Rozendo28 Mar '08 - 1:24 
Nicely done, really appreciate the constant work you've put in for the WPF community. *Edit - Goodlord my spelling.*
 
Kyle Rozendo
Developer :: Seriun
UK :: RSA
modified on Tuesday, May 13, 2008 5:34 AM

GeneralRe: BrilliantmvpJosh Smith28 Mar '08 - 3:22 
Thanks Kyle! Smile | :)
 
:josh:
My WPF Blog[^]
All of life is just a big rambling blog post.

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web03 | 2.6.130523.1 | Last Updated 28 Apr 2007
Article Copyright 2007 by Josh Smith
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid