CSLA .NET

Vibrant discussion about CSLA .NET and using the framework to build great business applications.

Silverlight 4 ComboBox is broken and wasting my time.

This post has 37 Replies | 16 Followers

Top 25 Contributor
Posts 201
Jaans Super Angry [8o|] Posted: Sun, Nov 21 2010 9:29 PM

I have just completely lost my cool in trying to get Microsoft's ComboBox control to work as I would *naturally* expect it to.

Background
This extends on from the work Rocky did about a year ago for the Microsoft ComboBox control of previous versions of Silverlight. Essentially it didn't support the concept of SelectedValue and SelectedValuePath which is essential for FK / Lookup scenarios that use only the Id / Key and so Rocky had to roll his own implementation (refer http://www.lhotka.net/weblog/SilverlightComboBoxControlAndDataBinding.aspx). 

Fastforward to Silverlight 4 and Microsoft annouces they've fixed all the ComboBox issues and added SelectedValue and SelectedValuePath support in SL4.

"Yay... but wait, not so fast!"

Disclaimer:
When binding directly to SelectedIndex property, the ComboBox works like a dream, but forget about it when using the new SelectedValue and SelectedValuePath.


Issues:

  • If I bind or change the ItemSource of the combobox after the SelectedValue has been bound to my business object (e.g. Id), then the combox box develops amnesia and forgets it even had a selected value. The selected value of drop down goes blank - you can still pick items once opening the drop down, but the initial selected value is not reapplied when the item source list is changed. The SeletedValue still maintains it's binding though, so if you now change the selection, your business object still gets updated.
  • Sometimes, if the combobox was not displayed (e.g. hidden, another tab, virtualised out of memory) and comes back, the original selection is missing. It's actually still all there - the binding is there and the selected value is correct - just the visual rendering doesn't show it.
  • If I bind to a nullable selected value and my business object being bound to has null for it (e.g. int?) - OR - the value I'm binding to does not exist in the ItemSource list (nullable or not), boom! Amnesia becomes Alzheimers and the ComboBox simple loses its binding altogether. So if you now change the selection, your business object will never see that value. The binding expression is simply gone and never comes back.

Sure I can (and I have received repeated comments to) just not bother with the Microsoft ComboBox and use those from control suites like Telerik or Component one, but that immediately places dependencies of code, contract, funds, maintainability, etc. on a 3rd party vendor. Sometime I generally avoid, unless I absolutely have to.

Besides, I simply believe that something as rudimentary as a ComboBox should work right!

And so I have spent more time than I care to admit (or afford) trying to get to the bottom of this and make it work for me. I very nearly came to a solution where you simply have to change the control and do nothing else: E.g.

<ComboBox ... /> 
becomes
<custom:ComboBox ... /> 

But I just couldn't make it work... came close though, but in the end you have to not bind to SelectedValue (the root / axis of all this Evil) and instead bind to SelectedValueProper. E.g.

<ComboBox SelectedValue="{Binding Path=Id, Mode=TwoWay}" ... /> 
becomes
<custom:ComboBox SelectedValueProper="{Binding Path=Id, Mode=TwoWay}" ... /> 

Here's the full class listing - use as you wish, but for obvious reasons, only on the condition that I cannot be held liable.

Note while this takes concepts from Rocky's implementation for the previous Silverlight version (I think SL2) of the Combox this code below is for the Silverlight 4 version of the ComboBox.

