AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Programmierung allgemein GUI-Design mit VCL / FireMonkey / Common Controls Delphi ListView Breite der letzten Spalte beim Resize der Form
Thema durchsuchen
Ansicht
Themen-Optionen

ListView Breite der letzten Spalte beim Resize der Form

Ein Thema von Dalai · begonnen am 13. Okt 2023 · letzter Beitrag vom 25. Okt 2023
Antwort Antwort
Benutzerbild von Dalai
Dalai

Registriert seit: 9. Apr 2006
1.682 Beiträge
 
Delphi 5 Professional
 
#1

ListView Breite der letzten Spalte beim Resize der Form

  Alt 13. Okt 2023, 16:44
Hallo in die Gemeinde .

Gegeben sei ein TListView mit Anchors an allen 4 Kanten auf einem TForm. Die letzte Spalte des ListView soll immer die restliche Breite des ListView einnehmen, wenn das Formular in der Breite verändert wird. Ich will also eine Art TListColumn.AutoSize anhand der Breite statt des enthaltenen Texts. Dafür bietet sich das OnResize-Event des ListView an.
Delphi-Quellcode:
procedure TfmMain.ListViewResize(Sender: TObject);
var
  Llv: TListView;
  Lcolwidth, Ltotalcolwidth, i: integer;
  Lcol: TListColumn;
begin
    Llv:= nil;
    if Sender is TListView then
        Llv:= (Sender as TListView);
    if NOT Assigned(Llv) then
        Exit;
    Ltotalcolwidth:= 0;
    for i:= 0 to Pred(Llv.Columns.Count) do begin
        Inc(Ltotalcolwidth, Llv.Columns[i].Width);
    end;
    Lcol:= Llv.Columns[Llv.Columns.Count-1];
    Lcolwidth:= Llv.ClientWidth - Ltotalcolwidth - Lcol.Width;
    if (Lcolwidth >= Lcol.MinWidth) then
        Lcol.Width:= Lcolwidth;
end;
Probleme:
  • Das ListView flackert im unteren Bereich, weil der horizontale Scrollbalken ein- und ausgeblendet wird, wenn die Breite verkleinert wird. Der Scrollbalken verschwindet wieder, wenn man einmal auf den nach rechts zeigenden Pfeil klickt; es wäre also eigentlich gar nicht nötig, dass das ListView den Scrollbalken anzeigt.
    Es scheint so, als wird der Scrollbalken pro Pixel Verkleinerung abwechselnd entweder angezeigt oder ausgeblendet.
    Siehe auch Bild im Anhang (bitte Stil und teilweise fehlende Themes ignorieren).
  • Wenn eine der anderen Spalten vergrößert wird, wird die letzte Spalte bei einer Breitenänderung schmaler, unter Umständen sehr viel schmaler.

Erreichen will ich Folgendes:
  1. Die letzte Spalte soll beim Verändern der Breite den restlichen Raum einnehmen. Das ist mit obigem Code soweit abgedeckt.

  2. Sobald eine der anderen Spalten vergrößert wird, so dass das ListView einen horizontalen Scrollbalken anzeigt, soll die Breite der letzten Spalte nicht verändert werden, solange der Scrollbalken sichtbar ist. Damit soll vor allem verhindert werden, dass die letzte Spalte bei einer Breitenänderung plötzlich sehr schmal wird.
    Ich weiß, dass man mit GetWindowLong(Llv.Handle, GWL_STYLE); ermitteln kann, ob ein Scrollbalken sichtbar ist, aber die Flackerei macht eine sinnvolle Verwendung unmöglich, siehe nächster Punkt.

  3. Die Flackerei durch das Ein- und Ausblenden des horizontalen Scrollbalkens soll verschwinden oder unterdrückt werden.

Grüße
Dalai
Miniaturansicht angehängter Grafiken
dp_listview_breite-scrollbalken.png  
  Mit Zitat antworten Zitat
Benutzerbild von jaenicke
jaenicke

Registriert seit: 10. Jun 2003
Ort: Berlin
9.648 Beiträge
 
