Çalışma Zamanı Tasarımını Kayıt Etmek ve Oluşturmak

Yazdığınız makaleleri ve üyelerimizin işine yarayacağını düşündüğünüz kodlarınızı gönderebilirsiniz. Bu foruma soru sormayın!
Cevapla
Kullanıcı avatarı
sabanakman
Kıdemli Üye
Mesajlar: 3081
Kayıt: 17 Nis 2006 08:11
Konum: Ah bi Antalya olaydı keşke (Ankara)

Çalışma Zamanı Tasarımını Kayıt Etmek ve Oluşturmak

Mesaj gönderen sabanakman »

Herkese iyi çalışmalar;

Bu yazımın amacı program kullanıcılarının çalışma zamanında kendine özgü bir ekran görüntüsünü oluşturup bunu kayıt etmek ve bu kaydı tekrar yüklemeye yarayan bildiğim bir tekniği paylaşıma açmaktır. Bu duruma daha çok rapor tasarım araçlarında rastlanmaktadır. Hatta bilindik delphi IDE'si ve VCL'i bile tasarlanan formları .dfm dosyalarında buna benzer bir tekniği kullanarak kayıt edip kullanmaktadır. Bu yazıda tasarım ve görüntü organizasyonu üzerine fazla yoğunlaşamayacağım. Sadece kayıt ve yükleme işleminden bahsedecğim.

Bu konuya giriş yapmadan önce TComponent mimarisi hakkında bilinmesi gereken önemli bir kaç noktadan bahsetmek gerekmektedir. TComponent sınıfı bileşen sınıfı oluşturmak için gereken temel sınıftır. Tüm Delphi bileşenleri bu sınıftan türetilerek oluşturulmaktadır. Bu bileşenin bariz bir özelliği vardır, her bileşen tıpkı disk üzerindeki klasörler gibidir. Nasıl ki bir klasör içinde başka klasörler hatta onların içinde de başka başka klasörler oluşturabiliyorsak aynı durum bileşenler içinde geçerlidir. Her bileşen sahibi olduğu bileşenleri kendi bünyesinde liste halinde tutmaktadır. Haliyle de bir bileşenin silinmesi de bu bileşenin bünyesinde bulunan tüm bileşenlerin silinmesini sağlar (Bir klasör silinince tüm alt klasörler de silinir). Mesela çalışma zamanında şu kodu test edersek formumuzun üzerinde bulunan Panelin içinde bir TEdit bileşeni belirecektir.

Kod: Tümünü seç

procedure TForm1.Button1Click(Sender: TObject);
begin
  with TEdit.Create(Self) do begin
    Parent:=Panel1;
    Show;
  end;
end;
Örneğimizde klasör benzetmesine dönersek, Form1'in altında Edit klasörü oluşturulmaktadır. Artık Form1.Free; kodu oluşturulan bu bileşeni de silecektir. Yine de klasör benzetmemize göre farklı bir noktaya değinmem gerekecek. Bileşen isimlendirmeleri (Name özelliği belirleme). Bir bileşen altında bulunan bileşenlere aynı değere sahip 2. isim verilemese bile, boş olarak istenildiği kadar bileşen oluşturulabilir (Boş klasör ismi verilemez). Delphi nesneleri ise şu yapıdadır:
Application
....|
....|-->DataModul Bileşeni
....|............|
....|............|-->Görsel Olmayan Bileşenler (TTable,TQuery vs. vs. gibi bileşenler)
....|
....|-->Form Bileşeni
................|
................|-->Görsel veya Görsel Olmayan Bileşenler (Tüm delphi bileşenleri)
Bu yapımıza göre Application bileşeni altında aynı isme sahip TForm veya TDataModul bileşeni bulundurulamaz ve aynı mantığa göre her DataModul veya Form bileşenlerinin kendi bünyesinde aynı isme sahip iki bileşen bulundurulamaz.

Kısacası anlatmaya çalıştığım nokta tasarım anında oluşturulacak nesneler için temel bir bileşen oluşturmak ve bu tasarım nesnelerini bu temel nesnenin bünyesinde tutmak. Dosya kayıt işlemleri için ise Name özelliğine değer ataması yapabilmek. Bu 2 maddeyi uygulamak o kadar da önemli değil aslında ama işlem organizasyonumuzun kolaylığı ve örneklerin anlaşılabilmesi için gerekli. Mesela Design isimli bir bileşen bünyesinde oluşturulmuş bir projeyi Dosya->Yeni diyerek boşaltmak için yapılması gereken tek şey Design.Free; kodunu yazarak tüm tasarımı silmek olacak.

