|
Prev: Read Cannot
Next: saving all data on form
From: Mike Williams on 5 Jul 2008 02:42 "Mike Williams" <mikea(a)whiskyandCoke.com> wrote in message news:e6oFp5l3IHA.5112(a)TK2MSFTNGP03.phx.gbl... > All of the above of course relies on you being able to detect > when a list of sub menus is rolling down, which is something > I don't yet know how to do, but I think there must be a way > of detecting it? Further to my previous response, I've just been checking things out and it looks as though with a bit of subclassing we can detect the WM_MENUSELECT message and have our code run in response to that event. A little investigation shows that the WM_MENUSELECT message is received whenever the user selects or hovers over a dropped down menu item, but it is also received whenever the user causes one of the main menu lists to drop down, so we can run code in response to that event which performs the tasks outlined in my previous response, which should give you exactly the effect you are after in just a single block of code. As far as the user is concerned, the Copy, Paste and other menu items under your Edit menu (for example) will be either active or non active immediately they drop down, and their state will depend on the state of the TextBox that was active and the contents of the Windows clipboard at the time the menu list dropped down, which I think is exactly what you are after. Mike
From: Mike Williams on 5 Jul 2008 04:12 "Mike Williams" <mikea(a)whiskyandCoke.com> wrote in message news:e6oFp5l3IHA.5112(a)TK2MSFTNGP03.phx.gbl... .. . . further to my recent responses in this thread, I've just been playing about trying to come up with a workable solution and so far this is what I have come up with, which seems to work fine. At the moment it is very "rough and ready" and it can be tidied up and made much more general purpose in lots of ways, but it does illustrate that the general principle is sound. Start a new VB project using one Form and one bas code module. Place a TextBox on the Form and make sure you name it "txtSubclass". Then drop various other controls of different kinds onto the Form, including PictureBoxes and various other things and at least a few more TextBoxes. Make at least one of the controls a CommandButton and use the following for its click event (this is just so you will be more easily able to test the operation of the program): Private Sub Command1_Click() Clipboard.Clear End Sub Use the standard VB menu editor to create a menu called mnuEdit with two sub menus called mnuCopy and mnuPaste. Then paste in the following two blocks of code into the project. When you run the project and use the Edit menu you should see that both the Copy and Paste menus are disabled unless the control that currently has the focus is a TextBox. Also, when the currently active control is a TextBox (any of the TextBoxes on your Form) you should see that the Paste menu is enabled only if there is some text on the clipboard and the Copy menu is enabled only is the currently active TextBox has some text selected. The two blocks of code, one for the module and one for the Form, are as shown below: Mike ' *** START OF FORM CODE *** Option Explicit Private Sub Command1_Click() Clipboard.Clear End Sub Private Sub Form_Load() Me.Show DoEvents txtSubclass.Text = "1" txtSubclass.Visible = False Hook Me.hwnd End Sub Private Sub Form_Unload(Cancel As Integer) Unhook Me.hwnd End Sub Private Sub mnuCopy_Click() Clipboard.SetText Me.ActiveControl.SelText, vbCFText End Sub Private Sub mnuPaste_Click() Me.ActiveControl.SelText = Clipboard.GetText(vbCFText) End Sub Private Sub txtSubclass_Change() ' This event fires each time the user causes ' a main menu list to drop down (and on other ' menu events) Dim c1 As Control, s1 As String Set c1 = Me.ActiveControl If TypeOf c1 Is TextBox Then If c1.SelLength > 0 Then mnuCopy.Enabled = True Else mnuCopy.Enabled = False End If s1 = Clipboard.GetText(vbCFText) If Len(s1) > 0 Then mnuPaste.Enabled = True Else mnuPaste.Enabled = False End If Caption = Len(s1) Else ' a textBox is not the current control mnuCopy.Enabled = False mnuPaste.Enabled = False End If End Sub ' *** END OF FORM CODE *** ' ' *** START OF MODULE CODE *** Option Explicit Private Declare Function SetWindowLong Lib "user32" _ Alias "SetWindowLongA" _ (ByVal hwnd As Long, _ ByVal nindex As Long, _ ByVal dwNewLong As Long) As Long Private Declare Function CallWindowProc Lib "user32" _ Alias "CallWindowProcA" _ (ByVal lpPrevWndFunc As Long, _ ByVal hwnd As Long, _ ByVal uMsg As Long, _ ByVal wParam As Long, _ ByVal lParam As Long) As Long Public DefWindowProc As Long Private Const GWL_WNDPROC As Long = (-4) Private Const WM_DESTROY = &H2 Private Const WM_MENUSELECT = &H11F Public Sub Unhook(hwnd As Long) If DefWindowProc Then Call SetWindowLong(hwnd, GWL_WNDPROC, DefWindowProc) DefWindowProc = 0 End If End Sub Public Sub Hook(hwnd As Long) DefWindowProc = SetWindowLong(hwnd, GWL_WNDPROC, _ AddressOf WindowProc) End Sub Function WindowProc(ByVal hwnd As Long, _ ByVal uMsg As Long, ByVal wParam As Long, _ ByVal lParam As Long) As Long Select Case uMsg Case WM_DESTROY: If DefWindowProc <> 0 Then Call Unhook(hwnd) End If Case WM_MENUSELECT Form1.txtSubclass.Text = _ -(Val(Form1.txtSubclass.Text)) Case Else WindowProc = CallWindowProc _ (DefWindowProc, hwnd, uMsg, wParam, lParam) End Select End Function ' *** END OF MODULE CODE ***
From: Mike Williams on 5 Jul 2008 05:02 "Mike Williams" <mikea(a)whiskyandCoke.com> wrote in message news:e6oFp5l3IHA.5112(a)TK2MSFTNGP03.phx.gbl... .. . . one final thing, I inadvertently left the code "Caption = Len(s1)" in the txtSubClass_Change event. I was using that purely for test purposes and it serves no actual purpose in the working project, so you can get rid of it. Mike
From: Bob Butler on 5 Jul 2008 09:38 "Mike Williams" <mikea(a)whiskyandCoke.com> wrote in message news:e4gJnbn3IHA.4284(a)TK2MSFTNGP04.phx.gbl... > "Mike Williams" <mikea(a)whiskyandCoke.com> wrote in message > news:e6oFp5l3IHA.5112(a)TK2MSFTNGP03.phx.gbl... > > . . . further to my recent responses in this thread, I've just been > playing about trying to come up with a workable solution and so far this > is what I have come up with, which seems to work fine. No need to subclass; you can do the enable/disable of the subitems in the Click event of the top-level menu. That fires before the menu displays. Private Sub mnuEdit_Click() Dim c1 As Control Dim s1 As String Set c1 = Me.ActiveControl If TypeOf c1 Is TextBox Then mnuCopy.Enabled = (c1.SelLength > 0) s1 = Clipboard.GetText(vbCFText) mnuPaste.Enabled = (Len(s1) > 0) Else mnuCopy.Enabled = False mnuPaste.Enabled = False End If End Sub
From: Mike Williams on 5 Jul 2008 12:33
"Bob Butler" <noway(a)nospam.ever> wrote in message news:Of9fGSq3IHA.2580(a)TK2MSFTNGP06.phx.gbl... > No need to subclass; you can do the enable/disable > of the subitems in the Click event of the top-level menu. > That fires before the menu displays. Well, well, well! We live and learn :-) I created a Form and used the menu editor to create various menus with sub menus, including a menu called Edit with sub menus Copy and Paste. I then (in the IDE) clicked (and sometimes double clicked) the various menu items so that VB brought up the code window when I did so and created the "event wrapper" for me to place code in. When I clicked (or double clicked) the main menu item (in this case, the Edit menu) in the IDE VB did not react at all and so I just assumed, without any further checking, that there were no click events for top level menus and I therefore decided that I would need to subclass in order to get at that event, which I did! I should have checked further of course and I would probably have found the top level menu Click event, but I did not :-( Thanks for putting me right on that one, because it simplifies the code quite a lot, removing the need for subclassing as it does. Mind you, having said that, the basic idea of course remains the same and it is still very sound, except you just remove the code I previously had in the txtSubClass_Change event and place it instead in the mnuEdit_Click event. So we don't need the module and we can simplify the Form code to something like the following: Mike Option Explicit Private Sub mnuCopy_Click() Clipboard.SetText Me.ActiveControl.SelText, vbCFText End Sub Private Sub mnuPaste_Click() Me.ActiveControl.SelText = Clipboard.GetText(vbCFText) End Sub Private Sub mnuEdit_Click() Dim s1 As String If TypeOf Me.ActiveControl Is TextBox Then If Me.ActiveControl.SelLength > 0 Then mnuCopy.Enabled = True Else mnuCopy.Enabled = False End If s1 = Clipboard.GetText(vbCFText) If Len(s1) > 0 Then mnuPaste.Enabled = True Else mnuPaste.Enabled = False End If Else ' a textBox is not the current control, and we are ' dealing only with TextBoxes in this simple example mnuCopy.Enabled = False mnuPaste.Enabled = False End If End Sub |