Перетаскивание файлов пользовательских форм Excel

Я хотел бы реализовать объект Drag&Drop в пользовательской форме в Excel 2016. Цель состоит в том, чтобы разрешить перетаскивание файлов (из проводника Windows) в пользовательскую форму Excel и поймать событие перетаскивания для извлечения пути к файлу(ам) и имена.

До сих пор я обнаружил, что это достижимо с помощью очень старого элемента управления, который в последние годы больше не предоставляется Microsoft — элемента управления Treeview. Этот элемент управления идеально подходит для моих нужд, однако требует специальной регистрации старого OCX и файла TLB, которые не распространены на машинах времени выполнения стандартных пользователей, не являются обычными (и рабочими) их инструментами регистрации, такими как Regtlibv12 / Regtlib в новой Windows 10 с 64-разрядной версией Office 2016.

Интересно, возможно ли, что Microsoft не предлагает никаких средств управления этим в последние годы? Вы знаете, достижимо ли это со стандартными 64-разрядными предложениями Windows 10 и Office 2016?


person Mor Sagmon    schedule 09.07.2017    source источник
comment
Помогает ли вам что-то вроде это?   -  person Plagon    schedule 09.07.2017
comment
Нет, это не имеет ничего общего с Drag & Drop. Это стандартный вызов файлового диалога.   -  person Mor Sagmon    schedule 10.07.2017


Ответы (1)


Вы можете сделать это, подключив пользовательскую форму и используя Windows API, я адаптировал некоторый код из Здесь

Обратите внимание на это авторское право:

Этот код изначально был написан Девом Ашишем. «Это не должно быть изменено или распространено, кроме как как часть приложения. «Вы можете использовать его в любом приложении» при условии, что уведомление об авторских правах останется без изменений. '' Код любезно предоставлен Девом Ашишем

(Хотя я кое-что адаптировал) В пользовательской форме поместите этот код:

Private Declare Function FindWindow Lib "user32" Alias "FindWindowA" (ByVal 
lpClassName As String, ByVal lpWindowName As String) As Long

Function hWnd() As Long
Dim hWndThis As Long
If Val(Application.Version) > 8 Then
    hWndThis = FindWindow(lpClassName:="ThunderDFrame", lpWindowName:=Me.Caption)
Else
    hWndThis = FindWindow(lpClassName:="ThunderXFrame", lpWindowName:=Me.Caption)
End If
hWnd = hWndThis
End Function

Private Sub UserForm_Initialize()
Call sEnableDrop(Me, hWnd)
Call sHook(hWnd)
End Sub

Private Declare Function apiCallWindowProc 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 apiSetWindowLong Lib "user32" _
Alias "SetWindowLongA" _
(ByVal hWnd As Long, _
ByVal nIndex As Long, _
ByVal wNewWord As Long) _
As Long

Private Declare Function apiGetWindowLong Lib "user32" _
Alias "GetWindowLongA" _
(ByVal hWnd As Long, _
ByVal nIndex As Long) _
As Long

Private Declare Sub sapiDragAcceptFiles Lib "shell32.dll" _
Alias "DragAcceptFiles" _
(ByVal hWnd As Long, _
ByVal fAccept As Long)

Private Declare Sub sapiDragFinish Lib "shell32.dll" _
Alias "DragFinish" _
(ByVal hDrop As Long)

Private Declare Function apiDragQueryFile Lib "shell32.dll" _
Alias "DragQueryFileA" _
(ByVal hDrop As Long, _
ByVal iFile As Long, _
ByVal lpszFile As String, _
ByVal cch As Long) _
As Long

Private lpPrevWndProc  As Long
Private Const GWL_WNDPROC  As Long = (-4)
Private Const GWL_EXSTYLE = (-20)
Private Const WM_DROPFILES = &H233
Private Const WS_EX_ACCEPTFILES = &H10&
Private hWnd_Frm As Long

Sub sDragDrop(ByVal hWnd As Long, _
                        ByVal Msg As Long, _
                        ByVal wParam As Long, _
                        ByVal lParam As Long)

