Вызов выбора TTreeView при перетаскивании node
Я реализую функции drag-and-drop для TTreeView
. В событии OnStartDrag
я создаю DragOcject
моего производного класса:
TTreeDragControlObject = class(TDragObject)
private
FDragImages: TDragImageList;
FText: String;
protected
function GetDragImages: TDragImageList; override;
end;
procedure TfrmMain.tvTreeStartDrag(Sender: TObject;
var DragObject: TDragObject);
begin
DragObject := TTreeDragControlObject.Create;
TTreeDragControlObject(DragObject).FText := tvTree.Selected.Text;
end;
И это моя переопределяющая функция GetDragImages
моего DragObcject
:
function TTreeDragControlObject.GetDragImages: TDragImageList;
var
Bmp: TBitmap;
begin
if FDragImages = nil then
begin
FDragImages := TDragImageList.Create(nil);
Bmp := TBitmap.Create;
try
Bmp.Width := Bmp.Canvas.TextWidth(FText) + 25;
Bmp.Height := Bmp.Canvas.TextHeight(FText);
Bmp.Canvas.TextOut(25, 0, FText);
FDragImages.Width := Bmp.Width;
FDragImages.Height := Bmp.Height;
FDragImages.SetDragImage(FDragImages.Add(Bmp, nil), 0, 0);
finally
Bmp.Free;
end;
end;
Result := FDragImages;
end;
Все работает отлично, за исключением того, что при перетаскивании узлов дерева происходит сбой графики:
![The node glitch]()
Как я могу избежать такого поведения?
Ответы
Ответ 1
Основываясь на ответах @Sean и @bummi, я бы опубликовал весь код и выводы, которые работали для меня в D5.
В WinXP XPManifest не необходимо обязательно - Hide/ShowDragImage
.
В Win7 требуется XPManifest. Hide/ShowDragImage
не a must.
Заключение - используйте как XPManifest, так и HideDragImage
и ShowDragImage
, чтобы обеспечить работу телевизора как на XP/Win7.
type
TTreeDragControlObject = class(TDragControlObject)
private
FDragImages: TDragImageList;
FText: String;
protected
function GetDragImages: TDragImageList; override;
public
destructor Destroy; override;
procedure HideDragImage; override;
procedure ShowDragImage; override;
property DragText: string read FText write FText;
end;
TForm1 = class(TForm)
TreeView1: TTreeView;
procedure TreeView1StartDrag(Sender: TObject; var DragObject: TDragObject);
procedure TreeView1DragOver(Sender, Source: TObject; X, Y: Integer; State: TDragState; var Accept: Boolean);
procedure TreeView1EndDrag(Sender, Target: TObject; X, Y: Integer);
private
FDragObject: TTreeDragControlObject;
public
end;
...
{ TTreeDragControlObject}
destructor TTreeDragControlObject.Destroy;
begin
FDragImages.Free;
inherited;
end;
procedure TTreeDragControlObject.HideDragImage;
begin
GetDragImages.HideDragImage;
end;
procedure TTreeDragControlObject.ShowDragImage;
begin
GetDragImages.ShowDragImage;
end;
function TTreeDragControlObject.GetDragImages: TDragImageList;
var
Bmp: TBitmap;
begin
if FDragImages = nil then
begin
FDragImages := TDragImageList.Create(nil);
Bmp := TBitmap.Create;
try
Bmp.Width := Bmp.Canvas.TextWidth(FText) + 25;
Bmp.Height := Bmp.Canvas.TextHeight(FText);
Bmp.Canvas.TextOut(25, 0, FText);
FDragImages.Width := Bmp.Width;
FDragImages.Height := Bmp.Height;
FDragImages.SetDragImage(FDragImages.Add(Bmp, nil), 0, 0);
finally
Bmp.Free;
end;
end;
Result := FDragImages;
end;
{ TForm1 }
procedure TForm1.TreeView1StartDrag(Sender: TObject; var DragObject: TDragObject);
begin
FDragObject := TTreeDragControlObject.Create(TTreeView(Sender));
FDragObject.DragText := TTreeView(Sender).Selected.Text;
DragObject := FDragObject;
end;
procedure TForm1.TreeView1DragOver(Sender, Source: TObject; X, Y: Integer;
State: TDragState; var Accept: Boolean);
begin
Accept := Source is TTreeDragControlObject;
end;
procedure TForm1.TreeView1EndDrag(Sender, Target: TObject; X, Y: Integer);
begin
FDragObject.Free;
end;
Обратите внимание, что в вашем коде как FDragImages
, так и var DragObject
происходит утечка памяти. Я бы предложил использовать TDragControlObject
вместо TDragObject
(теперь ваш огонь tvTreeEndDrag
?), Это не срабатывало для меня)
Ответ 2
Использование TXPManifest устраняет эту ошибку в D7.
использует Windows, сообщения, SysUtils, варианты, классы, графика, элементы управления, формы, Диалоги, XPMan, ComCtrls;
дополнительные:
procedure Win7UpdateFix(Form: TForm; CharCode: Word);
var i: Integer;
begin
if Assigned(Form) and (Win32MajorVersion >= 6) and (Win32Platform = VER_PLATFORM_WIN32_NT) then //Vista, Win7
begin
case CharCode of
VK_MENU, VK_TAB: //Alt or Tab
begin
for i := 0 to Form.ComponentCount-1 do
begin
if Form.Components[i] is TWinControl then
begin
//COntrols that disappear - Buttons, Radio buttons, Checkboxes
if (Form.Components[i] is TButton)
or (Form.Components[i] is TRadioButton)
or (Form.Components[i] is TCheckBox) then
TWinControl(Form.Components[i]).Invalidate;
end;
end;
end;
end;
end;
end;
procedure TForm1.FormKeyDown(Sender: TObject; var Key: Word;
Shift: TShiftState);
begin
if key=VK_MENU then
begin
Win7UpdateFix(Self,key)
end;
end;
Ответ 3
Такое же поведение происходит в Delphi 2010, а TXPManifest делает не исправление. Совсем недавно я столкнулся с одной и той же проблемой в приложении Delphi 2010. Решение состоит в том, чтобы реализовать методы HideDragImage()/ShowDragImage(), подобные этому...
TTreeDragControlObject = class(TDragObject)
private
FDragImages: TDragImageList;
FText: String;
protected
function GetDragImages: TDragImageList; override;
public
procedure HideDragImage; override;
procedure ShowDragImage; override;
end;
... и затем...
procedure TTreeDragControlObject.HideDragImage;
begin
FDragImages.HideDragImage
end;
procedure TTreeDragControlObject.ShowDragImage;
begin
FDragImages.ShowDragImage
end;
Согласованность этого заключается в том, что функция API окна ImageList_DragShowNolock() вызывается непосредственно перед и после рисования изображения перетаскивания (через сообщение Windows TVM_SELECTITEM (TVGN_DROPHILITE)). Без вызова этой функции изображение перетаскивания не будет правильно окрашено. Потребность в ImageList_DragShowNolock (False/True), ограничивающая TVM_SELECTITEM + TVGN_DROPHILITE, является плохо документированной, и, если другие форумы должны судить, это является распространенной причиной жалобы.