Asıl konumuza dönecek olursak; burada anahtar nesne ve metotlarımız bileşen tasarımını kayıt etmek için TStream.WriteComponent(TComponent); yapılan kayıtlardan bileşenleri tekrar oluşturmak için TStream.ReadComponent(TComponent);'dir. Bunları kullanabilmek için dikkat edilmesi gereken noktalar bulunmaktadır. Bunlar bana göre olmazsa olmaz dediğimiz türden kurallardır:
1- .dfm dosyasına yazılan özellik ve değerler published bloğunda tanımlıdır. Hatta onClick:=Button1Click; biçimindeki bir değerde Button1Click'te nereden geldi denilebilir. Bu da aslında published bloğunda tanımlanmıştır. Delphinin özellikler penceresinde (Object Inspector) olayların listeye gelebilmesi published olarak tanımlanan aynı tipteki olayların varlığına bağlıdır. Başka bir blok public,protected,private içinde tanımlanan bir olay bile kullanılabilsede bu ancak kodla yapılabilir. Bunu ne Object Inspector'dan ayarlayabilirsiniz ne de .dfm dosyasına kayıt edip kullanabilirsiniz. İllaki published bloğunda olacak.
2- .dfm biçiminde yapılacak kayıtlarda ve bundan tekrar oluşturmada kullanılacak sınıfların muhakkak RegisterClass(TBitBtn); prosedürü ile kayda geçmesi gerekmektedir.WriteComponent ile kaydetme esnasında her ne kadar sorun çıkarmasa da, ReadComponent oluşturma esnasında bu sınıf bulunamadı diye bir hata oluşturacaktır.
3- Oluşturulan tasarım nesnelerine ait olaylarında düzgün kayıt edilip okunabilmesi için kayıt edilecek kök bileşende tanımlı olması gerekmektedir. Haydaaa bu da nedemek dediğinizi duyar gibi oldum. Mesela bir form tanımı TForm1=class(TForm) şeklinde başlar. Buraya yerleştirilen her bileşen ve olayları TForm1 sınıfına aittir. Button1 nesnesine ait onClick olayı procedure TForm1.Button1Click(Sender: TObject); şeklinde tanımlıdır. Dikkat edilecek olursa Button1Click TForm1'e ait bir metotdur. Stream.WriteComponent(Form1); komutu bilindik .dfm dosyasını ortaya çıkaracaktır. Stream.WriteComponent(Button1); kullanılırsa tüm buton özelliklerinin .dfm biçiminde kayıt edilmesini sağlayacaktır fakat biri hariç. onClick=Button1Click; Bu değer elde edilemez çünkü Button1Click TForm1'e ait bir metotdur, Buttton1'e değil.
4- WriteComponent .dfm biçiminde kayıt edebilir ancak bu .dfm'nin yapısı ikilik biçimindedir. Bu ikilik biçimi metin biçiminde görebilmek için dönüştürücü ObjectBinaryToText fonksiyonuna ihtiyacımız olacak. ObjectTextToBinary ise bu metin biçimindeki .dfm değerlerini tekrar ikilik biçimine dönüştürür ve ReadComponent için kullanıma hazır hale getirir. Zaten örneğimizde bu dönüşümleri yapacak kodlar mevcut.
Not:Bu 4 maddeye değişik yorumlar getirenler olabilir, malum kaynak ve yabancı dil sıkıntısı çektiğim için ben bu kadarını buraya kadar çözüp, üretebildim. Ayrıca bu kadar detaylı bir anlatım ve kaynağa ihtiyaç muhakkak var kendimden biliyorum.

Hikaye yeter, uygulama isteriz dediğinizi duyar gibiyim. Eh!.. Ne yapalım, bu noktaya gelebilmek için yukarıdaki bilgilerin gerekli olması gerektiğini düşünüyorum. İlk yapılması gereken işlemimiz kayıt işlemini gerçekleştireceğimiz bölgesel ana sınıfı tanımlamak. Mesela bir formu kayıt etmek için oluşturulan bölgesel ana sınıf TForm1 gibi bir sınıftır. Bizde buna benzer bir sınıf oluşturacağız. Fakat bir sorunumuz var. Sınıfı hangi nesneden türetmemiz gerekiyor? Benim aklıma en yatkın olanı TScroolBox'tır. Yazacağımız tüm olayları (bilgileri kayıt edilecek tasarım nesnelerine ait olan olaylarda dahil onClick, onKeyDown vs.) bu sınıf içinde tanımlamamız gerekecek.

Kod: Tümünü seç

unit unit_designer;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

const WM_DESIGNER_RELEASE=WM_USER+4;

