代码之家  ›  专栏  ›  技术社区  ›  Uli Gerhardt

使用dragimage在拖放期间绘制tpaintbox

  •  2
  • Uli Gerhardt  · 技术社区  · 14 年前

    在我的应用程序(Delphi2007)中,我想将项目从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注意到 绘制可以获得的拖动图像 更新或屏幕绘制问题 除非你使用图像列表 隐藏拖动图像的api函数 当绘画发生时 什么隐藏图像的方法 是的)。不幸的是,如果你 不要拥有控制权 被画成这样并不是真的 选择权。

    我没有上一句提到的问题。尽管如此,我还是找不到合适的地方和合适的图像列表(它是 我的测试项目中的imagelist1-可能是listview1.getdragimages)调用imagelist\u dragleave。

    2 回复  |  直到 14 年前
        1
  •  2
  •   mghie    14 年前

    关键是在重新绘制绘制框之前隐藏拖动图像,然后再次显示。如果在问题中替换此代码:

    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就可以了。

    感谢您的问题中的演示代码,让我们看到问题的极好方式。

        2
  •  1
  •   Mark Robinson    14 年前

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

    编辑:

    经过进一步的研究-如果你拖动一个图标,使鼠标只进入框(但图标没有),然后框被正确绘制,只有当图标进入画框时,工件才会出现。

    我添加了代码,以便如果state是dsdragmove,那么它将强制重新绘制,这样做很有效,但是会受到闪烁的影响。