15. februar 2011 - 23:29Der er
15 kommentarer og 1 løsning
Gemme programopsætning og position på skærm
Jeg har et program med 2 stk DBGrid og jeg vil gerne gemme de ændringer i opsætning som brugeren foretager (kolonne rækkefølge og bredde, Formens størrelse m.m.). Det hele virker som jeg ønsker, bortset fra positionen på skærmen, den kan jeg ikke få til at virke. Jeg har forsøgt alle muligheder under Form1.Position, men uden held.
Jeg har lavet denne OnClose Event:
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction); var Udfil : system.text; Filnavn, tekst : string; begin filnavn := 'setup.txt'; system.assign(Udfil, Filnavn); rewrite(Udfil); writeln(udfil,form1.ClientHeight); {her gemmes skærmhøjden} writeln(udfil,form1.ClientWidth); {her gemmes skærmbredden} writeln(udfil,form1.Height); {her gemmes ??? -ingen effekt} writeln(udfil,form1.Width); {her gemmes ??? -ingen effekt} writeln(udfil,form1.Left); {her gemmes venstre position på skærm} writeln(udfil,form1.Top); {her gemmes top positionen på skærm} writeln(udfil,form1.checkbox1.checked ); {her gemmes status for checkbox} writeln(udfil,form1.checkbox2.checked ); {her gemmes status for checkbox} writeln(udfil,form1.checkbox3.checked ); {her gemmes status for checkbox} system.close(udfil); DBGrid1.Columns.SaveToFile('kolonnesettings1.txt') ; {her gemmes kolonnebredderne} DBGrid2.Columns.SaveToFile('kolonnesettings2.txt') ; {her gemmes kolonnebredderne} end;
Er der nogen der har et bud på hvordan jeg sætter programmets position ?
Prøv at gemme dette som U_FormsIni og sæt den i din Uses list lige efter Forms. Det er den jeg bruger og det virker helt fint med form størrelse og position.
Procedure TForm.DoCreate; begin inherited; LoadFromIni; end;
Procedure TForm.DoDestroy; begin inherited; SaveToIni; end;
function TForm.GetFormRec: TFormRec; begin Result.Top := Top; Result.Left := Left; Result.Width := Width; Result.Height := Height; Result.State := Ord(WindowState); end;
Procedure TForm.LoadFromIni; var Rec: TFormRec; Filename: string; iniFile: TIniFile; Temp: string; begin Filename := ChangeFileExt(Application.ExeName,'.ini'); IniFile := TIniFile.Create(Filename); try with IniFile do begin try If ValueExists(conSectionFormsPos, Self.Name) then begin Temp := ReadString(conSectionFormsPos, Self.Name, ''); Rec.Top := StrToIntDef(Trim(StringChunk(Temp, 1, '*')), 20); Rec.Left := StrToIntDef(Trim(StringChunk(Temp, 2, '*')), 20); Rec.Width := StrToIntDef(Trim(StringChunk(Temp, 3, '*')), 200);; Rec.Height := StrToIntDef(Trim(StringChunk(Temp, 4, '*')), 300);; Rec.State := StrToIntDef(Trim(StringChunk(Temp, 5, '*')), 0); FormRec := Rec; end; except end; end; finally IniFile.Free; end; end;
procedure TForm.SaveToIni; var Rec: TFormRec; Filename: string; IniFile: TIniFile; begin Filename := ChangeFileExt(Application.ExeName,'.ini'); IniFile := TIniFile.Create(Filename); try with IniFile do begin try begin Rec := FormRec; Writestring(conSectionFormsPos, Self.Name, IntToStr(Rec.Top) + ' * ' + IntToStr(Rec.Left) + ' * ' + IntToStr(Rec.Width) + ' * ' + IntToStr(Rec.Height) + ' * ' + IntToStr(Rec.State)); end except end; end; finally IniFile.Free; end; end;
procedure TForm.SetFormRec(const FormRec: TFormRec); var intState: Integer; begin intState := FormRec.State; if intState = Ord(wsNormal) then begin Top := FormRec.Top; Left := FormRec.Left; if Width > 100 then Width := FormRec.Width; if Height > 100 then Height := FormRec.Height; WindowState := wsNormal; end; end;
function StringChunk(aString: string; aChunk: integer; aDivider: string = ' '): string; var I: integer; Counter: integer; ResultString: string; TempString: string; begin ResultString := ''; if aChunk = 0 then aChunk := 1; if aString = '' then Exit; aString := StringReplace(aString, ' ', ' ', [rfReplaceAll, rfIgnoreCase]); Counter := 1; for I := 1 To Length(aString) do begin TempString := Copy(aString, I, 1); if TempString = aDivider then Counter := Counter + 1 else if Counter = aChunk then ResultString := ResultString + TempString end; Result := ResultString; end;
Når jeg forsøger med TIniFile giver den mig fejl omkring try/finally/end, så det må jeg studere nærmere. Jeg får ikke set på det før i morgen, men foreløbig tak for jeres input.
Har kigget på jeres forslag og må konstatere at det er svært at læse andres kode.
Løsningen fra hrc er nok elegant, men jeg har ikke lyst til at skrive data i andre menneskers registreringsdatabase.
Løsningen fra HugoPedersen kan jeg ikke lige gennemskue hvordan jeg skal kalde.
Løsningen fra stone kan jeg ikke få til at virke. (der er read i begge procedurer)
Så vidt jeg kan se er forskellen på jeres løsninger og min, metoden til at gemme og indlæse data og jeg har derfor valgt at arbejde videre med min egen løsning og jeg er kommet frem til følgende som virker:
VIGTIGT: Form1.Position sættes til poDefault husk at også fjerne evt. form2.position i koden Det er ikke Client.Width og Client.Height der skal gemmes, men derimod Form2.Width og Form2.Height.
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction); var Udfil : system.text; Filnavn, tekst : string; begin filnavn := 'SetupFm1.txt'; system.assign(Udfil, Filnavn); rewrite(Udfil); writeln(udfil,form1.Height); writeln(udfil,form1.Width); writeln(udfil,form1.Left); writeln(udfil,form1.Top); system.close(udfil); end;
function FileExist(Navn : string) : boolean; {checker om en fil eksisterer} var f :System.Text; begin System.Assign(f,Navn); {$i-} {slå compiler kontrol fra for at generere fejkoden} Reset(f); system.Close(f); {$i+} {slå compiler kontrol til for at få fejkoden} FileExist := IOResult = 0; end;
procedure TForm1.FormCreate(Sender: TObject); var Indfil : system.text; Filnavn, tekst : string; tal, kode : integer;
begin filnavn := 'SetupFm1.txt'; if FileExist(filnavn) then begin system.assign(Indfil, Filnavn); reset(Indfil); readln(indfil,tekst); val(tekst,tal,kode); if kode = 0 then form1.Height := tal //skærmhøjde pos else form1.Height := 660; readln(indfil,tekst); val(tekst,tal,kode); if kode = 0 then form1.Width := tal //skærmbredde pos else form1.Width := 950; readln(indfil,tekst); val(tekst,tal,kode); if kode = 0 then form1.Left := tal //venstre skærm pos else form1.Left := 50; readln(indfil,tekst); val(tekst,tal,kode); if kode = 0 then form1.Top := tal //øverste skærm position else form1.Top := 34; system.close(indfil); end; end;
Jeg ved godt at det måske kunne skrives mere elegant, men det virker og det er det vigtigste for mig.
Tak for jeres input. Hvis nogen insisterer på point, så læg et svar og ellers lukker jeg spørgsmålet og beholder pointene selv :-)
Min løsning skal du ikke kalde på anden måde end at du skal tilføje den unit i din uses list efter Forms. Så ordner den det hele selv. Det er en 'udvidelse' af funktionerne i TForm
Jeg har en anden unit der benytter registry på nøjagtig samme måde.
Hvis du smider en mailadresse skal jeg gerne sende dig et eksempel på hvordan det kan gøres.
Jeg vil dog skynde mig at sige at jeg har oplevet problemer med at restore forms til maximized eller minimized. Jeg har bare ikke haft tid til at rode med det. Men måske en af dagene.
Tak for ordene. Hvis man skulle gemme skærmens positioner i en fil er det lidt mere kompliceret for så er en liste af skærmpositioner nødvendig, en cache om man vil. Den kommer her (intet skal forhindre mig i at gå amok):
unit UFormDims;
interface
uses SysUtils, ContNrs, Classes, Forms, Windows;
type TFormDim = class const Version = 1; // Save version strict private fTop: integer; fLeft: integer; fWidth: integer; fHeight: integer; fName: string; public constructor Create(aReader: TReader); overload; // Load from file constructor Create(aForm: TForm); overload; // Save form procedure Restore(aForm: TForm); // Restore form procedure Backup(aForm: TForm); // Backup form procedure SaveToWriter(aWriter: TWriter); // Save to stream (here a filestream) property Name: string read fName; end;
TFormList = class strict private fList: TObjectList; // Hides properties of the TObjectList function IndexOfForm(aForm: TForm): integer; private function GetItems(const aIndex: integer): TFormDim; public constructor Create; destructor Destroy; override; function LoadFromFile(const aFilename: string): boolean; procedure LoadFromStream(aStream: TStream); function SaveToTile(const aFilename: string): boolean; procedure SaveToStream(aStream: TStream); procedure Restore(aForm: TForm); // Restore form (wrapper) procedure Backup(aForm: TForm); // Backup form (wrapper) end;
var FormList: TFormList; // Global instance (yirk!)
implementation
const FormFile = 'FormDims.dat';
{ TFormDim }
constructor TFormDim.Create(aReader: TReader); begin inherited Create; case aReader.ReadInteger of 1: begin fName := aReader.ReadString; fTop := aReader.ReadInteger; fLeft := aReader.ReadInteger; fWidth := aReader.ReadInteger; fHeight := aReader.ReadInteger; end; end; end;
procedure TFormDim.SaveToWriter(aWriter: TWriter); begin aWriter.WriteInteger(Version); // Register save version aWriter.WriteString(fName); aWriter.WriteInteger(fTop); aWriter.WriteInteger(fLeft); aWriter.WriteInteger(fWidth); aWriter.WriteInteger(fHeight); aWriter.FlushBuffer; end;
{ TFormList }
procedure TFormList.Backup(aForm: TForm); var Index: integer; begin Index := IndexOfForm(aForm); if Index < 0 then fList.Add(TFormDim.Create(aForm)) else GetItems(Index).Backup(aForm); end;
constructor TFormList.Create; begin inherited Create; fList := TObjectList.Create; end;
destructor TFormList.Destroy; begin try fList.Free; finally inherited; end; end;
function TFormList.GetItems(const aIndex: integer): TFormDim; begin result := fList[aIndex] as TFormDim; end;
function TFormList.IndexOfForm(aForm: TForm): integer; var i : integer; Found : boolean; begin result := -1; i := -1; Found := false; while (i < fList.Count - 1) and not Found do begin inc(i); Found := SameText(GetItems(i).Name,aForm.Name); end; if Found then result := i; end;
function TFormList.LoadFromFile(const aFilename: string): boolean; var fs: TFileStream; begin result := false; if FileExists(aFilename) then begin fs := TFileStream.Create(aFilename,fmOpenRead); try LoadFromStream(fs); result := true; finally fs.Free; end; end; end;
procedure TFormList.LoadFromStream(aStream: TStream); var i: integer; Reader: TReader; begin Reader := TReader.Create(aStream,1024); try for i := 0 to Reader.ReadInteger - 1 do fList.Add(TFormDim.Create(Reader)); Reader.FlushBuffer; finally Reader.Free; end; end;
procedure TFormList.Restore(aForm: TForm); var Index: integer; begin Index := IndexOfForm(aForm); if Index >= 0 then GetItems(Index).Restore(aForm); end;
procedure TFormList.SaveToStream(aStream: TStream); var i: integer; Writer: TWriter; begin Writer := TWriter.Create(aStream,1024); try Writer.WriteInteger(fList.Count); for i := 0 to fList.Count - 1 do GetItems(i).SaveToWriter(Writer); Writer.FlushBuffer; finally Writer.Free; end; end;
function TFormList.SaveToTile(const aFilename: string): boolean; var fs: TFileStream; begin result := true; try fs := TFileStream.Create(aFilename,fmCreate); try SaveToStream(fs); finally fs.Free; end; except on e: exception do begin result := false; // Ignore but return false OutputDebugString(pchar(e.Message)); end; end; end;
Der laves global instans af listen. I formenes OnCreate kaldes den FormList.Restore(self) mens den i OnDestroy kaldes FormList.Backup(self)
Jeg ved godt koden er lidt overvældende, men med ovenstående unit har du alle dine skærmbilleder samlet i en effektiv struktur som kan gemmes i en database eller i en fil ... eller i hvad der nu er streamtyper til.
Jeg takker for input, men går videre med min egen løsning, fordi den kan jeg gennemskue og fejlrette i. Måske ændrer jeg den lidt m.h.til at indlæse til memo eller stream, men ellers: "as long as it works....... who cares what it looks like!".
Nu vil jeg påstå at der ikke er noget at rette i min kode. Den virker, den har mulighed for udvidelser og så svært er den sgu da heller ikke. Det er bare fordi det er pakket ind i klasser at det fylder lidt.
I øvrigt er jeg uenig med din "bare det virker". Jeg har tit brugt timer på efterfølgende fejlrettelser af den slags kode. Det koster stort set det samme at gøre det ordentligt. Det er vel også der man skelner mellem håndværk og klamp...
Jeg synes du skal kigge på koden. Der er masser af små finurligheder mht. streams, lister og den slags.
Altså en gammel version af Delphi. Hvis du flytter konstanten ud af klassen og fjerne "strict", antager jeg den virker. Hos mig virker det fint i D2009.
const Version = 1; // Save version
type TFormDim = class private fTop: integer; ...
Versionsnummeret er der for at håndtere forskellige versioner af gemmerutinen. Hvis man skulle føje mere til, så tæller man blot nummeret op og laver en indlæsning som håndterer versionen også.
Synes godt om
Ny brugerNybegynder
Din løsning...
Tilladte BB-code-tags: [b]fed[/b] [i]kursiv[/i] [u]understreget[/u] Web- og emailadresser omdannes automatisk til links. Der sættes "nofollow" på alle links.