Als kleines Extra möchte ich noch demonstrieren, wie man das Layout der Rebar-Bänder speichern könnte, so dass sich das Control bei jedem Programmstart immer mit dem gleichen Aussehen präsentiert. Die Betonung liegt dabei auf "könnte", denn diese Vorgehensweise ist keineswegs ein offizieller Weg. Es ist eine Möglichkeit von vielen.
Beginnen wir mit den Grundeinstellungen, die wir zum Laden und Speichern brauchen. Da wäre zuerst ein Record, das unsere Daten aufnehmen soll:
type
rbBandArray = packed record
Index,
ID,
Width : integer;
end;
Dieses Record wird den Index, die ID und die Breite von jeweils einem Rebar-Band aufnehmen. Im Programm nutzen wir dazu ein dynamisches Array, das aus mehreren dieser Records besteht. Außerdem legen wir noch den Registryschlüssel und den Namen des Eintrags fest, unter dem wir diese Werte speichern wollen:
const szRegKey = 'Software\Win32-API-Tutorials\Rebar-Demo'; szValName = 'RebarBandLayout';
Beginnen wir mit dem logischen ersten Schritt: dem Speichern der Daten. Idealerweise ist der folgende Code beim Beenden des Programms aufzurufen, damit die Werte auch wirklich erst dann gespeichert werden, wenn der Anwender das Programm schließt.
Zuerst ermitteln wir die Anzahl der Bänder:
bIdx := SendMessage(rb,RB_GETBANDCOUNT,0,0);
Mit Hilfe einer for-Schleife können wir nun die Werte (ID, Stil und Größe) jedes Bandes ermitteln:
for i := 0 to bIdx - 1 do begin ZeroMemory(@bi,sizeof(bi)); bi.cbSize := sizeof(TRebarBandInfo); bi.fMask := RBBIM_ID or RBBIM_SIZE or RBBIM_STYLE; SendMessage(rb,RB_GETBANDINFO,i,LPARAM(@bi));
Die ermittelten Werte speichern wir in unser dynamisches Array; als Index (sowohl für das Rebar-Band als auch für das jeweilig benutzte Record) dient dabei der aktuelle Schleifenwert:
fba[i].Index := i; fba[i].ID := bi.wID; fba[i].Width := bi.cx;
Eine kleine Besonderheit ist das Speichern des Stils: wenn ein Rebar-Band in einer neuen Zeile beginnt, dann besitzt es das Attribut RBBS_BREAK. Um Speicher zu sparen (und so das Record, das in der Registry gespeichert wird, klein zu halten) habe ich es so gemacht, dass der Index des Bandes als negative Zahl angegeben wird, wenn das Stilattribut gesetzt wird.
if(bi.fStyle and RBBS_BREAK <> 0) then fba[i].Index := 0 - fba[i].Index; end;
So kann man Bänder in neuen Zeilen recht einfach unterscheiden, ohne eine weitere Variable im Record nutzen zu müssen.
Das komplette dynamische Array mit dem Bandlayout kann nun in der Registry gespeichert werden. Auf die dazu notwendigen Befehle und ihre Bedeutung möchte ich an der Stelle nicht eingehen, stattdessen würde ich Sie bei evtl. Fragen auf das
if(RegCreateKeyEx(HKEY_CURRENT_USER,szRegKey,0,nil,0, KEY_READ or KEY_WRITE,nil,reg,nil) = ERROR_SUCCESS) then try RegSetValueEx(reg,szValName,0,REG_BINARY, @fba[0],length(fba) * sizeof(rbBandArray)); finally RegCloseKey(reg); end
Beim Laden gehen wir natürlich den Weg in umgekehrter Reihenfolge: wir lesen zuerst die Daten aus der Registry, bevor wir das Rebar-Control ändern können. Doch auch hier ermitteln wir zuerst die Anzahl der Bänder, weil wir diesen Wert für einen notwendigen Vergleich brauchen werden.
Nach dem Öffnen des Registryschlüssels ermitteln wir aber erst einmal mit Hilfe von "RegQueryValueEx", ob der Eintrag überhaupt existiert:
if(RegOpenKeyEx(HKEY_CURRENT_USER,szRegKey,0,KEY_READ, reg) = ERROR_SUCCESS) then try dwType := REG_NONE; dwLen := 0;
if(RegQueryValueEx(reg,szValName,nil,@dwType,nil, @dwLen) = ERROR_SUCCESS) and
So weit, so gut. Da wir die Daten binär gespeichert haben, müssen die vorhandenen Daten (wenn sie denn vorhanden sind) vom gleichen Typ sein:
(dwType = REG_BINARY) and
Und selbstverständlich sollten überhaupt Daten vorhanden sein.
Ein nützlicher Nebeneffekt des o.g. Registrybefehls ist, dass er den benötigten Speicher zurückliefert, wenn der übergebene Puffer zu klein oder gar Null ist. Das Ergebnis, in der Variablen dwLen, sollte also größer als Null sein:
((dwLen > 0) and
Außerdem muss berücksichtigt werden, dass man die Daten in der Registry manuell ändern kann. Wenn ich böswillig bin, dann lösche ich einfach ein paar der gespeicherten Bytes, was sicher unangenehme Folgen für das Programm hat. Nun bietet die folgende Methode zwar keinen vollkommenen Schutz, aber wenn Sie prüfen, ob sich die Anzahl der Bytes ohne Rest durch die Größe des o.g. Records dividieren lässt, können Sie relativ sicher Manipulationen erkennen:
(dwLen mod sizeof(rbBandArray) = 0) and
Überlegen Sie bitte: das Record hat eine Grundgröße von 12 Bytes, weil drei Membervariablen (Index, ID und Width) vom Typ integer benutzt wurden. Es wird für jedes Band verwendet, das bedeutet bei unserem Beispiel mit seinen 3 Bändern, dass 36 Bytes in der Registry gespeichert werden.
Der Umkehrschluss: wenn Sie 36 Bytes aus der Registry lesen, dann lässt sich dieser Wert ohne Rest durch 12 teilen und ergibt logischerweise 3. Würden Sie die Daten manipulieren und z.B. sechs Bytes entfernen, dann ließen sich die verbliebenen 30 Bytes genau zweimal durch 12 teilen, ergeben aber einen Rest von 6. Die Bedingung stimmt also nicht mehr, und die Daten in der Registry würden auch nicht berücksichtigt werden.
Ein weiterer Nebeneffekt, den wir uns zunutze machen: wenn die Datengröße nach eben definierter Bedingung stimmt, dann sollte die Division durch die Recordgröße logischerweise auch die Anzahl der Bänder des Rebar-Controls ergeben:
(dwLen div sizeof(rbBandArray) = dword(bIdx))) then
Nur wenn diese Bedingungen alle erfüllt sind, erzeugen wir ein dynamisches Array mit der benötigten Größe und laden die Daten
begin
SetLength(fba,dwLen div sizeof(rbBandArray));
RegQueryValueEx(reg,szValName,nil,@dwType,@fba[0],@dwLen);
end;
finally
RegCloseKey(reg);
end;
Nachdem wir das dynamische Array gefüllt haben, können wir nun das Rebar-Control "umbauen". Auch hier benutzen wir am besten eine for-Schleife, in der wir aber zur Sicherheit prüfen, ob die gespeicherten Indexwerte innerhalb unseres Rebar-Controls liegen, und ob die Suche nach der ID auch wirklich Ergebnisse bringt. (Nur für den Fall, dass jemand die gespeicherten Werte manipuliert hat, ohne dabei die Anzahl der Bytes zu ändern. :o))
for i := 0 to length(fba) - 1 do if(abs(fba[i].Index) < SendMessage(rb,RB_GETBANDCOUNT,0,0)) and (SendMessage(rb,RB_IDTOINDEX,fba[i].ID,0) <> -1) then begin
Mit Hilfe der Nachricht "RB_IDTOINDEX" können nun herausfinden, an welcher Position sich ein Band mit einer bestimmten ID befindet. Für die nächsten Schritte benötigen wir nämlich den Indexwert des Bandes, der sich aber ändern kann. Die einzige Konstante ist demzufolge die ID, die sich nicht ändert (zumindest nicht in diesem Fall):
bIdx := SendMessage(rb,RB_IDTOINDEX,fba[i].ID,0);
Ähnlich wie beim Speichern der Werte holen wir uns die aktuellen Stil- und Größenwerte des Bandes. Die ID interessiert uns diesmal nicht, da wir sie ja bereits durch unser Record kennen:
ZeroMemory(@bi,sizeof(bi));
bi.cbSize := sizeof(TRebarBandInfo);
bi.fMask := RBBIM_STYLE or RBBIM_SIZE;
SendMessage(rb,RB_GETBANDINFO,bIdx,LPARAM(@bi));
Wir ändern die Breite des Bandes, indem wir den im Record gespeicherten Width-Wert an die cx-Membervariable des TRebarBandInfo-Records übergeben:
bi.cx := fba[i].Width;
Ist der Indexwert kleiner als Null, dann erinnern wir uns daran, dass wir auf diese Weise gespeichert haben, dass ein Band in einer neuen Zeile beginnt. Dementsprechend setzen wir das RBBS_BREAK-Attribut (bzw. wir müssen es entfernen, wenn es sich um einen positiven Indexwert handelt):
if(fba[i].Index < 0) then bi.fStyle := bi.fStyle or RBBS_BREAK
else bi.fStyle := bi.fStyle and not RBBS_BREAK;
Diese "gepatchten" Werte werden nun mit der Nachricht "RB_SETBANDINFO" an das Rebar-Band zurückgegeben:
SendMessage(rb,RB_SETBANDINFO,bIdx,LPARAM(@bi));
Um nun zu guter Letzt das aktuelle Band an seine neue Position zu verschieben, verwenden wir die Nachricht "RB_MOVEBAND", der wir den alten und den neuen Index übergeben. Der alte Index ist natürlich der, den wir anfangs ermittelt haben, und der neue wird ja vom dynamischen Array (sprich: von den Registrydaten) bereitgestellt. Angesichts der Tatsache, dass dieser Wert aber auch negativ sein kann, müssen wir die Funktion "abs" benutzen, um ihn ohne Vorzeichen zu übergeben:
SendMessage(rb,RB_MOVEBAND,bIdx,abs(fba[i].Index)); end;
Das war´s.
Das Beispielprogramm enthält diese Funktionalität, aber sie ist standardmäßig durch bedingte Kompilierung deaktiviert. Wenn Sie sie verwenden wollen, dann entfernen Sie bitte den Punkt in der Zeile
{.$DEFINE LAYOUTSAVING}
dadurch werden zusätzliche Funktionen und Anweisungen freigeschaltet, die das Laden und Speichern des Bandlayout demonstrieren. Wenn Sie das Programm nicht mehr verwenden wollen, dann entfernen Sie bitte manuell die angelegten Registryeinträge im Schlüssel HKEY_CURRENT_USER.