Delphi拖拽能否“提升”为对接?

2024-03-11

我有一个TPageControl其页面都是使用附加的各种形式ManualDock()。用户应该能够通过拖动选项卡来重新排列选项卡,这已经可以使用了。然而,也应该可以取消停靠的表单。

现在我有以下代码:

procedure TMainForm.PageControlMouseDown(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
  if (Button = mbLeft) and (Shift * [ssShift, ssCtrl] = [])
    and PageControl.DockSite
  then begin
    PageControl.BeginDrag(False, 32);
  end;
end;

If either the Shift or the Ctrl key are held down, then a docking operation will be started, otherwise the tabs can be rearranged by dragging them.

不过,使用按键作为修饰符很尴尬。有没有办法在鼠标光标位于页面控件的选项卡区域之外时取消活动的拖动操作,并开始停靠子窗体?这是 Delphi 2009 的情况。


我现在有一个适合我的解决方案,所以我会回答自己 - 也许有人也有这个用途。

让我们从一个创建一个小示例应用程序开始TPageControl有 8 个停靠表单,带有允许运行时重新排序选项卡的代码。选项卡将实时移动,并且当取消拖动时,活动选项卡索引将恢复为其原始值:

unit uDragDockTest;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  ComCtrls;

type
  TForm1 = class(TForm)
    procedure FormCreate(Sender: TObject);
  private
    fPageControl: TPageControl;
    fPageControlOriginalPageIndex: integer;
    function GetPageControlTabIndex(APosition: TPoint): integer;
  public
    procedure PageControlDragDrop(Sender, Source: TObject; X, Y: Integer);
    procedure PageControlDragOver(Sender, Source: TObject; X, Y: Integer;
      AState: TDragState; var AAccept: Boolean);
    procedure PageControlEndDrag(Sender, Target: TObject; X, Y: Integer);
    procedure PageControlMouseDown(Sender: TObject; AButton: TMouseButton;
      AShift: TShiftState; X, Y: Integer);
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.FormCreate(Sender: TObject);
const
  FormColors: array[1..8] of TColor = (
    clRed, clGreen, clBlue, clYellow, clLime, clMaroon, clTeal, clAqua);
var
  i: integer;
  F: TForm;
begin
  fPageControlOriginalPageIndex := -1;

  fPageControl := TPageControl.Create(Self);
  fPageControl.Align := alClient;
  // set to False to enable tab reordering but disable form docking
  fPageControl.DockSite := True;
  fPageControl.Parent := Self;

  fPageControl.OnDragDrop := PageControlDragDrop;
  fPageControl.OnDragOver := PageControlDragOver;
  fPageControl.OnEndDrag := PageControlEndDrag;
  fPageControl.OnMouseDown := PageControlMouseDown;

  for i := Low(FormColors) to High(FormColors) do begin
    F := TForm.Create(Self);
    F.Caption := Format('Form %d', [i]);
    F.Color := FormColors[i];
    F.DragKind := dkDock;
    F.BorderStyle := bsSizeToolWin;
    F.FormStyle := fsStayOnTop;
    F.ManualDock(fPageControl);
    F.Show;
  end;
end;

const
  TCM_GETITEMRECT = $130A;

function TForm1.GetPageControlTabIndex(APosition: TPoint): integer;
var
  i: Integer;
  TabRect: TRect;
begin
  for i := 0 to fPageControl.PageCount - 1 do begin
    fPageControl.Perform(TCM_GETITEMRECT, i, LPARAM(@TabRect));
    if PtInRect(TabRect, APosition) then
      Exit(i);
  end;
  Result := -1;
end;

procedure TForm1.PageControlDragDrop(Sender, Source: TObject; X, Y: Integer);
var
  Index: integer;
begin
  if Sender = fPageControl then begin
    Index := GetPageControlTabIndex(Point(X, Y));
    if (Index <> -1) and (Index <> fPageControl.ActivePage.PageIndex) then
      fPageControl.ActivePage.PageIndex := Index;
  end;
end;

procedure TForm1.PageControlDragOver(Sender, Source: TObject; X, Y: Integer;
  AState: TDragState; var AAccept: Boolean);
var
  Index: integer;
begin
  AAccept := Sender = fPageControl;
  if AAccept then begin
    Index := GetPageControlTabIndex(Point(X, Y));
    if (Index <> -1) and (Index <> fPageControl.ActivePage.PageIndex) then
      fPageControl.ActivePage.PageIndex := Index;
  end;
end;

procedure TForm1.PageControlEndDrag(Sender, Target: TObject; X, Y: Integer);
begin
  // restore original index of active page if dragging was canceled
  if (Target <> fPageControl) and (fPageControlOriginalPageIndex > -1)
    and (fPageControlOriginalPageIndex < fPageControl.PageCount)
  then
    fPageControl.ActivePage.PageIndex := fPageControlOriginalPageIndex;
  fPageControlOriginalPageIndex := -1;
end;

procedure TForm1.PageControlMouseDown(Sender: TObject; AButton: TMouseButton;
  AShift: TShiftState; X, Y: Integer);
begin
  if (AButton = mbLeft)
    // undock single docked form or reorder multiple tabs
    and (fPageControl.DockSite or (fPageControl.PageCount > 1))
  then begin
    // save current active page index for restoring when dragging is canceled
    fPageControlOriginalPageIndex := fPageControl.ActivePageIndex;
    fPageControl.BeginDrag(False);
  end;
end;

end.

将其粘贴到编辑器中并运行它,所有必需的组件及其属性将在运行时创建和设置。

请注意,只有双击选项卡才能取消停靠表单。这也有点难看,无论距选项卡的距离如何,拖动光标都会显示直到释放鼠标左键。当鼠标位于页面控制选项卡区域之外且有几个像素边距时,如果自动取消拖动并取消停靠表单,效果会更好。

这可以通过创建自定义来实现DragObject in the OnStartDrag页面控件的处理程序。在这个对象中,鼠标被捕获,因此拖动时的所有鼠标消息都可以在其中处理。当鼠标光标位于选项卡影响矩形之外时,拖动将被取消,并且会启动活动页面控制表中表单的停靠操作:

type
  TConvertDragToDockHelper = class(TDragControlObjectEx)
  strict private
    fPageControl: TPageControl;
    fPageControlTabArea: TRect;
  protected
    procedure WndProc(var AMsg: TMessage); override;
  public
    constructor Create(AControl: TControl); override;
  end;

constructor TConvertDragToDockHelper.Create(AControl: TControl);
const
  MarginX = 32;
  MarginY = 12;
var
  Item0Rect, ItemLastRect: TRect;
begin
  inherited;
  fPageControl := AControl as TPageControl;
  if fPageControl.PageCount > 0 then begin
    // get rects of first and last tab
    fPageControl.Perform(TCM_GETITEMRECT, 0, LPARAM(@Item0Rect));
    fPageControl.Perform(TCM_GETITEMRECT, fPageControl.PageCount - 1,
      LPARAM(@ItemLastRect));
    // calculate rect valid for dragging (includes some margin around tabs)
    // when this area is left dragging will be canceled and docking will start
    fPageControlTabArea := Rect(
      Min(Item0Rect.Left, ItemLastRect.Left) - MarginX,
      Min(Item0Rect.Top, ItemLastRect.Top) - MarginY,
      Max(Item0Rect.Right, ItemLastRect.Right) + MarginX,
      Max(Item0Rect.Bottom, ItemLastRect.Bottom) + MarginY);
  end;
end;

procedure TConvertDragToDockHelper.WndProc(var AMsg: TMessage);
var
  MousePos: TPoint;
  CanUndock: boolean;
begin
  inherited;
  if AMsg.Msg = WM_MOUSEMOVE then begin
    MousePos := fPageControl.ScreenToClient(Mouse.CursorPos);
    // cancel dragging if outside of tab area with margins
    // optionally start undocking the docked form (can be canceled with [ESC])
    if not PtInRect(fPageControlTabArea, MousePos) then begin
      fPageControl.EndDrag(False);
      CanUndock := fPageControl.DockSite and (fPageControl.ActivePage <> nil)
        and (fPageControl.ActivePage.ControlCount > 0)
        and (fPageControl.ActivePage.Controls[0] is TForm)
        and (TForm(fPageControl.ActivePage.Controls[0]).DragKind = dkDock);
      if CanUndock then
        fPageControl.ActivePage.Controls[0].BeginDrag(False);
    end;
  end;
end;

该类源自TDragControlObjectEx而不是来自TDragControlObject这样它就会自动释放。现在,如果处理程序TPageControl在示例应用程序中创建(并设置为页面控制对象):

procedure TForm1.PageControlStartDrag(Sender: TObject;
  var ADragObject: TDragObject);
begin
  // do not cancel dragging unless page control has docking enabled
  if (ADragObject = nil) and fPageControl.DockSite then
    ADragObject := TConvertDragToDockHelper.Create(fPageControl);
end;

then the tab dragging will be canceled when the mouse moves far enough away from the tabs, and if the active page is a dockable form then a docking operation for it will be started, which can still be canceled with the ESC key.

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Delphi拖拽能否“提升”为对接? 的相关文章

  • 桌面 Delphi 应用程序是否可以通过 Windows 8 认证(使用 Windows 应用程序认证套件)?

    显然 Delphi 任何版本 不支持安全异常处理程序 https forums embarcadero com thread jspa messageID 473469 Visual Studio 中的 SAFESEH 开关 在 Windo
  • 如何将接口类型传递给过程

    如何将接口类型传递给过程参数 type Hello PortType interface ISoapInvokable 243CBD89 8766 F19D 38DF 427D7A02EAEE function GetDeneme s st
  • 作为属性的类引用

    谷歌对于这类搜索毫无用处 因为你会得到数亿个结果 其中没有一个与特定问题相关 问题很简单 Delphi 中是否可以有类引用属性 如果是这样 怎么办 这是我尝试过的 type TMyObject class end TMyObjectClas
  • 如何仅在某些列中设置带有复选框的 TListView?

    我正在使用 Delphi 2010 并且我试图允许用户在 TListView 中的每行 2 个选项之间进行选择 使用 TListView 我可以将样式设置为 vsReport 并启用复选框 但这只会让我每行有 1 个复选框 我需要的是每行
  • 在 Delphi 7 中使用 TScreen

    我的 Delphi 7 应用程序显示 Screen DesktopWidth Screen DesktopHeight Screen Monitors 0 Width Screen Monitors 0 Height 并且 如果选择了第二台
  • Java 拖放图像并在拖动时显示图像缩略图

    我有一个带有图像节点的网格布局 我想在我的应用程序中添加 dnd 功能 例如 当我将图像节点拖放到目标 JPanel 时 进行一些操作 例如以原始大小显示 删除等 我已经实现了这与一种使用 Transferhandler 的方法和一种使用
  • delphi分组框标题颜色变化

    我正在使用 BDS 2006 想知道您是否可以使用项目中存在的 XPmanifest 更改组框和单选按钮组标题的颜色 因为它始终是蓝色 唯一的方法是重写 Paint 方法TGroupBox http docwiki embarcadero
  • android中如何实现列表之间的拖放?

    我试图做到这一点 以便用户可以从一个列表视图中拖动文本视图 然后将其放入另一个列表视图中 但我发现这非常困难 到目前为止我发现的最大问题是 onTouchEvents 似乎只能在 ACTION DOWN 事件起源的视图中听到 我将单击一个列
  • Delphi - 将字符串从 UTF-8 转换回来

    我在将 UTF 8 编码字符串转换回 delphi 可用的字符串时遇到问题 该应用程序是用 XE8 编写的 并部署在 Windows 和 OSX 上 该应用程序分别在 Windows 和 OSX 上使用 LimeLM API dll 和 d
  • 从单个应用程序中的多个线程调用 dll 函数是否安全?

    我正在 Delphi 2009 中编写一个服务器应用程序 它实现了多种类型的身份验证 每种身份验证方法都存储在单独的 dll 中 第一次使用身份验证方法时 会加载适当的 dll 仅当应用程序关闭时 DLL 才会被释放 在服务器线程 连接 之
  • 在 Delphi XE 中将类作为过程的参数传递

    我需要做的是这样的 procedure A type of form var form TForm begin form type of form Create application form showmodal freeandnil f
  • 如何在全日历上获取外部拖放事件的开始和结束日期

    我有一个关于完整日历拖放功能的快速问题 这是我的 JS 代码 calendar fullCalendar header left prev next today right title editable true droppable tru
  • Delphi XE 中的数据绑定向导 - 可以将其配置为映射到 MSXML 接口吗?

    Delphi XE 中的数据绑定向导生成继承自 Delphi 自己的 DOM ADOM XML v4 实现的类和接口 它似乎不支持针对模式进行验证 解析时验证 选项仅适用于 MSXML 供应商type 从 VCL 源代码以及 IDE 中 X
  • Delphi 的免费加密库 [关闭]

    Closed 此问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 help closed questions 目前不接受答案 我正在为 Delphi 2010 寻找一个免费且最新的加密库 该库实现 RSA 和 AES Rijnda
  • 如何在 macOS 上的 SwiftUI 中设置拖动图像

    我正在尝试更改 GridView 的默认拖放预览图像 该图像似乎包含网格中的所有可见项目 据我了解 我应该能够设置NSItemProvider previewImageHandler块来自定义所使用的图像 我似乎找不到任何关于返回自定义图像
  • 从delphi应用程序调用.net4.0 com服务器后出现错误异常

    我们正在将代码库从 BDS2006 迁移到 Rad Studio XE 我们发现了一些非常奇怪的行为 如果我们在从 Net4 0 中实现的 COM 服务器创建一些对象后进行无效的浮点运算 即除以零 我们不会没有得到正常异常 即 EDivis
  • 如何追踪手柄泄漏?

    在我的一个应用程序中 我观察到句柄数量不断增加 在不使用应用程序的情况下 该数字大约每秒增加一次 因此后台处理代码的某些部分一定存在句柄泄漏 我如何追踪此类泄漏 有什么工具可以帮助解决这个问题吗 跟踪句柄泄漏时要寻找哪些模式 导致手柄泄漏的
  • 以 png 格式剪辑幻灯片 (Delphi 2010)

    I have a filmstrip of images in png format like this 我想知道如何剪辑每个图像并将这些图像放入 TImageList 控件中 并始终保留透明度 EDIT 是的 在设计时 RRUZ 提到的技
  • Qt:将拖放委托给子级的最佳方式

    我在 QWidget 上使用拖放 我重新实现了 DragEnterEvent dragLeaveEvent dragMoveEvent 和 dropEvent 效果很好 在我的 QWidget 中 我有其他 QWidget 子级 我希望它们
  • 在TImageViewer中,如何获取用户点击图片的位置?

    在TImageViewer控件中 用户可以缩放或平移图片 我的问题是 当用户点击图片时 如何获取用户在图片上的点击位置 尤其是用户可以对图片进行放大 缩小或平移之后 如何获取对应的图片点击位置呢 As shown below How to

随机推荐

  • Paypal Express 沙箱访问被拒绝

    我正在尝试使用 Paypal 沙箱 API 并得到此响应 Access Denied You don t have permission to access http api 3t sandbox paypal com nvp on thi
  • WCF:如何将多个服务组合到单个 WSDL 中

    在我的 ASP NET WebForms 项目中 我引用了 WCF 服务库项目 其中包含针对每个业务对象的不同 WCF 服务 这些服务托管在 IIS 中 并且可以通过我在 Global asax 中定义的路由获取 WSDL 每个服务通过一个
  • postgres 关系中的 ALTER TYPE 错误不存在

    使用以下内容 CREATE TYPE user types AS ENUM it accounting processes CREATE TABLE my users my user id integer NOT NULL my user
  • GPUImage imageFromCurrentFramebuffer 有时为 GPUImageLookupFilter 及其子类返回 nil

    我一直在使用GPUImage对于我的项目 我遇到了这个问题imageFromCurrentFramebuffer对于某些返回 nilGPUImageLookupFilter s 我子类化了GPUImageFilterGroup就像在GPUI
  • 使用 ASP.NET 和 MVC 3,如何创建隐藏字段,以便正确绑定以数组作为列表中每个项目的值的列表?

    我有一个查询语句列表 当需要在末尾添加另一个语句时 需要将这些查询语句发回 MVC 控制器 对于我现在尝试创建的测试 页面以过滤器列表开头 执行此示例时 页面将为过滤器创建字段 如下所示
  • 缩放时更改 ImageView 的大小

    我正在使用 chrisbanes PhotoView 来实现捏缩放 图像在捏和双击时缩放 但我看不到图像在缩放时拉伸到全屏 在缩放时 看起来图像在框内缩放并且部分图像消失关于缩放 我如何实现图像缩放 以便图像的高度在缩放时增加 我正在使用
  • 如何为javascript接口配置proguard?

    我已经实现了一个使用 JavascriptInterface 的 Webview 当不进行混淆时它工作得很好 但是一旦 Proguard 处于活动状态 它就不起作用了 我在这里查看了其他答案 但仍然无法使其正常工作 一些WebView类 p
  • 更改 Android 中的 MAC 地址

    我正在尝试更改已 root 的 Note 2 Android 设备的 Mac 地址 我做了以下事情 busybox ifconfig wlan0 关闭 busybox ifconfig wlan0 硬件以太 00 11 22 33 44 5
  • Rails 4 simple_form owns_to 关联未定义方法错误

    使用 Rails 4 当我想从对象 Document Document 渲染表单 使用 simple form 时 出现以下错误 undefined method document type id for
  • 搜索结果 url 中的 django csrf_token

    搜索结果 url 中有 csrf 不知道为什么会存在以及如何删除它 搜索效果很好 这是网址 search csrfmiddlewaretoken 675d1340034e094866d15a921407e3fc q testing 这是视图
  • 可靠的全双工串行通信

    我正在设计一种设备 它将加密从 PC 发送的长 假设无限 数据流并将其发回 我计划在运行全双工的设备上使用单个串行端口 并通过硬件握手来 阻止 数据 在每个块后发送一个 CRC 值 该设备只会缓冲有限数量的块 理想情况下 只有一个缓冲区累积
  • 是否可以在 OSX 中检测 Power Nap / DarkWake 模式

    我有一个通过 launchd 启动的守护进程 即使在 DarkWake 期间 它也会运行 并且在 OS X 再次返回睡眠状态之前没有时间完成 我可以想到以下解决方案 但没有找到实现此目的的方法 不要让该守护进程在 DarkWake 期间运行
  • Ropemacs 使用教程

    有很多网站都有相关说明安装Ropemacs 但到目前为止我找不到任何有关如何操作的说明use安装完成后即可 我已经安装了它 或者至少看起来是这样 Emacs 在其顶部菜单栏中有 Rope 菜单 怎么办 到目前为止 我只能使用 显示文档 默认
  • Docker 中的非 root 用户

    我有一个基本的 Dockerfile FROM ubuntu xenial USER test ENTRYPOINT bin bash 对于这个 Dockerfile 我希望能够创建一个没有密码的用户 并且当 Docker 容器运行时 我希
  • 通过 websocket 使用 javascript 传输文件

    你好 我正在尝试传输文件 我有一些程序将文件转换为二进制并使用 C 通过网络传输它们 我想知道我是否能够使用 javascript 和 websocket 传输文件 任何有关如何将我的 C 程序集成到 JavaScript 中的示例将不胜感
  • 无法在 Sublime Text 中的 REPL R 中运行选定的行

    按照这些说明为 sublime text 设置 REPLhttp www kevjohnson org using r in sublime text 3 http www kevjohnson org using r in sublime
  • 什么是擦除

    什么是擦除以及擦除对仿制药的限制是什么 Erasure http download oracle com javase tutorial java generics erasure html是在编译时使用类型但在运行时不存在的结果 这是 J
  • 反序列化后如何处理SerializationException

    我在 Spring Kafka 设置中使用 Avro 和 Schema 注册表 我想以某种方式处理SerializationException 在反序列化期间可能会抛出该异常 我找到了以下两个资源 https github com spri
  • 自动布局或使用 NSAttributedString 计算高度来实现 UITableViewCell 的动态高度的最佳方法是什么?

    我已经按照教程进行了http raywenderlich com 73602 dynamic table view cell height auto layout http raywenderlich com 73602 dynamic t
  • Delphi拖拽能否“提升”为对接?

    我有一个TPageControl其页面都是使用附加的各种形式ManualDock 用户应该能够通过拖动选项卡来重新排列选项卡 这已经可以使用了 然而 也应该可以取消停靠的表单 现在我有以下代码 procedure TMainForm Pag