【问题标题】:Painting TPaintBox during Drag&Drop with DragImage在拖放期间使用拖动图像绘制 TPaintBox
【发布时间】:2011-01-14 18:36:36
【问题描述】:

在我的应用程序(Delphi 2007)中,我想将项目从 ListView 拖到 PaintBox 并突出显示 PaintBox 的 OnPaint 处理程序中的相应区域。然而,我总是得到丑陋的文物。您对我如何摆脱它们有什么建议吗?

测试项目:只需创建一个新的 VCL 应用程序并将 Unit1.pas 中的代码替换为以下代码。然后启动应用程序并将列表项拖到 PaintBox 中的矩形上方。

unit Unit1;

interface

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

type
  TForm1 = class(TForm)
  private
    PaintBox1: TPaintBox;
    ListView1: TListView;
    ImageList1: TImageList;
    FRectIsHot: Boolean;
    function GetSensitiveRect: TRect;
    procedure PaintBox1DragOver(Sender, Source: TObject; X, Y: Integer;
      State: TDragState; var Accept: Boolean);
    procedure PaintBox1Paint(Sender: TObject);
  public
    constructor Create(AOwner: TComponent); override;
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

uses
  TypInfo;

const
  IconIDs: array[TMsgDlgType] of PChar = (IDI_EXCLAMATION, IDI_HAND,
    IDI_ASTERISK, IDI_QUESTION, nil);

{ TForm1 }

constructor TForm1.Create(AOwner: TComponent);
var
  Panel1: TPanel;
  mt: TMsgDlgType;
  Icon: TIcon;
  li: TListItem;
begin
  inherited Create(AOwner);
  Width := 600;
  Height := 400;

  ImageList1 := TImageList.Create(Self);
  ImageList1.Name := 'ImageList1';
  ImageList1.Height := 32;
  ImageList1.Width := 32;

  ListView1 := TListView.Create(Self);
  ListView1.Name := 'ListView1';
  ListView1.Align := alLeft;
  ListView1.DragMode := dmAutomatic;
  ListView1.LargeImages := ImageList1;

  Panel1 := TPanel.Create(Self);
  Panel1.Name := 'Panel1';
  Panel1.Caption := 'Drag list items here';
  Panel1.Align := alClient;

  PaintBox1 := TPaintBox.Create(Self);
  PaintBox1.Name := 'PaintBox1';
  PaintBox1.Align := alClient;
  PaintBox1.ControlStyle := PaintBox1.ControlStyle + [csDisplayDragImage];
  PaintBox1.OnDragOver := PaintBox1DragOver;
  PaintBox1.OnPaint := PaintBox1Paint;
  PaintBox1.Parent := Panel1;

  ListView1.Parent := Self;
  Panel1.Parent := Self;

  Icon := TIcon.Create;
  try
    for mt := Low(TMsgDlgType) to High(TMsgDlgType) do
      if Assigned(IconIDs[mt]) then
      begin
        li := ListView1.Items.Add;
        li.Caption := GetEnumName(TypeInfo(TMsgDlgType), Ord(mt));
        Icon.Handle := LoadIcon(0, IconIDs[mt]);
        li.ImageIndex := ImageList1.AddIcon(Icon);
      end;
  finally
    Icon.Free;
  end;
end;

function TForm1.GetSensitiveRect: TRect;
begin
  Result := PaintBox1.ClientRect;
  InflateRect(Result, -PaintBox1.Width div 4, -PaintBox1.Height div 4);
end;

procedure TForm1.PaintBox1Paint(Sender: TObject);
var
  r: TRect;
begin
  r := GetSensitiveRect;
  if FRectIsHot then
  begin
    PaintBox1.Canvas.Pen.Width := 5;
    PaintBox1.Canvas.Brush.Style := bsSolid;
    PaintBox1.Canvas.Brush.Color := clAqua;
  end
  else
  begin
    PaintBox1.Canvas.Pen.Width := 1;
    PaintBox1.Canvas.Brush.Style := bsClear;
  end;
  PaintBox1.Canvas.Rectangle(r.Left, r.Top, r.Right, r.Bottom);
end;

procedure TForm1.PaintBox1DragOver(Sender, Source: TObject; X,
  Y: Integer; State: TDragState; var Accept: Boolean);
var
  r: TRect;
  MustRepaint: Boolean;
begin
  MustRepaint := False;

  if State = dsDragEnter then
  begin
    FRectIsHot := False;
    MustRepaint := True;
  end
  else
  begin
    r := GetSensitiveRect;
    Accept := PtInRect(r, Point(X, Y));

    if Accept <> FRectIsHot then
    begin
      FRectIsHot := Accept;
      MustRepaint := True;
    end;
  end;

  if MustRepaint then
    PaintBox1.Invalidate;