type
  TDesigner=class(TScrollBox)
  //<-burası her ne kadar belirtilmese de published bloğudur->\\
    procedure ScrClick(Sender:TObject);
    procedure lblClick(Sender:TObject);
  private
    procedure SetHintActive(const Active:Boolean);
    procedure WMRelease(var Message: TMessage); message WM_DESIGNER_RELEASE;
  public
    constructor Create(AOwner:TComponent);override;
    destructor Destroy; override;
    function Ekle(const Sinif:TControlClass):TControl;
    property HintActive:Boolean write SetHintActive;
  protected
  published
  end;

  TControlCrack=class(TControl);
  
function DesignerLoad(const OwnerAndParent:TWinControl):TDesigner;

implementation

function DesignerLoad(const OwnerAndParent:TWinControl):TDesigner;
var i:Integer; S,Name:String;
begin
  Result:=TDesigner.Create(OwnerAndParent);
  Name:='Designer';i:=0;
  repeat
    Inc(i);
    S:=Format('%s%d',[Name,i]);
    if not Assigned(OwnerAndParent.FindComponent(S)) then Break;
  until False;
  Result.Name:=Name;
  Result.Parent:=OwnerAndParent;
  Result.Align:=alClient;
  Result.Color:=clWindow;
  Result.Show;
  Result.HintActive:=True;
end;

{ TDesigner }

constructor TDesigner.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  OnClick:=ScrClick;
end;

destructor TDesigner.Destroy;
begin
  inherited Destroy;
end;

function TDesigner.Ekle(const Sinif: TControlClass): TControl;
begin
  Result:=Sinif.Create(Self);
  with TControlCrack(Result) do begin
    Parent:=Self;
    OnClick:=lblClick;
    Hint:=ClassName+#13#10+Name+'->'+Owner.Name+'('+Owner.ClassName+')';//Result.Owner=Self'tir
    Show;
  end;
end;

procedure TDesigner.WMRelease(var Message: TMessage);
begin
  Free;
end;

procedure TDesigner.SetHintActive(const Active: Boolean);
var i:Integer;
begin
  if Active then begin
    Hint:=ClassName+#13#10+Name+'->';
    if Assigned(Owner) then Hint:=Hint+Owner.Name+'('+Owner.ClassName+')'
    else Hint:=Hint+'(Boş)'
  end;
  ShowHint:=Active;
  for i:=0 to ComponentCount-1 do
   if Components[i] is TControl then
    with TControlCrack(Components[i]) do ShowHint:=Active;
end;

procedure TDesigner.lblClick(Sender: TObject);
begin
  HintActive:=False;
  PostMessage(Self.Handle,WM_DESIGNER_RELEASE,0,0);
end;

procedure TDesigner.ScrClick(Sender: TObject);
begin
  ShowMessage(IntToStr(ComponentCount));
end;

initialization
  RegisterClass(TDesigner);
  RegisterClass(TLabel);
end.
Konuyu fazla saptırmadan kodlara biraz açıklık getirmem gerekiyor. TControlCrack sınıfı TControl sınıfında erişilemeyeven onClick, Hint,ShowHint ve daha bir çok protected özelliğe erişmemizi sağlar. ScrClick tasarım nesnemize tıklanınca çalışacak olay ve lblClick ise TDesigner üzerine eklenen kontrole tıklanınca çalışacak olaylardır. Son olarak ise bu nesnenin bünyesinde bulunan herhangi bir bileşenden bu tasarım bileşenini direk yok etmek istersek bunu alt bileşenlerden yapmak isteyenler için hataya yol açmayan bir yöntem olarak mesaj göndererek işlem yaptırmayı uyguladım. Eğer PostMessage kullanılmadan direk Free metodu çalıştırılırsa belki bazı Access Violation hataları gelebilir. Bunu engellemek için bu yöntem kullanıldı. Düşünsenize alt klasörden kök klasörü silmeye kalkarsanız bu klasör kullanımda diye hata gelebilir. Buradaki işlem ise bir mesajı mesaj kuyruğuna göndererek işlem bittikten sonra mesajı çalıştırmaktadır. Her neyse biz örneğimize geçelim. .dfm dosyası:

Kod: Tümünü seç

