Rick Strahl's Weblog
Rick Strahl's FoxPro and Web Connection Weblog
White Papers | Products | Message Board | News |

Showing Windows AutoComplete Dropdowns in Visual FoxPro


4 comments
February 17, 2008 •

Windows internally has a neat feature which is the ability to make a text box automatically pick up file system paths and the URL history and assign it to a textbox as a dynamic drop down. You've undoubtedly seen this behavior all over the place in Windows itself (like the Explorer file selection boxes) or in other applications.

Here's what this should look like in a custom application:

SystemAutoComplete

As you type path information the behavior shows a drop down of all the available files and directories below the input box.

The process of doing the above is pretty simple from a Windows perspective - there's an API call that extends an input control with the required functionality to show the drop down of files.

The API function that handles this is:

HRESULT SHAutoComplete(      
    HWND hwndEdit,     DWORD dwFlags );

 

 

You pass an hWnd handle to a window or control and a flag that defines the behavior of the auto complete dropdown.

In FoxPro code this looks like this:

DECLARE SHAutoComplete IN "shlwapi.dll" LONG hwndEdit, LONG dwFlags

with the following constants applied:

#DEFINE SHACF_AUTOAPPEND_FORCE_OFF  0x80000000
#DEFINE SHACF_AUTOAPPEND_FORCE_ON  0x40000000
#DEFINE SHACF_AUTOSUGGEST_FORCE_OFF  0x20000000
#DEFINE SHACF_AUTOSUGGEST_FORCE_ON  0x10000000
#DEFINE SHACF_DEFAULT  0x0
#DEFINE SHACF_FILESYSTEM  0x1
#DEFINE SHACF_URLHISTORY  0x2
#DEFINE SHACF_URLMRU  0x4
#DEFINE SHACF_URLALL  SHACF_URLHISTORY +SHACF_URLMRU

Seems easy enough, right?

Only one problem: VFP controls don't have an HWnd property (not even an internal one) so there's no way to attach this behavior to native controls.

Another Tack: ActiveX Controls

The alternative is that you can use an ActiveX text control and use it to apply this functionality. You can use the RichText Control from the common control library for example and sure enough that works. If you want to do this all you have to do is:

  • Add a RichTextControl onto the page
  • Add the following code:

    DECLARE SHAutoComplete IN"shlwapi.dll" LONG hwndEdit, LONG dwFlags
    SHAutoComplete(this.txtProjectFile_Rtf.Hwnd,268435457)   && File Autosuggest

And that's it. It works, but if you're after a clean UI experience you'll probably be a little disappointed:

UIProblems

You can see that the RichTextBox shows in 3D (Win98 style) formatting. Although there are a few options (Appearance and Borderstyle) that you can play around with none of them let you get a themed look that matches your typical FoxPro form interface - the control looks out of place.

Yet, if you look closely on the first screenshot (which is also running in FoxPro) you'll notice that the textbox actually DOES look correct. There's some extra code involved in making that happen.

The way that code works is by placing a TextBox control underneath the rich text control, changing its style to flat and no borders then overlaying the rich text control over the textbox so that only its borders show with the Rich Textbox overlaid on top and acting as the 'real' input control.

Reusable Code? FoxPro and ActiveX Hell

You can do this manually but it's a bit of work everytime you want a file input box, so I got to thinking that it'd be easier to create something reusable since it's a fairly common scenario.

My first preference would have been to build a TextBox extender. Rather than creating a new UI class I figured an extender would be perfect so that you wouldn't have to add a new control to the page. The idea was that you'd pass in a TextBox control and the code would dynamically add the rich text box, use BindEvent to capture changes and transfer them back to the textbox. In other words it would fake input with the RTF control but otherwise leave the old control completely intact.

Sounds easy right? But there ended up being a number of problems with this approach. The worst of which is that that Appearance and BorderStyle cannot be set past control initialization on the RichText control. Assigning these values didn't work to change the control's display - even if I create custom (PRG based) subclass of the RichText control and assign the values as defaults. In the end the only way I could make this work was by requiring the RTF control to be on the form with the proper formatting applied which is lame. There were a few other problems too - the extender approach required keeping track of the TextBox and the Control and that in turn caused form unload errors due to cyclic references. I managed to get around this by binding all possible Form unloadnig events (QueryUnload, Unload, Release, Destroy) but there ended up being a bunch of associated code.

In the end I decided that this wasn't worth the effort - the requirement for the extra RTF control on the page pretty much defeats the purpose so I decided to just create a visual subclass of the RTF control to provide this functionality.

The end result is a simple wwAutoComplete Control that can be just dropped onto the form. The control code looks like this:

