Entwickler-Ecke

Grafische Benutzeroberflächen (VCL & FireMonkey) - mehrere StringGrids scrollen


Bronstein - Sa 11.11.06 16:21
Titel: mehrere StringGrids scrollen
Hallo,
habe ein Problem mit dem Scrollen. Ich habe auf meinem Formular 6 StringGrids. Diese sollen beim Scrollen aufeinander abgestimmt werden.

Habe mal als Anlage mein Formular als Screenshot.

Nun zu den Funktionen die ich gerne hätte. Wen ich am StringGrid4 horizontal scrolle, sollen die Stringgrids 1, 2, 3 auch mitscrollen. Wenn ich beim StringGrid3 vertikal scrolle, soll das Grid5 auch mitscrollen.

Habe bis jetzt nur hinbekommen wenn ich bei stringGrid 4 scrolle dass Grid3 mitscrollt.

Hier mal der Code dazu:


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
private 
    { Private declarations } 
     OldGridProc3, OldGridProc4: TWndMethod; 
   [...] 
   procedure Grid3WindowProc(var Message: TMessage); 
   procedure Grid4WindowProc(var Message: TMessage); 
   [...] 

procedure TForm1.FormCreate(Sender: TObject); 
[...] 
//Für das Scrollen beider Grids
  OldGridProc3 := StringGrid1.WindowProc;
  OldGridProc4 := StringGrid3.WindowProc;
  StringGrid3.WindowProc := Grid3WindowProc;
  StringGrid4.WindowProc := Grid4WindowProc;
[...] 
procedure TForm1.Grid3WindowProc(var Message: TMessage);
begin
  OldGridProc3(Message);
  if ((Message.Msg = WM_VSCROLL) or (Message.Msg = WM_HSCROLL) or
      (Message.msg = WM_Mousewheel)) then
  begin
    OldGridProc4(Message);
  end;
end;

procedure TForm1.Grid2WindowProc(var Message: TMessage);
begin
  OldGridProc4(Message);
  if ((Message.Msg = WM_VSCROLL) or (Message.Msg = WM_HSCROLL) or
     (Message.msg = WM_Mousewheel)) then
  begin
    OldGridProc3(Message);
  end;
end;


Coder - Sa 11.11.06 17:13

Kennst du StringGrid1.ScrollBy?
Vielleicht kannst du damit ja was anfangen.

MfG


Lannes - Sa 11.11.06 17:20

Hallo,

denke das Dir StringGrid.TopRow und StringGrid.LeftCol weiter hilft.


Bronstein - Sa 11.11.06 19:08

Habe die Lösung von Lannes genommen.

Ist ja wirklich easy


Coder - Sa 11.11.06 19:10

Nett wäre jetzt wenn du die Lösung hier postest damit andere nicht nochmal Fragen müssen :wink:


Bronstein - Mo 13.11.06 10:31

Möchte es jetzt doch mal mit ScrollBy versuchen. Ich dachte das könnte jetzt so funktionieren.

Doch irgendwie sieht mein StringGrid dann komisch aus.

Hier mal der Code für die Horizontale Scrollbar:


Delphi-Quelltext
1:
2:
3:
4:
5:
procedure TForm1.ScrollBar1Scroll(Sender: TObject; ScrollCode: TScrollCode;
  var ScrollPos: Integer);
begin
 StringGrid1.ScrollBy(ScrollBar1.Left,  ScrollBar2.Top);
end;



Hier noch der Code für meine letzte Lösung für den horizontalen Bereich

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
procedure TForm1.StringGrid2DrawCell(Sender: TObject; ACol, ARow: Integer;
  Rect: TRect; State: TGridDrawState);
begin
  StringGrid1.LeftCol := StringGrid2.LeftCol;
  StringGrid3.LeftCol := StringGrid2.LeftCol;
  StringGrid4.LeftCol := StringGrid2.LeftCol;
end;


Lannes - Mo 13.11.06 11:54

Hallo,

ich dachte die Lösung mit LeftCol usw. wäre "wirklich easy", welches Problem ist denn da entstanden?

