在论坛上看一个贴,问能不能在ListView的列表头移动时响应事件,随翻查了ListView的事件,发现竟没有。于是自己动手来写一个。
其实要响应这个事件也不算难,关键是了解ListView的结构,ListView的列头其实也是一个窗口,不信用Spy看一下。我们只要获得了这个头的句柄,那它的一举一动便逃不过程序的眼睛了(^ ^)。
获得了句柄,再用一个新的窗口过程代替旧的窗口过程,当然这个新的窗口过程最后得成一个类的成员,这个用Delphi的MakeObjectInstance可以轻松完成。这样就可以在新的窗口过程中操纵列表头。
理论已经具备,接下来就是动手,想了一想,该功能还是挺常用的,不如扩展到控件去,造福他人,岂不是快哉!
于是就有了下面的控件,可以装上去,用起来更方便,OnHeadMoved事件,处理这个事件即可。不过先声明,这个事件可是等你移动完成之后才触发的:
unit ListviewEx;
{*******************************************
* brief: 一个可以响应列表头移动事件的ListView
* autor: linzhenqun
* date: 2005-8-14
* email: linzhengqun@163.com
* blog: http://blog.csdn.net/linzhengqun
********************************************}
interface
uses
Windows, Messages, SysUtils, Classes, Controls, ComCtrls, CommCtrl;
type
TListviewEx = class(TListView)
private
FHeaderHandle: THandle;
FHeaderInst: Pointer;
FOldHeaderProc: Pointer;
FOnHeaderMoved: TNotifyEvent;
FDown: Boolean;
FColsWidth: array of Integer;
procedure SaveColumnsWidth;
function ColumnsWidthChange: Boolean;
protected
procedure CreateWnd; override;
procedure NewHeaderProc(var Message: TMessage);
public
destructor Destroy; override;
published
property OnHeadMoved: TNotifyEvent read FOnHeaderMoved
write FOnHeaderMoved;
end;
procedure Register;
implementation
procedure Register;
begin
RegisterComponents('Samples', [TListviewEx]);
end;
{ TListviewEx }
function TListviewEx.ColumnsWidthChange: Boolean;
var
i, C: Integer;
begin
Result := False;
if Length(FColsWidth) > Columns.Count then
C := Columns.Count
else
C := Length(FColsWidth);
for i := 0 to C - 1 do
if FColsWidth[i] <> Columns[i].Width then
begin
Result := True;
break;
end;
end;
procedure TListviewEx.CreateWnd;
begin
inherited;
FDown := False;
FHeaderHandle := ListView_GetHeader(Handle);
if FHeaderHandle <> 0 then
begin
FHeaderInst := MakeObjectInstance(NewHeaderProc);
FOldHeaderProc := Pointer(GetWindowLong(FHeaderHandle, GWL_WNDPROC));
SetWindowLong(FHeaderHandle, GWL_WNDPROC, Longint(FHeaderInst));
end;
end;
destructor TListviewEx.Destroy;
begin
if FHeaderHandle <> 0 then
begin
SetWindowLong(FHeaderHandle, GWL_WNDPROC, Longint(FOldHeaderProc));
FreeObjectInstance(FHeaderInst);
end;
inherited;
end;
procedure TListviewEx.NewHeaderProc(var Message: TMessage);
begin
Message.Result := CallWindowProc(FOldHeaderProc, FHeaderHandle,
Message.Msg, Message.WPARAM, Message.LPARAM);
if not (csDesigning in ComponentState) then
if Message.Msg = WM_LBUTTONDOWN then
begin
FDown := True;
SaveColumnsWidth;
end
else if Message.Msg = WM_LBUTTONUP then
begin
if FDown and ColumnsWidthChange then
if Assigned(FOnHeaderMoved) then
FOnHeaderMoved(Self);
FDown := False;
end
else if Message.Msg = WM_LBUTTONDBLCLK then
begin
if ColumnsWidthChange then
if Assigned(FOnHeaderMoved) then
FOnHeaderMoved(Self);
FDown := False;
end;
end;
procedure TListviewEx.SaveColumnsWidth;
var
i: Integer;
begin
FColsWidth := nil;
SetLength(FColsWidth, Columns.Count);
for i:= 0 to Columns.Count - 1 do
FColsWidth[i] := Columns[i].Width;
end;
end.