{------------------------------------------------------------------------------} { Unit Name: kEditPnl Purpose : Panel that has Edit and Label same time Author : Vesa Lappalainen Date : 08.09.1997 Changed : 15.9.2001/vl + VCL/CLX compiling (define const CLX) Changed : 29.9.2001/vl + Label is showing the caption in CLX because Panel does not show underlines in CLX ToDo : How to make all events to CtrlPanel } {------------------------------------------------------------------------------} unit kEditPnl; {------------------------------------------------------------------------------} { Tässä mallissa luodaan oma komponentti TEditPnl, jossa kulkee yhdessä sekä Edit-kenttä, että siihen liittyvä ohjeteksti. Alkuperäinen komponentti on kirjoitettu vain TEditille, mutta sitten TEdit on poistettu ja korvattu yleisellä TControl-komponentilla. TEditPanel on sitten saatu yleisen TCtrlPanelin erikoistapauksena. Näin sama tekmiikka voidaan helpommin periä mille tahansa komponentille. Lopuksi on lisätty TIniSave-luokan käyttö. Tämän ansiosta panelin päällä oleva komponentin "arvo" tallettuu automaattisesti Ini-tiedostoon ja ladataan automaattisesti seuraavan käynnistyksen yhteydessä. Vesa Lappalainen 08.09.1997 / Graafisten käyttöliittymien ohjelmointi -97 Toimii vain kun lomake tuhotaan tai itse komponentti tuhotaan. Jos on esim. panelin päällä ja paneli tuhotaan, niin tallettaa väärin. (Vesa Lappalainen 19.11.1999) Tuo korjaantui kun talletus tehdään viestin WM_DESTROY-käsittelyssä. Nimittäin WM_DESTROY käsitellään ennen Destroy-metodia ja näin WInControllin Text-ominaisuus on hävinnyt (puskuri = nil => tyhjä merkkijono). Vesa Lappalainen 28.12.2000 Komponentin käyttö: =================== TEditPanel: ----------- 1) vedä komponentti lomakkeelle. 2) syntyy EditPanel1 ja EditPanel1Edit. 3) jos nimi muutetana esim. EditPanelNimi, muuttuu paneelin Caption automaattisesti Nimi. 4) Kumpaakin komponenttiin voidaan vapaasti viitata ohjelmakoodissa. 5) Tarvittaessa Edit-komponentin tekstiin päästään käsiksi myös s := EditPanelNimi.Text; tai s := EditPanelNimi.AsString; Voidaan myös kirjoittaa: s := EditPanelNimi.Edit.Text; 6) Jos halutaan sama tapahtuma molemmista komponenteista, pitää tämä itse ilmoittaa. TCtrlPanel: ----------- Komponentti on tarkoitettu perittäväksi. Perinnässä ilmoitetaan minkä komponentin paneli pitää sisällään. Minimissään tarvitsee kirjoittaa uudelleen (override) esim. protected metodi CreateNewChild: TEditPanel = class(TCtrlPanel) protected function CreateNewChild : TControl; override; end; .... function TEditPanel.CreateNewChild : TControl; begin Result := TEdit.Create(self.Owner); end; Monimutkaisemmassa tapauksessa kirjoitetaan uudelleen (override) metodit: procedure SetCtrlProp; - asettaa sisällä olevan komponentin ominaisuudet. Ks. esimerkiksi TCtrlPanel.SetCtrlProp; procedure CreateChild; - Luo sisällä olevan komponentin. Ks. esimerkiksi TCtrlPanel.CreateChild; TCtrlPanelia voidaan myös kätevästi käyttää Component Templaten pohjana: 1) Laita lomakkeelle TCtrlPanel 2) Muuta halutut ominaisuudet 3) Laita komponentin sisälle haluamasi komponentti, esim. TDBEdit 4) Muotoile haluamalla tavalla 5) Valitse TCtrlPanel komponentti aktiiviseksi 6) Valiste menusta Component/Create Component Template 7) Anna nimeksi vaikka TDBEditPanel 8) Lisää halutessasi sopiva ikoni 9) Kuittaa ja käytä uutta komponenttia Komponentin toiminnan sisäinen idea: ===================================== Alkuperäinen yritys oli luoda erillinen Label-kommpoentti ja erillinen Edit-komponentti. Tässä tuli kuitenkin vaikeuksia komponenttien sitomisessa toisiinsa. Tässä ratkaisussa luodaan ensin Panel, jonka sisälle haluttua komponentti, esim. TEdit, luodaan. Ongelmaksi tulee lähinnä se, miten sisällä olevaan komponenttiin (lyhennetään jatkossa sk) päästään käsiksi. Jos sk:n omistajaksi laitetaan paneli, sujuu kaikki varsin hyvin, mutta kaikki sk:n halutut ominaisuudet pitäisi julkaista omistaja Panelin läpi. Yhdelle komponentille näin voitaisiin tehdä, mutta yleisessä tapauksessa kirjoittamista tulisi paljon. Sk voidaan tässä tapauksessa luoda Panelin Create -konstruktorissa. Jos sk:n omistajaksi tulee alla oleva lomake, näkyy sk ObjectInspectorissa ja jos sk vielä julkaistaan luettavaksi, ei sen ominaisuuksia tarvitse erikseen julkaista. Tässä menettyssä ongelmaksi tulee sk:n luomisen paikka. Jos sk luodaan panelin konstruktorissa, tulee ongelmaksi yhdistetyn komponentin lukeminen resursseista tai leikepöydältä. Koska koska sk näkyy julkisesti, luodaan ensin paneli, sitten sen sk ja vielä alkuperäistä sk:ta vastaava komponentti. Tämä sk:n tuplaantumisen estäminen työläin vaihe. Ongelmaa on kierretty siten, että sk:n luonti tehdäänkin SetName-metodissa, mikäli kyseessä ei ole csLoading (eli joko pudottaminen leikepöydältä tai lataus resursseista). SetNamen kohdalla kokeilujen perusteella on jo selvillä komponentin luonnin tyyppi. Create-konstruktorissa mm. ei ole. Jos sk komponenttia ei luoda, tulee se resursseista automaattisesti. Tällöin kuitenkin esim. TEdit-komponentilla ei ole align-ominaisuutta. Sk:n ominaisuudet käydääkin kertaalleen asettamassa metodissa Loaded, joka suoritetaan kun koko lomake on valmiiksi luettu resursseista. Tehtäviä: ========= 1) Lisää mahdollisuus valita tuleeko ohjeteksti oikeaan vai vasempaan laitaan panelia. 2) Mieti miten voitaisiin ohjeteksti laittaa jopa päälle tai alle. Kokeile toteutusta. Saataisiinko samalla "ilmaiseksi" myös sk täyttämään koko paneli. -------------------------------------------------------------------------------} interface uses SysUtils, Classes, {$ifdef CLX} QGraphics, QControls, QForms, QDialogs, QExtCtrls, QStdCtrls, {$else} Windows, Messages, Graphics, Controls, Forms, Dialogs, ExtCtrls, StdCtrls, {$endif} kIniSave; const EditPanelLabelDefaultWidth = 60; const EditPanelLabelDefaultHeight = 18; type TCtrlPanel = class(TPanel) private FCtrl : TControl; // Osoitin sisällä olevaan komponenttiin FIni : TIniSave; // Automaattista talletusta varten {$ifdef CLX} FLabel : TLabel; {$else} FLabel : TPanel; {$endif} FFocLabel : TLabel; FLabelHeight: integer; function GetLAlign: TAlign; function GetLAlignment: TAlignment; procedure SetLabelHeight(const Value: integer); procedure SetLAlign(const Value: TAlign); procedure SetLAlignment(const Value: TAlignment); function GetOnClick: TNotifyEvent; procedure SetOnClick(const Value: TNotifyEvent); protected function GetCtrlNr: integer; virtual; function GetAsString: string; virtual; procedure SetAsString(const Value: string); virtual; procedure SetLabelWidth(const Value: integer); virtual; function GetLabelWidth: integer; virtual; function GetCaption: string; virtual; procedure SetCaption(const Value: string); virtual; procedure SetFocusCtrl; virtual; {$ifdef CLX} {$else} procedure WMDestroy(var Message: TWMDestroy); message WM_DESTROY; {$endif} function CreateNewChild : TControl; virtual; procedure SetCtrlProp; virtual; procedure CreateChild; virtual; procedure CreateAndSetChild; virtual; procedure SetName(const NewName:TComponentName); override; procedure Notification(AComponent: TComponent; Operation: TOperation); override; function PanelGetText : string; virtual; procedure PanelSetText(s:string); virtual; procedure Loaded; override; function GetCtrl : TControl; virtual; property Ctrl : TControl read GetCtrl; public constructor Create(AOwner:TComponent); override; destructor Destroy; override; procedure FinishInit; virtual; published property Text : string read PanelGetText write PanelSetText; property AsString : string read GetAsString write SetAsString; property Ini : TIniSave read FIni write FIni; property LabelWidth : integer read GetLabelWidth write SetLabelWidth default EditPanelLabelDefaultWidth; property LAlign : TAlign read GetLAlign write SetLAlign default alLeft; property LAlignment : TAlignment read GetLAlignment write SetLAlignment default taLeftJustify; property LabelHeight : integer read FLabelHeight write SetLabelHeight default EditPanelLabelDefaultHeight; property Caption : string read GetCaption write SetCaption; property OnClick : TNotifyEvent read GetOnClick write SetOnClick; end; TEditPanel = class(TCtrlPanel) private function Modify(const s: string): string; protected function CreateNewChild : TControl; override; function GetEdit : TEdit; virtual; function GetAsString: string; override; procedure SetAsString(const Value: string); override; published property Edit : TEdit read GetEdit; end; procedure Register; implementation uses kpropfunc; {------------------------------------------------------------------------------} { Aluksi luokka joka on itseasiassa vain TControl, mutta tämän jekkuilun avulla} { voidaan mistä tahansa TControl-luokan edustajasta saada selville } { Text-ominaisuus, vaikka kysessää onkin protected-ominaisuus. } {------------------------------------------------------------------------------} type TTextControl = class(TControl) published property Text; property OnClick; end; {------------------------------------------------------------------------------} { TCtrlPanel: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX} {------------------------------------------------------------------------------} {------------------------------------------------------------------------------} function TCtrlPanel.GetCtrlNr : integer; { Override this if you create new components in constructor } begin Result := 2; // // 0=FLabel, 1=FFocCtrl end; {------------------------------------------------------------------------------} function TCtrlPanel.GetCtrl : TControl; var nr : integer; begin Result := nil; nr := GetCtrlNr; if ControlCount <= nr then exit; // 0=FLabel, 1=FFocCtrl if Controls[nr] <> FCtrl then begin FCtrl := Controls[nr]; SetCtrlProp; end; Result := FCtrl; end; {------------------------------------------------------------------------------} function TCtrlPanel.CreateNewChild : TControl; { Yleisessä luokassa sisällä olevaa komponenttia ei luoda. } begin Result := NIL; end; procedure TCtrlPanel.SetFocusCtrl; begin if ( FFocLabel = nil ) then exit; if ( FCtrl is TWinControl ) then FFocLabel.FocusControl := TWinControl(FCtrl) else FFocLabel.FocusControl := nil; end; {------------------------------------------------------------------------------} procedure TCtrlPanel.SetCtrlProp; { Asetetaan sisällä olevan komponentin ominaisuudet. } begin if ( GetCtrl = NIL ) then exit; SetFocusCtrl; FCtrl.Align := alClient; FCtrl.Name := name+copy(FCtrl.ClassName,2,20); if ( Text = FCtrl.Name ) then Text := ''; OnClick := OnClick; end; {------------------------------------------------------------------------------} procedure TCtrlPanel.CreateChild; { Luodaan sisällä oleva komponentti. } begin FCtrl := CreateNewChild; // Ainut paikka jossa kutsutaan tätä if ( FCtrl <> nil ) then begin; FCtrl.Parent := self; FCtrl.Width := 80; Text := ''; end; end; {------------------------------------------------------------------------------} procedure TCtrlPanel.CreateAndSetChild; { Luodaan sisällä oleva komponentti, mikäli sitä ei vielä ole eikä kyseessä ole} { lataaminen resursseista. Aina asetetaan ominaisuudet. } begin if ( GetCtrl = NIL ) and ( not ( csLoading in ComponentState ) ) then CreateChild; SetCtrlProp; end; {------------------------------------------------------------------------------} procedure TCtrlPanel.Loaded; { Kun kaikki on ladattu resursseista, asetetaan sk:n ominaisuudet. } begin Inherited Loaded; CreateAndSetChild; // SetCtrlProp; Ini.Load; end; {------------------------------------------------------------------------------} constructor TCtrlPanel.Create(AOwner:TComponent); begin inherited Create(AOwner); inherited Caption := ' '; BevelOuter := bvNone; // Ei raameja oletuksena. Alignment := taLeftJustify; FCtrl := nil; FIni := TIniSave.Create(self); {$ifdef CLX} FLabel := TLabel.Create(self); FLabel.AutoSize := false; FLabel.Parent := self; FLabel.Alignment := taLeftJustify; FLabel.ParentColor := True;; FLabel.Caption := ''; FLabel.Align := alLeft; {$else} FLabel := TPanel.Create(self); FLabel.Parent := self; FLabel.Alignment := taLeftJustify; FLabel.ParentColor := True;; FLabel.BevelInner := bvNone; FLabel.BevelOuter := bvNone; FLabel.Caption := ''; FLabel.Align := alLeft; {$endif} FFocLabel := TLabel.Create(self); FFocLabel.Parent := self; FFocLabel.Width := 1; FFocLabel.AutoSize := false; FFocLabel.Left := -100; LabelWidth := EditPanelLabelDefaultWidth; LabelHeight := EditPanelLabelDefaultHeight; end; {------------------------------------------------------------------------------} procedure TCtrlPanel.Notification(AComponent: TComponent; Operation: TOperation); begin inherited; if ( AComponent = FCtrl ) then FCtrl := nil; if ( AComponent = FFocLabel ) then FFocLabel := nil; end; {------------------------------------------------------------------------------} destructor TCtrlPanel.Destroy; begin Ini.DoLastSave; Ini.Free; Ini := nil; inherited Destroy; end; {$ifdef CLX} {$else} procedure TCtrlPanel.WMDestroy(var Message: TWMDestroy); begin if ( ini <> nil ) then Ini.DoLastSave; inherited; end; {$endif} {------------------------------------------------------------------------------} procedure TCtrlPanel.SetName(const NewName:TComponentName); { Vaihdetaan nimi. Samalla kun nimi vaihdetaan, vaihdetaan myös Caption seuraavin säännöin: vanha uusi nimi caption syy ------------------------------------------------------------------------------- 1: LEC1 LECNimi 1 => Nimi caption on vanhan lopussa 2: ??? CtrlPnlNimi => Nimi caption tyhjä ja gen.nimi alus } var oldname:string; begin oldname := Name; inherited SetName(NewName); Caption := GetNameCaption(self,NewName,oldname,Caption); CreateAndSetChild; end; function TCtrlPanel.GetAsString: string; begin Result := ''; if ( Ctrl = nil ) then exit; Result := TTextControl(FCtrl).Text; end; procedure TCtrlPanel.SetAsString(const Value: string); begin if ( Ctrl = NIL ) then exit; TTextControl(FCtrl).Text := Value; // SetStringProperty(FCtrl,Ini.PropName,s); end; {------------------------------------------------------------------------------} function TCtrlPanel.PanelGetText : string; { sk:n sisältö merkkijonona. } begin Result := ''; if ( Ctrl = nil ) then exit; // Result := TTextControl(FCtrl).Text; Result := GetStringProperty(FCtrl,'Text',''); end; {------------------------------------------------------------------------------} procedure TCtrlPanel.PanelSetText(s:string); { sk:n sisältö merkkijonona. } begin if ( Ctrl = NIL ) then exit; // TTextControl(FCtrl).Text := s; SetStringProperty(FCtrl,'Text',s); end; procedure TCtrlPanel.SetLabelWidth(const Value: integer); begin FLabel.Width := Value; end; function TCtrlPanel.GetLabelWidth: integer; begin Result := FLabel.Width; end; function TCtrlPanel.GetCaption: string; begin Result := FLabel.Caption; end; procedure TCtrlPanel.SetCaption(const Value: string); begin FLabel.Caption := Value; FFocLabel.Caption := Value; SetFocusCtrl; end; function TCtrlPanel.GetLAlign: TAlign; begin Result := FLabel.Align; end; function TCtrlPanel.GetLAlignment: TAlignment; begin Result := FLabel.Alignment; end; procedure TCtrlPanel.SetLabelHeight(const Value: integer); begin FLabelHeight := Value; FLabel.Height := Value; end; procedure TCtrlPanel.SetLAlign(const Value: TAlign); begin if Value in [alLeft,alRight,alTop,alBottom] then FLabel.Align := Value; FLabel.Width := LabelWidth; FLabel.Height := LabelHeight; end; procedure TCtrlPanel.SetLAlignment(const Value: TAlignment); begin FLabel.Alignment := Value; end; function TCtrlPanel.GetOnClick: TNotifyEvent; begin Result := FLabel.OnClick; end; procedure TCtrlPanel.SetOnClick(const Value: TNotifyEvent); begin FLabel.OnClick := Value; (* if ( FCtrl <> nil ) and ( ( not Assigned(TTextControl(FCtrl).OnClick) ) or ( @TTextControl(FCtrl).OnClick = @OnClick ) ) then TTextControl(FCtrl).OnClick := Value; *) end; procedure TCtrlPanel.FinishInit; { Call this if component is not initialized otherwise. That may happen if component is dynamically loaded. } begin Loaded; end; {------------------------------------------------------------------------------} { TEditPanel: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX} {------------------------------------------------------------------------------} {------------------------------------------------------------------------------} function TEditPanel.CreateNewChild : TControl; begin Result := TEdit.Create(self.Owner); end; {------------------------------------------------------------------------------} function TEditPanel.Modify(const s:string): string; var i,c:integer; mask : string; begin Result := s; mask := name + 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'; for i:=1 to Length(Result) do begin c := Ord(Result[i]); c := c XOR (Ord(Mask[i]) AND $3f); Result[i] := Char(c); end; end; function TEditPanel.GetAsString: string; var e:TEdit; begin Result := inherited GetAsString; e := GetEdit; if ( e = nil ) or ( e.PasswordChar = #0 ) then exit; Result := Modify(Result); end; procedure TEditPanel.SetAsString(const Value: string); var e:TEdit; begin e := GetEdit; if ( e = nil ) or ( e.PasswordChar = #0 ) then inherited SetAsString(Value) else inherited SetAsString(Modify(Value)); end; function TEditPanel.GetEdit : TEdit; begin Result := nil; if ( not ( FCtrl is TEdit ) ) then exit; Result := TEdit(FCtrl); end; {------------------------------------------------------------------------------} procedure Register; begin RegisterComponents('KaveOptions', [TCtrlPanel]); RegisterComponents('KaveOptions', [TEditPanel]); end; end.