Zum Thema ScrollBy lies mal die Delphi-Hilfe.
Mit ScrollBy verschiebt man nur den Bildausschnitt, deshalb sieht das bei Dir dann komisch aus.

Ps.: Bei den Schweitzern gibt es auch eine Lösung zum Thema "StringGrids synchronisieren"


Bronstein - Mo 13.11.06 12:06

Ich habe ja mehrere StringGrids. Jetzt habe ich aber eines dabei das immer die Überschrift mehrere Zellen ist. Somit hat dieses StringGrid eine andere Anzahl Spalten.

Und dann passt es natürlich nicht, da er ja mit dieser Lösung nach spalten scrollt


Lannes - Mo 13.11.06 12:48

Hallo,

das kannst Du doch über einen Faktor regeln, oder?


Bronstein - Mo 13.11.06 20:20

Es handelt sich dabei ja um einen Jahreskalender.

Die eine StringGrids haben 365 Spalten (Breite 30) und das andere hat 12 Spalten.

Ich dachte jetzt, dass ich das eine auch mit 365 Spalten mache.
Dazu habe ich alle Spalten auf die Breite 0 gesetzt bis auf das des 15. Tag. Das habe ich mit dem Faktor 30*Anzahl Tage im Monat gemacht.

Jetzt wechselt er aber schon am Tag 15 den Monatsnamen.

Mit 28 bzw. 30 habe ich es auch schon versucht. Das passt auch nicht so richtig


Lannes - Di 14.11.06 01:11

Hallo,

in dem Fall, versuch doch mal die Monats-Anzeige mit in das StringGrid zu setzen.

