Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Programmieren allgemein (https://www.delphipraxis.net/40-programmieren-allgemein/)
-   -   ListView Spaltenbreite bei Form onResize neu berechnen? (https://www.delphipraxis.net/191451-listview-spaltenbreite-bei-form-onresize-neu-berechnen.html)

a.def 17. Jan 2017 23:31


ListView Spaltenbreite bei Form onResize neu berechnen?
 
Ich stehe wieder vor einem kleinen Problem.

Ich habe eine ListView mit ein paar Spalten. Sagen wir der einfachheithalber mal 5.
Aktuell ist es so, dass ich in einem Array in Prozent stehen habe, wie breit jede Spalte beim Form OnResize sein soll.
Delphi-Quellcode:
 aColumnDefWidth: array[0 .. 4] of Byte = (16, 16, 20, 20, 25); // = 97%
(Edit: [0 .. 4] hinzugefügt, vergessen gehabt)


Mein Helfer:
Delphi-Quellcode:
function getPercentageValue(const aBase: Extended; const aPercentage: Extended): Int64;
begin
 if (aPercentage > 0) and (aBase > 0) then
  Result := Trunc(aBase / 100 * aPercentage)
 else
  Result := 0;
end;
Und im FormResize dann in etwa
Delphi-Quellcode:
for i := 0 to ListView1.Columns.Count - 1 do
 ListView1.Columns[i].Width := getPercentageValue(ListView1.Width, aColumnDefWidth[i]) - 2;
Das funktioniert wunderbar. Beim verändern der Größe meines Formulars werden alle Spalten genau so groß (in Prozent) gemacht, wie ich es in aColumnDefWidth definiert habe.

Nun zum Problem bzw. zur Frage:
angenommen ich verändere die Spaltenbreite manuell im ListView zur Laufzeit - wie schaffe ich es, dass die prozentuale von mir zur Laufzeit veränderte Breite beibehalten wird?

nahpets 17. Jan 2017 23:46

AW: ListView Spaltenbreite bei Form onResize neu berechnen?
 
Im Ereignis OnColumnResize des Listviews Deine Routine aufrufen?

a.def 17. Jan 2017 23:48

AW: ListView Spaltenbreite bei Form onResize neu berechnen?
 
Wie genau müsste ich die da denn aufrufen?
Verstehe ich gerade nicht so genau.

Ich verändere ja die Größe der kompletten Anwendung, nicht die einer einzelnen Spalte.

Edit: OnColumnResize gibt es bei mir gar nicht.

nahpets 18. Jan 2017 00:07

AW: ListView Spaltenbreite bei Form onResize neu berechnen?
 
Sorry, hatte in der Delphi 7 versehentlich in die CLX-Hilfe geschaut, statt in die der VCL.

Die VCL scheint das Ereignis nicht zu kennen :-(

Schade.

Dann eventuell im Ereignis OnMouseUp.

Dort die Position des Mauszeigers ermitteln und wenn der innerhalb der Spaltenüberschriften ist wieder
Delphi-Quellcode:
for i := 0 to ListView1.Columns.Count - 1 do
 ListView1.Columns[i].Width := getPercentageValue(ListView1.Width, aColumnDefWidth[i]) - 2;
aufrufen. Nur macht das ja dann die Änderung der Spaltenbreite wieder rückgängig.

HolgerX 18. Jan 2017 04:25

AW: ListView Spaltenbreite bei Form onResize neu berechnen?
 
Hmm..

Oder statt des Rückgängig machens, speicherst Du in deinem Array die veränderten Prozentwerte und behält somit das Spaltenverhältnis.

EWeiss 18. Jan 2017 05:19

AW: ListView Spaltenbreite bei Form onResize neu berechnen?
 
Verstehe nicht warum du ein Array für die Spaltenbreite benötigst.
Verwende doch einfach LVSCW_AUTOSIZE oder LVSCW_AUTOSIZE_USEHEADER

gruss

t.roller 18. Jan 2017 06:40

AW: ListView Spaltenbreite bei Form onResize neu berechnen?
 
Liste der Anhänge anzeigen (Anzahl: 1)
http://stackoverflow.com/questions/9...n-virtual-mode

Delphi-Quellcode:
//   List View Column Autosize (Virtual Mode)  
unit Unit1;

interface

uses
//  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, StdCtrls,
//  Forms, Dialogs, StrUtils, ComCtrls, CommCtrl;
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes,
  Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.ComCtrls, Winapi.CommCtrl,
  Vcl.StdCtrls, System.StrUtils, Vcl.ExtCtrls;

type
  TSampleRecord = record
    Column1: string;
    Column2: string;
    Column3: string;
    Column4: string;
  end;
  TSampleArray = array [0..49] of TSampleRecord;

type
  TForm1 = class(TForm)
    Button1: TButton;
    ListView1: TListView;
    Panel1: TPanel;
    procedure FormCreate(Sender: TObject);
    procedure Button1Click(Sender: TObject);
    procedure FormResize(Sender: TObject);
  private
    SampleArray: TSampleArray;
    procedure AutoResizeColumn(const AListView: TListView;
      const AColumn: Integer);
    procedure OnListViewData(Sender: TObject; Item: TListItem);
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

//   TForm1.AutoResizeColumn - auto-size column  
// AListView - list view object instance
// AColumn - index of the column to be auto-sized

procedure TForm1.AutoResizeColumn(const AListView: TListView;
  const AColumn: Integer);
var
  S: string;
  I: Integer;
  MaxWidth: Integer;
  ItemWidth: Integer;
begin
  // set the destination column width to the column's caption width
  // later on we'll check if we have a wider item
  MaxWidth := ListView_GetStringWidth(AListView.Handle,
    PChar(AListView.Columns.Items[AColumn].Caption));
  // iterate through all data items and check if their captions are
  // wider than the currently widest item if so then store that value
  for I := 0 to High(SampleArray) do
  begin
    case AColumn of
      0: S := SampleArray[I].Column1;
      1: S := SampleArray[I].Column2;
      2: S := SampleArray[I].Column3;
      3: S := SampleArray[I].Column4;
    end;
    ItemWidth := ListView_GetStringWidth(AListView.Handle, PChar(S));
    if MaxWidth < ItemWidth then MaxWidth := ItemWidth;
  end;
  // here is hard to say what value to use for padding to prevent the
  // string to be truncated; I've found the suggestions to use 6 px
  // for item caption padding and 12 px for subitem caption padding,
  // but a few quick tests confirmed me to use at least 7 px for items
  // and 14 px for subitems
  if AColumn = 0 then
    MaxWidth := MaxWidth + 7
  else
    MaxWidth := MaxWidth + 14;
  // and here we set the column width with caption padding included
  AListView.Columns.Items[AColumn].Width := MaxWidth;
end;

//   TForm1.FormCreate - setup the list view and fill custom data  
procedure TForm1.FormCreate(Sender: TObject);
var
  I: Integer;
begin
  ListView1.ViewStyle := vsReport;
  ListView1.Columns.Add.Caption := 'Column 1';
  ListView1.Columns.Add.Caption := 'Column 2';
  ListView1.Columns.Add.Caption := 'Column 3';
  ListView1.Columns.Add.Caption := 'Column 4';
  ListView1.OwnerData := True;
  ListView1.OnData := OnListViewData;
  ListView1.Items.Count := High(SampleArray);

  for I := 0 to High(SampleArray) do
  begin
    SampleArray[I].Column1 := 'Cell [0, ' + IntToStr(I) + '] ' +
      DupeString('x', I);
    SampleArray[I].Column2 := 'Cell [1, ' + IntToStr(I) + '] ' +
      DupeString('x', High(SampleArray) - I);
    SampleArray[I].Column3 := 'Cell [2, ' + IntToStr(I) + '] ' +
      DupeString('x', I);
    SampleArray[I].Column4 := 'Cell [3, ' + IntToStr(I) + '] ' +
      DupeString('x', High(SampleArray) - I);
  end;
end;

//   TForm1.FormCreate - custom handler for OnData event  
procedure TForm1.OnListViewData(Sender: TObject; Item: TListItem);
begin
  Item.Caption :=  SampleArray[Item.Index].Column1;
  Item.SubItems.Add(SampleArray[Item.Index].Column2);
  Item.SubItems.Add(SampleArray[Item.Index].Column3);
  Item.SubItems.Add(SampleArray[Item.Index].Column4);
end;

//   TForm1.Button1Click - auto-resize all 4 columns  
procedure TForm1.Button1Click(Sender: TObject);
begin
  AutoResizeColumn(ListView1, 0);
  AutoResizeColumn(ListView1, 1);
  AutoResizeColumn(ListView1, 2);
  AutoResizeColumn(ListView1, 3);
end;

procedure TForm1.FormResize(Sender: TObject);
begin
  Button1.Click;
end;

end.

t.roller 18. Jan 2017 07:49

AW: ListView Spaltenbreite bei Form onResize neu berechnen?
 
Bei mir muss die Variable so aussehen:
Delphi-Quellcode:
  aColumnDefWidth: array [0..4] of Byte = (16, 16, 20, 20, 25); // = 97%
Im Übrigen halte ich die prozentuale Aufteilung nicht für sinnvoll,
weil auf den Inhalt keine Rücksicht genommen wird.

a.def 18. Jan 2017 09:19

AW: ListView Spaltenbreite bei Form onResize neu berechnen?
 
Zitat:

Zitat von EWeiss (Beitrag 1359128)
Verstehe nicht warum du ein Array für die Spaltenbreite benötigst. Verwende doch einfach LVSCW_AUTOSIZE oder LVSCW_AUTOSIZE_USEHEADER gruss

Zitat:

Zitat von t.roller (Beitrag 1359135)
Im Übrigen halte ich die prozentuale Aufteilung nicht für sinnvoll, weil auf den Inhalt keine Rücksicht genommen wird.

Naja die Sache ist, ich möchte den Benutzer selber entscheiden lassen wie er die Spalten aufteilt.
Sie sollen nicht auto-angepasst und es soll nicht die Mindest-Größe berechnet werden.

Bestes Beispiel für mein Vorhaben bietet der JDownloader 2.0.

Die einfachste Möglich wäre mit einem OnColumnResize-Event. Das hat die normale ListView aber nicht und eine extra Komponente möchte ich dafür nicht installieren.

Uwe Raabe 18. Jan 2017 09:50

AW: ListView Spaltenbreite bei Form onResize neu berechnen?
 
Wie gehst du denn damit um, wenn der User eine Spaltenbreite ändert? Damit würde ja die Summe aller Spaltenbreiten nicht mehr den Listview komplett ausfüllen und deine Prozente passen nicht mehr.

Aviator 18. Jan 2017 09:58

AW: ListView Spaltenbreite bei Form onResize neu berechnen?
 
Ich habe leider keine Lösung für dich die mit dem TListView funktioniert.

Nur einen Tipp mit welcher Komponente du in Zukunft deine Anwendungen entwickeln könntest/solltest: VirtualTreeView (GitHub)

Wenn du dich doch dazu durchringen kannst eine andere Komponente zu nutzen, dann würde ich dir die Komponente wärmstens ans Herz legen. Dort kannst du nämlich alles frei definieren. Die Lernkurve ist zwar sehr steil beim VST, aber es loht sich definitiv.

Und du brauchst die Komponente ja auch nicht zu installieren, einfach nur eine Instanz erzeugen und fertig. :stupid:

Zitat:

Zitat von Uwe Raabe (Beitrag 1359150)
Wie gehst du denn damit um, wenn der User eine Spaltenbreite ändert? Damit würde ja die Summe aller Spaltenbreiten nicht mehr den Listview komplett ausfüllen und deine Prozente passen nicht mehr.

Wenn der User unbedingt eine Spaltenbreite verändern will, dann muss er für mein Empfinden damit leben, dass die Spaltenbreite aller Spalten eben etwas "überhängt". Alternativ könnte man aus dem Array das die Prozentwerte der Spalten angibt einen Record machen und den noch mit einer Property
Delphi-Quellcode:
Priority
versehen. Die höchste Priorität (1) bleibt unverändert, die niedrigste Priorität (bspw. 5) wird in der Breite so verkleinert, dass noch alles auf den Schirm passt.

Wenn man es dann richtig machen will, dann spendiert man dem Record noch eine Property
Delphi-Quellcode:
MinWidth
. Würde der Wert unterschritten, dann geht man zur nächsten Spalte und verkleinert diese. Wurde die Spalte vom Benutzer jetzt so groß gezogen, dass, selbst wenn alle Spalten auf MinWidth runtergerechnet wurden, nicht alles auf den Schirm passt, dann hat der User eben Pech gehabt und muss horizontal scrollen.

Das wäre jetzt mal meine schnell dahingeschriebene Vorgehensweise.

a.def 18. Jan 2017 10:01

AW: ListView Spaltenbreite bei Form onResize neu berechnen?
 
Genau daran arbeite ich gerade aber es geht alles schief.

Meine Idee ist aktuell WMEnterSizeMove-Event die aktuellen, prozentualen Breiten aller Spalten in ein extra Array zu schreiben
und dann im FormResize-Event wieder umzurechnen. Klappt aber nicht ganz.

Delphi-Quellcode:
procedure TForm1.WMEnterSizeMove(var msg: TMessage);
var
 i: Byte;
begin
 iAllColWidth := 0;
 for i := 0 to ListView1.Columns.Count - 1 do
  Inc(iAllColWidth, ListView1.Columns[i].Width);

 SetLength(lvDefaultWidthInPercent, Length(aDefaultSettings.lvDefaultWidthInPercent));
 for i := 0 to ListView1.Columns.Count - 1 do
  lvDefaultWidthInPercent[i] := getPercent(iAllColWidth, ListView1.Column[i].Width);

 inherited;
end;

// OnResize (Form)
for i := 0 to ListView1.Columns.Count - 1 do
 ListView1.Columns[i].Width := getPercentageValue(ListView1.Width, lvDefaultWidthInPercent[i]) - 2;
VirtualTreeView ist wirklich eine tolle Sache. Nur beherrscht die auch die verschiedenen Ansichten wie die ListView? Konnte ich auf deren Webseite noch nicht ausfindig machen.

Edit:
im Prinzip funktioniert alles mit obigen Code nun wie gewüscht. Einziges Problem ist, dass ich getPercentageValue als Base ListView1.Width übergebe und somit alle Spalten insgesamt immer maximal so breit sein können wie die ListView. iAllColWidth kann ich zwar übergeben, aber dann werden die Spalten kleiner statt größer :D

Aviator 18. Jan 2017 11:07

AW: ListView Spaltenbreite bei Form onResize neu berechnen?
 
Zitat:

Zitat von a.def (Beitrag 1359152)
VirtualTreeView ist wirklich eine tolle Sache. Nur beherrscht die auch die verschiedenen Ansichten wie die ListView? Konnte ich auf deren Webseite noch nicht ausfindig machen.

Also der VST ist ja darauf ausgelegt einen Tree anzuzeigen. Man kann ihn auch dazu "missbrauchen" eine Tabelle anzuzeigen was ja im Prinzip nichts anderes ist als Nodes nur auf der Root Ebene zu erzeugen welche eine bestimmte Anzahl an Spalten haben.

Die Kachel Ansicht funktioniert damit nicht direkt von Haus aus. Möglich wäre es schon indem man sehr viel selbst zeichnet (evtl. wäre hier der VirtualDrawTree der bessere Kandidat). Dann muss eben pro Node und pro Column unterschieden werden was man zeichnet und angeklickt hat. Ist etwas mehr Arbeit weil es nicht direkt die eigentliche Funktion abbildet, aber möglich ist es schon. Hatte mal so etwas ähnliches gemacht bei dem es darauf ankam, dass jede "Zelle" etwas spezielles machen sollte.

Durch die Abstraktion und das Virtuelle und die vielen Events die man zur Verfügung hat kann man sehr vieles mit der Komponente machen.

Aber man muss dazu sagen, dass man im Grunde alles selbst programmieren muss. Bei der ListView Komponente stellt man ja nur den Style um und schon hat man das gewünschte Ergebnis.

Das musst du also schlussendlich für dich selbst entscheiden ob sich der Aufwand für dich lohnt. Also ich will die Komponente nicht mehr missen und verwende sie fast überall für alles mögliche. :)

Uwe Raabe 18. Jan 2017 11:14

AW: ListView Spaltenbreite bei Form onResize neu berechnen?
 
Folgender Code im Listview-Resize könnte ein Ansatz sein. Dabei werden die Prozente nicht global abgelegt, sondern aus der aktuellen Aufteilung ermittelt. Ausnahme ist die initiale Anzeige.
Delphi-Quellcode:
procedure TForm154.ListView1Resize(Sender: TObject);
var
  I: Integer;
  widthArr: TArray<Double>;
  cnt: Integer;
  totalWidth: Integer;
begin
  cnt := ListView1.Columns.Count;
  SetLength(widthArr, cnt);
  totalWidth := 0;
  for I := 0 to cnt - 1 do begin
    totalWidth := totalWidth + ListView1.Columns[I].Width;
  end;
  if totalWidth = 0 then begin
    { initiale Aufteilung festlegen }
    widthArr[0] := 0.16;
    widthArr[1] := 0.16;
    widthArr[2] := 0.20;
    widthArr[3] := 0.20;
    widthArr[4] := 1 - Sum(Copy(WidthArr, 0, 4));
  end
  else begin
    for I := 0 to cnt - 1 do begin
      widthArr[I] := ListView1.Columns[I].Width/totalWidth;
    end;
  end;
  totalWidth := 0;
  for I := 0 to cnt - 2 do begin
    ListView1.Columns[I].Width := Round(ListView1.ClientWidth*widthArr[I]);
    totalWidth := totalWidth + ListView1.Columns[I].WidthType;
  end;
  { wegen Rundungsfehlern bekommt die letzte Spalte einfach den Rest }
  ListView1.Columns[cnt - 1].Width := ListView1.ClientWidth - totalWidth;
end;


Alle Zeitangaben in WEZ +1. Es ist jetzt 19:02 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