-
[RESOLVED] WithEvents (or something like it)
See post it in the codebank, here for the solution I wound up using.
Here's the problem I'm trying to work out:
- I've got a form with MANY labels on it, most being a few different control arrays.
- This form must be allowed to be dragged around by any "gray space" including any label, rather than just the title bar.
- I'd like to build a "generic" routine I call to get this "dragging" job done. It will monitor for dragging of the form, any labels, and other specifically specified controls. But I'd like to NOT have to specifically specify the labels.
- If possible, I'd like to accomplish this without sub-classing, as I often still debug these forms and enjoy being able to click the IDE STOP button while working on them.
Any/all ideas on how to get this accomplished would be greatly appreciated. Above, I said "I've got a form" that needs this. In reality, I've got several, with more to come, and I'm tired of attaching mouse_down events to all the labels on the form each time I do this. It'd be much better if I could just call a generic routine and have it all done for me.
Thanks In Advance,
Elroy
p.s. The "dragging" part is not the problem. It's having a generic routine that can catch the events of all the labels on the form, even when some of those labels are control arrays.
-
Re: WithEvents (or something like it)
Elroy
I don't have an answer off-hand, but I do have a question:
What is the need to drag the form from other than the title bar?
Perhaps a screen-shot of the form would further explain the need.
Spoo
-
Re: WithEvents (or something like it)
Well Spoo, the answer is relatively easy.
In several cases, I've got large forms, and some of my users attempt to use these forms with the monitor resolutions set to small (say 800x600). To prevent problems, I make sure the forms don't get resized, which makes them larger than the monitor. To accommodate this, I allow the user to drag the forms around by any "gray" space. I'm definitely not going to re-work my forms to fit smaller monitors, as my approach has worked just fine for years.
If you'd like to see samples of these forms, they can be seen here or here.
Also, I started this thread just to make something I'm already doing a bit easier. I'm in the process of creating yet another of these large forms. And, rather than put code under all the labels that will be on the form, I'd like to make a single call to some instantiated class that will just deal with it all. That's where catching the events of label control arrays comes in.
Thanks,
Elroy
-
Re: WithEvents (or something like it)
I'm picturing in my head a Form with a bunch of labels on it and nothing else. I don't know about other specifically specified controls but if it was just the Form itself and the labels I would have a picturebox that overlays the entire Form that contains a snap shot of the Form and when you mouse down you will always mouse down on the picturebox which is what you use to 'drag' the Form around. This, however, would only work if there is no user intervention on the Form or labels (or other controls) or code that dynamically changes the appearance of the controls or code that adds/deletes controls. This brings up some questions:
1) At what point do you take the snap shot
2) What triggers the snap shot to be taken
EDIT: After looking at the pictures of your Forms from the above links I'm beginning to think this approach won't work for you
-
Re: WithEvents (or something like it)
Hi Ordinary Guy,
Thanks for the idea, but goodness no. There's LOTS of user intervention on the form. That's the whole idea.
The idea of other specifically specified controls is to handle other things such as a TabControl that may be on the form, or possibly locked Textboxes that are used more like labels. I wouldn't want my class that I'm considering to automatically handle these other controls. Rather, I could just individually specify them to the class.
However, with labels (and possibly drawing controls as well), it'd be very nice if this class just automatically found them, and then monitored for mouse_down, mouse_move, and mouse_up events which would allow me to move the form around when being dragged by one of these controls. The problem is that most (but not necessarily all) of these labels and drawing controls are control arrays. The links provided above (and again here or here) should give you a good idea of what I'm talking about.
Thanks,
Elroy
-
Re: WithEvents (or something like it)
OK, I edited my post before I saw the above post by you so I know it wont work
-
Re: WithEvents (or something like it)
If you've only got a few control arrays, what's wrong with the old tried-and-tested method of
SendMessage Me.hWnd, WM_NCLBUTTONDOWN, HTCAPTION, 0&
in the labels MouseDown event handler?
-
Re: WithEvents (or something like it)
Hi Colin,
Yeah, that's (basically) the way I'm doing it now. Let me show you a snippet out of one of my forms:
Code:
Private Sub Form_MouseDown(Button As Integer, Shift As Integer, x As Single, y As Single)
DoMouseDown Button
End Sub
Private Sub Form_MouseUp(Button As Integer, Shift As Integer, x As Single, y As Single)
DoMouseUp
End Sub
Private Sub Form_MouseMove(Button As Integer, Shift As Integer, x As Single, y As Single)
DoMouseMove Button
End Sub
Private Sub lblMisc_MouseDown(Index As Integer, Button As Integer, Shift As Integer, x As Single, y As Single)
DoMouseDown Button
End Sub
Private Sub lblMisc_MouseUp(Index As Integer, Button As Integer, Shift As Integer, x As Single, y As Single)
DoMouseUp
End Sub
Private Sub lblMisc_MouseMove(Index As Integer, Button As Integer, Shift As Integer, x As Single, y As Single)
DoMouseMove Button
End Sub
Private Sub lblMisc2_MouseDown(Index As Integer, Button As Integer, Shift As Integer, x As Single, y As Single)
DoMouseDown Button
End Sub
Private Sub lblMisc2_MouseUp(Index As Integer, Button As Integer, Shift As Integer, x As Single, y As Single)
DoMouseUp
End Sub
Private Sub lblMisc2_MouseMove(Index As Integer, Button As Integer, Shift As Integer, x As Single, y As Single)
DoMouseMove Button
End Sub
Private Sub lblMisc3_MouseDown(Index As Integer, Button As Integer, Shift As Integer, x As Single, y As Single)
DoMouseDown Button
End Sub
Private Sub lblMisc3_MouseUp(Index As Integer, Button As Integer, Shift As Integer, x As Single, y As Single)
DoMouseUp
End Sub
Private Sub lblMisc3_MouseMove(Index As Integer, Button As Integer, Shift As Integer, x As Single, y As Single)
DoMouseMove Button
End Sub
Private Sub lblMisc4_MouseDown(Index As Integer, Button As Integer, Shift As Integer, x As Single, y As Single)
DoMouseDown Button
End Sub
Private Sub lblMisc4_MouseUp(Index As Integer, Button As Integer, Shift As Integer, x As Single, y As Single)
DoMouseUp
End Sub
Private Sub lblMisc4_MouseMove(Index As Integer, Button As Integer, Shift As Integer, x As Single, y As Single)
DoMouseMove Button
End Sub
Private Sub lblMisc5_MouseDown(Index As Integer, Button As Integer, Shift As Integer, x As Single, y As Single)
DoMouseDown Button
End Sub
Private Sub lblMisc5_MouseUp(Index As Integer, Button As Integer, Shift As Integer, x As Single, y As Single)
DoMouseUp
End Sub
Private Sub lblMisc5_MouseMove(Index As Integer, Button As Integer, Shift As Integer, x As Single, y As Single)
DoMouseMove Button
End Sub
Private Sub tabExamData_MouseDown(Button As Integer, Shift As Integer, x As Single, y As Single)
DoMouseDown Button
End Sub
Private Sub tabExamData_MouseUp(Button As Integer, Shift As Integer, x As Single, y As Single)
DoMouseUp
End Sub
Private Sub tabExamData_MouseMove(Button As Integer, Shift As Integer, x As Single, y As Single)
DoMouseMove Button
End Sub
There's not any fundamental problem with doing things that way. The DoMouseDown, DoMouseMove, and DoMouseUp procedures do the actual form moving.
I was just thinking that there'd be a way to clean some of this up. That's all I'm trying to do. Maybe there's not a way to do it, but I just thought I'd explore it, giving me one less thing to think about when creating one of these forms, or attaching this functionality to some existing form.
Best Regards,
Elroy
p.s. Also Colin, the way you dragged the form around doesn't always allow you to drag the title-bar well off the top of the monitor. I've posted my method elsewhere, but that's not really the problem I'm trying to solve in this thread. Thanks.
-
1 Attachment(s)
Re: WithEvents (or something like it)
Quote:
Originally Posted by
Elroy
I was just thinking that there'd be a way to clean some of this up.
Yes, there is a way, but only "some", as you said.
The controls array will have to be handled appart.
Attachment 146553
-
Re: WithEvents (or something like it)
Any Control that was set to Enabled = False, will "delegate" their Mouse-Messages to the underlying Container.
Knowing that, the solution comes down to writing/defining your own ucLabel.ctl
(which despite having Disabled-State, will render its Text in the normal ForeColor).
The easiest way to do that (if you don't want to fiddle around with your own Text-Renderings) is,
to simply wrap an existing Label-Control within your new ucLabel.ctl.
Here's a small example - the following should go into a new ucLabel.ctl (which hosts a Label, named Label1):
Code:
Option Explicit
'also set the UserControl to CanGetFocus = False in the Property-Grid
Private Sub UserControl_Initialize()
UserControl.Enabled = False
Label1.BackColor = vbRed 'set whatever default-props you want on your Label
End Sub
Public Property Get Caption() As String 'expose any Prop you want (Caption is obvious)
Caption = Label1.Caption
End Property
Public Property Let Caption(ByVal RHS As String)
Label1.Caption = RHS
End Property
Private Sub UserControl_Resize() 'adjust the Label1-Size according to the hosting UserControl
Label1.Move 0, 0, UserControl.Width, UserControl.Height
End Sub
Now place an instance of your new ucLabel on your Form - and check the behaviour with the following Code:
Code:
Option Explicit
Private Declare Function ReleaseCapture& Lib "user32" ()
Private Declare Function SendMessageA& Lib "user32" (ByVal hWnd&, ByVal Msg&, ByVal wParam&, lParam As Any)
Private Sub Form_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single)
ReleaseCapture
SendMessageA hWnd, &H112, &HF012&, 0&
End Sub
HTH
Olaf
-
Re: WithEvents (or something like it)
@Eduardo-: Cute little project, but I broke it by copying Label1 and then pasting it, saying "Yes" to "Do I want to create a Control Array". Being able to handle Control Arrays is one of the primary things I'm trying to do.
@Olaf: Not a bad idea at all. Although I cringe a bit at turning all my labels into "heavy-weight" controls, and all my drawing objects as well. But that might be the only way to accomplish what I want (without getting into sub-classing). I'll have to consider whether that trade-off is worth it.
It all comes back to not being able to do "WithEvents" with control arrays .... sooo annoying.
Best Regards,
Elroy
-
Re: WithEvents (or something like it)
Hmmm, actually, my drawing objects (Shapes and Lines) aren't a problem. They're already handled correctly.
So, that takes me back to my labels, and Olaf has me thinking. If I just disable them, they work like I want. However, then the text is grayed, which I don't want.
I'm now wondering if there's some way to stop an Label.Enabled = False from tampering with the label's color. That's almost worth asking in a different thread, but I'll continue on here.
EDIT1: Maybe some temporary sub-classing would get this done. I'm looking into that: Leave them enabled in the IDE, call my class, do some sub-classing, explore all my controls and disabling the labels but putting the color back, undo the sub-classing, return to program.
-
Re: WithEvents (or something like it)
Yes, you can do it subclassing the WM_LBUTTONDOWN message.
With the array of labels (or any label) you won't have problems, but if there were arrays of windowed controls (like pictureboxes) that won't work either.
-
Re: WithEvents (or something like it)
Quote:
Originally Posted by
Elroy
... I cringe a bit at turning all my labels into "heavy-weight" controls, ...
Well, the hosting Control could of course also be a Windowless-Control.
Quote:
Originally Posted by
Elroy
But that might be the only way to accomplish what I want (without getting into sub-classing). I'll have to consider whether that trade-off is worth it.
One can solve that (for a lot of Forms) by writing a little *.frm-Content Replacer-Routine, which later
(when your own ucLabel is "Property-Complete" or "Property-Compatible") replaces all occurences of:
"Begin VB.Label"
with the String:
"Begin MyLabelOCX.ucLabel"
Quote:
Originally Posted by
Elroy
It all comes back to not being able to do "WithEvents" with control arrays .... sooo annoying.
That'd also be solvable (once you have your own Label-Control) using an (On Error-buffered)
re-delegation of the internal Mouse-Events to a (Public) routine on the ParentForm.
As for your complex Forms (each sitting in a separate *.frm-File) ...
I'd choose a completely different approach which only uses one single (generic, nearly empty)
VBForm as the Host-Area - and then build all the Forms-Content dynamically (from Form-Definitions
which sit in a DB-Field ... and all your Label-Controls you currently host on your old Forms,
I'd render directly onto the Form.hDC with graphics-methods (according to the definitions in said DB-Field).
Same thing for the large amount of CheckBoxes (which would also easily be replacable with direct renderings of the 2 or 3 states).
The more you do with direct graphics-methods, the faster will the Form load, and also proper
Zooming of your Forms Content (Label-Texts etc.) would be way easier (speaking about DPI-awareness).
Olaf
-
1 Attachment(s)
Re: WithEvents (or something like it)
You can subclass only when it's not in the IDE.
Attached is version 2 of the other sample I posted.
Attachment 146563
PS: test compiled or remove the comment in the line (It also handles arrays of windowed controls)
-
2 Attachment(s)
Re: WithEvents (or something like it)
Why can't you just make all of the Label controls a single control array?
Did you rule that out and I just missed it in the posts above? They can use different fonts, be of varying sizes, etc.
Attachment 146565
Code:
Option Explicit
Private MouseDownX As Single
Private MouseDownY As Single
Private Sub Form_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single)
If Button And vbLeftButton Then
MouseDownX = X
MouseDownY = Y
End If
End Sub
Private Sub Form_MouseMove(Button As Integer, Shift As Integer, X As Single, Y As Single)
If Button And vbLeftButton Then
Move Left + (X - MouseDownX), Top + (Y - MouseDownY)
End If
End Sub
Private Sub Image1_Click()
Unload Me
End Sub
Private Sub Label1_MouseDown(Index As Integer, Button As Integer, Shift As Integer, X As Single, Y As Single)
If Button And vbLeftButton Then
MouseDownX = X
MouseDownY = Y
End If
End Sub
Private Sub Label1_MouseMove(Index As Integer, Button As Integer, Shift As Integer, X As Single, Y As Single)
If Button And vbLeftButton Then
Move Left + (X - MouseDownX), Top + (Y - MouseDownY)
End If
End Sub
-
Re: WithEvents (or something like it)
Quote:
Originally Posted by
dilettante
Why can't you just make all of the Label controls a single control array?
Hi Dilettante,
Yeah, I've thought of that, and it's certainly not the worst of ideas. It would require that I go back and rename lots of labels, although I could do that in Notepad++ easy enough. I'd also need to make sure I didn't have any Index collisions.
However, in certain circumstances, it actually makes some degree of organizational sense to have more than one control array. Also, each of these forms always have a few straggling non-control-array labels that may change according to certain user conditions. I'd still have to handle each of these manually. And you're essentially moving the form the same way I am to make sure I can drag the titlebar off the top of the screen.
Hi Eduardo,
Yeah, if I'm going to go the sub-classing route, what you've done isn't a bad alternative. However, you've already pointed out the downside (no good IDE support), but you've handled it fairly well with your InIDE check. You actually can sub-class in the IDE. You just have to be careful to not use the "STOP" button on the IDE toolbar (or break and then stop). Actually, to examine your code, I immediately did the following to execute it in the IDE, and it worked fine:
Code:
'If InIDE Then
' Set iECH = New cEventHandler
' iECH.SetObject nForm, mEventReceiver
' mCol.Add iECH
'Else
Set mForm = nForm
Set mHwnds = New Collection
Set mOldProcs = New Collection
iOldProc = SetWindowLong(mFormHwnd, GWL_WNDPROC, AddressOf gWindowProc)
gSubclassedWindowsData1.Add iOldProc, CStr(mFormHwnd)
gSubclassedWindowsData2.Add mFormHwnd, CStr(mFormHwnd)
mHwnds.Add mFormHwnd
mOldProcs.Add iOldProc
'End If
Hi Olaf,
You've definitely provided me with some interesting ideas. I'm not sure I've ever designed a lightweight UC before. It's good to know that that's an option. I'll continue to explore your suggestions.
Thanks to ALL,
Elroy
-
Re: WithEvents (or something like it)
Karl Peterson has a couple of samples that might help -
-being able to drag a form
http://vb.mvps.org/samples/WinRgn/
- being able to control the border properties (that are normally read only)
http://vb.mvps.org/samples/FormBdr/
-
Re: WithEvents (or something like it)
After examining some of the above code, I'm going to mark this one as resolved.
Edwardo has come up with a good sub-classing alternative (although I'm not sure why we don't just let everything subclass since we're subclassing anyway). And Olaf has provided some good ideas for developing a custom user control to get it done.
Thanks to all for assisting me with exploring this issue.
All The Best,
Elroy
-
2 Attachment(s)
Re: WithEvents (or something like it)
Quote:
Originally Posted by
Elroy
although I'm not sure why we don't just let everything subclass since we're subclassing anyway
It was to leave some dragging capabilities in the IDE, but not really needed I think.
So, I attach version 3.
Also, if you need just to drag the form and the labels, it can be simplified. Check the other attached file.
-
Re: [RESOLVED] WithEvents (or something like it)
Hey Eduardo,
Thanks. :) I appreciate you pulling this together. I'm still contemplating whether or not I actually want to sub-class. If I do, I may explore ways to make it more IDE safe. There are a few ways to do this:
- Use comctl32.dll (#410, #412, & #413) instead of the SetWindowLong call to do the subclassing.
- When in the callback, check for WM_DESTROY, and actually unhook when that's detected.
- Do a machine-language patch-up so that we can execute some code "after" the IDE stop button is clicked.
It's sort of on my "todo" list to get some of this sorted, but I've never taken the time. Also, there's some pretty good threads floating around here that already talk about it. If I bite-the-bullet on this, I would need it to be IDE safe (even with the STOP button), as I need to be able to safely trace my code in some of these forms.
Again, many thanks. :)
Elroy
-
Re: [RESOLVED] WithEvents (or something like it)
Quote:
Originally Posted by
Elroy
Hey Eduardo,
Thanks. :) I appreciate you pulling this together. I'm still contemplating whether or not I actually want to sub-class. If I do, I may explore ways to make it more IDE safe. There are a few ways to do this:
- Use comctl32.dll (#410, #412, & #413) instead of the SetWindowLong call to do the subclassing.
- When in the callback, check for WM_DESTROY, and actually unhook when that's detected.
- Do a machine-language patch-up so that we can execute some code "after" the IDE stop button is clicked.
It's sort of on my "todo" list to get some of this sorted, but I've never taken the time. Also, there's some pretty good threads floating around here that already talk about it. If I bite-the-bullet on this, I would need it to be IDE safe (even with the STOP button), as I need to be able to safely trace my code in some of these forms.
Again, many thanks. :)
Elroy
My question is: why do you need the dragging feature to work in the IDE?
About subclassing, I use a modified version of vbAccelerator subclassing technique.
There are other that are supposed to be better, based on Paul Caton's technique.
I don't know much about this technique, but what I don't like it that it involves to copy much code on each object that use it (correct me if I'm wrong).
And that you are bound to put the Window's procedure as the last procedure of the object.
The one that I use is not perfect (from time to time I get a crash when debbuging), but has worked fairly well for me in many years.
Perhaps if I polish it a little bit I could share it, but it is considered "old fashioned" now.
And I never worked with comctl32.dll subclassing.
PS: you may want to take a look to:
http://vb.mvps.org/samples/HookXP/
http://vb.mvps.org/articles/vsm20090716.pdf
-
Re: [RESOLVED] WithEvents (or something like it)
Quote:
Originally Posted by
Eduardo-
why do you need the dragging feature to work in the IDE?
You're sort of right that I don't absolutely need it to work in the IDE. I just do my best to have everything up and running in the IDE that I have in my compiled program. I'm not sure I've got the best of reasons for that position, other than being able to test everything. It's just sort of a "rule" I try to abide by.
Take Care,
Elroy
-
Re: [RESOLVED] WithEvents (or something like it)
Quote:
Originally Posted by
Elroy
You're sort of right that I don't absolutely need it to work in the IDE. I just do my best to have everything up and running in the IDE that I have in my compiled program. I'm not sure I've got the best of reasons for that position, other than being able to test everything. It's just sort of a "rule" I try to abide by.
Take Care,
Elroy
Yes... but once you tested it you know it works.
Nevertheless I agree with your point of view that it's best to have all the features as possible available in the IDE, because it's where we usually spend most of the time working, so we can detect if anything isn't working right, but when that comes to the cost of having crashes, I prefer not to do so with that set of features.
I have most of the subclassing working in the IDE in my projects, but there were a couple of things that caused me problems and then I decided to do that check, if in the IDE then don't process that message. But I do the subclassing with a dll, so it's safer.
-
Re: [RESOLVED] WithEvents (or something like it)
Elroy, I know you have this resolved, but let me pose 1 more potential solution...
If you are only interested in allowing user to click unoccupied real estate and/or windowless controls, then this could be really simple.
Add an image control to the form, no image. During form resize, resize image to full scalewidth,scaleheight of form and during form load, set its zOrder top most. The image control has mouse events and being top most, it will overlap all windowless controls.
Just a thought. Potential downside & needs to be tested: flicker
Edited: Shape controls are no worry any way you go, they can't be clicked on
-
Re: [RESOLVED] WithEvents (or something like it)
Ya know, I really enjoy reading all you experts' advice (and your code) for these types of unique situations. I especially have admired the Fox's (LaVolpe) posts, especially this last one. Appears extra simple...I tried this simple code (which I think LaVolpe refers to) and it seems to work great.
Code:
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
Private Declare Sub ReleaseCapture Lib "User32" ()
Private Sub Image1_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single)
Const WM_NCLBUTTONDOWN = &HA1
Const HTCAPTION = 2
If Button = vbLeftButton Then
ReleaseCapture
Call SendMessage(Me.hWnd, WM_NCLBUTTONDOWN, HTCAPTION, 0&)
End If
End Sub
-
Re: [RESOLVED] WithEvents (or something like it)
I might be jumping the gun, but I'm testing a "bulletproof" IDE Subclassing component. PM me if you want to test the latest binary.
Uses SetWindowSubclass API / no teardown issues / A single forwarding callback, means no User Object leaks.
No thunks to manage the lifetime of / no Global mem leaks on crashing / no assembly / no DEP issues.
No SetProp / GetProp issues like SubTimer/SSubTmr6
Break/Pause/End support unlike DbgWProc.
Elroy/Krool and (I forget who originally wrote the IAT hook) can take credit for the inspiration per one of Elroy's threads.
-
Re: [RESOLVED] WithEvents (or something like it)
I kept making changes/improvements to the solution I wound up using so I just posted it in the codebank, here. It'll be easier to keep updated that way.
Again, LaVolpe, thanks for the best idea to solve this problem.
Regards,
Elroy
-
Re: [RESOLVED] WithEvents (or something like it)
Sometimes the simplest solutions are the hardest to see. Glad it worked for you and won't be ideal for all similar situations, i.e., the underlying windowless control needs to have click events acted on. That would require a workaround for the workaround.
Though you use the form's Move method to reposition, I personally prefer the API solution like that which Sam posted in #26 above. Why? Honors Escape key, while dragging, to snap back to starting point if user decides to cancel/abort
-
Re: [RESOLVED] WithEvents (or something like it)
Quote:
Originally Posted by
LaVolpe
Sometimes the simplest solutions are the hardest to see.
You are so right about that. That's why I do love these forums.
Regarding the "Call SendMessage(Me.hWnd, WM_NCLBUTTONDOWN, HTCAPTION, 0&)" approach to moving the form, it doesn't allow you to get the form's title bar well off the top of the screen, which is necessary for some of my forms running on low resolution monitors.
All The Best,
Elroy