Delphi-получить и установить положение полосы прокрутки ListView
Это может показаться глупым и простым вопросом, и все же я не смог найти удовлетворительного ответа. В принципе, у меня есть TListview (style = vsReport) с данными. Иногда мне приходится обновлять его, и поэтому я должен очистить listview и снова заполнить его обновленными данными.
однако, когда я это делаю, позиция полосы прокрутки сбрасывается до 0. Я хотел бы иметь возможность получить позицию полосы прокрутки до очистки и установить ее обратно в то, что это было до. Если обновленные данные имеют точно такое же количество строк, как и старые данные, мне нужно, чтобы полоса прокрутки была в том же положении, что и раньше; если нет, мне просто нужно, чтобы она была более или менее в том же месте, что и раньше.
кажется легким, не так ли? Тем не менее, все, что я нашел, это хаки или твики с TopItem и MakeVisible. Есть ли какой-нибудь подходящий метод для этого?
спасибо!
2 ответов
сохранить верхний элемент перед очисткой,
FSaveTop := ListView1.TopItem;
после обновления прокрутите список так, чтобы позиция " y " сохраненного верхнего элемента была 0 (+высота заголовка):
var
R: TRect;
begin
if Assigned(FSaveTop) then begin
// account for header height
GetWindowRect(ListView_GetHeader(ListView1.Handle), R);
ListView1.Scroll(0, FSaveTop.Position.Y - (R.Bottom - R.Top));
end;
end;
на самом деле, поскольку вы повторно заполняете listview, вам нужно разработать механизм, чтобы найти, какой элемент вы хотите быть наверху, а не сохранять ссылку на него.
Если вам не нравится изменять положение прокрутки через "верхний элемент", так как функции, такие как SetScrollInfo, SetScrollPos не будет обновлять клиентскую область элемента управления, вы можете использовать GetScrollInfo чтобы получить "НКО"TScrollInfo перед очисткой списка, а затем отправить, что многие WM_VSCROLL сообщения с SB_LINEDOWN после заполнения.
сохранить позиции прокрутки:
var
FPos: Integer;
SInfo: TScrollInfo;
begin
SInfo.cbSize := SizeOf(SInfo);
SInfo.fMask := SIF_ALL;
GetScrollInfo(ListView1.Handle, SB_VERT, SInfo);
FPos := SInfo.nPos;
...
после заполнения прокрутите (при условии, что положение прокрутки равно 0):
var
R: TRect;
begin
...
R := ListView1.Items[0].DisplayRect(drBounds);
ListView1.Scroll(0, FPos * (R.Bottom - R.Top));
или
var
i: Integer;
begin
...
for i := 1 to FPos do
SendMessage(ListView1.Handle, WM_VSCROLL, SB_LINEDOWN, 0);
type
TForm1 = class(TForm)
ListView1: TListView;
Timer1: TTimer;
Timer2: TTimer;
procedure FormCreate(Sender: TObject);
procedure Timer1Timer(Sender: TObject);
procedure Timer2Timer(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
pb:array [0..20] of Tprogressbar;
i:integer;
mintop,spacetop,readtop:integer;
implementation
{$R *.dfm}
procedure TForm1.FormCreate(Sender: TObject);
begin
mintop:=19;
spacetop:=14;
for i:=0 to 20 do
begin
listview1.AddItem('Item no'+inttostr(i),nil);
pb[i]:=Tprogressbar.create(self);
pb[i].Parent:=listview1;
pb[i].width:=120;
pb[i].height:=14;
pb[i].top:=mintop+i*spacetop;
pb[i].position:=i*5;
pb[i].Left:=listview1.Column[0].Width;
end;
end;
procedure TForm1.Timer1Timer(Sender: TObject);
var
z,FPos: Integer;
SInfo: TScrollInfo;
begin
SInfo.cbSize := SizeOf(SInfo);
SInfo.fMask := SIF_ALL;
GetScrollInfo(ListView1.Handle, SB_VERT, SInfo);
FPos := SInfo.nPos;
form1.caption:='FPOS='+inttostr(fpos);
if fpos>0 then
begin
for z:=0 to 11 do
begin
pb[z+fpos-1].Top:=mintop+z*spacetop;
form1.caption:=form1.caption+' Z='+inttostr(z)+' !';
end;
end;
end;
procedure TForm1.Timer2Timer(Sender: TObject);
var x:integer;
begin
for x:=0 to 20 do
begin
pb[x].Position:=Pb[x].position+1;
end;
end;
end.
/// Code by BRTH1 - Simao Coelho - Portugal