Hope that helps someone else too (then I would feel vindicated!) Big Smile
Jaans

    public class ComboBoxEx : ComboBox
    {
        #region Fields

        private bool _suppressSelectionChangedUpdatesRebind = false;

        #endregion

        #region Properties

        public static readonly DependencyProperty SelectedValueProperProperty =
            DependencyProperty.Register(
                "SelectedValueProper",
                typeofobject ),
                typeofComboBoxEx ),
                new PropertyMetadata( ( o, dp ) =>
                                      {
                                          var comboBoxEx = o as ComboBoxEx;
                                          if ( comboBoxEx == null )
                                              return;

                                          comboBoxEx.SetSelectedValueSuppressingChangeEventProcessing( dp.NewValue );
                                      } ) );

        [Browsabletrue ), EditorBrowsableEditorBrowsableState.Always )]
        public object SelectedValueProper
        {
            get { return (object)GetValueSelectedValueProperProperty ); }
            set { SetValueSelectedValueProperPropertyvalue ); }
        }

        #endregion

        #region Constructor and Overrides

        public ComboBoxEx()
        {
            SelectionChanged += ComboBoxEx_SelectionChanged;
        }

        /// <summary>
        /// Updates the current selected item when the <see cref="P:System.Windows.Controls.ItemsControl.Items"/> collection has changed.
        /// </summary>
        /// <param name="e">Contains data about changes in the items collection.</param>
        protected override void OnItemsChangedSystem.Collections.Specialized.NotifyCollectionChangedEventArgs e )
        {
            // Must re-apply value here because the combobox has a bug that 
            // despite the fact that the binding still exists, it doesn't 
            // re-evaluate and subsequently drops the binding on the change event
            SetSelectedValueSuppressingChangeEventProcessingSelectedValueProper );
        }

        #endregion

        #region Events

        private void ComboBoxEx_SelectionChangedobject sender, SelectionChangedEventArgs e )
        {
            // Avoid recursive stack overflow
            if ( _suppressSelectionChangedUpdatesRebind )
                return;

            if ( e.AddedItems != null && e.AddedItems.Count > 0 )
            {
                //SelectedValueProper = GetMemberValue( e.AddedItems[0] );
                SelectedValueProper = SelectedValue// This is faster than GetMemberValue
            }
            // Do not apply the value if no items are selected (ie. the else)
            // because that just passes on the null-value bug from the combobox
        }

        #endregion

        #region Helpers

        /// <summary>
        /// Gets the member value based on the Selected Value Path
        /// </summary>
        /// <param name="item">The item.</param>
        /// <returns></returns>
        private object GetMemberValueobject item )
        {
            return item.GetType().GetPropertySelectedValuePath ).GetValue( item, null );
        }

        /// <summary>
        /// Sets the selected value suppressing change event processing.
        /// </summary>
        /// <param name="newSelectedValue">The new selected value.</param>
        private void SetSelectedValueSuppressingChangeEventProcessingobject newSelectedValue )
        {
            try
            {
                _suppressSelectionChangedUpdatesRebind = true;
                SelectedValue = newSelectedValue;
            }
            finally
            {
                _suppressSelectionChangedUpdatesRebind = false;
            }
        }

        #endregion
    }
Top 500 Contributor
Posts 34
mesh replied on Mon, Nov 22 2010 1:32 AM

Right now I'm experiencing similar issues!

I'm having problem with Combobox that is binded (ItremsSource, SelectedValue, SelectedValuePath) to a list (tried NVL and BLB), that is part of of Csla ViewModel<...>.Model property, but ONLY AFTER undo. I think my problem is this.

Quick solution is moving list from from Model to ViewModel, but that is writing much more code.

Btw, NotUndoable is not working in SL?

mesh

Top 10 Contributor
Posts 9,395

mesh:

Btw, NotUndoable is not working in SL?

NotUndoable doesn't work with managed backing fields. If you want to use it, you need to use a private backing field and also override OnGetState/OnSetState.

Rocky

Top 500 Contributor
Posts 24

I'm feeling your pain. I have a form that has a Country combobox and depending on the Country selected I populate the province combobox and depending on the province I populate the Town combobox etc. This turn out to be a nightmare. Why couldn't the silverlight team just do this right from the beginning and why is there no fix. There is lot of people complaining about the stupid behaviour of the SL4 combobox.

Top 500 Contributor
Posts 24

I have used your custom combobox and it is working like you would expect a combobox to work. Thanks Jaans for the code you have definitely save me a lot of time.

Top 25 Contributor
Posts 201
Jaans replied on Mon, Nov 22 2010 6:28 PM

