Solution for Adding items to AutoCompleteBox - improvements invited!

    Discuţie generală

  • Hi all,

    There's been several descriptions of ways to go to persist data added to an AutoCompleteBox during a user session but I wanted to look at an option where the user's involvement was absolutely minimal. For example I wanted to negate the use of popup screens etc. The option I describe later is useful where the underlying entity has a database generated Primary Key (naturally) and only one other field requiring a non-null value, a perfect description of the average look-up table!

    Before I go any further I'd like to acknowledge Tim Leung for providing the basis of this option in his excellent blog, so thanks Tim! Unrelated specifically to this post but nonetheless worthy of acknowledgement for a superb bit of UI 'usefulness' is Alan Silver and his superb set of controls, especially his toast!

    I wanted to avoid the situation where the database gets hit every time an item was selected (or typed and invalid) to check against the input; also, no requirement for a user to click a screen button to pop-up a dialog. The code below is an extract from one of my viewmodels and is heavily commented so hopefully everyone can see what's going on!

    using System.Linq;
    using System.Windows.Controls;
    using LightSwitchApplication.UserCode;
    using Microsoft.LightSwitch;
    using Microsoft.LightSwitch.Presentation;
    using Microsoft.LightSwitch.Presentation.Extensions;
    using Microsoft.LightSwitch.Threading;
    using PixataCustomControls.Presentation.Controls;
    namespace LightSwitchApplication
        public partial class dtl_Company
            partial void dtl_Company_Created ()
                this.FindControl ( "acbAddressTown" ).ControlAvailable += acbAddressTownAvailable;
            private void acbAddressTownAvailable ( object sender, ControlAvailableEventArgs e )
                // Remove event handler
                this.FindControl ( "acbAddressTown" ).ControlAvailable -= acbAddressTownAvailable;
                // Get references to required AutoCompleteBox and set 'LostFocus' event handler
                ((AutoCompleteBox)e.Control).LostFocus += acbAddressTownChanged;
            private void acbAddressTownChanged ( object sender, System.Windows.RoutedEventArgs e )
                // This event will fire EVERY time the control loses focus and
                // regardless of what has actually been input (or not!)
                // Obviously we only want the main body of the method to
                // run if the user-typed input is NOT represented in the list...
                // Get hold of the input text...
                string inputText = ((AutoCompleteBox)sender).Text;
                if (!string.IsNullOrEmpty ( inputText ))
                    // A non-empty string has been input
                    bool addNewData = false;
                    if (null == ((AutoCompleteBox)sender).SelectedItem)
                        // The user's input is NOT in the list and therefore there is no valid
                        // selection. Was a valid option selected previously?
                        if (null == this.CompanyAddresses.SelectedItem.Address.AddressTown)
                            // There was NO previous selection
                            addNewData = true;
                            // There WAS a previous VALID selection...
                            if (this.CompanyAddresses.SelectedItem.Address.AddressTown.TownCity != inputText)
                                // ...and it's not the same as that represented by the user-typed text
                                addNewData = true;
                    if (addNewData)
                        // Requires adding to the list
                        // Details Dispatcher Thread
                        this.Details.Dispatcher.BeginInvoke ( () =>
                            // User HAS entered a non-empty value which isn't currently in the list
                            if (this.ShowMessageBox ( "Do you want to ADD '" + inputText.ToString ().ToUpper () + "' to the list of Towns/Cities?""Add Town/City"MessageBoxOption.YesNo ) == System.Windows.MessageBoxResult.Yes)
                                // Create a NEW Address Town entity and add to the AddressTowns entityset
                                AddressTown requestedItem = this.DataWorkspace.TreelinksData.AddressTowns.AddNew ();
                                // Set the required AddressTown property using user's input text
                                requestedItem.TownCity = inputText;
                                // Set the required screen entity property to the new object
                                this.CompanyAddresses.SelectedItem.Address.AddressTown = requestedItem;
                                // Drop back into UI thread to refresh the AutoCompleteBox's list items. Note this is done
                                // BEFORE fully exiting our previous thread (nested threading). This is because otherwise
                                // we would ALWAYS have to call the '....Choices.Refresh()' method wether or not we'd
                                // actually added anything and that would be inefficient
                                Dispatchers.Main.BeginInvoke ( () =>
                                    // Call 'DataContext.Choices.Refresh on the AutoCompleteBox. Note that,
                                    // because this method does NOT attempt to persist the new list addition
                                    // (leave that until the user invokes the screen-based save), it will
                                    // actually reside at the bottom of the list and will NOT therefore
                                    // honour any sorting order (at this time) that may be in place for
                                    // the list via its underlying query
                                    ((IContentItem)((AutoCompleteBox)sender).DataContext).Choices.Refresh ();
                                    // Notify the user unobtrusively, using Pixata's superb toast!
                                    PixataToastHelper.ShowToast ( "INFORMATION""The data '" + inputText.ToString ().ToUpper () + "' has been added to the list"PixataToastHelper.PixataToastStyles.Alert );
                                } );
                        } );

    The solution has been very useful and works well. Remember though that any new items added to any lists will only be persisted when the user saves the overall screen data.

    I'd love to genericise this; as you can see there are references to specific screen collections, entity properties etc, but time is a little limited so any contributions are welcomed, indeed encouraged!

    Ian Mac

    Ian Mac

    13 martie 2012 12:58

Toate mesajele

  • Thanks, I will try this method.
    19 martie 2012 02:11
  • Hi Ian,

    That's a really neat way of handling this issue. Thanks for the kind mention of my toast!

    FREE custom controls for Lightswitch! A collection of useful controls for Lightswitch developers. Download from the Visual Studio Gallery.

    If you're really bored, you could read about my experiments with .NET and some of Microsoft's newer technologies at

    21 martie 2012 16:08
  • Hi Alan,

    Thanks and you're most welcome! Seriously I don't know what I'd do without your control extensions!

    You may have noticed I'm looking out for a genericised version of this code, since it's a pain to copy/paste and modify for each autocompletebox that needs this functionality, even though most of what you see in the code is comments! The use of generics in Lightswitch as it pertains to the Lightswitch libraries/references I just haven't had time to get round to, but the pain points for genericising are:

    1. A typed entity
    2. A typed entity property
    3. 'Newing' up up the correct entity
    4. In certain cases, a typed visual collection

    It would be so much cleaner and easier of course!

    Ian Mac

    Ian Mac

    21 martie 2012 17:30
  • Thank you! Will need something like this in the future
    21 martie 2012 18:21