Reputation: 489
we are migrating Delphi 7 code to Delphi 10.2 tokyo. As part of this we have migrated all the components. List view component is not working as expected. It is not displaying column headings and data when ViewStyle = vsReport and for other styles(VsIcon,VsList) it is displaying data and Headers.
About the component:
we have added additional property DataSet, user can set DataSet property at design time and the component will take care of opening the Dataset, setting column headings and adding data when user calls BuildListView Method of this component.This component has popup menu to add, delete and edit the selected item. i have deleted this popup menu code. since it is not required.
Here is the code: This package has Runtime Package(64 bit) and Design time package (32 bit). Design time package has the code to register the component.
Run time pkg BPL Source:
package MylistView_rn;
{$R *.res}
{$IFDEF IMPLICITBUILDING This IFDEF should not be used by users}
{$ALIGN 8}
{$ASSERTIONS ON}
{$BOOLEVAL OFF}
{$DEBUGINFO OFF}
{$EXTENDEDSYNTAX ON}
{$IMPORTEDDATA ON}
{$IOCHECKS ON}
{$LOCALSYMBOLS ON}
{$LONGSTRINGS ON}
{$OPENSTRINGS ON}
{$OPTIMIZATION OFF}
{$OVERFLOWCHECKS OFF}
{$RANGECHECKS OFF}
{$REFERENCEINFO ON}
{$SAFEDIVIDE OFF}
{$STACKFRAMES ON}
{$TYPEDADDRESS OFF}
{$VARSTRINGCHECKS ON}
{$WRITEABLECONST OFF}
{$MINENUMSIZE 1}
{$IMAGEBASE $400000}
{$DEFINE DEBUG}
{$ENDIF IMPLICITBUILDING}
{$RUNONLY}
{$IMPLICITBUILD ON}
requires
rtl,
vcl,
dbrtl,
adortl,
vcldb;
contains
uMyListView1 in 'uMyListView1.pas';
end.
Run time package Unit Source(uMyListView1.pas):
unit uMyListView1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
ComCtrls, Menus, DB, ExtCtrls, Buttons, ADODB;
const ARROW_SIZE = 10;
type
TMyListView = class(TListView)
private
FDataSet: TCustomADODataSet;
FPressedColumn: integer; //column index
FLastPressedColumn: integer; //last column to be pressed
FSortDir: integer; //-1 = descending, 1 = ascending
FSortOrder: integer;
FMyHeaderHandle: HWND;
FMyHeaderInstance: Pointer;
FMyDefHeaderWndProc: Pointer;
FHeaderPopupMenu: TPopupMenu;
FDragging: boolean;
procedure SetListViewColumns;
function GetArrowPos(ColumnIndex: integer): TPoint;
function DeleteLastArrow(DC: HDC): boolean;
function DrawArrow(DC: HDC; Direction: integer; Offset: TPoint): boolean;
function DrawColText(DC: HDC; ColumnIndex: integer): boolean;
procedure WndProc(var msg : TMessage); override;
procedure MyHeaderWndProc(var Message: TMessage);
protected
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
function BuildListView: boolean;
published
property DataSet: TCustomADODataSet read FDataSet write FDataSet;
end;
implementation
constructor TMYListView.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
FPressedColumn := -1;
FLastPressedColumn := -1;
FSortOrder := 0;
FDragging := FALSE;
FMyHeaderInstance := MakeObjectInstance(MyHeaderWndProc);
end;
destructor TMYListView.Destroy;
begin
inherited Destroy;
end;
procedure TMYListView.WndProc(var msg : TMessage);
begin
inherited WndProc(Msg);
if (Msg.Msg = WM_PARENTNOTIFY) and (Msg.WParam = WM_CREATE) then
Begin
inherited;
FMyHeaderHandle := Msg.lParam;
FMyDefHeaderWndProc := Pointer(GetWindowLong(FMyHeaderHandle, GWL_WNDPROC));
SetWindowLong(FMyHeaderHandle, GWL_WNDPROC, LongInt(FMyHeaderInstance));
end;
end;
procedure TMYListView.MyHeaderWndProc(var Message: TMessage);
var DC: HDC;
Pos: TPoint;
begin
with Message do
Begin
Result := CallWindowProc(FMyDefHeaderWndProc, FMyHeaderHandle, Msg, WParam, LParam);
if Msg = WM_RBUTTONDOWN then
Begin
Pos.x := lParamLo;
Pos.y := lParamHi;
Pos := ClientToScreen(Pos);
FHeaderPopupMenu.Popup(Pos.x, Pos.y);
end
//Draw SortArrows
else if Msg = WM_PAINT then
Begin
DC := GetDC(FMyHeaderHandle);
DeleteLastArrow(DC);
if (FPressedColumn > -1) then
Begin
DrawColText(DC, FPressedColumn);
DrawArrow(DC, FSortDir, GetArrowPos(FPressedColumn));
end;
end;
end;
end;
function TMYListView.DrawColText(DC: HDC; ColumnIndex: integer): boolean;
var ColTitle: string;
Rect: TRect;
TextAlign, i: integer;
Brush: TBrush;
begin
Result := FALSE;
if FPressedColumn < 0 then Exit;
ColTitle := Columns[ColumnIndex].Caption;
while copy(ColTitle, length(ColTitle), 1) = '.' do ColTitle := copy(ColTitle, 1, Length(ColTitle) - 1);
GetWindowRect(WindowFromDC(DC), Rect);
with Rect do
Begin
Bottom := Bottom - Top - 2;
Top := 2;
Left := 6;
for i := 0 to Columns.Count - 1 do
Begin
if Columns[i].Index = ColumnIndex then break;
Left := Left + Columns[i].Width;
end;
Right := Left + Columns[ColumnIndex].Width - 8;
end;
Brush := TBrush.Create;
with Brush do
Begin
Style := bsSolid;
Color := clBtnFace;
SelectObject(DC, Handle);
end;
FillRect(DC, Rect, Brush.Handle);
TextAlign := DT_VCENTER;
if Columns[i].Alignment = taRightJustify then
Begin
if (ColumnIndex = FPressedColumn) then Rect.Left := Rect.Left + ARROW_SIZE;
Rect.Right := Rect.Right - 4;
TextAlign := TextAlign + DT_RIGHT;
end
else
Begin
if (ColumnIndex = FPressedColumn) then Rect.Right := Rect.Right - ARROW_SIZE - 10;
TextAlign := TextAlign + DT_LEFT;
end;
SelectObject(DC, Font.Handle);
SetBkColor(DC, GetSysColor(COLOR_3DFACE));
DrawTextEx(DC, PWideChar(ColTitle), Length(ColTitle), Rect, TextAlign + DT_END_ELLIPSIS, nil);
Result := TRUE;
end;
function TMYListView.DeleteLastArrow(DC: HDC): boolean;
var Rect: TRect;
Brush: TBrush;
begin
Result := FALSE;
if FLastPressedColumn < 0 then Exit;
with Rect do
Begin
TopLeft := GetArrowPos(FLastPressedColumn);
Left := Left - 1;
Top := Top - 1;
Right := Left + 12;
Bottom := Top + 13;
end;
Brush := TBrush.Create;
with Brush do
Begin
Color := clBtnFace;
Style := bsSolid;
SelectObject(DC, Handle);
end;
FillRect(DC, Rect, Brush.Handle);
Brush.Free;
DrawColText(DC, FLastPressedColumn);
Result := TRUE;
end;
function TMYListView.GetArrowPos(ColumnIndex: integer): TPoint;
var i: integer;
begin
Result.x := 0;
Result.Y := 0;
if ColumnIndex < 0 then Exit;
i := 0;
while i < Columns.Count do
Begin
Result.x := Result.x + Columns[i].Width;
if i = ColumnIndex then break;
i := i + 1;
end;
with Columns[ColumnIndex] do if Alignment = taRightJustify then Result.x := Result.x - Width + 4
else Result.x := Result.x - 20;
Result.y := 2;
end;
function TMYListView.DrawArrow(DC: HDC; Direction: integer; Offset: TPoint): boolean;
var Pen: TPen;
begin
Pen := TPen.Create;
Pen.Style := PSSolid;
case Direction of
1: //Asc
Begin
Pen.Color := clWhite;
SelectObject(DC, Pen.Handle);
//The White
MoveToEx(DC, Offset.x + 0, Offset.y + ARROW_SIZE, nil);
LineTo(DC, Offset.x + ARROW_SIZE, Offset.y + ARROW_SIZE);
LineTo(DC, Offset.x + 5, Offset.y + 0);
Pen.Color := clBtnShadow;
SelectObject(DC, Pen.Handle);
//The Grey
LineTo(DC, Offset.x + 0, OffSet.y + ARROW_SIZE);
end;
-1: //Desc
Begin
Pen.Color := clWhite;
SelectObject(DC, Pen.Handle);
//The White
MoveToEx(DC, Offset.x + ARROW_SIZE, Offset.y + 0 + 1, nil);
LineTo(DC, Offset.x + 5, Offset.y + ARROW_SIZE + 1);
Pen.Color := clBtnShadow;
SelectObject(DC, Pen.Handle);
//The Grey
LineTo(DC, Offset.x + 0, Offset.y + 0 + 1);
LineTo(DC, Offset.x + ARROW_SIZE, Offset.y + 0 + 1);
end;
end;
Pen.Free;
Result := TRUE;
end;
procedure TMYListView.SetListViewColumns;
var
NewColumn: TListColumn;
i: integer;
begin
if FDataSet <> nil then with FDataSet, Self.Columns do
begin
Clear; //clears any columns
for i := 0 to FieldCount - 1 do
if Fields[i].Visible then
begin
NewColumn := Add;
NewColumn.Caption := Fields[i].DisplayLabel;
NewColumn.Width := Fields[i].DisplayWidth * 10;
NewColumn.Alignment := Fields[i].Alignment;
end;
end;
end;
function TMYListView.BuildListView: boolean;
var NewListItem: TListItem;
i: integer;
begin
Result := FALSE;
FPressedColumn := -1;
FLastPressedColumn := -1;
Items.Clear;
if FDataSet = NIL then
begin
MessageDlg('The Dataset is NIL', mtError, [mbOK], 0);
Exit;
end;
try
FDataset.Open;
SetListViewColumns;
with FDataSet do
Begin
Items.BeginUpdate;
if not EOF then while not EOF do
begin
NewListItem := Items.Add;
for i := 0 to FieldCount - 1 do
begin
if Fields[i].Visible then
begin
if i = 0 then NewListItem.Caption := Fields[0].DisplayText
else if Fields[i].Visible then NewListItem.SubItems.Add(Fields[i].DisplayText);
end;
end;
Next;
Application.ProcessMessages;
end;
end;
Result := TRUE;
finally
FDataSet.Close;
Items.EndUpdate;
end;
end;
end.
Design time pkg BPL Source:
package MyListView_dcl;
{$R *.res}
{$IFDEF IMPLICITBUILDING This IFDEF should not be used by users}
{$ALIGN 8}
{$ASSERTIONS ON}
{$BOOLEVAL OFF}
{$DEBUGINFO OFF}
{$EXTENDEDSYNTAX ON}
{$IMPORTEDDATA ON}
{$IOCHECKS ON}
{$LOCALSYMBOLS ON}
{$LONGSTRINGS ON}
{$OPENSTRINGS ON}
{$OPTIMIZATION OFF}
{$OVERFLOWCHECKS OFF}
{$RANGECHECKS OFF}
{$REFERENCEINFO ON}
{$SAFEDIVIDE OFF}
{$STACKFRAMES ON}
{$TYPEDADDRESS OFF}
{$VARSTRINGCHECKS ON}
{$WRITEABLECONST OFF}
{$MINENUMSIZE 1}
{$IMAGEBASE $400000}
{$DEFINE DEBUG}
{$ENDIF IMPLICITBUILDING}
{$DESIGNONLY}
{$IMPLICITBUILD ON}
requires
rtl,
Vcl,
dbrtl,
adortl,
MylistView_rn;
contains
RegMyListView in 'RegMyListView.pas';
end.
Design time package Unit Source(RegMyListView.pas):
unit RegMyListView;
interface
Uses Classes,uMyListView1;
procedure Register;
implementation
procedure Register;
begin
RegisterComponents('MyComponents', [TMyListView]);
end;
end.
After installing the component , Drop the TMyListView , TADOCOnnection,TADOQuery and button on form.
if i comment the below line in TMYListView.WndProc Procedure or if i totally remove MyHeaderWndProc Procedure and its associated code, it is working as expected.
SetWindowLong(FMyHeaderHandle, GWL_WNDPROC, LongInt(FMyHeaderInstance));
I have no idea what's wrong with the code.
Upvotes: 0
Views: 574
Reputation: 164
Maybe this helps, depending on your compiling target (SetWindowLong is marked as superseded): https://msdn.microsoft.com/en-us/library/windows/desktop/ms644898(v=vs.85).aspx
Upvotes: 0