Rick Strahl's Web Log

Wind, waves, code and everything in between...
ASP.NET • C# • HTML5 • JavaScript • AngularJs
Contact   •   Articles   •   Products   •   Support   •   Search
Ad-free experience sponsored by:
ASPOSE - the market leader of .NET and Java APIs for file formats – natively work with DOCX, XLSX, PPT, PDF, images and more

Silverlight ListBox Selections or what’s with SelectionChanged Events?


 

I was musing today on Microsoft’s obsession with OnSelectionChanged events on list controls in general and particularily in Silverlight, which is what I’m working with at the moment.In one of the apps I’m mucking around with I have a ton of sidebar menu selections that get displayed and the easiest way to drive these is with ListBoxes.

They look something like this:

SilverlightMenuList

As you can see this ‘menu’ is a collection of controls – a StackPanel with a border element at the top:

<StackPanel x:Name="SidebarMenu2" Margin="10,40,10,10">
    <Border x:Name="SidebarMenu2Header" Style="{StaticResource MenuHeaderStyle}" >
        <TextBlock Text="Update" Style="{StaticResource MenuHeaderTextStyle}"></TextBlock>
    </Border>

    <Border BorderBrush="Black" BorderThickness="1" >
        <StackPanel Background="White" >
            <ListBox x:Name="SidebarMenu2MenuItems" BorderThickness="0" Padding="5" 
                     SelectionChanged="SidebarMenu2MenuItems_SelectionChanged"
                     >
                <ListBox.Items>
                    <ListBoxItem Tag="Save" Content="Save" ></ListBoxItem>
                    <ListBoxItem Tag="Close" Content="Close"></ListBoxItem>
                    <ListBoxItem Tag="Delete" Content="Delete"></ListBoxItem>
                </ListBox.Items>
            </ListBox>
        </StackPanel>
    </Border>
</StackPanel>

I thought to use a listbox for this because it provides the default behavior of clickability as well highlighting the menu selection. The alternative would be having to create all of this behavior manually which is decidedly non-trivial. Additionally listboxes can also be data bound, which isn’t the example above,  but if menus get more complex using dictionary data sure would be easier to use than individual control markup for each item.

Ok, so using a listbox is handy, but as shown above with the SelectionChanged event the behavior is not working all that well for a menu. The selection change event is implemented like this:

private void SidebarMenu2MenuItems_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
    object item = this.SidebarMenu2MenuItems.SelectedItem;
    if (item == null)
        return;

    ListBoxItem listItem = item as ListBoxItem;
    string tag = (string)listItem.Tag;

    if (tag == "Save")            
        this.SaveItem();            
    else if (tag == "Close")
        NavigatorPanel.Navigate(new ItemListPage());
    
}

So far so good. This actually works to make a single selection or another selection that is different which is the most common thing you might do.

But in this scenario SelectionChanged becomes a problem when you try to save more than once in a row. This is a common problem with SelectionChanged events. As the name implies SelectionChanged fires only – well when the selection changes. But what if I saved to accept my changes to commit the changes, then make some more edits and save again? I’m not  re-selecting the same selection and I’m expecting the save to fire again. Only it doesn’t…

This behavior is also prominent in list controls in ASP.NET and even Windows Forms and frankly the absence of an easy way to trap ItemClicks as opposed to SelectionChanged operations are annoying. I’d argue 90% the behavior that is expected is an ItemClick not a SelectionChange.

Ok, so how can this be fixed in the Silverlight code above? My first thought was to hook up a MouseLeftButtonDown event:

 

<ListBox x:Name="SidebarMenu2MenuItems" BorderThickness="0" 
         Padding="5" 
         MouseLeftButtonDown="MenuClickHandler"    
         >
    <ListBox.Items>
        <ListBoxItem Tag="Save" Content="Save" MouseLeftButtonDown="MenuClickHandler"></ListBoxItem>
        <ListBoxItem Tag="Close" Content="Close"></ListBoxItem>
        <ListBoxItem Tag="Delete" Content="Delete"></ListBoxItem>
    </ListBox.Items>
</ListBox>

But I was surprised that this didn’t work. Either by handling the event on the listbox itself or on each of the items. Apparently the listbox eats those events.

What did work in the end was the MouseLeftButtonUp event on the listbox:

<ListBox x:Name="SidebarMenu2MenuItems" BorderThickness="0" Padding="5" 
         MouseLeftButtonUp="MenuClickHandler">