Delphi 11 Alexandria
 
#2

AW: ListView Breite der letzten Spalte beim Resize der Form

  Alt 13. Okt 2023, 16:50
Setze doch die Breite der letzten Spalte einfach auf LVSCW_AUTOSIZE.
Sebastian Jänicke
AppCentral
  Mit Zitat antworten Zitat
Benutzerbild von Dalai
Dalai

Registriert seit: 9. Apr 2006
1.682 Beiträge
 
Delphi 5 Professional
 
#3

AW: ListView Breite der letzten Spalte beim Resize der Form

  Alt 13. Okt 2023, 17:50
Setze doch die Breite der letzten Spalte einfach auf LVSCW_AUTOSIZE.
Das passt die Spaltenbreite an den enthaltenen Text an, macht also dasselbe wie TListColumn.AutoSize:= True; . Ich will aber die gesamte verbleibende Breite des ListView ausnutzen.

Mit ListView_SetColumnWidth(Llv.Handle, Lcol.Index, LVSCW_AUTOSIZE_USEHEADER); spart man sich zwar die Iteration über die Spalten und die Berechnung der verbleibenden Breite, aber es führt zum selben Ergebnis wie der Code oben, mit Flackern und allem .

Grüße
Dalai
  Mit Zitat antworten Zitat
Papaschlumpf73
Online

Registriert seit: 3. Mär 2014
Ort: Berlin
442 Beiträge
 
Delphi 12 Athens
 
#4

AW: ListView Breite der letzten Spalte beim Resize der Form

  Alt 13. Okt 2023, 18:00
Das geht doch mit letzte Spalte.Width := -2 oder nicht?
  Mit Zitat antworten Zitat
Benutzerbild von Dalai
Dalai

Registriert seit: 9. Apr 2006
1.682 Beiträge
 
Delphi 5 Professional
 
#5

AW: ListView Breite der letzten Spalte beim Resize der Form

  Alt 13. Okt 2023, 18:26
Nein, Lcol:= ColumnHeaderWith; erzeugt ebenfalls nicht das gewünschte Verhalten. Die letzte Spalte wird beim Verbreitern nicht auf den verbleibenden Platz vergrößert. Zudem soll die letzte Spalte nicht schmaler werden, wenn die anderen Spalten vergrößert werden.

Grüße
Dalai
  Mit Zitat antworten Zitat
Benutzerbild von jaenicke
jaenicke

Registriert seit: 10. Jun 2003
Ort: Berlin
9.648 Beiträge
 
Delphi 11 Alexandria
 
#6

AW: ListView Breite der letzten Spalte beim Resize der Form

  Alt 13. Okt 2023, 18:27
Mit ListView_SetColumnWidth(Llv.Handle, Lcol.Index, LVSCW_AUTOSIZE_USEHEADER); spart man sich zwar die Iteration über die Spalten und die Berechnung der verbleibenden Breite, aber es führt zum selben Ergebnis wie der Code oben, mit Flackern und allem .
Das ist dann ein Bug in deiner Delphiversion. Siehe Anhang... dort habe ich nur in Delphi 11 AutoSize auf True gesetzt.

Ich selbst verwende meistens die Virtual TreeView. Damit kann man viel mehr selbst anpassen. Allerdings läuft die aktuelle Version auch nur mit aktuellen Delphiversionen, d.h. du müsstest eine passende Version nutzen. Die alte Version im Anhang lief ab Delphi 4.
Angehängte Dateien
Dateityp: zip VTSourceOnly_4.7.0.zip (359,0 KB, 0x aufgerufen)
Dateityp: zip Project171.zip (965,8 KB, 3x aufgerufen)
Sebastian Jänicke
AppCentral
  Mit Zitat antworten Zitat
Benutzerbild von Dalai
Dalai

Registriert seit: 9. Apr 2006
1.682 Beiträge
 
Delphi 5 Professional
 
#7

AW: ListView Breite der letzten Spalte beim Resize der Form

  Alt 13. Okt 2023, 19:50