end;

end.

编辑:这是故障图片:DragImage artefact http://img269.imageshack.us/img269/6535/15778780.png

我希望看到带有粗边框的完整蓝色矩形。然而,在拖动图像下方可以看到未突出显示的矩形。

编辑 2: This site 谈论“绘画问题”:

ImageList SDK 指出,当 绘制可以得到的拖动图像 更新或屏幕绘画问题 除非你使用 ImageList_DragLeave 隐藏拖动图像的 API 函数 当绘画发生时(这是 中的 HideDragImage 方法是什么 类)。不幸的是,如果你 不拥有当前的控制权 画做这不是真的 选项。

我没有最后一句中提到的问题。尽管如此,我还是找不到正确的位置和正确的图像列表(在我的测试项目中 不是 ImageList1 - 可能是 ListView1.GetDragImages)来调用 ImageList_DragLeave。

【问题讨论】:

  • 我已将源代码复制到D2009并运行它。无论拖动哪个对象都没有故障。顺便说一句,运行 Vista。
  • 好吧,这暗示 D2007、XP 或我的显卡可能是罪魁祸首。感谢您的测试!
  • 我刚刚在我的家用电脑(XP、Turbo Delphi)上测试了它,它看起来和上图一模一样。
  • 我相信这可能是 XP 的问题。我在虚拟机中安装了 XP,并且有 D7 和 D2007,两者都出现故障。
  • 我猜它只有在启用桌面合成 (Aero) 的情况下才能在 Vista 上运行?

标签: delphi drag-and-drop delphi-2007 paintbox


【解决方案1】:

在 XP,Delphi 2010 上测试 - 我得到了工件,所以它与 XP 相关,在 D2010 中没有修复

编辑:

经过进一步调查 - 如果您拖动图标以使鼠标仅进入框(但图标没有),则框被正确绘制,只有当图标进入绘画框时才会出现伪影。

我添加了代码,以便如果状态为 dsDragMove 则它会强制重新绘制并且这有效,但会出现闪烁

【讨论】:

  • 我也试过了,正如你所说,它闪烁得很厉害。此外,IIRC 的人工制品并没有消失——它们只是更小了。
【解决方案2】:

关键是在重绘画框之前隐藏拖拽图像,之后再显示。如果您在问题中替换此代码:

procedure TForm1.PaintBox1DragOver(Sender, Source: TObject; X,
  Y: Integer; State: TDragState; var Accept: Boolean);
var
  r: TRect;
  MustRepaint: Boolean;
begin
  MustRepaint := False;

  if State = dsDragEnter then
  begin
    FRectIsHot := False;
    MustRepaint := True;
  end
  else
  begin
    r := GetSensitiveRect;
    Accept := PtInRect(r, Point(X, Y));

    if Accept <> FRectIsHot then
    begin
      FRectIsHot := Accept;
      MustRepaint := True;
    end;
  end;

  if MustRepaint then
    PaintBox1.Invalidate;
end;

有了这个

procedure TForm1.PaintBox1DragOver(Sender, Source: TObject; X,
  Y: Integer; State: TDragState; var Accept: Boolean);
var
  r: TRect;
begin
  if State = dsDragEnter then
  begin
    FRectIsHot := False;
    PaintBox1.Invalidate;
  end
  else
  begin
    r := GetSensitiveRect;
    Accept := PtInRect(r, Point(X, Y));

    if Accept <> FRectIsHot then
    begin
      FRectIsHot := Accept;
      ImageList_DragShowNolock(False);
      try
        PaintBox1.Refresh;
      finally
        ImageList_DragShowNolock(True);
      end;
    end;
  end;
end;

它应该工作。嗯,它适用于我在 Windows XP 64 位上的 Delphi 2007。

感谢您问题中的演示代码,这是让我们看到问题的绝佳方式。

【讨论】:

  • 在 XP 32bit/Turbo Delphi 下为我工作。我仍然发现了一个(古怪的)小故障:在 other 应用程序上拖动留下痕迹时使用 Alt-Tab。但是谁做这样的事情就不值得更好了。 :-)
  • @Ulrich:我认为无法在应用程序中修复所有此类故障。拖放是一个困难的过程,在系统级别上有很多特殊处理。 Raymond Chen 有很多关于拖动主题的文章,尤其是LockWindowUpdate()。其中有很多有趣的信息。
  • 我想了这么多。因此,我对您的解决方案感到满意。是的,雷蒙德的东西很有趣。我经常阅读 OldNewThing。
  • 我在我们的 real 应用中实现了您的解决方案,它运行良好。非常感谢!
猜你喜欢
  • 2010-11-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-01-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多