which did handle the click. I now end up with a generic menu click handler that all the listbox ‘menus’ are hooked up to on the form:

private void MenuClickHandler(object sender, MouseButtonEventArgs e)
{
    ListBox list = sender as ListBox;
    object item = list.SelectedItem;
    if (item == null)
        return;

    ListBoxItem listItem = item as ListBoxItem;
    string tag = (string)listItem.Tag;

    if (tag == "Save")
        this.SaveItem();
    else if (tag == "Close")
        NavigatorPanel.Navigate(new ItemListPage());

    this.SidebarMenu2MenuItems.SelectedItem = null;
}

One thing to keep in mind with this approach is that it doesn’t account for keyboard navigation – so if you move the up and down arrows manually the menu will move, but nothing actually handles the selection. Incidentally this is another example of the shortcoming of SelectedChanged events as a mechanism for selection – when you use keyboard navigation you probably don’t want to fire the operations in the menu – only when you click or press space/enter should anything happen. But this is a pain in the ass to handle. Handling selection reliably on a control is not trivial as you have a host of events to capture (Mouse, Keyboard, plus navigation and possibly hotkeys – not trivial).

You ever run into these types of scenarios or am I making a mountain out of a molehill? I have the same issues in ASP.NET in many situations where you have to respond to SelectedIndexChanged events which often don’t handle the behavior I want to implement. This is why I more often than not implement my own list views these days. In Silverlight this is easy as well, but for basic behavior like the above this is overkill.

Not rocket science but searching around for workarounds earlier I saw several other posts on forums with similar issues, so I’d thought I post this. I’m struggling slowly through  the Silverlight UI learning curve at the moment so expect a few more of these type of basic WTF posts <s>…


I'll be at DevIntersection in Vegas this fall giving sessions on ASP.NET Core with Angular and Localization. Thinking of coming? Use discount code STRAHL and save a few bucks. If you do be sure to stop by and say hello!

ASP.NET DevIntersection 2017. Rick Strahl Coupon Code

Posted in Silverlight  

The Voices of Reason


 

Aaron Fischer
December 11, 2008

# re: SilverLight ListBox Selections or what’s with SelectionChanged Events?

You could have made an itemsCollection and uesed a button for your item template. For what your doing you really don't need anything from the listbox control.

Rick Strahl
December 11, 2008

# re: Silverlight ListBox Selections or what’s with SelectionChanged Events?

@Aaron - sure I could used any of 20 different ways to do this, but the point is list behavior in general. Never mind whether this is the best way - to me at least it seemed the quickest way to get the behavior I need without having to do custom layout. Buttons would need individual click handlers even if they all point to the same handler.

Andy Hawken
December 12, 2008

# re: Silverlight ListBox Selections or what’s with SelectionChanged Events?

I've run into the same problem but was able to use a clearselection type method to ensure each click was registered: would that be viable in silverlight?

Ryan Riley
December 12, 2008

# re: Silverlight ListBox Selections or what’s with SelectionChanged Events?

Instead of using RoutedEvents, you could use plain old DataBinding. Create a property in your code behind or presentation model called "SelectedWhatever." Then in your property setter, fire a method to handle the change. In your XAML, you'll need to bind to the SelectedItem attribute:

<ListBox ItemsSource="{Binding SourceList}"
            SelectedItem="{Binding SelectedWhatever}" />


The beauty of this approach is that you don't have to worry about events at all, unless you want to hook up INotifyPropertyChanged or INotifyPropertyChanging and listen to the property changing instead of explicitly calling the method you want to perform when the property changes. My team on a recent project is using this with great success following the MVVM pattern, which doesn't easily allow for RoutedEvents.

Rick Strahl
December 12, 2008

# re: Silverlight ListBox Selections or what’s with SelectionChanged Events?

@Andy - yes I too played around with clearing hte selected item, but there are apparently a host of bugs around clearing selections (it would make all selection 'sticky' and end up letting you select nothing after initial selection) and even with that I was unable to completely unselect the list.

@Ryan - That's a great idea for some scenarios, but I don't think this does anything for the selection issue. I wonder though whether setting SelectedItem via a Binding will properly unselect if you set whatever you're binding to to null - Doing that in code had uhm unpredictable results (the last selection showed as 'half' selected, but clicking again would not fire the item again <shrug>).

Stephen
December 12, 2008

# re: Silverlight ListBox Selections or what’s with SelectionChanged Events?

