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