**************************************************
*-- Class:        wwautocomplete (c:\wwapps\wwhelp\wwdialogs.vcx)
*-- ParentClass:  olecontrol
*-- BaseClass:    olecontrol
*-- Time Stamp:   02/17/08 06:08:01 PM
*-- OLEObject = C:\Windows\system32\richtx32.ocx
*
DEFINE CLASS wwautocomplete AS olecontrol 
    Height = 22
    Width = 300
 
BorderStyle = 0
Appearance = 0
OleClass = "RichText.RichTextCtrl.1"
 
    *-- High level modes: FILE, URL, URLMRU
    cautocompletemode = "FILE"

    *-- Top margin of the RTF control inside of the textbox. Use this to account for lack of margins in the RTF control.
    ntopmargin = 4
    
Name = "wwautocomplete" 
 
    PROCEDURE extendautocomplete
        LOCAL lnFlags, loRtf
 
        lnFlags = this.cAutoCompleteMode
 
        LOCAL loRtf as RichText.RichTextCtrl.1
        loRtf = this
 
        IF VARTYPE(lnFlags) = "C"
            lnFlags = UPPER(lnFlags)
            DO CASE
                CASE lnFlags = "FILE"
                    lnFlags = 268435457
                CASE lnFlags = "URL"
                    lnFlags = 268435458
                CASE lnFlags = "URLMRU"
                   lnFlags = 268435460
                OTHERWISE
                    lnFlags = 268435457
            ENDCASE
        ENDIF
 
 
        loRtf.Parent.AddObject(loRtf.Name + "_RTF","TextBox")
 
        LOCAL loTextBox as TextBox
        loTextBox = EVALUATE("loRtf.Parent." + loRtf.Name + "_RTF")
 
        loTextBox.Left = loRtf.Left
        loTextBox.Top = loRtf.Top
        loTextBox.Height = loRtf.Height
        loTextBox.Width = loRtf.Width
        loTextBox.TabStop = .F.
 
        loRtf.Left = loRtf.Left + 2
        loRtf.Width = loRtf.Width - 3
        loRtf.Top = loRtf.Top + this.nTopMargin
        loRtf.Height = loRtf.Height - this.nTopMargin - 1
        loRtf.zOrder( 1)
        loTextBox.zorder(0)
        loTextBox.Visible = .t.
 
        DECLARE SHAutoComplete IN "shlwapi.dll" INTEGER hwndEdit, INTEGER dwFlags
        SHAutoComplete(loRTF.Hwnd, lnFlags )
 
        loRtf = null
        loTextBox = null
    ENDPROC
 
 
    PROCEDURE Init 
        this.ExtendAutoComplete()
    ENDPROC
 
 
ENDDEFINE
*
*-- EndDefine: wwautocomplete
**************************************************
 

With this control in place you can simply drag the control onto the surface and set the cAutoComplete mode, which defaults to file. It will do the rest and give you the formatting as shown in the first screen shot above.

This is fine if you're starting from scratch. Unfortunately, if you need to replace a TextBox control you'll have to make sure you search for all .Value assignments and replace them with the .Text property of the RichText control. You can't even implement a Value property of your own - Visual FoxPro doesn't allow that on OleControl even though it doesn't have a Value property. Grrr...

Posted in:

Feedback for this Weblog Entry


Re: Showing Windows AutoComplete Dropdowns in Visual FoxPro



Fernando D. Bozzo
March 15, 2008

Hi Rick, about the autocomplete textbox, VFP 9 textbox is configurable to do so, with properties like AutoComplete, AutoCompTable and AutoCompSource. Why the need to make a new one?

Regards.

Re: Showing Windows AutoComplete Dropdowns in Visual FoxPro



Rick Strahl
March 15, 2008

Fernando, yeah but have you looked at how crappy that looks and how badly it behaves? It's a non-starter feature IMHO. I've had better looking autocomplete support in my own libraries for years.

Add to that that the directory and URL history APIs aren't supported directly - it's all manual.

re: Showing Windows AutoComplete Dropdowns in Visual FoxPro



Craig Roberts
July 31, 2008

Rick. You are using RichTextCtrl which has security problems - http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=69256&SiteID=1 Is there a better OLE object to use in 2008 with VFP 9?

Thanks for ALL of your great contributions.

re: Showing Windows AutoComplete Dropdowns in Visual FoxPro



Rick Strahl
August 06, 2008

@Craig - not that I know of. I looked around other controls like the Microsoft Forms controls but it didn't work or fit right. There might be third party controls but I didn't want to include them.

The security issue is not really a problem for a desktop app but only when running inside of a hosted and limited security environment like VBA, Office etc... VFP desktop full trust apps shouldn't be affected by this issue.

 
© Rick Strahl, West Wind Technologies, 2003 - 2024