object FormAna: TFormAna
  Left = 192
  Top = 114
  Width = 696
  Height = 480
  Caption = 'FormAna'
  Color = clBtnFace
  Font.Charset = DEFAULT_CHARSET
  Font.Color = clWindowText
  Font.Height = -11
  Font.Name = 'MS Sans Serif'
  Font.Style = []
  OldCreateOrder = False
  ShowHint = True
  OnCreate = FormCreate
  PixelsPerInch = 96
  TextHeight = 13
  object Panel1: TPanel
    Left = 0
    Top = 0
    Width = 688
    Height = 281
    Align = alTop
    TabOrder = 0
    object Memo1: TMemo
      Left = 96
      Top = 40
      Width = 273
      Height = 201
      TabOrder = 0
    end
    object Button1: TButton
      Left = 384
      Top = 216
      Width = 75
      Height = 25
      Caption = '>>'
      TabOrder = 1
      OnClick = Button1Click
    end
    object Button2: TButton
      Left = 8
      Top = 56
      Width = 75
      Height = 25
      Caption = '>>'
      TabOrder = 2
      OnClick = Button2Click
    end
  end
end
.pas dosyası:

Kod: Tümünü seç

unit form_ana;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, unit_designer, ExtCtrls;

type
  TFormAna = class(TForm)
    Panel1: TPanel;
    Memo1: TMemo;
    Button1: TButton;
    Button2: TButton;
    procedure FormCreate(Sender: TObject);
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
    Designer:TDesigner;
  end;

  TDesignerCrack=class(TDesigner);

var
  FormAna: TFormAna;

implementation

{$R *.dfm}

function ComponentToString(Component: TComponent): string;
var
  BinStream:TMemoryStream;
  StrStream: TStringStream;
  s: string;
begin
  BinStream := TMemoryStream.Create;
  try
    StrStream := TStringStream.Create(s);
    try
      BinStream.WriteComponent(Component);
      BinStream.Seek(0, soFromBeginning);
      ObjectBinaryToText(BinStream, StrStream);
      StrStream.Seek(0, soFromBeginning);
      Result:= StrStream.DataString;
    finally
      StrStream.Free;
    end;
  finally
    BinStream.Free
  end;
end;

function StringToComponent(Value: string;const Instance:TComponent=nil): TComponent;
var
  StrStream:TStringStream;
  BinStream: TMemoryStream;
begin
  StrStream := TStringStream.Create(Value);
  try
    BinStream := TMemoryStream.Create;
    try
      ObjectTextToBinary(StrStream, BinStream);
      BinStream.Seek(0, soFromBeginning);
      Result := BinStream.ReadComponent(Instance);
    finally
      BinStream.Free;
    end;
  finally
    StrStream.Free;
  end;
end;

procedure TFormAna.FormCreate(Sender: TObject);
begin
  ShowHint:=True;Hint:=ClassName;//test formu için
  Designer:=DesignerLoad(Self);
  with Designer.Ekle(TLabel) do begin
    Name:='Label1';
    Left:=20;
    Top:=10;
  end;
end;

procedure TFormAna.Button1Click(Sender: TObject);
begin
  Designer:=TDesigner(StringToComponent(Memo1.Lines.Text));
  Designer.Parent:=Self;
end;

procedure TFormAna.Button2Click(Sender: TObject);
begin
  Memo1.Lines.Text:=ComponentToString(Designer);
end;

end.
Burada ilk dikkati çekmek istediğim nokta ComponentToString, StringToComponent prosedürleridir. Makalemizin kilit noktaları bunlardır. Geri kalanı delphi mimarisi ile alakalı bilgiler. Eğer örneği uygularsanız ana form ilk açıldığında altta beyaz bir ScroolBox ve üzerinde ise bir TLabel nesnesi göreceksiniz. İlk adım olarak Button1'e tıklarsanız Memo1.Lines.Text:=ComponentToString(Designer); kodu çalıştırılacak ve Memo içinde Designer isimli nesnemizin .dfm kodları görünecektir. Designer içindeki label nesnesine tıklarsanız Designer nesnemiz silinecektir. Çünkü TDesigner sınıfımızda Ekle metodunda onClick atamasında Designer'ı yok eden lblClick ataması yapıldı. Son olarak Memo içindeki değerlerle oynamalar yapalım. Mesela Color = clWindow değerini clRed yapalım. Label1 nesnemizin Left=20 özelliğini 150 yapalım ve Button2'ye tıklayalım.

Kod: Tümünü seç

  Designer:=TDesigner(StringToComponent(Memo1.Lines.Text));
  Designer.Parent:=Self;
