在拖放过程中使用 DragImage 绘制 TPaintBox
在我的应用程序(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: 此网站谈“绘画问题”:
ImageList SDK 指出,当 绘制拖动图像即可得到 更新或屏幕绘制问题 除非你使用ImageList_DragLeave 隐藏拖动图像的API函数 当绘画发生时(即 中的 HideDragImage 方法是什么 类)。不幸的是,如果你 不拥有正在发生的控制权 画这样做并不是真正的 选项。
我没有遇到最后一句提到的问题。尽管如此,我无法找到正确的位置和正确的图像列表(它不是我的测试项目中的ImageList1 - 可能是ListView1.GetDragImages)来调用ImageList_DragLeave。
In my application (Delphi 2007) I want to drag items from a ListView to a PaintBox and highlight corresponding areas in the PaintBox's OnPaint handler. However I always get ugly artefacts. Do you have any advice how I can get rid of them?
Test project: Just create a new VCL application and replace the code in Unit1.pas with the following. Then start the app and drag list items over the rectangle in the 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.
Edit: Here is a picture of the glitch:DragImage artefact http://img269.imageshack.us/img269/6535/15778780.png
I expect to see the complete blue rectangle with thick border. However beneath the drag image one can see the un-highlighted rectangle.
Edit 2: This site talks about "Painting Issues":
The ImageList SDK notes that when
drawing the drag image you can get
issues with updates or screen painting
unless you use the ImageList_DragLeave
API function to hide the drag image
whilst the painting occurs (which is
what the HideDragImage method in the
class does). Unfortunately, if you
don't own the control that's being
painted doing this isn't really an
option.
I don't have the problem mentioned in the last sentence. Nevertheless I wasn't able to find the right place and the right imagelist (it's not ImageList1 in my test project - probably ListView1.GetDragImages) to call ImageList_DragLeave.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
关键是在重绘绘画框之前隐藏拖动图像,并在重绘之后再次显示它。如果您在问题中替换此代码:
这样
它应该可以工作。嗯,对于我在 Windows XP 64 位上使用 Delphi 2007 来说确实如此。
感谢您问题中的演示代码,这是让我们看到问题的好方法。
The key is to hide the drag image before the paint box is redrawn, and to show it again after that. If you replace this code in your question:
with this
it should work. Well, it does for me with Delphi 2007 on Windows XP 64 bit.
And kudos for the demonstration code in your question, excellent way to let us see the problem.
在 XP、Delphi 2010 上测试 - 我得到了工件,因此它与 XP 相关,并且在 D2010 中未修复
编辑:
经过进一步调查 - 如果您拖动图标,以便鼠标仅进入框(但图标没有),那么盒子绘制正确,只有当图标进入油漆箱时才会出现伪像。
我添加了代码,以便如果状态是 dsDragMove 那么它将强制重新绘制,这有效,但遭受了闪烁
Tested on XP, Delphi 2010 - I get the artifacts, so it's XP related and not fixed in D2010
Edit:
Upon further investigation - if you drag an icon so that the mouse only just enters the box (but the icon doesn't) then the box is drawn correctly, it's only when the icon enters the paintbox that the artifacts occur.
I added code so that if state was dsDragMove then it would force a repaint and this worked, but suffered from flicker