i want to select the listitem by hovering the mouse not by clicking.. how to achieve in vba..
i saw a code in a forum using vb.net
Private Sub ListView1_MouseMove(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles ListView1.MouseMove
Dim itm As ListViewItem
itm = Me.ListView1.GetItemAt(e.X, e.Y)
If Not itm Is Nothing Then
MessageBox.Show(itm.Text)
End If
itm = Nothing
End Sub
i have this also.. but this doesnt go to other row item.. always selecting the first item.
Private Sub ListView1_MouseMove(ByVal Button As Integer, ByVal Shift As Integer, ByVal x As stdole.OLE_XPOS_PIXELS, ByVal y As stdole.OLE_YPOS_PIXELS)
Dim itm As ListItem
Me.ListView1.MultiSelect = False
Set itm = Me.ListView1.HitTest(x, y)
If Not itm Is Nothing Then
itm.Selected = True
End If
End Sub
As I said in my comment, it is a matter of conversion between what Excel unit 'offers' (pixels) and what a list view needs (twips). The working solution will be the next:
Please, copy the next API functions on top of the form code module (in the declarations area):
Option Explicit
Private Declare PtrSafe Function GetDC Lib "user32" _
(ByVal hwnd As Long) As Long
Private Declare PtrSafe Function GetDeviceCaps Lib "gdi32" _
(ByVal hDC As Long, ByVal nIndex As Long) As Long
Private Declare PtrSafe Function ReleaseDC Lib "user32" _
(ByVal hwnd As Long, ByVal hDC As Long) As Long
Use the next modified event:
Private Sub ListView1_MouseMove(ByVal Button As Integer, ByVal Shift As Integer, _
ByVal x As stdole.OLE_XPOS_PIXELS, ByVal y As stdole.OLE_YPOS_PIXELS)
Dim itm As MSComctlLib.ListItem
Me.ListView1.SelectedItem.Selected = False ' unselect a previous selected subitem
ConvertPixelsToTwips x, y 'make the necessary units conversion
Set itm = ListView1.HitTest(x, y) 'set the object using the converted coordinates
If Not itm Is Nothing Then
itm.Selected = True
End If
End Sub
Copy the next function, too:
Private Sub ConvertPixelsToTwips(ByRef x As stdole.OLE_XPOS_PIXELS, _
ByRef y As stdole.OLE_YPOS_PIXELS)
Dim hDC As Long, RetVal As Long, TwipsPerPixelX As Long, TwipsPerPixelY As Long
Const LOGPIXELSX = 88
Const LOGPIXELSY = 90
Const TWIPSPERINCH = 1440
hDC = GetDC(0)
TwipsPerPixelX = TWIPSPERINCH / GetDeviceCaps(hDC, LOGPIXELSX)
TwipsPerPixelY = TWIPSPERINCH / GetDeviceCaps(hDC, LOGPIXELSY)
RetVal = ReleaseDC(0, hDC)
x = x * TwipsPerPixelX: y = y * TwipsPerPixelY
End Sub
I am not the 'father' of the above function. I found the bases on the internet, some years before. I remember that I modified something, but I do not remember what...
Please, try the proposed solution and send some feedback.
Related
I’m trying to implement mousewheel scroll in a large text box. I found Peter Thornton’s code and it works well for frames and userforms (only using it for the former right now), but not for text box because text boxes don’t have a .ScrollTop property.
The code I use now isn’t actually a scroll-wheel function. Full code below, but the relevant portion is:
If TypeName(mControl) = "TextBox" Then
If reasonCustKeyPressed Then
lngSelStart = .SelStart
.CurLine = .CurLine
lngOldLinePos = lngSelStart - .SelStart
reasonCustKeyPressed = False
End If
If lParam.Hwnd > 0 Then
.CurLine = Application.Max(0, .CurLine - cTBOX_SCROLLCHANGE)
Else
.CurLine = Application.Min(.LineCount - 1, .CurLine + cTBOX_SCROLLCHANGE)
End If
lngSelStart = .SelStart
If .CurLine < .LineCount - 1 Then
.CurLine = .CurLine + 1
.SelStart = .SelStart - 1
Else
.SelStart = Len(.Text)
End If
lngNewLineLen = .SelStart - lngSelStart
.SelStart = Application.Min(lngSelStart + lngOldLinePos, lngSelStart + lngNewLineLen)
End If
Can anybody give any suggestions on how I could implement actual scroll wheel functionality? One idea I have is to find:
Whether the scroll bar was active (the content isn't always long enough to activate it - but don't know how, windows API?).
Storing .SelStart in a temporary variable
Find the top / bottom line somehow (I can't find any property of the textbox like this in documentation)
Increase the bottom line / decrease the top line (as appropriate) by setting .CurLine
Resetting .SelStart to the temporary variable (or top / bottom line, if the line stored in the temporary variable is no longer visible).
This isn't ideal either, however, because it doesn't preserve the previous cursor position if you scroll too far. I might be able to work around it by storing the .SelStart variable in the module's state and jumping back to it on the KeyDown event. There are some really big gaps, however, that I don't really have a good idea of how to fill. Any ideas (for this or other, more elegant solutions)? Thank you in advance.
Full code:
Option Explicit
' Based on code from Peter Thornton here:
' http://social.msdn.microsoft.com/Forums/en-US/7d584120-a929-4e7c-9ec2-9998ac639bea/mouse-scroll-in-userform-listbox-in-excel-2010?forum=isvvba
Private Type POINTAPI
X As Long
y As Long
End Type
Private Type MOUSEHOOKSTRUCT
pt As POINTAPI
Hwnd As Long
wHitTestCode As Long
dwExtraInfo As Long
End Type
Private Declare Function FindWindow Lib "user32" _
Alias "FindWindowA" ( _
ByVal lpClassName As String, _
ByVal lpWindowName As String) As Long
Private Declare Function GetWindowLong Lib "user32.dll" _
Alias "GetWindowLongA" ( _
ByVal Hwnd As Long, _
ByVal nIndex As Long) As Long
Private Declare Function SetWindowsHookEx Lib "user32" _
Alias "SetWindowsHookExA" ( _
ByVal idHook As Long, _
ByVal lpfn As Long, _
ByVal hmod As Long, _
ByVal dwThreadId As Long) As Long
Private Declare Function CallNextHookEx Lib "user32" ( _
ByVal hHook As Long, _
ByVal nCode As Long, _
ByVal wParam As Long, _
lParam As Any) As Long
Private Declare Function UnhookWindowsHookEx Lib "user32" ( _
ByVal hHook As Long) As Long
Private Declare Function PostMessage Lib "user32.dll" _
Alias "PostMessageA" ( _
ByVal Hwnd As Long, _
ByVal wMsg As Long, _
ByVal wParam As Long, _
ByVal lParam As Long) As Long
Private Declare Function WindowFromPoint Lib "user32" ( _
ByVal xPoint As Long, _
ByVal yPoint As Long) As Long
Private Declare Function GetCursorPos Lib "user32.dll" ( _
ByRef lpPoint As POINTAPI) As Long
Declare Function GetActiveWindow Lib "user32" () As Long
Private Const WH_MOUSE_LL As Long = 14
Private Const WM_MOUSEWHEEL As Long = &H20A
Private Const HC_ACTION As Long = 0
Private Const GWL_HINSTANCE As Long = (-6)
Private Const WM_KEYDOWN As Long = &H100
Private Const WM_KEYUP As Long = &H101
Private Const VK_UP As Long = &H26
Private Const VK_DOWN As Long = &H28
Private Const WM_LBUTTONDOWN As Long = &H201
Private Const cFRAME_SCROLLCHANGE As Long = 20
Private Const cTBOX_SCROLLCHANGE As Long = 1
Private mLngMouseHook As Long
Private mControlHwnd As Long
Private mbHook As Boolean
Private lngOldLinePos As Long
Dim mControl As Object
Sub HookFormScroll(oControl As Object, strFormCapt As String)
Dim lngAppInst As Long
Dim hwndUnderCursor As Long
Set mControl = oControl
hwndUnderCursor = FindWindow("ThunderDFrame", strFormCapt)
Debug.Print "Form window: " & hwndUnderCursor
If mControlHwnd <> hwndUnderCursor Then
UnhookFormScroll
Debug.Print "Unhook old proc"
mControlHwnd = hwndUnderCursor
lngAppInst = GetWindowLong(mControlHwnd, GWL_HINSTANCE)
If Not mbHook Then
mLngMouseHook = SetWindowsHookEx(WH_MOUSE_LL, AddressOf mouseProc, lngAppInst, 0)
mbHook = mLngMouseHook <> 0
If mbHook Then Debug.Print "Form hooked"
End If
End If
End Sub
Sub UnhookFormScroll()
If mbHook Then
UnhookWindowsHookEx mLngMouseHook
mLngMouseHook = 0
mControlHwnd = 0
mbHook = False
End If
End Sub
Private Function mouseProc( _
ByVal nCode As Long, ByVal wParam As Long, _
ByRef lParam As MOUSEHOOKSTRUCT) As Long
Dim lngSelStart As Long, lngNewLineLen As Long
On Error GoTo errH 'Resume Next
If (nCode = HC_ACTION) Then
If GetActiveWindow = mControlHwnd Then
If wParam = WM_MOUSEWHEEL Then
mouseProc = True
With mControl
If TypeName(mControl) = "Frame" Then
If lParam.Hwnd > 0 Then
.ScrollTop = Application.Max(0, .ScrollTop - cFRAME_SCROLLCHANGE)
Else
.ScrollTop = Application.Min(.ScrollHeight - .InsideHeight, .ScrollTop + cFRAME_SCROLLCHANGE)
End If
Else
If TypeName(mControl) = "TextBox" Then
If reasonCustKeyPressed Then
lngSelStart = .SelStart
.CurLine = .CurLine
lngOldLinePos = lngSelStart - .SelStart
reasonCustKeyPressed = False
End If
If lParam.Hwnd > 0 Then
.CurLine = Application.Max(0, .CurLine - cTBOX_SCROLLCHANGE)
Else
.CurLine = Application.Min(.LineCount - 1, .CurLine + cTBOX_SCROLLCHANGE)
End If
lngSelStart = .SelStart
If .CurLine < .LineCount - 1 Then
.CurLine = .CurLine + 1
.SelStart = .SelStart - 1
Else
.SelStart = Len(.Text)
End If
lngNewLineLen = .SelStart - lngSelStart
.SelStart = Application.Min(lngSelStart + lngOldLinePos, lngSelStart + lngNewLineLen)
End If
End If
End With
Exit Function
End If
End If
End If
mouseProc = CallNextHookEx( _
mLngMouseHook, nCode, wParam, ByVal lParam)
Exit Function
errH:
UnhookFormScroll
End Function
Daniel Pineault's code worked fine for me. It's one module and a few lines of code to put on your form.
I am looking for codes that would screenshot display in my screen (not the whole screen). I got already a program with the help of google but unfortunately, the program is just pasting the screenshot in excel. How could I paste it directly to new email in Outlook? Thanks. Btw here's the code that I got.
Option Explicit
Private Const CCHDEVICENAME = 32
Private Const CCHFORMNAME = 32
Private Const SRCCOPY = &HCC0020 ' (DWORD) destination = source
Private Type DEVMODE
dmDeviceName As String * CCHDEVICENAME
dmSpecVersion As Integer
dmDriverVersion As Integer
dmSize As Integer
dmDriverExtra As Integer
dmFields As Long
dmOrientation As Integer
dmPaperSize As Integer
dmPaperLength As Integer
dmPaperWidth As Integer
dmScale As Integer
dmCopies As Integer
dmDefaultSource As Integer
dmPrintQuality As Integer
dmColor As Integer
dmDuplex As Integer
dmYResolution As Integer
dmTTOption As Integer
dmCollate As Integer
dmFormName As String * CCHFORMNAME
dmUnusedPadding As Integer
dmBitsPerPel As Long
dmPelsWidth As Long
dmPelsHeight As Long
dmDisplayFlags As Long
dmDisplayFrequency As Long
End Type
Declare Function ReleaseDC Lib "user32.dll" (ByVal hWnd As Long, ByVal hdc As Long) As Long
Declare Function OpenClipboard Lib "user32.dll" (ByVal hWnd As Long) As Long
Declare Function EmptyClipboard Lib "user32.dll" () As Long
Declare Function SetClipboardData Lib "user32.dll" (ByVal wFormat As Long, ByVal hMem As Long) As Long
Declare Function CloseClipboard Lib "user32.dll" () As Long
Declare Function SelectObject Lib "gdi32.dll" (ByVal hdc As Long, ByVal hObject As Long) As Long
Declare Function DeleteDC Lib "gdi32.dll" (ByVal hdc As Long) As Long
Declare Function BitBlt Lib "gdi32.dll" (ByVal hDestDC As Long, ByVal x As Long, ByVal y As Long, ByVal nWidth As Long, ByVal nHeight As Long, ByVal hSrcDC As Long, ByVal xSrc As Long, ByVal ySrc As Long, ByVal dwRop As Long) As Long
Declare Function CreateDC Lib "gdi32.dll" Alias "CreateDCA" (ByVal lpDriverName As String, ByVal lpDeviceName As String, ByVal lpOutput As String, lpInitData As DEVMODE) As Long
Declare Function CreateCompatibleBitmap Lib "gdi32.dll" (ByVal hdc As Long, ByVal nWidth As Long, ByVal nHeight As Long) As Long
Declare Function CreateCompatibleDC Lib "gdi32.dll" (ByVal hdc As Long) As Long
Declare Function CountClipboardFormats Lib "user32" () As Long
Declare Function IsClipboardFormatAvailable Lib "user32" (ByVal wFormat As Long) As Long
Declare Function CreateIC Lib "GDI32" Alias "CreateICA" (ByVal lpDriverName As String, ByVal lpDeviceName As String, ByVal lpOutput As String, lpInitData As Long) As Long
Declare Function GetDeviceCaps Lib "GDI32" (ByVal hdc As Long, ByVal nIndex As Long) As Long
Declare Function EnumClipboardFormats Lib "user32" (ByVal wFormat As Long) As Long
Sub GetPrintScreen()
Call CaptureScreen(35, 200, 975, 445)
End Sub
I think this is the part that I should edit.
Public Sub ScreenToGIF_NewWorkbook()
Dim wbDest As Workbook, wsDest As Worksheet
Dim FromType As String, PicHigh As Single
Dim PicWide As Single, PicWideInch As Single
Dim PicHighInch As Single, DPI As Long
Dim PixelsWide As Integer, PixelsHigh As Integer
Call TOGGLEEVENTS(False)
Call GetPrintScreen
If CountClipboardFormats = 0 Then
MsgBox "Clipboard is currently empty.", vbExclamation, "Nothing to Paste"
GoTo EndOfSub
End If
'Determine the format of the current clipboard contents. There may be multiple
'formats available but the Paste methods below will always (?) give priority
'to enhanced metafile (picture) if available so look for that first.
If IsClipboardFormatAvailable(14) <> 0 Then
FromType = "pic"
ElseIf IsClipboardFormatAvailable(2) <> 0 Then
FromType = "bmp"
Else
MsgBox "Clipboard does not contain a picture or bitmap to paste.", _
vbExclamation, "No Picture"
Exit Sub
End If
Application.StatusBar = "Pasting from clipboard ..."
Set wbDest = Workbooks.Add(xlWBATWorksheet)
Set wsDest = wbDest.Sheets(1)
wbDest.Activate
wsDest.Activate
wsDest.Range("B3").Activate
'Paste a picture/bitmap from the clipboard (if possible) and select it.
'The clipboard may contain both text and picture/bitmap format items. If so,
'using just ActiveSheet.Paste will paste the text. Using Pictures.Paste will
'paste a picture if a picture/bitmap format is available, and the Typename
'will return "Picture" (or perhaps "OLEObject"). If *only* text is available,
'Pictures.Paste will create a new TextBox (not a picture) on the sheet and
'the Typename will return "TextBox". (This condition now checked above.)
On Error Resume Next 'just in case
wsDest.Pictures.Paste.Select
On Error GoTo 0
'If the pasted item is an "OLEObject" then must convert to a bitmap
'to get the correct size, including the added border and matting.
'Do this via a CopyPicture-Bitmap and then a second Pictures.Paste.
If TypeName(Selection) = "OLEObject" Then
With Selection
.CopyPicture Appearance:=xlScreen, Format:=xlBitmap
.Delete
ActiveSheet.Pictures.Paste.Select
'Modify the FromType (used below in the suggested file name)
'to signal that the original clipboard image is not being used.
FromType = "ole object"
End With
End If
'Make sure that what was pasted and selected is as expected.
'Note this is the Excel TypeName, not the clipboard format.
If TypeName(Selection) = "Picture" Then
With Selection
PicWide = .Width
PicHigh = .Height
.Delete
End With
Else
'Can get to here if a chart is selected and "Copy"ed instead of "Copy Picture"ed.
'Otherwise, ???.
If TypeName(Selection) = "ChartObject" Then
MsgBox "Use Shift > Edit > Copy Picture on charts, not just Copy.", _
vbExclamation, "Got a Chart Copy, not a Chart Picture"
Else
MsgBox "Excel pasted a '" & TypeName(Selection) & "' instead of a Picture.", _
vbExclamation, "Not a Picture"
End If
'Clean up and quit.
ActiveWorkbook.Close SaveChanges:=False
GoTo EndOfSub
End If
'Add an empty embedded chart, sized as above, and activate it.
'Positioned at cell B3 just for convenient debugging and final viewing.
'Tip from Jon Peltier: Just add the embedded chart directly, don't use the
'macro recorder method of adding a new separate chart sheet and then relocating
'the chart back to a worksheet.
With Sheets(1)
.ChartObjects.Add(.Range("B3").Left, .Range("B3").Top, PicWide, PicHigh).Activate
End With
'Paste the [resized] bitmap into the ChartArea, which creates ActiveChart.Shapes(1).
On Error Resume Next
ActiveChart.Pictures.Paste.Select
On Error GoTo 0
If TypeName(Selection) = "Picture" Then
With ActiveChart
'Adjust the position of the pasted picture, aka ActiveChart.Shapes(1).
'Adjustment is slightly greater than the .ChartArea.Left/Top offset, why ???
'''' .Shapes(1).IncrementLeft -1
'''' .Shapes(1).IncrementTop -4
'Remove chart border. This must be done *after* all positioning and sizing.
' .ChartArea.Border.LineStyle = 0
End With
'Show pixel size info above the picture-in-chart-soon-to-be-GIF/JPEG/PNG.
PicWideInch = PicWide / 72 'points to inches ("logical", not necessarily physical)
PicHighInch = PicHigh / 72
DPI = PixelsPerInch() 'typically 96 or 120 dpi for displays
PixelsWide = PicWideInch * DPI
PixelsHigh = PicHighInch * DPI
Else
'Something other than a Picture was pasted into the chart.
'This is very unlikely.
MsgBox "Clipboard corrupted, possibly by another task."
End If
EndOfSub:
Call TOGGLEEVENTS(True)
End Sub
Public Sub TOGGLEEVENTS(blnState As Boolean)
'Originally written by Zack Barresse
With Application
.DisplayAlerts = blnState
.EnableEvents = blnState
.ScreenUpdating = blnState
If blnState Then .CutCopyMode = False
If blnState Then .StatusBar = False
End With
End Sub
Public Function PixelsPerInch() As Long
Application.DefaultWebOptions.PixelsPerInch.
Dim hdc As Long
hdc = CreateIC("DISPLAY", vbNullString, vbNullString, 0)
PixelsPerInch = GetDeviceCaps(hdc, 88) 'LOGPIXELSX = 88 = Logical pixels/inch in X
DeleteDC (hdc)
End Function
Public Sub CaptureScreen(Left As Long, Top As Long, Width As Long, Height As Long)
Dim srcDC As Long, trgDC As Long, BMPHandle As Long, dm As DEVMODE
srcDC = CreateDC("DISPLAY", "", "", dm)
trgDC = CreateCompatibleDC(srcDC)
BMPHandle = CreateCompatibleBitmap(srcDC, Width, Height)
SelectObject trgDC, BMPHandle
BitBlt trgDC, 0, 0, Width, Height, srcDC, Left, Top, SRCCOPY
OpenClipboard 0&
EmptyClipboard
SetClipboardData 2, BMPHandle
CloseClipboard
DeleteDC trgDC
ReleaseDC BMPHandle, srcDC
End Sub
I have a code module where I create a text box with pressing a specific key and reduce the current selected cell value by the amount inserted in the text box. I came to the point where I created the text box. Now I need to access the events of the text box outside the Worksheet modules. I found out that I could create a class module with the WihtEvents property. Unfortunate this does not seem to work. Here the code which is executed to make the control:
Dim objControl As BankingEventSink
Private Sub ReduceCell()
If IsNumeric(ActiveCell.Text) Then
Dim value As Double
value = CDbl(ActiveCell.Text)
ActiveSheet.Shapes.AddOLEObject(ClassType:="Forms.TextBox.1").Name = "ReduceCellTextBox"
With ActiveSheet.OLEObjects("ReduceCellTextBox")
.Top = ActiveCell.Top + ActiveCell.Height
.Left = ActiveCell.Left
End With
ActiveSheet.OLEObjects("ReduceCellTextBox").Activate
Set objControl = New BankingEventSink
objControl.Init (ActiveSheet.OLEObjects("ReduceCellTextBox").Object)
Else
RethrowKeys ("{BS}{-}")
End If
End Sub
The code of the class module:
Dim WithEvents objOLEControl As MSForms.TextBox
Public Sub Init(oleControl As MSForms.TextBox)
Set objOLEControl = oleControl
End Sub
Private Sub ReduceCellTextBox_Change()
MsgBox "Changed"
End Sub
Private Sub ReduceCellTextBox_KeyDown(ByVal KeyCode As MSForms.ReturnInteger, _
ByVal Shift As Integer)
MsgBox "Key down: " & KeyCode
End Sub
What ever I write in the text box no event is triggered. Where is the mistake?
To remove the title bar from a VBA userform, you need to use API's FindWindow, SetWindowLong, GetWindowLong and SetWindowPos. HERE is my one stop place for APIs
Create your userform and place a textbox in it. For example
Next paste this code in the userform.
Private Declare Function FindWindow Lib "user32" Alias "FindWindowA" ( _
ByVal lpClassName As String, ByVal lpWindowName As String) As Long
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 GetWindowLong Lib "user32" _
Alias "GetWindowLongA" (ByVal hwnd As Long, _
ByVal nIndex As Long) As Long
Private Declare Function SetWindowPos Lib "user32" ( _
ByVal hwnd As Long, ByVal hWndInsertAfter As Long, _
ByVal x As Long, ByVal y As Long, ByVal cx As Long, _
ByVal cy As Long, ByVal wFlags As Long) As Long
Private Const GWL_STYLE = (-16)
Private Const WS_CAPTION = &HC00000
Private Const WS_BORDER = &H800000
Private Enum ESetWindowPosStyles
SWP_SHOWWINDOW = &H40
SWP_HIDEWINDOW = &H80
SWP_FRAMECHANGED = &H20
SWP_NOACTIVATE = &H10
SWP_NOCOPYBITS = &H100
SWP_NOMOVE = &H2
SWP_NOOWNERZORDER = &H200
SWP_NOREDRAW = &H8
SWP_NOREPOSITION = SWP_NOOWNERZORDER
SWP_NOSIZE = &H1
SWP_NOZORDER = &H4
SWP_DRAWFRAME = SWP_FRAMECHANGED
HWND_NOTOPMOST = -2
End Enum
Private Type RECT
Left As Long
Top As Long
Right As Long
Bottom As Long
End Type
Dim FrmWndh As Long, lStyle As Long
Dim tR As RECT
Private Sub UserForm_Activate()
FrmWndh = FindWindow(vbNullString, Me.Caption)
lStyle = GetWindowLong(FrmWndh, GWL_STYLE)
lStyle = lStyle And Not WS_CAPTION
SetWindowLong FrmWndh, GWL_STYLE, lStyle
SetWindowPos FrmWndh, 0, tR.Left, tR.Top, _
tR.Right - tR.Left, tR.Bottom - tR.Top, _
SWP_NOREPOSITION Or SWP_NOZORDER Or SWP_FRAMECHANGED Or WS_BORDER
Me.Repaint
End Sub
Private Sub TextBox1_KeyDown(ByVal KeyCode As MSForms.ReturnInteger, ByVal Shift As Integer)
If KeyCode = 27 Then Unload Me
End Sub
When you now run the userform, it will look like this. Since we have removed the userform's title bar, I have added a code so that when you press ESC from the textbox, the userform will unload. You can change that to whatever (reasonable) you like.
I am pretty new to the WinApi calls although familiar with VBA. What I am trying to do is to hook to a child class SysTreeView32 of VBE window (Project Explorer TreeView). I would like to expand/collapse the tree view elements by modifying the registry keys (or alternatively sending the mouse clicks (mouse_event) although I prefer the first option).
I can find the Excel Main Window by using this code:
Declare Function FindWindow Lib "user32" Alias "FindWindowA" _
(ByVal lpClassName As String, ByVal lpWindowName As String) As Long
Sub Find_Window()
Dim hWndExcel As Long
hWndExcel = FindWindow("XLMAIN", Application.Caption)
MsgBox hWndExcel
End Sub
With the help of Window Detective I can access the names, properties, etc. of the child classes.
But I cannot work it out how to access/activate(or even return the HWID of) the SysTreeView32 child class to collapse/expand elements(folders). I am not sure how to iterate over the elements yet, but I will research this afterwards. The problem here is accessing the SysTreeView32 class. How can I achieve it?
When I try to msgbox this
FindWindow("wndclass_desked_gsk", Application.Caption)
or
FindWindow("SysTreeView32", Application.Caption)
a 0 is returned so I am obviously doing something wrong :/
Thank you for your time.
you ought to be using:
application.vbe.mainwindow.caption
here's some sample collapse code
Private Const TVE_COLLAPSE = &H1
Private Const TVE_COLLAPSERESET = &H8000
Private Const TVE_EXPAND = &H2
Private Const TVE_EXPANDPARTIAL = &H4000
Private Const TVE_TOGGLE = &H3
Private Const TV_FIRST = &H1100
Private Const TVM_EXPAND = (TV_FIRST + 2)
Private Const TVM_GETNEXTITEM = (TV_FIRST + 10)
Private Const TVGN_ROOT = &H0
Private Const TVGN_NEXTVISIBLE = &H6
Declare Function FindWindowEx Lib "user32" Alias "FindWindowExA" _
(ByVal hWnd1 As Long, ByVal hWnd2 As Long, ByVal lpsz1 As String, ByVal lpsz2 As String) As Long
Public Declare Function SendMessage Lib "user32" Alias "SendMessageA" _
(ByVal hWnd As Long, ByVal wMsg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
Sub CollapseProjects()
Dim hWndVBE As Long, hWndPE As Long, hWndTvw As Long, hNode As Long, varReturn
hWndVBE = FindWindowEx(0, 0, "wndclass_desked_gsk", Application.VBE.MainWindow.Caption)
hWndPE = FindWindowEx(hWndVBE, 0, "PROJECT", vbNullString)
hWndTvw = FindWindowEx(hWndPE, 0, "SysTreeView32", vbNullString)
hNode = SendMessage(hWndTvw, TVM_GETNEXTITEM, TVGN_ROOT, 0&)
Do While hNode <> 0
varReturn = SendMessage(hWndTvw, TVM_EXPAND, TVE_COLLAPSE, hNode)
hNode = SendMessage(hWndTvw, TVM_GETNEXTITEM, TVGN_NEXTVISIBLE, hNode)
Loop
End Sub
further to your comment, here's the code to collapse only the 'Microsoft Excel Objects' nodes
Declare Function FindWindowEx Lib "user32" Alias "FindWindowExA" _
(ByVal hWnd1 As Long, ByVal hWnd2 As Long, ByVal lpsz1 As String, ByVal lpsz2 As String) As Long
Public Declare Function SendMessage Lib "user32" Alias "SendMessageA" _
(ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
Public Declare Function SendMessageB Lib "user32" Alias "SendMessageA" _
(ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, ByRef lParam As Any) As Long
Private Const MAX_ITEM As Long = 256
Private Const TV_FIRST As Long = &H1100
Private Const TVM_EXPAND As Long = (TV_FIRST + 2)
Private Const TVM_GETNEXTITEM As Long = (TV_FIRST + 10)
Private Const TVM_GETITEM As Long = (TV_FIRST + 12)
Const TVE_COLLAPSE As Long = &H1
Const TVE_EXPAND As Long = &H2
Private Const TVGN_ROOT As Long = &H0
Private Const TVGN_NEXT As Long = &H1
Private Const TVIF_TEXT As Long = &H1
Private Const TVGN_NEXTVISIBLE = &H6
Private Type TVITEM ' was TV_ITEM
mask As Long
hItem As Long
state As Long
stateMask As Long
pszText As String
cchTextMax As Long
iImage As Long
iSelectedImage As Long
cChildren As Long
lParam As Long
End Type
Sub CollapseXLObjects()
Dim hWndVBE As Long
Dim hWndPE As Long
Dim hWndTvw As Long
Dim hNode As Long
Dim tvi As TVITEM
Dim nChild As Long
Dim sText As String
Dim varReturn
hWndVBE = FindWindowEx(0, 0, "wndclass_desked_gsk", Application.VBE.MainWindow.Caption)
hWndPE = FindWindowEx(hWndVBE, 0, "PROJECT", vbNullString)
hWndTvw = FindWindowEx(hWndPE, 0, "SysTreeView32", vbNullString)
hNode = SendMessage(hWndTvw, TVM_GETNEXTITEM, TVGN_ROOT, 0&)
Do While hNode <> 0
tvi.hItem = hNode
tvi.mask = TVIF_TEXT
tvi.cchTextMax = MAX_ITEM
tvi.pszText = String(MAX_ITEM, 0)
nChild = SendMessageB(hWndTvw, TVM_GETITEM, 0&, tvi)
If InStr(1, tvi.pszText, "Microsoft Excel Objects", vbTextCompare) > 0 Then
varReturn = SendMessage(hWndTvw, TVM_EXPAND, TVE_COLLAPSE, hNode)
Else
varReturn = SendMessage(hWndTvw, TVM_EXPAND, TVE_EXPAND, hNode)
End If
hNode = SendMessage(hWndTvw, TVM_GETNEXTITEM, TVGN_NEXTVISIBLE, hNode)
Loop
End Sub
If calling this sub from another sub (in my case a sub in an XL addin linked to a custom button which opens the VBE), and another module is highlighted (which might be in another project) then I've found the
Application.VBE.MainWindow.Caption
doesn't work. To capture the highlighted module I use:
Private Sub VisualBasicEditor()
On Error Resume Next
WinName = "Microsoft Visual Basic - " + ActiveWorkbook.Name + " [Running] - [" + Application.VBE.ActiveCodePane.CodeModule.Name + " (Code)]"
Application.VBE.MainWindow.Visible = True
Call CollapseXLObjects
End Sub
and in the module containing Sub CollapseXLObjects declare
Public WinName As String
and in Sub CollapseXLObjects
hWndVBE = FindWindowEx(0, 0, "wndclass_desked_gsk", WinName)
I need to create a userform for my excel vba program., But how can I add "minimize" button for this userform? Thanks.
You can try this code :
Private Sub ToggleButton1_Click()
If ToggleButton1.Value = True Then
Me.Height = Me.Height * 0.25
Else
Me.Height = dHeight
End If
End Sub
Private Sub UserForm_Initialize()
dHeight = Me.Height
End Sub
[EDIT]
Found this link where they explain how to do it with an API to minimize.
Here is the given code :
Option Explicit
Private Declare Function FindWindow Lib "user32" Alias "FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName As String) As Long
Private Declare Function GetWindowLong Lib "user32" Alias "GetWindowLongA" (ByVal hWnd As Long, ByVal nIndex As Long) As Long
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 DrawMenuBar Lib "user32" (ByVal hWnd As Long) As Long
Private Declare Function ShowWindow Lib "user32" (ByVal hWnd As Long, ByVal nCmdShow As Long) As Long
Private Const GWL_STYLE As Long = (-16) 'Sets a new window style
Private Const WS_SYSMENU As Long = &H80000 'Windows style
Private Const WS_MINIMIZEBOX As Long = &H20000
Private Const WS_MAXIMIZEBOX As Long = &H10000
Private Const SW_SHOWMAXIMIZED = 3
Private Sub UserForm_Activate()
Dim lFormHandle As Long, lStyle As Long
'===========================================
'= Originally from Dax =
'= Modified with comments by Ivan F Moala =
'= 22/07/01 =
'===========================================
'Lets find the UserForm Handle the function below retrieves the handle
'to the top-level window whose class name ("ThunderDFrame" for Excel)
'and window name (me.caption or UserformName caption) match the specified strings.
lFormHandle = FindWindow("ThunderDFrame", Me.Caption)
'The GetWindowLong function retrieves information about the specified window.
'The function also retrieves the 32-bit (long) value at the specified offset
'into the extra window memory of a window.
lStyle = GetWindowLong(lFormHandle, GWL_STYLE)
'lStyle is the New window style so lets set it up with the following
lStyle = lStyle Or WS_SYSMENU 'SystemMenu
lStyle = lStyle Or WS_MINIMIZEBOX 'With MinimizeBox
lStyle = lStyle Or WS_MAXIMIZEBOX 'and MaximizeBox
'Now lets set up our New window the SetWindowLong function changes
'the attributes of the specified window , given as lFormHandle,
'GWL_STYLE = New windows style, and our Newly defined style = lStyle
SetWindowLong lFormHandle, GWL_STYLE, (lStyle)
'Remove >'< if you want to show form Maximised
'ShowWindow lFormHandle, SW_SHOWMAXIMIZED 'Shows Form Maximized
'The DrawMenuBar function redraws the menu bar of the specified window.
'We need this as we have changed the menu bar after Windows has created it.
'All we need is the Handle.
DrawMenuBar lFormHandle
End Sub
Regards,
Max
'Place this code in a Module
Private Declare Function FindWindowA Lib "USER32" _
(ByVal lpClassName As String, _
ByVal lpWindowName As String) As Long
Private Declare Function GetWindowLongA Lib "USER32" _
(ByVal hWnd As Long, _
ByVal nIndex As Long) As Long
Private Declare Function SetWindowLongA Lib "USER32" _
(ByVal hWnd As Long, _
ByVal nIndex As Long, _
ByVal dwNewLong As Long) As Long
Option Explicit
Sub FormatUserForm(UserFormCaption As String)
Dim hWnd As Long
Dim exLong As Long
hWnd = FindWindowA(vbNullString, UserFormCaption)
exLong = GetWindowLongA(hWnd, -16)
If (exLong And &H20000) = 0 Then
SetWindowLongA hWnd, -16, exLong Or &H20000
Else
End If
End Sub
Sub ShowForm()
UserForm1.Show
End Sub
'Place this code in a UserForm with one Command Button named CommandButton1.
Option Explicit
Private Sub CommandButton1_Click()
Unload Me
End Sub
Private Sub UserForm_Initialize()
Call FormatUserForm(Me.Caption)
End Sub