Eğer söylediklerimi harfiyen yaptıysanız gördükleriniz, daha önce bu konuda uygulama yapmadıysanız çok hoşunuza gidecektir. Çünkü oluşan TDesigner nesnemiz kırmızı renkte bir TScrollBox olacak ve üzerindeki label nesnesi ise biraz daha sağ tarafta çıkacaktır. Hatta daha ileri gidip object Label1: TLabel satırındaki TLabel yazısını TStaticText yaparsak ve Designer oluşturucu olan Button2 ye tıklarsak TStaticText sınıfı bulunamadı hatası gelecektir. Bunun sebebini 2. maddede belirtmiştim. Eğer RegisterClass(TStaticText); kodunu kullanmış olsaydım bu değerde düzgün çalışacaktı. Kilit bir başka husus ise TStream->WriteComponent,ReadComponent metotları bileşenlerin altında bulunan diğer bileşenlerin özelliklerini çıkarmaz (Alt klasörlerinde altındaki diğer alt klasörler aynı anda görüntüye gelmez). Yani Form1 için ComponentToString(Form1) fonksiyonu Designer içinde oluşturulan bileşenlerin özelliklerini vermez. Çünkü for i:=0 to Form1.ComponentCount-1 do Form1.Components döngüsü hiçbir zaman Designer içindeki bileşelere ulaşamaz. Çünkü onlara for i:=0 to Form1.Designer.ComponentCount-1 do Form1.Designer.Components döngüsü ile erişilebilinir.

Diyelimki tasarımlarınızı ComponentToString ve StringToComponent fonksiyonlarını kullanarak değil, direk dosyaya yazarak ve okuyarak kullanmak istiyorsunuz o zaman şu kodları önerebilirim.

Kod: Tümünü seç

procedure ComponentSaveToFile(Component: TComponent;const FileName:String);
var
  FileStream:TFileStream;
begin
  FileStream:=TFileStream.Create(FileName,fmCreate);
  try
    FileStream.WriteComponent(Component);
  finally
    FileStream.Free
  end;
end;


function ComponentLoadFromFile(const FileName:String): TComponent;
var
  FileStream:TFileStream;
begin
  FileStream:=TFileStream.Create(FileName,fmOpenRead or fmShareDenyWrite);
  try
    Result := FileStream.ReadComponent(nil);
  finally
    FileStream.Free;
  end;
end;
TDesigner sınıfımızın içinde oluşturulan her bir nesne bu iki basit prosedür ve fonksiyon sayesinde çok kolay bir şekilde dosyaya kayıt edilebiliyor ve dosyadan okunabiliyor. Neyi nasıl kaydedeceğim diye kasılmanıza ve strese girmenize gerek yok :). Ne güzel bir şey değilmi. Keşke bu bilgiyi öğrendiğimden bir kaç ay önce elde edebilseydim. Böyle bir şeye ihtiyacım vardı ve klasik yollardan yapınca onlarca hatta bir kaç yüz kod satırı yazmak zorunda kaldım. Üstelik yukarıdaki 3-5 satır benim o kadar kod satırıma göre çok daha esnek ve çok daha sağlam. Hem TStream sınıfından kendiniz için türeteceğiniz sınıflarla dosya kayıt bilgilerini şifreleyebilirsiniz bile. Şimdi akla şu soru gelebilir. Biz bir formu yapıp kaydettiğimizde, programımız çalışırken Form1:=TForm1.Create(Application); kodunu yazdığımızda yaptığımız tasarım olduğu gibi geliyor, üstelik bu türden bir kayıt ve okuma işlemiyle uğraşmadığımız halde :) . Bu normal bir durumdur. Bu işlemle VCL uğraşmaktadır. Form tasarımlarımızı yaptığımız .pas dosyalarında {$R *.dfm} satırları mevcuttur. Bunun işlevi .dfm bilgilerini (TForm1 gibi sınıf ismiyle) .exe içine gömmektir. .exe içine gömülen bu bilgileri TResourceStream ile bu bilgiler doğrultusunda TCustomForm.Create içinde gerekli organizasyon bizim için düzenlenmektedir. TCustomForm.Create->InitInheritedComponent->InitComponent->InternalReadComponentRes prosedürlerini sırası ile takip ederseniz TResourceStream.Create(HInst, ResName, RT_RCDATA) içinde Instance := ReadComponent(Instance); atamasını görürsünüz ki bu da yukarıda bahsettiğim işlemi TResourceStream üzerinden gerçekleştirmektedir.

Tüm yazının amacı sadece bu ComponentSaveToFile prosedürü ve ComponentLoadFromFile fonksiyonu içindi. Her ne kadar basit gibi görünse de yukarıda belirttiğim 4 maddeye dikkat edilmezse işlem başarıyla gerçekleştirilemeyebilir. Konu ile ilgilenenlere yardımcı olabildiysem ne mutlu. Eğer atladığım, anlaşılmayan veya hatalı yerler varsa lütfen belirtin. Bu sayede bende birşeyler öğrenmeye devam ederim. Ayrıca bu yazı 2006'nın son günü ve bayramın ilk günü hediyem olsun. Böyle bir kaynak inşallah işinize yarar. Herkese çalışmalarında başarılar dilerim.
Şaban Şahin AKMAN
_________________
Derin olan kuyu değil kısa olan iptir. - .
edoyuko
Üye
Mesajlar: 24
Kayıt: 28 Ara 2006 01:29