Dim lngRet As Long, strTmp As String, intLen As Integer
Dim lngCount As Long, i As Long, strOut As String
Const cMAX_SIZE = 50
On Error Resume Next
If Msg = WM_DROPFILES Then
    strTmp = String$(255, 0)
    lngCount = apiDragQueryFile(wParam, &HFFFFFFFF, strTmp, Len(strTmp))
    For i = 0 To lngCount - 1
        strTmp = String$(cMAX_SIZE, 0)
        intLen = apiDragQueryFile(wParam, i, strTmp, cMAX_SIZE)
        strOut = strOut & Left$(strTmp, intLen) & ";"
    Next i
    strOut = Left$(strOut, Len(strOut) - 1)
    Call sapiDragFinish(wParam)
    MsgBox strOut

Else
    lngRet = apiCallWindowProc( _
                        ByVal lpPrevWndProc, _
                        ByVal hWnd, _
                        ByVal Msg, _
                        ByVal wParam, _
                        ByVal lParam)
End If
End Sub

Sub sEnableDrop(frm As UserForm, hWnd As Long)
Dim lngStyle As Long, lngRet As Long
lngStyle = apiGetWindowLong(hWnd, GWL_EXSTYLE)
lngStyle = lngStyle Or WS_EX_ACCEPTFILES
lngRet = apiSetWindowLong(hWnd, GWL_EXSTYLE, lngStyle)
Call sapiDragAcceptFiles(hWnd, True)
hWnd_Frm = hWnd
End Sub


Sub sHook(hWnd As Long)
lpPrevWndProc = apiSetWindowLong(hWnd, GWL_WNDPROC, AddressOf sDragDrop)
End Sub

Sub sUnhook(hWnd As Long)
Dim lngTmp As Long
lngTmp = apiSetWindowLong(hWnd, GWL_WNDPROC, lpPrevWndProc)
lpPrevWndProc = 0
End Sub

Функция sDragDrop помещает список файлов в окно сообщений, но вы можете установить переменную для его хранения.

Конечно, поскольку это зацепление за окно, это риск для стабильности!

person ainwood    schedule 09.07.2017
comment
Спасибо @ainwood! Приятно знать, что есть способ, хотя и неуклюжий, который проходит через перехват окна и отсутствие тривиального элемента управления в Excel, который просто поддерживает событие Drop. Можно было бы подумать, что это приемлемое ожидание в наши дни... Другое дело, что это делает всю форму сбрасываемой. Мне нужна определенная область, которую я также могу отформатировать (например, в файлах перетаскивания сюда текст с видимой рамкой и цветом заливки). - person Mor Sagmon; 10.07.2017
comment
Теоретически..... Вы должны иметь возможность добавить элемент управления в форму (например, Frame(, использовать GetWindow(hWndParent, GW_CHILD), чтобы найти окно этого элемента управления, и перехватить его. Однако проблема в том, что если у вас есть более одного на форме, вам нужно знать, какой элемент управления есть какой. (скажем) 4-й hWnd в списке Во-вторых, сообщения все равно передаются в пользовательскую форму, даже если перехватывают дочерний элемент. - person ainwood; 11.07.2017
comment
Конечно, вы можете просто создать фрейм «Перетащите файлы сюда» и полагаться на тот факт, что перетаскивание все равно будет передано в форму, даже если пользователь пропустит перетаскивание в нужной области. Или у вас может быть фрейм с MouseMove, который устанавливает логическое значение Allow Drop, если мышь находится над этим фреймом, и имеет код выхода, если логическое значение не установлено. - person ainwood; 11.07.2017
comment
Спасибо! Вы правы, но мне всегда нравились простые, элегантные решения, и это заходит слишком далеко для достижения того, что должно быть стандартным событием в наши дни. Я предпочитаю оставаться с надежным приложением и разрешаю загружать файлы только через диалоговое окно «Файл» с помощью кнопки загрузки. - person Mor Sagmon; 12.07.2017