Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations Mike Lewis on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

how to 'select text' on current form 3

Status
Not open for further replies.

themaj

Technical User
Apr 10, 2006
37
US
I'm looking to write one subroutine at the form level that will 'Select Text' on each Textbox as I tab through the form.
I don't necessarily want to add a sub to each textbox on the form.

Is this possible? Any ideas or suggestions?

Thanks
 
I see from your profile that you haven't yet acknowledged any of the answers given to you in this forum. If the answers weren't what you wanted read faq222-2244 to see how to get better answers. If they did help you read the FAQ to see how to acknowledge the answers.

For this question you could make your textboxes into a control array. In the GotFocus event:
1. Loop through the textboxes (using the array's Count property)
2. Use the SelLength property to clear all selections
3. Use the Index property, the SelLength and SelStart properties to select all the text

___________________________________________________________
If you want the best response to a question, please check out FAQ222-2244 first.
'If we're supposed to work in Hex, why have we only got A fingers?'
Drive a Steam Roller
Steam Engine Prints
 
John,

I considered using a control array but in my coding practise I name various controls (especially Textboxes) to more recognizable names corresponding to the database table fields. Is that bad programming?

I was hoping to detect when the Tab or Enter is executed and use the SelLength and SelStart IF the next control is a textbox.
I tried to do this in the form_keypress event but that occurs before the next control is the ActiveControl.

Does this all make sense?

BTW, I will read your suggested FAQ, thanks

 
How about something like this?

Code:
Option Compare Database

Private Sub SelText(ctrlName As Control)
ctrlName.SelStart = 0
ctrlName.SelLength = Len(ctrlName.Text)
End Sub

Private Sub Text0_GotFocus()
If Me.Text0.Text = "" Then
    Exit Sub
Else
    Call SelText(Text0)
End If
End Sub

Private Sub Text2_GotFocus()
If Me.Text2.Text = "" Then
    Exit Sub
Else
    Call SelText(Text2)
End If

End Sub
 
Captain,

The way I read this is that each Textbox still requires the SelText sub in it's GOTFOCUS. Am I reading this right?

My form has more than 50 textboxes spread over 3 Tabs of an SSTab control. They're not in a control array and I prefer it this way for now. I'm certainly open to another way if you can provide justification.

Selecting the text is not my problem; its trying to have only 1 routine that constantly looks at the various controls on the form as they become the ActiveControl.

At the end of the day I'm really just trying to shortcut the GOTFOCUS on all these controls.
Is there something at the form level that looks at all of its children as they receive focus? Wouldn't that be the easiest way?

I appreciate all of your input and suggestions.
 
You can do this by subclassing all text boxes on your form and selecting the text in the text box in the window procedure when it receives focus.

As all text boxes are created from the same class, they share the same window procedure allowing you to handle the events in a central place.

See the following code. This code goes in the form:
___
[tt]
Option Explicit
Private Declare Function SetWindowLong Lib "user32" Alias "SetWindowLongA" (ByVal hwnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long
Const GWL_WNDPROC = -4&

Private Sub Form_Load()
Dim ctl As Control
'subclass all text boxes
For Each ctl In Controls
If TypeOf ctl Is TextBox Then _
lpPrevWndProc = SetWindowLong(ctl.hwnd, GWL_WNDPROC, AddressOf WndProc)
Next
End Sub[/tt]
___

Following code goes in a standard module which you need to add to your project.
___
[tt]
Option Explicit
Private Declare Function CallWindowProc Lib "user32" Alias "CallWindowProcA" (ByVal lpPrevWndFunc As Long, ByVal hwnd As Long, ByVal msg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
Private Declare Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, lParam As Any) As Long
Const WM_SETFOCUS = &H7
Const EM_SETSEL = &HB1
Public lpPrevWndProc As Long
Function WndProc(ByVal hwnd As Long, ByVal uMsg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
If uMsg = WM_SETFOCUS Then SendMessage hwnd, EM_SETSEL, 0, ByVal -1&
WndProc = CallWindowProc(lpPrevWndProc, hwnd, uMsg, wParam, lParam)
End Function[/tt]
___

Now run the program and Tab through text boxes. The code works for any number of text boxes you have placed on your form.
 
Captain,

The way I read this is that each Textbox still requires the SelText sub in it's GOTFOCUS. Am I reading this right?

No, you create the sub and then each GOTFOCUS calls the sub.

Looking at it, I might change that to:

Code:
Option Compare Database


Private Sub SelText(ctrlName As Control)

If ctrlName.Text = "" Then
   Exit Sub
Else
   ctrlName.SelStart = 0
   ctrlName.SelLength = Len(ctrlName.Text)
End If
End Sub

Private Sub Text0_GotFocus()

    Call SelText(Text0)

End Sub

Private Sub Text2_GotFocus()

    Call SelText(Text2)

End Sub
 
CaptainD,
The way I read this still tells me that I have to create the Sub lines in each & every textbox_gotfocus. That was what I'm trying to avoid since there are over 50 textboxes on the form.

Hypetia,
This all looks interesting but looks a bit bigger than my experience. I'm going to review it and get a good understanding of what its all about. Thanks

For now I've decided to write a little snippet that automatcially creates all the textbox_gotfocus subroutines in one quickshot.

Please take a moment to review and let me know your thoughts on this method. I just paste this into the form, create the lones of code and then remove it.


Code:
Private Sub cmdGenerateEventsCode_Click()
Dim sClip As String
Dim sObj As String
Dim sSub As String
Dim sOrig As String
Dim ctl As Control
    
    Clipboard.Clear

    sOrig = "Private Sub XXX_GotFocus()"
    
    For Each ctl In Me.Controls
        If TypeOf ctl Is TextBox Then
            sObj = ctl.Name
            sSub = Replace(sOrig, "XXX", ctl.Name)
            sSub = sSub & vbCrLf & "  sSelectText " & ctl.Name & vbCrLf & "End Sub" & vbCrLf & vbCrLf
            sClip = sClip & sSub
        End If
    Next

    Clipboard.SetText sClip
  
    MsgBox "The code for all the textbox GotFocus events is in the Clipboard"

End Sub





 
Hypetia,

I honestly don't know exactly whats going on here with your suggested code but it works....very cool!!

It certainly reduces a ton of code in a small little package.

I'll definately add this to my little 'snippet' box for use later.

Xie xie, (thanks)

maj
 
Hypetia,

This ROCKS!!!!!!!! You just saved me a sh#tload of boring, repetitive coding. I just tossed it into my project and WHAMMO, all is happy.

Now....can you tell me anything about this that might cause trouble? Any counterindications with this code I should know about?

Oh...and I learned something new today so a cold beer is well deserved. Stop by Shanghai and buy you one as well.

Xie xie (thank you),

themaj

 
Hypetia

Why is the solution always so simple? I seem to always attempt to go about it the hard way. This wasn't my original post, but you help me with about 4 problems in one post. thanks

Jim Garry
 
Hypetia, I have been looking at the code, and am using it, but now can you attempt to explain what it is doing? I find it fasanating, just wish I understood it better.

Thank you again

Jim Garry
 
In simple words, every window has a window procedure which processes all messages and events pertaining to that window. Calling SetWindowLong in Form_Load with all text boxes replaces that window procedure with a user defined procedure (WndProc) which is written in the module.

This causes all messages and notifications to be routed to this custom procedure instead of the original window procedure of the text box. Our custom procedure checks the WM_SETFOCUS message (similar to GotFocus event) and sends the EM_SETSEL message to select the text in the text box.

We cannot do this by setting the SelStart and SelLength properties because in the window procedure, we don't have the reference to the text box object itself, but only its window handle.

As all text boxes are subclassed with the same window procedure, it acts like a common event handler for all text boxes allowing you to code the GotFocus event (or any other event if required) in a common place.

If you have never done subclassing before, this all stuff will look quite strange to you. I suggest you to do some reading on this subject. There are hundreds of subclassing code examples on this site.

See also thread222-701611 where we discussed subclassing and window procedures in detail.
 
Just to add here as an alternative solution, you can create a usercontrol (with an embedded text box), implement all properties of the textbox in it and just add the "select text" functionality in the embedded text box's getfocus.

The advantage is that, you dont have to subclass all the windows in your project.

Any other functionalities of the text, which have to be reflected over your whole project can be done in one file. (Think validations..)

The disadvantage is that, you will have to implement *ALL* the functionalities of the textbox (methods, events) in your user control.

------------------------------------------
The faulty interface lies between the chair and the keyboard.
 
>The advantage is that, you dont have to subclass all the windows in your project.

You can also achieve similar behavior using global subclassing.

What I do in my previous post is called instance subclassing in which a single window is subclassed by changing its window procedure using SetWindowLong function.

In global subclassing a whole window class is subclassed by changing its window procedure using SetClassLong function. Once the window procedure of a window class is altered, it affects all windows subsequently created from that class. You don't need to subclass individual windows.

See the following example.

Start a new VB project. Place some text boxes on your form (Form1) for testing. We assume that this is our main application form.

Now add another form (Form2) to the project. Place a text box on the form and insert the following code.
___
[tt]
Option Explicit
Private Declare Function SetClassLong Lib "user32" Alias "SetClassLongA" (ByVal hwnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long
Const GCL_WNDPROC = -24&

Sub CreateSubclass()
lpPrevWndProc = SetClassLong(Text1.hwnd, GCL_WNDPROC, AddressOf WndProc)
End Sub

Sub DestroySubclass()
If lpPrevWndProc Then SetClassLong Text1.hwnd, GCL_WNDPROC, lpPrevWndProc
End Sub[/tt]
___

The above form (Form2) is not a UI part of the application. It is only used as a helper for creating the subclass.

Now add a module (Module1) to the project and insert the following code.
___
[tt]
Option Explicit
Private Declare Function CallWindowProc Lib "user32" Alias "CallWindowProcA" (ByVal lpPrevWndFunc As Long, ByVal hwnd As Long, ByVal msg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
Private Declare Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, lParam As Any) As Long
Const WM_SETFOCUS = &H7
Const EM_SETSEL = &HB1
Public lpPrevWndProc As Long
Function WndProc(ByVal hwnd As Long, ByVal uMsg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
If uMsg = WM_SETFOCUS Then SendMessage hwnd, EM_SETSEL, 0, ByVal -1&
WndProc = CallWindowProc(lpPrevWndProc, hwnd, uMsg, wParam, lParam)
End Function

Sub Main()
'create the text box subclass
Form2.CreateSubclass
Unload Form2

'show the main application form
Form1.Show vbModal

'destroy the text box subclass
Form2.DestroySubclass
Unload Form2
End Sub[/tt]
___

Go to Project Properties, change the Startup Object to Sub Main and run the program. You can see that all the text boxes on Form1 exhibit the modified behavior, although you did not subclass any of the text boxes on Form1. If your application has other forms with text boxes, those text boxes will also exhibit the same behavior.

Global subclassing is helpful in globally changing the behavior of all windows belonging to a class. However, there is one disadvantage. Once you create a global subclass, you cannot create a window with original, unmodified behavior. The only way is to destroy the subclass and revert back to the original behavior.

This problem is addressed by creating another type of subclass, called a superclass. Superclassing allows you to create both modified and unmodified versions of a window as needed.

Following MSDN article further elaborates different types of subclassing.
About Window Procedures
 
This one's definitely going in the archive. Thanks Hypetia.

Bob
 
Hypetia,

Friends have commented to me about the hazards of subclassing but I'm not sure I understand the issue and what it has to do with 'IDE'. What I do know is that when I put your code into the project as you described, the VBStudio crashed when my app crashed. I was forewarned and thankfully had saved all the changes before running my crash test.
If this is true subclassing (which I think it is), there will be (at least) one drawback -
If you stop your program with the End button in the IDE, it will crash.
If you place a break point in your program, it will crash.
And if there is an error and the program stops(in the IDE), it will crash.
They discuss 'detacting when you're in the IDE' to avoid this potentially aggravating situation. Would you care to comment and give us all some more brain food?

the maj
 
Nothing wrong with subclassing. Usually what may go wrong is caused from someone not understanding what they are doing. Therefore, you have vendors taking advantage of this and creating code to do things for you. Only problem comes when the System upgrades and the code needs to be changed - You will need an Update from the vendor. If you have all the source code, then you can make the changes yourself.
You just need to know how to handle it and what to expect if you break some rules.

Just like with many things in life: You can drive a bumper scooter and not much dangerous will happen if you let the steering wheel go, and you do not get any problems from the authorities when you pass on the right , or whatever. However, don't try things like this while driving an automobile on the highway ...

What I usually do when testing is use Debug.? statements in the WndProc.

I also have a function which tests at the start of the program if the application is running in the IDE or not, and then sets a boolean variable to this test result.
Then, in many cases where I have a hook call, I test this variable and only call the hook if the application is not running from the VB developing interface, but only runs from the Exe/Dll.
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top