otomatik form oluşturma

Mesaj gönderen edoyuko »

üstad yazını okudum.emek vermişsin,uğraşmışsın,ellerine sağlık.kıymetli bir yazı.ilk fırsatta örnek bir çalışma yapmaya çalışacağım.(kodlar beni biraz korkutsada).
ben otomatik form oluşturmayı ,projemdeki raporlar için kullandığım rapor kriter ekranlarının exe'nin boyutunu arttırmaması için düşünüyordum. ama şimdi bu sorunu, bu formları dll içinden çağırarak aşmayı deniyorum.4 saattir bir noktada tıkandım. dll'de oluşturduğum formu programdan çağırıyorum.dll formu açıldığında anaformun pasif olmasını istediğim için showmodal ile çağırıyorum.buraya kadar okey ama alt+tab veya görev çubuğundan başka bir programa geçip tekrar benim programa döndüğümde dllden çağırdığım form ekranda gözükmüyor ve showmodal ile çağırdığım içinde programa müdahele edilemiyor. dllden çağırdığım formu stilini stayontop yapınca düzeliyor ama o formun sürekli ekranda olmasıda çirkin oluyor. dllden çağırdığım formun handle nosunu alarak sendmessage ve showwindow apileri ilede göstermeye uğraştım ama olmadı.ne yapabilirim?? saygılar..
Kullanıcı avatarı
sabanakman
Kıdemli Üye
Mesajlar: 3081
Kayıt: 17 Nis 2006 08:11
Konum: Ah bi Antalya olaydı keşke (Ankara)

Re: otomatik form oluşturma

Mesaj gönderen sabanakman »

edoyuko yazdı:ilk fırsatta örnek bir çalışma yapmaya çalışacağım.(kodlar beni biraz korkutsada)
Hiç korkmana gerek yok. Acemi işi biraz mimarinin temellerine daldım ve ortaya uzun bir yazı çıktı. Orta seviyeyi aşmış her delphi yazılımcısı için sadece şu kriterlere dikkat etmek yeterli olacaktır.
sabanakman yazdı:Tüm yazının amacı sadece bu ComponentSaveToFile prosedürü ve ComponentLoadFromFile fonksiyonu içindi. Her ne kadar basit gibi görünse de yukarıda belirttiğim 4 maddeye dikkat edilmezse işlem başarıyla gerçekleştirilemeyebilir.
Geri kalanı fasa fiso. Anladığım kadarıyla projeni belirli bir noktaya kadar getirmişsin ve yöntem değişikliğine gitmek istemiyorsun. Bahsettiğin konulara pek hakim olmadığım için yardım etmekten acizim ama istersen yazı içindeki örneği bir test et. Belki çok fazla uğraşmana gerek kalmadan değişiklik yapabilecek bir yapın vardır. Üstelik tasarımlar için .dll dosya değil direk disk üzerinde arşiv dosyaları halinde tutmak daha esnek bir yapı barındırmaktadır. Olmazsa sorunu forumdan sorarsan daha sağlıklı yardım alabilirsin.
Şaban Şahin AKMAN
_________________
Derin olan kuyu değil kısa olan iptir. - .
Kullanıcı avatarı
husonet
Admin
Mesajlar: 2962
Kayıt: 25 Haz 2003 02:14
Konum: İstanbul
İletişim:

Mesaj gönderen husonet »

Eline sağlık güzel olmuş şimdi makaleni okurken aklıma bir soru geldi soramadan geçemiycem. :oops:

WriteComponent bildiğim kadarıyla Binary şeklinde Stream a yazıyor peki bunu ini formatında nasıl yazarız stream a?

Teşekkürler

Gazete manşetleri
* DİKKAT :Lütfen forum kurallarını okuyalım ve uyalım...!
* Warez,crack vs. paylaşımı kesinlikle yasaktır.
Kullanıcı avatarı
sabanakman
Kıdemli Üye
Mesajlar: 3081
Kayıt: 17 Nis 2006 08:11
Konum: Ah bi Antalya olaydı keşke (Ankara)

Mesaj gönderen sabanakman »

