Avatar billede jfj Nybegynder
15. februar 2011 - 23:29 Der 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 ?
15. februar 2011 - 23:39 #1
Hej

Se lidt på TSCREEN omkring skærm-positioner positionen (jeg kan se du allerede har check på det... )

og lidt på TINIFILES omkring det at gemme dine data

(brug INITIALIZE og FINALIZE ).

Kristian
Avatar billede stone Forsker
16. februar 2011 - 07:49 #2
Form OnCreate//hent indstillinger

Inifile := TIniFile.Create( ChangeFileExt( Application.ExeName, '.INI' ) );
  try
    Top    := IniFile.ReadInteger( 'test', 'Top', 100 );
    Left    := IniFile.ReadInteger( 'test', 'Left', 100 );
    Caption := IniFile.ReadString( 'test', 'Caption', 'test' );
    if IniFile.ReadBool( 'test', 'InitMax', false ) then
      WindowState := wsMaximized
    else
    WindowState := wsNormal;
    Finally
    IniFile.Free;


Form OnClose//gem form indstillinger


  Inifile := TIniFile.Create( ChangeFileExt( Application.ExeName, '.INI' ) );
  try
    Top    := IniFile.ReadInteger( 'test', 'Top', 100 );
    Left    := IniFile.ReadInteger( 'test', 'Left', 100 );
    Caption := IniFile.ReadString( 'test', 'Caption', 'test' );
    if IniFile.ReadBool( 'test', 'InitMax', false ) then
      WindowState := wsMaximized
    else
    WindowState := wsNormal;
    Finally
    IniFile.Free;


//husk
var
IniFile: TiniFile;
Avatar billede hugopedersen Nybegynder
16. februar 2011 - 08:02 #3
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.

//*********************************************************
// Unit for saving and reading formposition using ini file
// © OZ8HP Hugo Pedersen
//*********************************************************
unit U_FormsIni;

interface

uses
  Dialogs, Classes, Inifiles, SysUtils, Windows, Forms;

{$M+}

type
  TFormRec = packed record
    Top: integer;
    Left: integer;
    Width: integer;
    Height: integer;
    State: Integer;
  end;

  TForm = class(Forms.TForm)
  strict private
    function GetFormRec : TFormRec;
    procedure SetFormRec(const FormRec : TFormRec);
  protected
    procedure DoCreate; override;
    procedure DoDestroy; override;
  published
    property FormRec : TFormRec read GetFormrec write SetFormRec;
  public
    procedure SaveToIni;
    procedure LoadFromIni;
  end;

implementation

uses
  U_Strings;

const
  conSectionFormsPos        = 'FORMS_POS';

{ TFormHelper }

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;


end.
Avatar billede hugopedersen Nybegynder
16. februar 2011 - 08:35 #4
Der mangler en funktion fra min U_Strings

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;
Avatar billede jfj Nybegynder
16. februar 2011 - 10:15 #5
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.
Avatar billede hrc Mester
16. februar 2011 - 14:05 #6
Hvad med registreringsdatabasen? Kan du ikke bedre gemme der?


const
  BaseRoot = '\software\myfirm\myapp\';
  RegValue = 'form';

procedure SaveToRegistry(aForm: TForm);
var
  Writer: TWriter;
  Stream: TMemoryStream;
begin
  with TRegistry.Create do
    try
      if OpenKey(BaseRoot + aForm.Name,true) then
      begin
        Stream := TMemoryStream.Create;
        Writer := TWriter.Create(Stream,1024);
        try
          Writer.WriteInteger(aForm.Top);
          Writer.WriteInteger(aForm.Left);
          Writer.WriteInteger(aForm.Width);
          Writer.WriteInteger(aForm.Height);
          Writer.FlushBuffer;
          WriteBinaryData(RegValue,Stream.Memory^,Stream.Size);
        finally
          Writer.Free;
          Stream.Free;
        end;
      end;
    finally
      Free;
    end;
end;

Den laver en enkelt linje i registry som indeholder alle oplysninger.

Læsning er tilsvarende let:

procedure ReadFromRegistry(aForm: TForm);
var
  Reader: TReader;
  Stream: TMemoryStream;
begin
  with TRegistry.Create do
    try
      if OpenKeyReadOnly(BaseRoot + aForm.Name) then
        if ValueExists(RegValue) then
        begin
          Stream := TMemoryStream.Create;
          Reader := TReader.Create(Stream,1024);
          try
            Stream.Size := GetDataSize(RegValue);
            ReadBinaryData(RegValue,Stream.Memory^,Stream.Size);
            Stream.Seek(0,soFromBeginning);
            aForm.Top := Reader.ReadInteger;
            aForm.Left := Reader.ReadInteger;
            aForm.Width := Reader.ReadInteger;
            aForm.Height := Reader.ReadInteger;
          finally
            Reader.Free;
            Stream.Free;
          end;
        end;
    finally
      Free;
    end;