I'm apologizing in advance for going off topic, but what is used to do that cool "ripped page" screenshot? it's love to get a hold of a program that could spice up my documentation/presentations with something like that

Rick Strahl
December 12, 2008

# re: Silverlight ListBox Selections or what’s with SelectionChanged Events?

@Stephen - I have one word for you. SnagIt. Run do not walk to get it if you do any sort of screen captures on a regular basis.

http://www.techsmith.com/screen-capture.asp

Matthew Randle
December 14, 2008

# re: Silverlight ListBox Selections or what’s with SelectionChanged Events?

Hi Rick,

Take your point about the inconsistency of the SelectionChanged event. Nulling the selection should cause the event to be fired on re-selection.

In WPF (not sure about Silverlight), you could use the Menu control instead of a listbox . With three lines of XAML you can make the menu control display items vertically (set the Menu.Items.ItemPanel to a layout panel that stacks vertically).

This would give you all the functionality you need from the listbox (databinding, mouse hover selection, etc) and obviously handles the re-select.

Wasnt sure from your post if you want the currently selected item to stay highlighted. If so, I think the two requirements (show selection and re-select) are at odds from a design viewpoint.

Matt

Rick Strahl
December 14, 2008

# re: Silverlight ListBox Selections or what’s with SelectionChanged Events?

@Matthew - AFAIK there's no menu control in Silverlight which makes sense since it's not meant to be a 'desktop' style environment with windows typically.

Again I want to clarify that the point of this post wasn't to say that the listbox doesn't exactly what I need it to do for the task at hand which happens to be menu creation. But rather that in many situations list OnSelectionChanged events are extremely inadequate to common usage scenario. Selection changing and actual selection IMHO are two completely different things although depending on the application they may overlap.

As to the list as a menu - it works fairly well actually, except the limitation that you apparently can't really deselect the listbox completely. Even if selected item is set to null (which still leaves a border highlight in place) or SelectedIndex to -1 (which breaks selection on the list entirely) effectively you cannot get a list deselected completely. That's clearly a bug or design flaw in the control. <s>

Matthew Randle
December 15, 2008

# re: Silverlight ListBox Selections or what’s with SelectionChanged Events?

Rick,

Yes. As you say in your post, an 'ItemSelected' (including keypress) event on the listbox would be useful.

I wonder if the selection problem is specific to Silverlight? In WPF, if you null the selection in a listbox (or set SelectedIndex to -1) it clears the selection highlight (as you would expect) and recognises re-selection via 'SelectionChanged'.

Im surprised the menu control is not available in Silverlight (its not). The menu control in WPF is not like a standard Win32 menu. You can orient it how you like and paste it anywhere on the screen. Would definitely be useful in a Silverlight app. Must have Win32 dependencies.

Got a Silverlight project coming up soon so keep the posts coming :)

Matt

Rohit
January 06, 2009

# re: Silverlight ListBox Selections or what’s with SelectionChanged Events?

I am using a listbox in silverlight.i want on selection of any items it would be highlighted or can say change the background colour of items.
how it is possible?


Thanks
Rohit

Abhijeet Patel
January 16, 2009

# re: Silverlight ListBox Selections or what’s with SelectionChanged Events?

What sucks is that the control does not support multi select like in asp.net or winforms.
I would've expected this out of the box. Have you guys had any luck with multiple selections?

Josh Santangelo
January 29, 2009

# re: Silverlight ListBox Selections or what’s with SelectionChanged Events?

How to deselect an item in a Silverlight ListBox:

// janky hack to deselect the list item
IEnumerable items = _ResultList.ItemsSource;
_ResultList.ItemsSource = null;
_ResultList.ItemsSource = items;

Works for me, though my list is databound.

RickB
July 12, 2009

# re: Silverlight ListBox Selections or what’s with SelectionChanged Events?

Thanks for sharing your thoughts.

I am also using a ListBox as a menu, in a WPF app, and was having a somewhat similar problem with the SelectionChanged event. In my case I am displaying multiple menus in a single ListBox. If an item represents a sub-menu, then the submenu will replace the current menu when the item is clicked. My problem was that the item in the submenu that was beneath the mouse pointer was also "selected" when the submenu was displayed. I tried several workarounds for the better part of the day, but using the MouseUp event appears to have fixed it. Thanks again!
 

West Wind  © Rick Strahl, West Wind Technologies, 2005 - 2017