Delete - Di 08.07.03 12:29
Titel: ...die Positionen der TCoolbar-Bands speichern/laden?
die Positionen der TCoolbar-Bands speichern/laden?
Vor einigen Monaten hatte wulfskin gefragt, wie man die Position der Bänder (Bands) in einer TCoolBar speichern kann. (
ToolBar speichern? [
http://www.delphi-forum.de/viewtopic.php?t=8835].) Da er damals den Tipp bekam, bessere Komponenten zu benutzen, habe ich meinen Vorschlag, den ich vorbereitet hatte, wieder gelöscht.
Erinnert habe ich mich daran, weil ich momentan an der NonVCL-Version (Rebar genannt; s. PSDK) sitze und den Beitrag für Luckies Tutorials eigentlich fertig habe. Aber Luckies Reaktion auf meine Demo war:
Zitat: |
Jetzt fehlt eigentlich nur noch, wie man das abspeichert, damit beim nächsten Start alles wieder so ist, wie man es sich zurecht gelegt hat. |
:) Darum habe ich den VCL-Code rekonstruiert und würde ihn hier gern als Idee vorstellen und zeigen, wie man es mit der normalen TCoolbar machen
könnte. Da ich nicht weiß, wie das in neueren Delphi-Versionen aussieht, gilt dieser Vorschlag erst mal nur bis Delphi 5, wo die TCoolbar keine eingebaute Speichermöglichkeit hat.
Als Speicherort habe ich mich für die Registry entschieden. Das kann zwar jeder von euch anders machen, aber da bei der Toolbar die Einstellungen auch in der Registry gesichert werden (s. Toolbarbeitrag in Luckies Tutorials), bietet sich das hier natürlich auch an. Daher die Grundlagen, einzufügen im Interface-Teil der Form-Unit:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12:
| uses Registry;
const szRegKey = 'Software\CoolbarDemo\CoolbarPos'; szValName = 'BandLayout'; type rbBandArray = packed record Index, ID, Width : integer; end; |
Die Idee ist folgende: jedes Band besitzt eine einzigartige ID, die bei der VCL-Version auch nur lesbar ist. Diese ID entspricht typischerweise dem Index des Bandes (quasi der Reihenfolge, in der sie erzeugt werden). Aber wenn man die Bänder verschiebt, dann ändert sich nur der Index - die ID dagegen bleibt unverändert!
Daher interessieren uns eben der Index, die ID und natürlich die Breite des Bandes. Auch von Interesse dürfte die Frage sein, ob so ein Band in einer neuen Zeile steckt oder an einem anderen Band dran hängt. Aber dazu gleich.
Zum Speichern benötigen eine Schleifenvariable (i), eine Registry-Variable (reg) und ein dynamisches Array des Typs "rbBandArray":
Delphi-Quelltext
1: 2: 3: 4: 5: 6:
| var i : integer; reg : TRegistry; fba : array of rbBandArray; begin SetLength(fba,0); |
Dann legen wir zunächst die Größe dieses Arrays fest, wobei wir die Anzahl der Bänder als Basis nehmen:
Delphi-Quelltext
1: 2: 3:
| with Coolbar1 do begin SetLength(fba,Bands.Count); |
In einer Schleife merken wir uns dann die o.g. Werte:
Delphi-Quelltext
1: 2: 3: 4: 5:
| for i := 0 to Bands.Count - 1 do begin fba[i].Index := Bands[i].Index; fba[i].ID := Bands[i].ID; fba[i].Width := Bands[i].Width; |
Jetzt die Sache, die jeder von euch nach eigenem Gutdünken handhaben kann: Wenn ein Band in einer neuen Zeile ist, dann ist seine Eigenschaft "Break" üblicherweise
true. Ich habe mich dafür entschieden, in so einem Fall, den Index als negativen Wert anzugeben:
Delphi-Quelltext
1: 2: 3: 4:
| if(Bands[i].Break) then fba[i].Index := 0 - fba[i].Index; end; end; |
Das heißt, aus dem Bandindex 2 wird bspw. -2. Wie gesagt, das kann jeder anders machen. Ich hätte auch eine vierte Membervariable in mein rbBandArray-Record einbauen können. Aber ich wollte die Anzahl der Bytes in der Registry möglichst klein halten, und dieser Wert hier ist der einzige, den man auch mit anderen Werten verbinden kann. Index, ID und Breite dagegen
sollten in separaten Variablen gespeichert werden.
Die so gesicherten Daten werden nun in der Registry abgelegt. Ich habe den Schlüssel HKEY_CURRENT_USER benutzt, was natürlich einen Sinn hat: a) es gibt keine Rechteprobleme unter NT, b) jeder User hat sein eigenes Layout:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9:
| if(length(fba) > 0) then begin reg := TRegistry.Create; with reg do try RootKey := HKEY_CURRENT_USER;
if(OpenKey(szRegKey,true)) then try |
Hervorzuheben ist die folgende Zeile:
Delphi-Quelltext
1:
| WriteBinaryData(szValName,fba[0],length(fba) * sizeof(rbBandArray)); |
Da das dynamische Array bei Null beginnt, müssen wir das auch angeben. Sonst speichern wir alles mögliche, nicht aber die uns interessierenden Werte. :) Und der Rest noch:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11:
| finally CloseKey; end; finally Free; end; end;
SetLength(fba,0); end; |
Voilà. Idealerweise ist dieser Code im "OnClose"-Ereignis einzufügen, bzw. über dieses aufzurufen, damit beim Beenden des Programms die aktuellen Band-Positionen gesichert werden.
Moderiert von jasocul: Sourcen an Style-Guide angepasst
Moderiert von jasocul: Beitrag geprüft am 16.05.2006
Delete - Di 08.07.03 12:32
Titel: Und die Positionen wieder auslesen
Und selbstverständlich die umgekehrte Richtung: wir holen zuerst die Daten aus der Registry. Da wir diesmal nichts schreiben, reicht es auch aus, die Registry nur lesend zu öffnen (auch wenn wir auf HKCU zugreifen):
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12:
| SetLength(fba,0);
reg := TRegistry.Create(KEY_READ); with reg do try RootKey := HKEY_CURRENT_USER;
if(OpenKey(szRegKey,false)) then try if(ValueExists(szValName)) then begin len := GetDataSize(szValName); |
An der Stelle ein kurzer Stop um eine Sache zu klären: Mit "GetDataSize" kann man ermitteln, wie viele Bytes ein Eintrag belegt. Abhängig davon müssen wir die Größe unseres Arrays setzen. Nun könnte man aber die Daten in der Registry manuell ändern. Ich könnte durchaus ein paar Bytes löschen, was unerwartete Folgen für das Programm hätte. Also sollten wir sicherstellen, dass a) überhaupt Daten vorhanden sind:
und b) dass diese ohne Rest durch die Größe des Arrays teilbar sind. Das dient dazu, besagte Byte-Löschungen zu entdecken:
Delphi-Quelltext
1:
| (len mod sizeof(rbBandArray) = 0) and |
Und c) sollte die Division der Bytes durch die Arraygröße die Anzahl der TCoolBar-Bands ergeben:
Delphi-Quelltext
1: 2:
| (len div sizeof(rbBandArray) = Coolbar1.Bands.Count) then begin |
Das ist zwar auch nicht 100% sicher (ich könnte die Werte an sich ändern, ohne die Bytegröße zu manipulieren), aber es schützt zumindest ein bisschen. Wenn ihr ein bspw. eine neue Programmversion veröffentlicht, in der ein TCoolbar-Band entfernt wurde oder auch hinzugekommen ist, dann werden beim Start dieser neuen Version die alten Einstellungen ignoriert, und das Programm präsentiert sich für den User erst mal wieder mit dem Grundaussehen.
Wie dem auch sei, wenn die o.g. Bedingungen erfüllt sind, dann setzen wir die Arraygröße und lesen die Werte aus der Registry:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10:
| SetLength(fba,len div sizeof(rbBandArray)); ReadBinaryData(szValName,fba[0],len); end; end; finally CloseKey; end; finally Free; end; |
Fehlt nur noch das "Umbauen" der TCoolbar. Dazu blockieren wir erst mal die Bildschirmausgabe. Das halte ich für nützlich, denn wenn jemand sehr viele Bänder hat, bzw. einen schwachen PC, dann kann man u.U. miterleben, wie sich die Bänder neu anordnen. Und das sieht nicht professionell aus. :)
Delphi-Quelltext
1: 2: 3: 4:
| if(length(fba) > 0) then with Coolbar1.Bands do begin BeginUpdate; |
In einer
for-Schleife prüfen wir zunächst ob der Index innerhalb der Anzahl der Bänder liegt, und ob die Methode "FindItemId" auch etwas zurückliefert:
Delphi-Quelltext
1: 2: 3: 4:
| for i := 0 to length(fba) - 1 do if(abs(fba[i].Index) < Count) and (FindItemId(fba[i].ID) <> nil) then begin |
Erst dann ändern wir den Index des jeweiligen Bandes, wodurch es verschoben wird. Da wir ja auch negative Werte haben können (an das "Break" denken!), benutzen wir aber die ABS-Funktion, um den Wert ohne Vorzeichen zu übergeben:
Delphi-Quelltext
1:
| FindItemID(fba[i].ID).Index := abs(fba[i].Index); |
Wenn es in eine neue Zeile muss, dann ist der Indexwert -wie gesagt!- kleiner als Null, was wir ohne umständliches
if direkt an die entsprechende Eigenschaft "Break" des Bandes weitergeben:
Delphi-Quelltext
1:
| (FindItemID(fba[i].ID) as TCoolBand).Break := (fba[i].Index < 0); |
Und die gespeicherte Breite muss auch noch eingestellt werden:
Delphi-Quelltext
1: 2: 3: 4: 5:
| (FindItemID(fba[i].ID) as TCoolBand).Width := fba[i].Width; end;
EndUpdate; end; |
Fertig. Dieser Teil gehört natürlich am besten ins "OnCreate" eurer Form.
Wie gesagt: es ist
eine mögliche Idee von vielen.
Und wenn das jemandem als Anregung weiterhilft, dann freut mich das.
Moderiert von jasocul: Sourcen an Style-Guide angepasst
Moderiert von jasocul: Beitrag geprüft am 16.05.2006