end;
Avatar billede jfj Nybegynder
16. februar 2011 - 23:17 #7
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 :-)
Avatar billede stone Forsker
17. februar 2011 - 00:08 #8
Her lige et som virker har lige testet den, fik postet garbish tidligere..

Uses IniFiles


procedure TTest.FormClose(Sender: TObject; var Action: TCloseAction);
var
IniFile: TiniFile;
begin
  IniFile := TIniFile.Create( ChangeFileExt( Application.ExeName, '.INI' ) );
  try
    IniFile.WriteInteger( 'test', 'Top', Top);
    IniFile.WriteInteger( 'test', 'Left', Left);
    IniFile.WriteString( 'test', 'Caption', Caption );
    IniFile.WriteBool( 'test', 'InitMax', WindowState = wsMaximized );
    Finally
    iniFile.Free;
  end;
end;

procedure TTest.FormCreate(Sender: TObject);
var
IniFile: TiniFile;
begin
Inifile := TIniFile.Create( ChangeFileExt( Application.ExeName, '.INI' ) );
  try
    Top    := IniFile.ReadInteger( 'test', 'Top', 100 );
    Left    := IniFile.ReadInteger( 'test', 'Left', 100 );
    Caption := IniFile.ReadString( 'test', 'Caption', 'test' );
    if IniFile.ReadBool( 'test', 'InitMax', false ) then
      WindowState := wsMaximized
    else
    WindowState := wsNormal;
    Finally
    IniFile.Free;

end;

end;
Avatar billede hugopedersen Nybegynder
17. februar 2011 - 08:40 #9
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.
Avatar billede jfj Nybegynder
17. februar 2011 - 09:12 #10
jamen jeg vil da gerne have eksemplet tilsendt. Min adresse er fjorgensen@c.dk
Avatar billede hugopedersen Nybegynder
17. februar 2011 - 09:39 #11
Eksemplet er afsendt i PM

Hvis andre vil have det så kan det hentes indtil 24. februar på https://rcpt.yousendit.com/1047853587/1ab4993db27fba922fb64241feb24e5e

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.
Avatar billede hrc Mester
17. februar 2011 - 10:35 #12
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.Backup(aForm: TForm);
begin
  // assert((fName = '') or SameText(fName,aForm.Name));
  fName := aForm.Name;
  fTop := aForm.Top;
  fLeft := aForm.Left;
  fWidth := aForm.Width;
  fHeight := aForm.Height;
end;

constructor TFormDim.Create(aForm: TForm);
begin
  inherited Create;
  Backup(aForm);
end;

procedure TFormDim.Restore(aForm: TForm);
begin
  // assert(SameText(fName,aForm.Name));
  aForm.Top := fTop;
  aForm.Left := fLeft;
  aForm.Width := fWidth;
  aForm.Height := fHeight;
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;

initialization
  FormList := TFormList.Create;
  FormList.LoadFromFile(FormFile);

finalization
  FormList.SaveToTile(FormFile);
  FormList.Free;

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.
Avatar billede jfj Nybegynder
21. februar 2011 - 22:53 #13
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!".

Tak til alle!
Avatar billede hrc Mester
22. februar 2011 - 09:58 #14
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.
Avatar billede jfj Nybegynder
22. februar 2011 - 10:41 #15
Nu er der bare det ved det, at din kode vist i eksemplet IKKE virker. Når jeg prøver at køre den går den i fejl allerede efter

unit UFormDims;
interface
uses
  SysUtils, ContNrs, Classes, Forms, Windows;
type
  TFormDim = class....med fejlmeddelelsen: Identifier expected.

Jeg ved ikke om linien skal være

TFormDim = class(TForm) men så går den i fejl ved
  const Version = 1; // Save version

og så var det jeg gav op og gik videre med mit eget som virker!
Avatar billede hrc Mester
22. februar 2011 - 15:34 #16
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å.
Avatar billede Ny bruger Nybegynder

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.

Loading billede Opret Preview

Log ind eller opret profil

Hov!

For at kunne deltage på Computerworld Eksperten skal du være logget ind.

Det er heldigvis nemt at oprette en bruger: Det tager to minutter og du kan vælge at bruge enten e-mail, Facebook eller Google som login.

Du kan også logge ind via nedenstående tjenester