Introduction

I can assure you that this article was very difficult for me to write. This is my thirtieth article on CodeProject, so I thought it was time for a new challenge. The challenge is not only for me, but for you, the reader, as well. Hopefully I have overcome the challenge of writing this article well enough so that you might now face the far more daunting challenge of learning how to think in WPF. If you are already a seasoned WPF developer, feel free to read on and see how someone else solves problems using WPF.

This article attempts to explain the thought processes I went through while designing and implementing a solution to a problem using WPF. If you are fairly new to WPF and are just starting to ascend its infamous learning cliff, perhaps this article will help to shed some light on why and how one might put many of the WPF concepts to use. I am not claiming that my way of approaching WPF is the "right" or "best" way, but simply the way I think about it.

Background

This article assumes that you are already somewhat familiar with WPF. We will not be covering the basics here. If you need to learn about the fundamentals of WPF, you might want to check out my five-part Guided Tour of WPF right here on CodeProject.

The Problem to be Solved

I wanted to create sleek-looking selection indicators for the items in a ListBox. Instead of having my ListBox look like this…

…I wanted it to look something like this instead…

In both of those screenshots above, the same three items are selected in the ListBox. The top image shows the standard look of a ListBox on Windows XP with the Olive theme. The bottom image shows the standard ListBox with triangular selection indicators instead of highlighted ListBoxItems. If you were to scroll through the ListBox, those selection indicators need to stay directly next to their associated ListBoxItem at all times (otherwise they're rather meaningless).

The selection indicators do not need to be interactive. If the user clicks on an indicator, nothing should happen. It is only a visual feature, and does not affect the state of the ListBox.

I also want the "selection indicator" functionality to be reusable, so that I can easily apply selection indicators to any ListBox in any application. I need this functionality to be encapsulated, but things like colors and font sizes should be customizable.

Where to Start?

At this point we have a pretty clear understanding of the problem which needs to be solved. Now it is time to compare and contrast various possible solutions to the problem. A few approaches come to mind, so let's review them.

We could give the ListBox an ItemTemplate containing the selection indicator. The template could have a trigger which hides the indicator when the ListBoxItem is not selected. The problem with this solution is that a selection indicator will appear to be "part of" a ListBoxItem. I'd prefer to have the indicators be external to the entire ListBox, as seen in the screenshot above. This aesthetic decision makes the ItemTemplate approach inappropriate.

We could render a selection indicator in the adorner layer of a ListBoxItem. This approach frees us from having to render the indicator within the ListBoxItem's bounds, but it presents a new problem. If the ListBox is placed directly next to another control, then the selection indicators might be rendered on top of that neighboring control. This would result in some strange visual problems, so it seems that we need to allocate some screen real estate specifically for the selection indicators.

We could create a ControlTemplate for ListBox and allocate some space in the template for selection indicators. This would allow us to make it appear that the selection indicators are external to the ListBox, and also give the indicators some space of their own. However, why should we insist to other developers that having selection indicators and using their own custom ControlTemplate are mutually exclusive options? What if they need both? Since there is no way to customize or "subclass" an existing ControlTemplate, this approach won't work either.

We just reviewed three possible approaches to how and where the selection indicators will be rendered. None of them worked out, but we learned three important points along the way. Those points are:

The selection indicators must be outside of the ListBox, according to my aesthetic preferences.

The selection indicators need to have their own space to exist, so that they do not overlap with neighboring controls.

Using selection indicators should not limit what else you can do with the ListBox, such as prohibiting you from applying a custom ControlTemplate.

The third point needs some clarification. We can only provide support for so much customization to the ListBox. If the user swaps out the ListBox's ItemsPanel with some other layout panel, we cannot guarantee that our selection indicators will always line up correctly with the selected items. We need to assume that the ListBoxItems will be stacked vertically, as seen by default.

Based on all of the points introduced above, we must now decide how to move forward and start implementing this feature. We can satisfy all of our constraints by creating a UserControl subclass containing a ListBox and a Grid panel, which hosts the selection indicators, directly next to it. The basic structure of that UserControl, which is called ListBoxWithIndicator, can be seen below:

How to Draw the Selection Indicators?

The selection indicators are not part of the ListBox. They exist in a neighboring panel, and must be created/positioned/removed when items are selected/scrolled/deselected in the ListBox. What is a good way to accomplish that in WPF? Before reading any further, think about that question for a while.

Welcome back. If you took some time to contemplate how the selection indicators should be managed, you probably realized that there are many ways to skin that cat. If your first instinct was to owner-draw little triangles next to the selected items, you should take a look around you and realize that you aren't in Kansas anymore. WPF certainly allows you to do low-level rendering, somewhat similar to working with an HDC or Graphics object, but that would be taking the high road for absolutely no good reason.

One seemingly viable approach would be to hook the ListBox's SelectionChanged event and, when it is raised, create some Polygon elements (i.e. triangular selection indicators) in the selection indicator area. You could position those Polygons so that they are each next to a selected ListBoxItem by setting their Margin's Top to some calculated offset. That would effectively "push" each Polygon down to the correct location next to a ListBoxItem.

That technique would certainly work, but it just doesn't feel "right" to me. In my opinion we should not be manually creating and positioning the selection indicators. They should create and position themselves, based purely on some XAML markup. This reduces the number of moving parts in our code, which means that there will be fewer bugs to fix. So how can we implement this logic without writing too much code?

The solution to this problem makes use of several powerful features of WPF: an items panel, data binding an attached property, and a DataTemplate. Let's review the solution I came up with to see how it works.

A selection indicator has a fixed width and height, and also has a fixed horizontal offset from the left edge of the container in which it lives. The only variable it does not know by itself is its vertical offset from the top of the container in which it lives. That vertical offset effectively determines which selected ListBoxItem it "points at."

Suppose we were to calculate the vertical offsets needed to display selection indicators next to each selected ListBoxItem, and store those offsets in a collection. If we supplied those values to an ItemsControl as its ItemsSource, the ItemsControl would look like this (the offset values are circled in red):

Obviously that is not the visual effect we're after, but it is a start. At this point we have an ItemsControl next to the ListBox, and it contains a list of Doubles which represent how far away from the top of the ItemsControl each selection indicator needs to be. Next we need to give the ItemsControl's ItemTemplate property a DataTemplate which renders a selection indicator, as seen below:

That certainly doesn't look right! What's the problem here? Why aren't the selection indicators next to the selected items? Take a moment, think about it before continuing. It's OK, I'll wait…

The problem here is that our selection indicators have no idea that the Double value they represent in the ItemsControl should be used as their vertical offset. Just because we tell the ItemsControl to render each item as a little triangle doesn't mean that it will position them at the correct location for us. We need to explain how those offset values should be put to use. To do that we can make use of some powerful WPF capabilities: a custom items panel and binding an attached property.

By default ItemsControl lays out its items in a vertical stack. We don't want it to do that in this situation. Instead we need it to lay out the items in a Canvas, so that we can tell the Canvas where to position the selection indicators. We inform the Canvas of each selection indicator's vertical offset by binding the attached Canvas.Top property on each indicator. That XAML is seen below, and is part of the ItemsControl declaration:

<!--
Host all of the selection indicators
within a Canvas panel.
--><ItemsControl.ItemsPanel><ItemsPanelTemplate><Canvas/></ItemsPanelTemplate></ItemsControl.ItemsPanel><!--
Position a selection indicator based on the
offset value to which it is bound.
--><ItemsControl.ItemContainerStyle><StyleTargetType="ContentPresenter">
<Setter Property="Canvas.Top" Value="{Binding Path=.}" />
</Style></ItemsControl.ItemContainerStyle>

Since ItemsControl internally creates a ContentPresenter to host each item, we need to set the Canvas.Top property on that element so that it will be positioned correctly by the Canvas. When this is in place, and a few visual tricks are applied to remove the highlight color of a selected ListBoxItem, the UI looks like this:

When are the Selection Indicator Offsets Calculated?

The exact details of how the offsets are calculated are not relevant for this discussion, but it is interesting to note when they are calculated. In two situations it is important to update the offsets, when the selected items change and when the items are scrolled. Here is the ListBoxWithIndicator constructor, which sets up handlers for those two events:

public ListBoxWithIndicator()
{
InitializeComponent();
// Set up the list of selection indicator offsets
// as the data source for the ItemsControl.
_indicatorOffsets = new ObservableCollection<double>();
_indicatorList.ItemsSource = _indicatorOffsets;
// Move the indicators when the set of
// selected items is modified.
_listBox.SelectionChanged += delegate
{
this.UpdateIndicators();
};
// Move the indicators when the ListBox's
// ScrollViewer is scrolled.
_listBox.AddHandler(
ScrollViewer.ScrollChangedEvent,
new ScrollChangedEventHandler(delegate
{
this.UpdateIndicators();
}));
}

The way that the ScrollViewer's ScrollChanged event is handled is pretty interesting in that we never actually have to find the actual ScrollViewer and directly hook its event. Instead we rely on the bubbling nature of the routed event and let the event come to us, so to speak. Initially I planned on writing some code which walked down the visual tree looking for the ListBox's ScrollViewer, but decided that it's both easier and safer to just listen for the bubbling event. It is safer to use this technique because the more code you write, the more possibilities there are for bugs!

How to Make the ListBox and Selection Indicators Customizable?

So far we have figured out a way to render the selection indicators and keep them up-to-date as the user interacts with the ListBox. One thing that we have not yet figured out is how to make it easy for a developer to use the ListBoxWithIndicator control. In my mind there are two major concerns: you need to be able to configure the ListBox from XAML, and you need to be able to easily specify what color(s) the selection indicators should be. Unfortunately XAML like this won't work:

<!-- This is invalid XAML. --><local:ListBoxWithIndicator><local:ListBoxWithIndicator.ListBox><ListBox.ItemsSource><SomeData/></ListBox.ItemsSource></local:ListBoxWithIndicator.ListBox></local:ListBoxWithIndicator>

The problem is that there's no way to easily access the inner ListBox from within XAML. You cannot set properties on a sub-object of an object in XAML, unless you are creating that sub-object. So, how can we let a developer set properties on the ListBox within our ListBoxWithIndicator control? Once again, take a moment to think about this one…

The solution I decided to use is to simply expose a dependency property on ListBoxWithIndicator, called ListBoxStyle, and then bind our ListBox's Style property to it. Here's how that works:

When you create an instance of the control, you can set its ListBoxStyle property to a Style which sets any number of properties on the inner ListBox.

I also created a public dependency property called IndicatorBrush which the selection indicators bind their Fill property against. That enables a developer to have control over the colors of the indicators too.

Conclusion

If you are new to WPF, but have experience with older UI platforms, it is no small feat to unlearn your old way of doing things and learn the WPF way. There are many ways that WPF offers the developer new powers, but you have to be willing to go through the humbling experience of being a newbie all over again. Hopefully this article will help to accelerate that painful process for you, assuming you need any help in the first place.

Comments and Discussions

The thing I wondered about at the first "thought point" was how do you get the values for the vertical offsets, with scrolling etc in mind? I saw the rest as somehow do-able but totally reliant on getting this offset under all circumstances. Can you give some pointers as to how this was done please? Then, your article would be perfect!

The demo application source code is available at the top of this article. In the code-behind file for the ListBoxWithIndicator control, there is an UpdateIndicators method. That method calculates the vertical offsets.

I am researching how to place glyphs on the left side of a WPF DataGrid to indicate the current drop position when rows are being dragged. Since nobody seems to have done this yet, I'm plowing some new ground. This article is a big help in getting started. Thanks for the nice work.

In response to Pete's comments, it seemed like you were open to feedback on the scope of the example, so here's mine:

I'm embarassed to admit it, but unlike your 5 lessons, this one got away from me somewhere around "By default ItemsControl lays out its items in a vertical stack. We don't want it to do that in this situation. Instead we need it to lay out the items in a Canvas, so that we can tell the Canvas where to position the selection indicators.".

Looking the actual project code really makes me feel dumb, because there's not that much going on! I'm still real green with WPF, though, so I have an excuse... I hope.

What's this all mean? There's probably nothing wrong with the example, depending on who your target audience is. If you're trying to help the absolute newbies, though, I'd vote for smaller steps. For example, I've not yet used a ResourceDictionary and a few other details add to the confusion like the style that "Prevents the selected items from having a different color background.". I'm sure this all pathetically simple and I'll be mortified by this post in 3 months, but right now, it all adds up to "maybe I'll come back to this later".

If you're trying to help the absolute newbies, though, I'd vote for smaller steps.

That isn't the goal for most of my articles. I wrote A Guided Tour of WPF[^] so that newbies could have a place to get started. Most of the articles I write assume that the reader is already somewhat versed in WPF.

Todd Beaulieu wrote:

I'm sure this all pathetically simple and I'll be mortified by this post in 3 months

Probably. Like you pointed out, this article isn't really too advanced. It does assume you already know the fundamentals of WPF, but not much more than that. Come back and read this article in three months. If you've been doing WPF during that entire time, this article will be easy as pie.

I appreciate you taking the time and effort to "think out loud" on how to tackle a WPF issue.

I'm just learning WPF, but this article seemed, to me at least, to be at the right level. Along with learning how to do something in WPF, I'm also trying to learn why to do something one way or another. There are some great WPF articles out there on how to do things, but I’m frequently left wondering why the author made particular choices.

Thanks for the explanation of your thought process and all of your other great WPF articles. It’s much appreciated!

Slightly off topic, but, however, do you know any convenient way of taking screenshots of WPF applications?

Suppose it's intended for printing. Then, I need a 300dpi or 600dpi image so that there were no square pixels. The vector and resolution-independent nature allows to do that. But how? It's easy to shoot a single Avalon control/window by rendering it into an image, but what about the whole application? It has multiple avalon HwndTargets, and possibly oldschool controls also.

The Magnifier tool in Windows Vista does just the same thing: Avalon controls are zoomed smoothly, others are zoomed with square pixels. How to employ the same technique for taking screenshots?

Does anybody have any idea what types of controls were used to create the NYT "menu bar" area? The area I'm referring to is the area that contains "menu items" HOME | WORLD | U.S. | etc etc etc | MORE. Each "menu item" then drops down a potentially scrollable list. I'd like to implement something very similar and have experimented with a Toolbar that contains a button and list box that are stack paneled together and used as the toolbar item. Maybe I just need to go further with that approach but thought I'd check to see if someone out there knows and has maybe already replicated its look and feel.

Hope this is an appropriate place to ask this question. If not, accept my apologies and feel free to point me in the right direction.

Haven't read the whole article, so I can't comment on the technical side, but I'd like to make the following observation: while the new selection type is interesting as a proof of concept, I believe it's less usable than the classical one and shouldn't be used in production software before at least making an usability study.

You raise a good point. However, using the selection indicators does not mean that you cannot use the standard ListBoxItem highlighting. I intentionally removed that highlighting from selected items, just to emphasize the visual role of a selection indicator. You could very easily have both effects in place, in fact, it's more work to only have selection indicators.

:josh:My WPF Blog[^]Without a strive for perfection I would be terribly bored.

Why don't you use the built-in icon feature of the ListViewItem: you place a simple icon on all selected items?

I am not using ListViewItems, I'm using ListBoxItems. Neither of those classes have a built-in icon feature. And, as Kent pointed out on this thread, putting an icon in a ListBoxItem wouldn't satisfy my aesthetic requirement of having the indicators be external to the ListBox.

More generally, though, WPF avoids the use of images as visual adornments (i.e. it does not have many Icon properties). Instead, the WPF way of doing things is to provide a DataTemplate which can, if you so choose, display an Image element with an icon in it. Or the template can contain just pure WPF elements. This gives the developer and visual designers freedom to choose what type of visual media to display, instead of being forced to show an icon or bitmap.

The selection indicator functionality in the demo app could be extended to allow for templating of the indicators. But, that wasn't on my list of requirements for the feature.

:josh:My WPF Blog[^]Without a strive for perfection I would be terribly bored.

I didn't decide to download Orcas until a few days ago. It was all your talking about WPF that finally did it! I was very interested to see it in action, and I must say that it is very, very cool. Great article, also!

I get all the news I need from the weather report - Paul Simon (from "The Only Living Boy in New York")

Thanks Pete. I'm starting to think that I might have chosen too complicated of a feature for this article. I've received quite a bit of feedback, both on this message board and via e-mail, stating that the article is great but an intimidating use of WPF for most newbies. Before writing the article I questioned that exact issue: will the complexity of the feature overshadow the "psychological" aspect of the article? I thought that it would be boring if I walked the reader through the thought process of solving a relatively simple problem, but perhaps I thought wrong. I'm still on the fence... What do you think?

:josh:My WPF Blog[^]Without a strive for perfection I would be terribly bored.

I think that you have enough articles out there for people to get their feet wet with the basics. I think that people will "get this" article when they have mastered the basics, but it is a very valuable resource for users who have already got to grips with WPF. More importantly, this is very valuable to people like me who are just starting out on this journey because it highlights the thought processes we are going to have to use.

All in all - I'd say you pitched it at just the right level. And to quote Nish's comment below, this is your best article yet.

And a deep sigh from me, because when I read your articles I just get this deep down sense that I will never understand WPF. What you're doing here looks like rocket science, magic, and voodoo all rolled into one. I think WPF will conquer me.

I felt the same way when I first started getting into WPF. It just takes a while to make the mental adjustment. Once you start working with it, the pieces start fitting together quite nicely. WPF, as a platform, is top notch.

:josh:My WPF Blog[^]Without a strive for perfection I would be terribly bored.

I'll have to agree with Marc! It does feel like voodoo! But yes, WPF does seem to be a great approach to UI. I guess you've just got to spend some time with it till it kind of suddenly clicks together. My one beef with WPF so far has been its text rendering which is kind of sad. I know that this has been discussed[^] to death already. Hopefully that'll get fixed soon!

Thanks a lot, Nish. I agree. Your comment means a lot, considering that you are a published author of considerable merit.

This is definitely my best article, in terms of prose, so far. It was very hard for me to write this one because it required me to shift gears from "Explain how the code works" to "Explain how I solved the problem." As I'm sure you are well aware, when designing and implementing a feature many thoughts pass through your mind. Capturing those thoughts, trying to organize them into a chronological sequence, and explain what they meant at the time proved to be a very elusive problem for me.

:josh:My WPF Blog[^]Without a strive for perfection I would be terribly bored.