【问题标题】:Is it possible to Alpha Blend a VCL control on a TForm?是否可以在 TForm 上对 VCL 控件进行 Alpha 混合?
【发布时间】:2012-09-27 18:06:12
【问题描述】:

是否可以对 TForm 上的 VCL 控件进行 Alpha 混合或实现类似的效果?

例如,考虑以下屏幕截图,其中两个 TPanel 放置在 TForm 上以及其他控件。两个面板都可以拖动(参见How to Move and Resize Controls at Run Time)。

现在,是否可以在拖动时使这些面板半透明,以便您可以看到下面的内容? (如通过图像处理产生的第二张图像所示)

【问题讨论】:

  • 恐怕不简单。 (顺便说一句:永远不要使用 JPG 进行屏幕截图,除非它们主要包含照片图像!改用 PNG -)
  • @Andreas Rejbrand:除了 Delphi 之外,是否可以使用任何语言来实现?必须有某种方法可以做到这一点,因为在 Firefox 中,如果您在屏幕上拖动上面的图像,您仍然可以看到它背后的内容。另一个可以看到类似效果的地方是 Windows 7 任务栏。
  • 你所看到的,例如正如你提到的,在 Firefox 中是 OLE 拖动的图像。

标签: delphi


【解决方案1】:

VCL 让您有机会在拖放过程中使用 specify a drag image list,这是一个简单的示例:

unit Unit1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.ExtCtrls;

type
  TPanel = class(Vcl.ExtCtrls.TPanel)
  protected
    function GetDragImages: TDragImageList; override;
  end;

  TForm1 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    Button3: TButton;
    CheckBox1: TCheckBox;
    Edit1: TEdit;
    Label1: TLabel;
    Panel1: TPanel;
    Panel2: TPanel;
    Panel3: TPanel;
    procedure FormCreate(Sender: TObject);
    procedure FormDragOver(Sender, Source: TObject; X, Y: Integer; State: TDragState; var Accept: Boolean);
    procedure PanelStartDrag(Sender: TObject; var DragObject: TDragObject);
    procedure PanelEndDrag(Sender, Target: TObject; X, Y: Integer);
  private
    FDragImages: TDragImageList;
  public
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

{ TPanel }

function TPanel.GetDragImages: TDragImageList;
begin
  Result := (Owner as TForm1).FDragImages;
end;

type
  TControlProc = reference to procedure(Control: TControl);

procedure IterateControls(Control: TControl; Proc: TControlProc);
var
  I: Integer;
begin
  if Assigned(Control) then
    Proc(Control);
  if Control is TWinControl then
    for I := 0 to TWinControl(Control).ControlCount - 1 do
      IterateControls(TWinControl(Control).Controls[I], Proc);
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  FDragImages := nil;
  // set display drag image style
  IterateControls(Self,
    procedure(Control: TControl)
    begin
      Control.ControlStyle := Control.ControlStyle + [csDisplayDragImage];
    end
  );
end;

procedure TForm1.FormDragOver(Sender, Source: TObject; X, Y: Integer; State: TDragState; var Accept: Boolean);
begin
  Accept := Source is TPanel;
end;

procedure TForm1.PanelEndDrag(Sender, Target: TObject; X, Y: Integer);
begin
  FreeAndNil(FDragImages);
end;

procedure TForm1.PanelStartDrag(Sender: TObject; var DragObject: TDragObject);
var
  Image: TBitmap;
begin
  if not (Sender is TPanel) then
    Exit;

  Image := TBitmap.Create;
  try
    Image.PixelFormat := pf32bit;
    Image.Width := TControl(Sender).Width;
    Image.Height := TControl(Sender).Height;
    TPanel(Sender).PaintTo(Image.Canvas, 0, 0);

    FDragImages := TDragImageList.Create(nil);
    FDragImages.Width := Image.Width;
    FDragImages.Height := Image.Height;
    FDragImages.SetDragImage(FDragImages.Add(Image, nil), 0, 0);
    FDragImages.ShowDragImage;
  except
    Image.Free;
    FreeAndNil(FDragImages);
    raise;
  end;
end;

end.

【讨论】:

  • +1 用于插入器类并覆盖 GetDragImages 方法。
  • 您的解决方案非常优雅,看起来这是完成任务的自然方式。尽管我以 Firefox 为例,但我真的希望面板在移动时远离其当前位置(不仅仅是它的图像)。我猜它可能可以通过隐藏源面板来产生相同的效果。我正在寻找一种更像 Windows 7 任务栏的功能(您可以在其中通过拖放重新排列应用程序图标)。
【解决方案2】:

您也可以在 Delphi 中执行此操作。基本思想是将控件放入启用了 alpha 混合的自动调整大小的边框形式。

根据您链接到的文章,在 MouseDown 事件中添加以下行:

  P := TWinControl(Sender).ClientToScreen(Point(0,0));
  frm := TForm.Create(nil);
  TWinControl(Sender).Parent := frm;
  frm.BorderStyle := bsNone;
  frm.AlphaBlend := true;
  frm.AlphaBlendValue := 128;
  frm.AutoSize := true;
  frm.Left := P.X;
  frm.Top := P.Y;
  frm.Position := poDesigned;
  frm.Show;

在 MouseMove 事件中设置父控件的 Left 和 Top 属性:

  GetCursorPos(newPos);

  Screen.Cursor := crSize;
  Parent.Left := Parent.Left - oldPos.X + newPos.X;
  Parent.Top := Parent.Top - oldPos.Y + newPos.Y;
  oldPos := newPos;

并在 MouseUp 事件中释放表单,将控件父级设置回原始父级并将屏幕位置转换为相对于它的新位置:

frm := TWinControl(Sender).Parent;
P := Point(frm.Left, frm.Top);
P := ScreenToClient(P);
TWinControl(Sender).Parent := Self;
TWinControl(Sender).Left := P.X;
TWinControl(Sender).Top := P.Y;
frm.Free;
Screen.Cursor := crDefault;
ReleaseCapture;
inReposition := False;

【讨论】:

  • 已经够近了,但是表单抢了焦点。
  • 这在大多数情况下都有效!那是横向思维!!我现在能看到的唯一问题是它把焦点从原始表单上转移了。
  • @TLama,frm.Show 之后的 SetFocus 怎么样?
  • @Uwe,可能有用,但我更喜欢ShowWindow(frm.Handle, SW_SHOWNOACTIVATE); frm.Visible := True;。而是使用Release 而不是Free。我知道,这里没有风险,但有些人可能会将其称为释放表单的正确方法。
  • 这个解决方案带有 ShowWindow(frm.Handle, SW_SHOWNOACTIVATE); frm.Visible := 真;完全按照我的设想工作!谢谢 TLama 和 Uwe Raabe!
【解决方案3】:

要实现显示控件图像的拖动操作,您必须创建TDragControlObject后代并实现GetDragImages方法,从这里您必须确保将 csDisplayDragImage 值添加到ControlStyle 要拖动的控件的属性。

你可以在这里Implementing Professional Drag & Drop In VCL/CLX Applications找到一篇关于这个主题的非常好的文章

【讨论】:

  • 我花了很长时间才弄清楚如何抓取我在 Mac 上的 Windows VM 中运行的应用程序的屏幕截图。 ;-)
  • 我只是在编写一个像你一样的 Delphi 示例,但使用了 TDragControlObject,但后来我看到了你的答案:),无论如何,我喜欢你使用插入器类并覆盖 GetDragImages 方法的想法。
  • 感谢链接“在 VCL/CLX 应用程序中实现专业拖放”
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-02-21
  • 2014-12-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-02-08
相关资源
最近更新 更多