Bu makaledeki yapı, borland yazılımcılarının kullandığı .dfm kayıt mimarisidir. Delphi IDE'sini kullanarak bir formu kayıt ederseniz makalede bahsettiğim sistemi kullanarak çalışma yapmaktadırlar. Hatta .exe'de formlar create edilirken'de .exe içine gömülmüş {$R *.res} .dfm bilgilerinden ReadComponent prosedürü çalıştırılarak formlar oluşturuyor. Makalemde de belirttiğim gibi .dfm biçimine müdahele etmek için kendi TStream sınıfını geliştirmen ve bunun okuma yazma metotlarındaki kodlara müdahele etmen gerekmektedir. Bunun için örnek yazmadım ama Marco Cantu'nun Delphi7 kitabında bu bilgiler var. Aslında WriteComponent ve ReadComponent prosedürleri kayıt işlemini direk tek kalemde organize ettiklerinden .ini biçimi için ayrı zahmete girmeye de gerek yoktur. Eğer dosyanın bir metin editöründen açıldığı zaman okunabilir olabilmesi için bunu istiyorsan o zaman kaydetmeden önce ComponentToString'le String değeri elde edip bunu kayıt edersen ve StringToComponent'i kullanarak kaydedilen bu değerden yükleme işlemini yaparsan o zaman ortaya çıkan bilgilere dışarıdan elle müdahele edilebilecek formata gelecektir. Bu noktada tabiki WriteComponent ve ReadComponent kullanılmayıp yerini ComponentToString ve StringToComponent'e bırakacaktır.
Şaban Şahin AKMAN
_________________
Derin olan kuyu değil kısa olan iptir. - .
Kullanıcı avatarı
Opt2000
Üye
Mesajlar: 216
Kayıt: 09 Tem 2003 10:04

Mesaj gönderen Opt2000 »

Selam,

@sabanakman güzel bir konuya değinmiş. Ben de @husonet'in sorusuna cevap olabilecek birşeyler yazayım. Küçük bir not, bu yazdıklarım sadece ini dosyasına yazmak için. Okumak için formatı biraz daha değiştirmek gerekebilir. Ayrıca her özellik tipi için örnek bulamadım. Onları commentledim, eğer bulabilirseniz ekleyebilirsiniz.

Kod, iç içe classları destekliyor ve onları ini dosyasına ayrı bir section olarak yazıyor. Recursive bir yapısı var. Kullanımı da çok kolay

Kod: Tümünü seç

  SavePropsToIni(btnTest, 'c:\TestButton.ini', btnTest.Name);
Küçük bir not daha ekleyeyim. Bileşen özelliklerinin string olarak tutulması (Ini dosyaları string sonuçta) çok akıllıca değil. Basit bir örnek vereyim, değerin bir buçuk (özellikle yazı ile yazdım) olan double bir özelliği Türkçe sistemde kaydettiğiniz zaman bu 1,5 olarak yazılır. Ama daha sonra sistemi İngilizceye çevirip okutmayı denediniz (ki program uyumsuzluklarından dolayı bunu yapmak gerekebiliyor, örneğin NetBeans) değeri okuyamayacak ve size default değeri gönderecektir.

Kod: Tümünü seç


procedure TForm1.SavePropsToIni(const AComponent:TObject; const IniFileName:string; const Section:string);
var
  IniFile:TIniFile;
  I:integer;
  PropList:PPropList;
  PropInfo:PPropInfo;
  AMethod:TMethod;
  PropCount:integer;
begin
  IniFile := TIniFile.Create(IniFileName);

  PropCount := GetPropList(AComponent, PropList);

  for I:=0 to PropCount -1 do
  begin
    PropInfo := PropList^[I];

    case PropInfo.PropType^.Kind of
//      tkUnknown:    Unknown özelliği olan bileşen bulamadım
      tkInteger:      IniFile.WriteInteger(Section, PropInfo.Name, GetInt64Prop(AComponent, PropInfo.Name));
      tkChar:         IniFile.WriteString(Section, PropInfo.Name, GetPropValue(AComponent, PropInfo.Name));
      tkEnumeration:  IniFile.WriteString(Section, PropInfo.Name, GetEnumProp(AComponent, PropInfo.Name));
      tkFloat:        IniFile.WriteFloat(Section, PropInfo.Name, GetFloatProp(AComponent, PropInfo.Name));
      tkString:       IniFile.WriteString(Section, PropInfo.Name, GetStrProp(AComponent, PropInfo.Name));
      tkSet:          IniFile.WriteString(Section, PropInfo.Name, GetSetProp(AComponent, PropInfo.Name, true));
      tkClass:
      begin
        if not Assigned(GetObjectProp(AComponent, PropInfo.Name)) then
          IniFile.WriteString(Section, PropInfo.Name, '')
        else
        begin
          IniFile.WriteString(Section, PropInfo.Name, Section + PropInfo.Name);
          SavePropsToIni(GetObjectProp(AComponent, PropInfo.Name), IniFileName, Section + PropInfo.Name);
        end;
      end;
      tkMethod:
      begin
        AMethod := GetMethodProp(AComponent, PropInfo);
        IniFile.WriteString(Section, PropInfo.Name, MethodName(AMethod.Code));
      end;
      tkWChar:        IniFile.WriteString(Section, PropInfo.Name, GetWideStrProp(AComponent, PropInfo.Name));
      tkLString:      IniFile.WriteString(Section, PropInfo.Name, GetStrProp(AComponent, PropInfo.Name));
      tkWString:      IniFile.WriteString(Section, PropInfo.Name, GetWideStrProp(AComponent, PropInfo.Name));
