Taoffi's blog

prisonniers du temps

WPF and a ComboBox dilemma (again!)

Sometime ago, I discussed a ComboBox mouse scroll issue in Silverlight. I, again, had a new problem with the ComboBox control… this time in a WPF application.

The ItemsSource update problem

In my case, I had a ComboBox whose ItemsSource was bound to a list of items which was updated dynamically, and its SelectedItem bound to one item of the list. The problem was that the ComboBox was correctly updating the SelectedItem, while the drop-down list continued to display the older list of items! ... Here is a sample illustration:

 

Figure 1: This is the first context:

 

Figure 2: The item list changed… the ComboBox correctly displays the selected item (2.476563)… which doesn’t exist at all in the items list. The drop-down list still displays the old items list!

 

Such odd situations make you feel that either there should be an error somewhere in your code… or that the CombBox itself is a control that you should reinvent yourself!

 

Check your code!

Here is my Xaml code… that really seems so ‘standard’


<ComboBox x:Name="combobox_rates"
    ItemsSource="{Binding Combinations, Mode=OneWay}"
    SelectionChanged="combobox_rates_SelectionChanged"
    Height="24"
    Width="280"
    ItemTemplate="{DynamicResource combi_list_template}"
    SelectedItem="{Binding Path=SelectedCombination}" />

According to someone advice, let-s add a IsSynchronizedWithCurrentItem=”True”:


<ComboBox x:Name="combobox_rates"
    IsSynchronizedWithCurrentItem="True"
    

Desperately, that doesn’t solve the problem!
Another advice: Bind the ItemsSource to an ObservableCollection<T> (instead of a List<T>): nothing changes…
A third one: add an UpdateSourceTrigger=PropertyChanged to the Binding… let’s just try

ItemsSource="{Binding Combinations, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"

 

The solution

Still, we didn’t try this… let’s use the IsAsync Binding attribute:

ItemsSource="{Binding Combinations, IsAsync=True, Mode=OneWay}"

Oh… that seems to work great!
Is sofware development a Science, an Art, or Craftsmanship?!

Scrollable ComboBox

I am working on a new Silverlight user interface version of Simplesite.net and, as anyone can imagine, that involves a good dive into Silverlight as a ‘Line Of Business’ application technology.

One of the problems I encountered was to make the Combo box control able to respond to Mouse Wheel messages.

After having searched on different blogs and web sites, I didn’t find a suitable solution and ended up by delving into the subject.

Some of the proposed solutions consisted in ‘bringing into view’ one of the combo box items to force the list to scroll up or down as required… that seemed a little bit like a good workaround but was not very efficient and had a bad visual aspect.

 

What is a Silverlight ComboBox?

One of the nice (and pedagogic) aspects in Expression Blend is to be able to edit any existent control template (right click the control, select Edit Template / Edit a copy).

 

 

In Expression Blend, a Control Template reveals all the components of the control… this helps to understand some of the internal mechanisms of the control and how to use them efficiently.

 

To know more about the ComboBox control, I used Blend to insert one somewhere, and selected to edit a copy of its template.

That revealed the main components of the control:

§ A Layout Grid, containing:

§ A border (content presenter border) containing the static part of the combobox;

§ A Popup containing a ScrollViewer which will display the items.

 

It now seems clear that the scroll problem should be handled at the ScrollViewer component.

 

The SetIsMouseWheelScrollingEnabled method of the ScrollViewer can simply be used to activate the Mouse Wheel message handling for this component.

 

The solution

I saved my template to the application resources (App.xaml) with x:key=”combo_box_template”

<!-- ************* scrollable combo box template *******************************-->

<ControlTemplate x:Key="combo_box_template" TargetType="ComboBox">

 

 

I then created a class named ScrollableComboBox deriving from ComboBox

The control template is loaded and applied at class’s instantiation… and Mouse Wheel handling activated on the corresponding ScrollViewer component.

 

 

public partial class ScrollableComboBox : ComboBox

{

       protected ScrollViewer            m_scroll_viewer            = null;

 

       public ScrollableComboBox()

       {

             this.Style   = (Style) App.Current.Resources["ScrollableComboBoxStyle"];

             this.Template = (ControlTemplate) App.Current.Resources["combo_box_template"];

             this.ApplyTemplate();

            

             m_scroll_viewer = (ScrollViewer)GetTemplateChild("ScrollViewer");

 

             if (m_scroll_viewer != null)

             {

                    m_scroll_viewer.SetIsMouseWheelScrollingEnabled(true);

             }

    }

 

 

Surprisingly, that is all we need to activate scrolling on a Silverlight ComboBox!

We can now insert a ScrollableComboBox anywhere we need.