+1 for being vindicated

Ps: Whisper Hannes, lyk my die boere moet mekaar maar help.

Not Ranked
Posts 2
Ricker1 replied on Tue, Nov 23 2010 1:22 AM

Hi Jaans,

Thank you for your work on the breakage of what should be a basic function from a combo box. I started my first SL4 RIA project and hit the wall several days ago and I have been looking for a solution this problem. Your solution appears to be best one I have seen and I have see a few. I have not needed to make a custom control in the past so I am have a problem with implementing your custom control. My project complies with no errors and I have no issues using in my view.xaml that is using the custom combo box but there something missing becuase it breaks at InitializeComponents(). Here is the error:

Error: Unhandled Error in Silverlight Application
Code: 4004   
Category: ManagedRuntimeError      
Message: Set property 'System.Windows.FrameworkElement.Style' threw an exception.

Is it possible for you to post a link to your complete implementation of your custom combo box? I sure hope the MS fixes this in SL5!

Thank you, Ricker

Top 25 Contributor
Posts 201
Jaans replied on Tue, Nov 23 2010 1:34 AM

Hi Ricker

Just a rough guess based on the above, but I suspect you may have had a style for your old "<ComboBox />" and you are still applying that same style to the <ComboBoxEx /> without updating the type of the style

Would you mind posting the following?

  • Control declaration <yournamespaceprefix:ComboBoxEx .. />
  • Name space declarations (very top of the .xaml)
  • If you are applying a style, the style declaration

Jaans

Top 25 Contributor
Posts 201
Jaans replied on Fri, Nov 26 2010 10:16 PM

Hi Ricker - Have you managed to resolve your issue?

Top 500 Contributor
Posts 17
Dominiek replied on Tue, Nov 30 2010 2:43 AM

YES! Thank you, this has saved the day for me.

 

Bedankt voor de moeite Jaans

Dominiek

Not Ranked
Posts 2
Ricker1 replied on Tue, Nov 30 2010 7:56 PM

Hello Jaans,

I have not resolved my issue of properly using the custom combo box. I moved on to other aspects of the project and the SL page on have been working on, but I am now at the point where this is the last part of the SL page that does not work. I will re-attempt tonight in making this work. I searched the entire project for any styles that target the combobox and found none. I am sure it is something small that I am missing. Thank you for checking up on me.

Ricker

Top 500 Contributor
Posts 20

That's great Jaans, thanks!

On a similar note, is anybody else annoyed with the fact that the ComboBox doesn't support key press events?  I don't need anything fancy, but just for it to function like a ComboBox does in any browser.  I have tried wiring up the KeyUp event, but these events don't fire when the ComboBox is open, only when it's closed.  It looks like the Popup control or whatever is inside the ComboBox is stealing those events.

Has anybody else solved this problem?  (PS I know about the AutoComplete control, but that's not what I want...I just want a working ComboBox)

Not Ranked
Posts 1
tracy replied on Thu, Dec 2 2010 1:59 PM

Is there any chance of a VB version?

 

Many Thanks

Top 25 Contributor
Posts 201
Jaans replied on Thu, Dec 2 2010 5:34 PM

Hi Tracy

Unfortunately no, not one that I wrote anyway, but you have a couple of alternative options:

  • Wrap the above in it's own C# assembly and reference in from your VB one. Assemblies are source language agnostic and can be mixed.
  • Write your own - it shouldn't be too hard to write the above as a VB version as it's only a few class members with small implementations
  • You could use an online C# <--> VB converter to aid you in the process. Sometimes the results are very good and other times they need some finishing off.

Hope that helps

Jaans

Not Ranked
Posts 1
Olof replied on Mon, Dec 6 2010 2:11 PM

Thank you!!!

Page 1 of 3 (38 items) 1 2 3 Next > | RSS

Copyright (c) 2006-2014 Marimer LLC. All rights reserved.
Email admin@lhotka.net for support.
Powered by Community Server (Non-Commercial Edition), by Telligent Systems