Ich weiß nicht, wie ich es genauer oder anders beschreiben soll, was ich erreichen will, als ich das im OP bereits getan habe. Setzt man AutoSize der letzten Spalte auf True, nimmt sie den verbleibenden Platz ein. Bei Größenänderung des ListView wird sie mit angepasst. All das ohne Flackern. Soweit so schön.

Vergrößert man eine der anderen Spalten, wird der horizontale Scrollbalken eingeblendet, was auch noch in Ordnung und gewünscht ist. Jetzt kommt das große Aber: Ändert man anschließend die Breite des Formulars (und damit des ListViews), wird die letzte Spalte schmaler und der Scrollbalken verschwindet. Das will ich nicht! Die letzte Spalte soll ihre Breite behalten, solange der Scrollbalken sichtbar ist. Danach darf/soll sie wieder der Breite des ListView folgen.

Vielleicht wird es anhand eines Beispiels deutlicher:
Vier Spalten mit Breiten 100, 40, 40 und 300. Breite der ersten Spalte wird auf 300 erhöht, wodurch der Scrollbalken erscheint (OK). Anschließend wird die Breite des Formulars/ListViews geändert und danach ist die letzte Spalte nur noch 100 breit und der Scrollbalken weg; sie soll aber bei 300 bleiben, bis das ListView so breit gemacht wurde, dass der Scrollbalken ausgeblendet wird.

Grüße
Dalai
  Mit Zitat antworten Zitat
Benutzerbild von jaenicke
jaenicke

Registriert seit: 10. Jun 2003
Ort: Berlin
9.648 Beiträge
 
Delphi 11 Alexandria
 
#8

AW: ListView Breite der letzten Spalte beim Resize der Form

  Alt 13. Okt 2023, 21:38
Das mit dem Beibehalten der Breite hatte ich falsch verstanden.

Also bei mir flackert mit deinem Code gar nichts und die Scrolleiste bleibt auch nicht so sichtbar wie in deinem Screenshot.

Mit der Virtual TreeView fährst du mit solchen speziellen Wünschen aber sicher am besten, denn dort kannst du das Feature direkt in die Breitensteuerung der Spalte einbauen, aber auch von außen sollte das machbar sein.
Sebastian Jänicke
AppCentral
  Mit Zitat antworten Zitat
Benutzerbild von Dalai
Dalai

Registriert seit: 9. Apr 2006
1.682 Beiträge
 
Delphi 5 Professional
 
#9

AW: ListView Breite der letzten Spalte beim Resize der Form

  Alt 25. Okt 2023, 00:30
Erstmal ein Dankeschön fürs Mitdenken und Mitmachen an alle Beteiligten .

Inzwischen hab ich die Sache für mich zufriedenstellend gelöst. Ich stelle mal hier den Code mit etwas Erklärung ein, damit es auch anderen weiterhelfen kann.

Die Grundlage bilden die Ereignisse OnResize und OnCanResize des ListView. OnCanResize wird vor dem Resize eines Controls ausgelöst, OnResize danach. OnResize verwende ich schon von Beginn an, OnCanResize ist in TControl als protected property deklariert und daher nicht direkt zuweis- und nutzbar. Um dies zu umgehen, hab ich mir eine kleine Unit geschrieben:
Delphi-Quellcode:
unit VCLHacks;

interface

uses ComCtrls, Controls;

type
  TMyListView = class(TListView)
  end;

procedure ListView_SetOnCanResize(const AListView: TListView; const AOnCanResize: TCanResizeEvent);

implementation

procedure ListView_SetOnCanResize(const AListView: TListView; const AOnCanResize: TCanResizeEvent);
begin
    TMyListView(AListView).OnCanResize:= AOnCanResize;
end;

end.
Hinweis:
Die nachfolgenden Code-Auszüge sind fürs Forum vereinfachte und verkürzte Beispiele. Im eigentlichen Programm gibt viele in einer TList gespeicherte ListViews, die alle dieselben Eigenschaften und Events zugewiesen bekommen.

