Auto-File and URL Completion for Text Boxes and Combo Boxes

Add Shell file or URL auto-complete for better UIs

AutoCompletion Demonstration Project

Version 5 of the Shell, shipped with Windows 2000 and ME or above, provides the ability to add Auto-Complete to any Text Box. This sample provides two simple extensions to the TextBox and ComboBox classes to add AutoComplete to your application.

Shell AutoCompletion

There are two variants to the Auto-Completion facilities provided with version 5 of the Windows Shell. The simple method, presented here, provides a hardcoded auto-completion list based on either files, directories or URLs on the system. A more sophisticated technique which will be the basis for a future article is to implement the COM IEnumString, IAutoComplete and IAutoComplete2 interfaces, which allows you to specify arbitrary auto-complete strings.

Note that if you plan to target older systems, such as NT4 or Win 98/SE, you should enclose any attempt to initialise Auto-Complete in a Try - Catch block since the call is not supported.

Using the simple version of the Auto-Complete method is very straightforward. The shell exposes an API call, SHAutoComplete, which takes a handle to a the TextBox to start auto-completing on, and a flag parameter which specifies which type of auto-completion should be applied.

Here's the code for the extended Text Box which provides auto-completion:

using System;
using System.Windows.Forms;
using System.Runtime.InteropServices;

namespace vbAccelerator.Controls.TextBox
{
    /// 
    /// Adds Shell File System and URL AutoCompletion facilities to 
    /// a text box
    /// 
    public class AutoCompleteTextBox : System.Windows.Forms.TextBox 
    {
        #region Unmanaged Code
        [Flags]
        public enum SHAutoCompleteFlags : uint
        {    
            SHACF_DEFAULT = 0x0,
            SHACF_FILESYSTEM = 0x1,
            SHACF_URLHISTORY = 0x2,
            SHACF_URLMRU = 0x4,
            SHACF_USETAB = 0x8,
            SHACF_URLALL = (SHACF_URLHISTORY | SHACF_URLMRU),
            SHACF_FILESYS_ONLY = 0x10,
            SHACF_FILESYS_DIRS = 0x20,
            SHACF_AUTOSUGGEST_FORCE_ON = 0x10000000,
            SHACF_AUTOSUGGEST_FORCE_OFF = 0x20000000,
            SHACF_AUTOAPPEND_FORCE_ON = 0x40000000,
            SHACF_AUTOAPPEND_FORCE_OFF = 0x80000000
        }
        
        [DllImport("shlwapi.dll")]
        private static extern int SHAutoComplete (
            IntPtr hwndEdit, 
            AutoCompleteTextBox.SHAutoCompleteFlags dwFlags );
        #endregion

        #region Member Variables
        private AutoCompleteTextBox.SHAutoCompleteFlags autoCompleteFlags = 
            SHAutoCompleteFlags.SHACF_FILESYS_ONLY;
        private bool flagsSet = false;
        private bool handleCreated = false;
        #endregion

        #region Implementation

        /// 
        /// Gets/sets the flags controlling automcompletion for the 
        /// text box
        /// 
        public AutoCompleteTextBox.SHAutoCompleteFlags AutoCompleteFlags
        {
            get
            {
                return this.autoCompleteFlags;
            }
            set
            {
                this.autoCompleteFlags = value;
                this.flagsSet = true;
                if (handleCreated)
                {
                    SetAutoComplete();
                }
            }
        }

        protected override void OnHandleCreated ( System.EventArgs e )
        {
            // call this first as SHAutoComplete may not be supported
            // on the OS
            base.OnHandleCreated(e);
            // don't do anything if we're in design mode:
            if (!this.DesignMode)
            {
                // remember we've created the handle for any future 
                // get/ set
                handleCreated = true;

                // if we've provided some flags then start autocompletion:
                if (flagsSet)
                {
                    SetAutoComplete();
                }
            }
        }

        private void SetAutoComplete()
        {
            SHAutoComplete(this.Handle, this.autoCompleteFlags);
        }

        /// 
        ///  Constructs an auto-complete capable text box but
        ///  does not automatically start auto-completion.
        /// 
        public AutoCompleteTextBox() : base()
        {
        }

        /// 
        /// Constructs an auto-complete capable text box and
        /// starts auto-completion with the specified flags.
        /// 
        /// Flags controlling
        /// auto-completion
        public AutoCompleteTextBox(
            AutoCompleteTextBox.SHAutoCompleteFlags autoCompleteFlags
            ) : this()
        {
            // Handle will not be available at this point; we need
            // to wait for HandleCreated:
            this.autoCompleteFlags = autoCompleteFlags;
            this.flagsSet = true;
        }

        #endregion
    }
}

AutoCompletion and ComboBoxes

Using the function with a ComboBox isn't much more difficult; all you need to do is to find the handle to the TextBox within the ComboBox itself and call the SHAutoComplete API on that. Some versions of Windows (XP) provide a message you can use to get the handle to the TextBox, however, it is just as simple to use the Windows FindWindowEx API call to get the handle, and this works regardless of the type of ComboBox or OS version. The code to do this is as follows:

using System.Runtime.InteropServices;

     [DllImport("user32.dll", CharSet = CharSet.Auto)]
     private static extern IntPtr FindWindowEx (
         IntPtr hWnd1, 
         IntPtr hWnd2, 
         [MarshalAs(UnmanagedType.LPTStr)] string lpsz1,
         IntPtr lpsz2);

     ...

     IntPtr textHandle = FindWindowEx(
          comboHandle, 
          IntPtr.Zero,
          "EDIT", 
          IntPtr.Zero);