Das könnt Dir eventuell dabei helfen >> Äquivalent zu ColSpan in HTML in TStringGrid [http://www.dsdt.info/tipps/?id=287]


Logikmensch - Di 14.11.06 10:43

Hallo Bronstein,

ich habe mir Dein Attachment angesehen und möchte nur mal fragen, warum Du das nicht mit nur einem StringGrid gemacht hast. Da kann man doch auch feste Spalten- und Zeilenbeschriftungen (fixedcols und -rows) machen und von der Breite her gibt es da doch sicher keinerlei Einschränkungen...

War nur so ne Idee...


Bronstein - Di 14.11.06 21:24

Das geht so viel ich weiß nicht.

Wenn man die Spalte vergrößert bzw. verkleinert wird auch die Spaltenbreite der Überschrift geändert.


Logikmensch - Mi 15.11.06 07:22

Oh, da habe ich mich missverständlich ausgedrückt. Mit 'Breite' meinte ich die Anzahl der Spalten der Tabelle. Mit StringGrid1.colcount:=365 lässt sich diese ja voreinstellen.

Ja, die Breite der Überschriften ändert sich, soweit ich weiß, mit den anderen Zellen der Spalte, wenn diese verändert wird (kann in den Options-properties an- und abgeschaltet werden).

Nein, ich meinte mit meinem Hinweis nur, dass ich die Notwendigkeit von mehr als nur einem StringGrid bisher noch nicht erkennen konnte. Aber tut ja auch nicht viel zur Sache.


Bronstein - Mi 15.11.06 08:58

Hallo, habe das mal mein Projekt als Anlage geschickt. Vielleicht kann mir dann jemand weiterhelfen.

Habe mal zwei Möglichkeiten.

Bei der ersten hat das MonatsStringGrid 12 Spalten , bei der 2. 365.


Bronstein - Do 16.11.06 08:36

Hat niemand eine Ahnung wie ich das Problem lösen könnte


Andreas Schilling - Do 16.11.06 13:34

Versuche es mal so. Ich habe deinen Quelltext mit 12 Monatsspalten dazu verwendet.

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
procedure TForm1.StringGrid2TopLeftChanged(Sender: TObject);
  var Monat : integer;
      strTag : string;
begin
  StringGrid1.LeftCol := StringGrid2.LeftCol;
  strTag := StringGrid4.Cells[StringGrid2.leftcol, 0];
  Monat := strtoint(copy(strtag, 42));
  StringGrid3.LeftCol := Monat - 1;
  StringGrid4.LeftCol := StringGrid2.LeftCol;
end;


Lannes - Fr 17.11.06 16:51

Hallo,
user profile iconLannes hat folgendes geschrieben:
... versuch doch mal die Monats-Anzeige mit in das StringGrid zu setzen...[/url]
eventuell kann ich Dich ja noch überzeugen :wink:

Hab da aus meinen Vorräten etwas zusammengesucht, ich poste mal die ganze Unit.
Zum Testen einfach ein StringGrid auf die Form legen, meine Unit übernehmen und Formcreate und GriDDrawCell im OI zuweisen.

Feiertage, Kalenderwoche und andere Erweiterungen überlass ich Dir,
geht aber nur wenn man das Grundprinzip der OnDrawCell-Behandlung versteht. :les:

Die Jahresdaten werden nicht in Grid.Cells geschrieben, sie werden nur mit Textout ausgegeben.
Das Array FJahrArr wird in der folgenden Form gefüllt(Tag,Monat;Wochentag):

Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
1  1  1
2  1  2
3  1  3
4  1  4
5  1  5
6  1  6
7  1  7
8  1  1
...


Auf dieses Array wird dann entsprechend der aktuellen Spalte(aCol) in DrawCell zugegriffen.


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:
84:
85:
86:
87:
88:
89:
90:
91:
92:
93:
94:
95:
96:
97:
98:
99:
100:
101:
102:
103:
104:
105:
106:
107:
108:
109:
110:
111:
112:
113:
114:
115:
116:
117:
118:
119:
120:
121:
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
  Grids;

const C_CLFIXED = $00CEBCA2;
      C_CLFIXEDHELL = $00EDE7DC;
      C_CLNOWORK = $00F4F1EA;
      C_CLWORK = clWhite;
      //damit Code lesbarer ist
      C_TAG = 1;
      C_MONAT = 2;
      C_WTAG = 3;
type
  TForm1 = class(TForm)
    StringGrid1: TStringGrid;
    procedure FormCreate(Sender: TObject);
    procedure StringGrid1DrawCell(Sender: TObject; aCol, aRow: Integer;
      Rect: TRect; State: TGridDrawState);
  private
    FJahr : Word;
    FJahrArr : Array [1..366,C_TAG..C_WTAG] of Word;//ev. erweitern
    { Private-Deklarationen }
  public
    { Public-Deklarationen }
  end;

var
  Form1: TForm1;
implementation

{$R *.DFM}

procedure TForm1.FormCreate(Sender: TObject);
var z : Integer;
    aDateTime : TDateTime;
    dammy : Word;
begin
  FJahr := StrToInt(FormatDateTime('yyyy',now));
  aDateTime := EncodeDate(FJahr,1,1)-1;//-1 damit ab 31.12 hochgezählt wird
  //Jahres-Array füllen
  for z := 1 to 366 do
    begin
    DecodeDate(aDateTime+z,dammy,FJahrArr[z,C_MONAT],FJahrArr[z,C_TAG]);
    FJahrArr[z,C_WTAG] := DayOfWeek(aDateTime+z);//Wochentag 1 bis 7 ermitteln
    end;
  with StringGrid1 do
    begin
    Ctl3D := False;
    FixedCols := 1;
    FixedRows := 2;
    RowCount := 100;
    DefaultColWidth := 16;
    DefaultRowHeight := 16;
    ColWidths[0] := 50;
    Cells[0,2] := 'Meier';
    Cells[0,3] := 'Hinz';
    Cells[0,4] := 'Kunz';
    Cells[0,5] := 'Müller';
    if FJahrArr[366,C_TAG] <> 31 then
      ColCount := 366// 28 Tg
      else
        ColCount := 367;// 29 Tg
    DefaultDrawing:=false;//im Grid alles selber zeichnen
    end;
end;

procedure TForm1.StringGrid1DrawCell(Sender: TObject; aCol, aRow: Integer;
  Rect: TRect; State: TGridDrawState);
var aTag, aMonat : Integer;
    R : TRect;
begin
  aTag   := FJahrArr[ACol,C_TAG];
  aMonat := FJahrArr[ACol,C_MONAT];
  R := Rect;
  with StringGrid1 do
    begin
    if (aRow = 0and (ACol >= FixedCols) then
      begin
      if aMonat mod 2 = 0 then
        Canvas.Brush.Color := C_CLFIXED
        else
          Canvas.Brush.Color := C_CLFIXEDHELL;
      Canvas.FillRect(Classes.Rect(R.Left,R.Top,R.Right+1,R.Bottom));
      if aTag = 1 then
        begin
        Canvas.Pen.Color := clBlack;
        Canvas.MoveTo(R.Left-1,R.Top);
        Canvas.LineTo(R.Left-1,R.Bottom);
        end;
      if aTag in [15..20then
        Canvas.TextOut(R.Left+2-((aTag-15)*17),R.Top+2,LongMonthNames[aMonat]);
      end
      else
        begin
        if (ACol < FixedCols) or (ARow = 1then
          Canvas.Brush.Color := C_CLFIXED
          else
            if FJahrArr[ACol,C_WTAG] in [1,7then
              Canvas.Brush.Color := C_CLNOWORK
              else
                Canvas.Brush.Color := C_CLWORK;
        if (ACol < FixedCols) and (ARow < FixedRows) then
          Canvas.FillRect(Classes.Rect(R.Left,0,R.Right,R.Bottom))
          else
            Canvas.FillRect(R);
        if (aRow = 1and (ACol >= FixedCols)  then
          Canvas.TextOut(R.Left+2,R.Top+2,Format('%.2d',[aTag]))
          else
            if (aRow = FixedRows-1and (ACol = FixedCols-1then
              Canvas.TextOut(R.Left+10,R.Top-8,IntToStr(FJahr))
              else
                Canvas.TextOut(R.Left+2,R.Top+2,Cells[aCol,aRow]);
        end;
    end;
end;

end.


Bronstein - Fr 17.11.06 23:14

Super genau das habe ich gesucht.

Verstehen tue ich es noch nicht. Muss ich mir erstmal genauer anschauen


Lannes - Sa 18.11.06 00:02

Hallo,
user profile iconBronstein hat folgendes geschrieben:
...Muss ich mir erstmal genauer anschauen
besser ist das :wink:

Welchen Zweck soll eigentlich das unterste Grid in Deinem Beispiel erfüllen?


Bronstein - Sa 18.11.06 13:04

Das ergibt die Auswertung des Schichtplaners. Sprich wieviel Personen am Tag anwesend sind, zu den einzelnen Schichten zusammengefasst.


Bronstein - Mi 22.11.06 12:45

Jetzt habe ich aber doch noch ein Problem. Habe jetzt folgenden Code in das DrawCell Ereignis gemacht, damit ich Zellen markieren kann:


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
//notwendig für die Markierung
  if (Selection.Left <= ACol) and (Selection.Right >= ACol) and (Selection.Top <= ARow) and (Selection.Bottom >= ARow) then
  begin
    Canvas.Brush.Color := TColor(Integer(Canvas.Brush.Color) xor $00FFFFFF);
    Canvas.FrameRect(Rect);
  end;


Jetzt kann ich aber nur Zellen markieren die hintereinander kommen. Ich möchte aber mit Hilfe der STRG-Taste z.B. die Zelle [5, 5] und die Zelle[7, 9] markieren


Lannes - Mi 22.11.06 15:25

Hallo,

wir kommen hier so langsam aber sicher sehr weit vom Thema ab(scrollen >> selectieren),
mach doch lieber ein neues Thema auf :wink:

Vorschlag:

Titel 1: [StringGrid] selectieren von nicht zusammenhängenden Zellbereichen

oder wenn Du nur einfärben willst(was ich annehme)
Titel 2: [StringGrid] einfärben von Zellbereichen mit Strg + Maus

zum Code:

Delphi-Quelltext
1:
if (Selection.Left <= ACol) and (Selection.Right >= ACol) and (Selection.Top <= ARow) and (Selection.Bottom >= ARow) then                    
diese If-abfrage ist imho mit folgendem gleichzusetzen:

Delphi-Quelltext
1:
2:
if gdSelected in State then
 //mach was
außerdem ist die Einfärbung nur temporaer, solange der Bereich selectiert ist.