Beim Starten werden die Eigenschaften und Ereignisse zugewiesen:
Delphi-Quellcode:
procedure TfmMain.FormCreate(Sender: TObject);
begin
    FScrollBarSize:= Windows.GetSystemMetrics(SM_CXVSCROLL);
    if ListView1.Columns.Count > 0 then
        ListView1.Columns[ListView1.Columns.Count-1].AutoSize:= True;
end;

procedure TfmMain.FormShow(Sender: TObject);
begin
    SetListViewEvents(ListView1);
end;

procedure TfmMain.SetListViewEvents(const AListView: TListView);
begin
    if NOT Assigned(AListView) then Exit;
    AListView.OnResize:= ListViewResize;
    ListView_SetOnCanResize(AListView, ListViewCanResize);
end;
Die Behandlung der Ereignisse sieht so aus:
Delphi-Quellcode:
procedure TfmMain.ListViewCanResize(Sender: TObject; var NewWidth, NewHeight: Integer; var Resize: Boolean);
var
  Llv: TListView;
  Lcol: TListColumn;
  Ltotalcolwidth, i: integer;
begin
    if FListViewItemsUpdating then Exit;
    if NOT (Sender is TListView) then Exit;
    Llv:= TListView(Sender);

    Ltotalcolwidth:= 0;
    for i:= 0 to Pred(Llv.Columns.Count) do
        Inc(Ltotalcolwidth, ListView_GetColumnWidth(Llv.Handle, i));

    Lcol:= Llv.Columns[Llv.Columns.Count-1];
    Lcol.AutoSize:= ((Ltotalcolwidth - FScrollBarSize) <= Llv.ClientWidth);
end;
AutoSize der letzten Spalte wird ausgeschaltet, wenn die Summe der Breite aller Spalten größer ist als die sichtbare Breite des ListView (ClientWidth). AutoSize wird wieder aktiviert, wenn die Summe der Breite der Spalten maximal so groß ist wie ClientWidth zulässt. Dadurch wird die letzte Spalte nicht mehr schmaler, wenn andere Spalten vergrößert werden und anschließend das ListView in der Breite verändert wird.

Da die ListViews während der Programmlaufzeit mit immer wieder neuen Daten gefüllt werden können, erscheint ggf. ein vertikaler Scrollbalken, was einen horizontalen Scrollbalken aktiviert. Um das zu behandeln, mache ich mir das im OnShow zugewiesene OnResize-Ereignis zunutze:
Delphi-Quellcode:
procedure TfmMain.ListViewResize(Sender: TObject);
var
  Llv: TListView;
begin
    Llv:= nil;
    if Sender is TListView then
        Llv:= (Sender as TListView);
    if Assigned(Llv) then
        ListViewAdjustLastColumnWidth(Llv);
end;

procedure TfmMain.ListViewAdjustLastColumnWidth(const AListView: TListView);
var
  Ltotalcolwidth, Lcolwidth, i: integer;
  Lcol: TListColumn;
begin
    if NOT Assigned(AListView) then
        Exit;

    Ltotalcolwidth:= 0;
    for i:= 0 to Pred(AListView.Columns.Count) do
        Inc(Ltotalcolwidth, ListView_GetColumnWidth(AListView.Handle, i));

    Lcol:= AListView.Columns[AListView.Columns.Count-1];
    Lcolwidth:= ListView_GetColumnWidth(AListView.Handle, Lcol.Index);
    if ((Ltotalcolwidth - FScrollBarSize) = AListView.ClientWidth) then begin
        Lcolwidth:= Lcolwidth - FScrollBarSize;
        ListView_SetColumnWidth(AListView.Handle, Lcol.Index, Lcolwidth);
        Exit;
    end;
    // [...]
end;
Die letzte Spalte wird verkleinert, wenn die Summe aller Spaltenbreiten die ClientWidth des ListViews um eine Scrollbalken-Breite überschreitet. Dadurch verschwindet der horizontale Scrollbalken wieder.

Grüße
Dalai

Geändert von Dalai (25. Okt 2023 um 00:37 Uhr)
  Mit Zitat antworten Zitat
Antwort Antwort


Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 21:56 Uhr.
Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz