none
Solution for Adding items to AutoCompleteBox - improvements invited!

    การสนทนาทั่วไป

  • 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;
                        }
                        else
                        {
                            // 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 มีนาคม 2555 12:58

ตอบทั้งหมด