我正在尝试创建一个TShellListView的后代,它接受从Windows资源管理器中删除的文件。我想处理组件定义中的拖放操作,而不必在任何使用该组件的应用程序中实现它(我已经找到了从Windows资源管理器中删除文件的示例,但所有这些都在应用程序/ TForm级别)。
我在构造函数中调用DragAcceptFiles(),并为WM_DROPFILES定义了一个消息处理程序。但是,当我在示例项目中使用此组件并从Windows资源管理器中拖动文件时:
我看到“未接受”图标(带斜线的圆圈),而不是我可以删除文件的指示。
如果我确实尝试删除文件,我就听不到Beep()。
我认为我没有正确警告Windows我的控件想要接受拖动的文件这一事实。任何人都可以建议我缺少什么吗?
这是我的组件代码,删除了不感兴趣的位:
unit LJLShellListView;
interface
type
TLJLShellListView = class(TShellListView)
private
procedure WMDropFiles(var Msg: TWMDropFiles); message WM_DROPFILES;
published
constructor Create(AOwner: TComponent);
end;
implementation
uses ShellAPI;
constructor TLJLShellListView.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
DragAcceptFiles(self.Handle, True);
end;
procedure TLJLShellListView.WMDropFiles(var Msg: TWMDropFiles);
begin
Beep(); // I never hear this.
end;
end.
答案 0 :(得分:8)
问题代码中的DragAcceptFiles
调用需要ShellListView的窗口句柄。当代码访问wincontrol的句柄时,VCL检查窗口是否已创建,如果没有,则VCL调用CreateHandle
并继续创建窗口并返回其句柄。在这个阶段,问题中的代码成功注册了ShellListView的文件拖放窗口。但是有一个问题,控制还没有成为父级。当它将成为父级时,本机控件将被销毁,然后在其新父级中重新创建,当然将获得不同的句柄,使注册状态无效。
也可能出于各种其他原因重新创建控件。出于这个原因,最好将我们的代码放在重写CreateWnd
和DestroyWnd
方法中,只要句柄发生变化或窗口即将被销毁,我们的代码就会运行。
答案 1 :(得分:3)
正如@SertacAkyuz所说,你的问题的解决方案是覆盖CreateWnd()
方法来调用DragAcceptFiles()
,而不是在构造函数中这样做。我只想提一下,在Windows Vista及更高版本中,用户界面权限隔离(UIPI)生效,因此如果您的应用程序在UAC下以高架状态运行,那么您还需要调用ChangeWindowMessageFilter()
以允许{{ 1}}($ 0049)和WM_COPYGLOBALDATA
消息将从较低权限的进程(如Windows资源管理器)传递到您的应用程序,否则拖放操作将无法正常工作。
WM_DROPFILES
已被弃用了很长时间,支持更新的IDropTarget
界面,这种界面更强大,更灵活。新代码应使用WM_DROPFILES
代替IDropTarget
。有关更多详细信息,请参阅MSDN:
Transferring Shell Objects with Drag-and-Drop and the Clipboard
WM_DROPFILES
提供IDropTarget
所提供的灵活功能之一是能够使用单个WM_DROPFILES
对象接受拖放,而不仅仅是特定IDropTarget
1}},也可以在应用程序的.exe文件本身上,以及在Shell弹出菜单中使用它,甚至允许其他应用程序直接将数据传递到您的应用程序,而无需使用窗口消息或其他IPC机制。