//      tkVariant:      Variant özelliği olan bileşen bulamadım
//      tkArray:        Array özelliği olan bileşen bulamadım
//      tkRecord:       Record özelliği olan bileşen bulamadım
//      tkInterface:    Interface özelliği olan bileşen bulamadım
      tkInt64:        IniFile.WriteInteger(Section, PropInfo.Name, GetInt64Prop(AComponent, PropInfo.Name));
//      tkDynArray:     DynArray özelliği olan bileşen bulamadım
    end;
  end;

  IniFile.Free;
end;

Son olarak koddaki bir diğer can sıkıcı durum, TCursor gibi aslında integer olan değerler. Yukarıdaki kod Cursor değeri için bir sayı veriyor. Bunu okurken büyük ihtimalle problem olmayacaktır, ama eğer problem yaratacak olursa, GetPropValue fonksiyonu ile string değerini alabilirsiniz. Sanırım daha sonra SetPropValue ile set edeceğiniz zaman sorun olmayacaktır.

Son olarak RTTI işlemlerinin çok yavaş olduğunu da belirtmem gerekiyor. Bu RTTI'nin genel yapısından dolayıdır ve C++ gibi dillerde de yavaştır. Bu yüzden özel durumlar dışında kullanmanızı tavsiye etmem.

Kolay gelsin,
Bahadır Alkaç
Kullanıcı avatarı
sabanakman
Kıdemli Üye
Mesajlar: 3081
Kayıt: 17 Nis 2006 08:11
Konum: Ah bi Antalya olaydı keşke (Ankara)

Mesaj gönderen sabanakman »

Sn Opt2000, sizin verdiğiniz bilgilerde direk makale başlığını karşılayacak başka bir makale olmuş. Aslında bu kodlarda bir çok özellik ve bilgi açığa çıkmaktadır. Paylaşımınız için teşekkürler.
Şaban Şahin AKMAN
_________________
Derin olan kuyu değil kısa olan iptir. - .
ssteeltr
Üye
Mesajlar: 135
Kayıt: 08 Nis 2005 03:41
Konum: Kayseri

Re: Çalışma Zamanı Tasarımını Kayıt Etmek ve Oluşturmak

Mesaj gönderen ssteeltr »

arkadaşlar ComponentToString(form1) bilgilerini veritabanına ekliyorum ve veritabanında bu bilgileri değiştirip tekrar form1 e yüklemek istiyorum burda
StringToComponent(Query.Fields[2].asstring); ile yüklemek istediğimde

Kod: Tümünü seç

A Component named LoadButton alreadyexists
hatası veriyor.

şimdi ben form1 e ait veritabanında değiştirdiğim haliyle bilgileri forma tekrar nasıl yüklerim.

mesela veritabanında Form1.DataBaseLinkEdit.Text:= değerini değitiriyorum. ve tekrar yüklemek istiyorum.
Süleyman Çelik
Kullanıcı avatarı
sabanakman
Kıdemli Üye
Mesajlar: 3081
Kayıt: 17 Nis 2006 08:11
Konum: Ah bi Antalya olaydı keşke (Ankara)

Re: Çalışma Zamanı Tasarımını Kayıt Etmek ve Oluşturmak

Mesaj gönderen sabanakman »

Burada bileşenler sıfırdan oluşturulmaktadırlar ve LoadButton isimli bir butonun olduğu formda tekrar LoadButton isimli bir bileşen daha oluşturulmaya çalışıldığından bu hata gelmektedir. Sadece text değerleri için komple bileşeni kayıt etmek biraz abes olacaktır. Bunun için .ini dosyalar (veya benzeri başka yöntemler) daha uygun olacaktır. Buradaki yöntem delphi formu tasarlayıp kayıt etmeye benzemektedir. Bu gibi görsel bileşen tasarımlarının kayıt edilmesinde kullanılması daha uygun olacaktır.
Şaban Şahin AKMAN
_________________
Derin olan kuyu değil kısa olan iptir. - .
Cevapla