Custom Controls

Tags:    delphi
Skrevet af Bruger #58 @ 17.06.2001
Få TCustomControl over udseendet

I denne artikel vil jeg beskrive forskellige måder at tegne sine egne kontroller på, og hvordan du ved hjælp af Windows API'et kan komme til at tegne overalt på kontrollen.

En af de ting der er vigtige for at en kontrol ser "Windows-agtig" ud er, at den bruger de rigtige farver til 3D, og bruger dem de rigtige steder. Først:

Eksempel: Sådan kan du finde 3D farverne

{ Dette eksempel kræver en form med fire TShape på.
  Disse shapes skal alle være 1 pixel tynde, og være
  placeret så de danner en boks. Kald dem shTop,
  shBottom, shLeft og shRight efter hvor de er
  placeret. }

procedure TForm1.FormCreate(Sender: TObject);
begin
  shTop.Pen.Color := clBtnHighlight;
  shBottom.Pen.Color := clBtnShadow;
  shLeft.Pen.Color := clBtnHighlight;
  shRight.Pen.Color := clBtnShadow;
end;

Resultat: En hævet boks. Det samme kunne være lavet meget nemmere med en TBevel. Det vigtigste er dog at lære hvordan du kan finde de rigtige farver, og bruge dem rigtigt. Men videre til noget andet: En mere avanceret API funktion.

Eksempel: DrawEdge API'et

{ Sæt både OnPaint og OnResize for formen til denne event }
procedure TForm1.MakeBorder(Sender: TObject);
var
  BRect: TRect;
begin
  BRect := ClientRect;
  Canvas.Brush.Color := clWindow;
  Canvas.FillRect(BRect);
  DrawEdge(Canvas.Handle, BRect, EDGE_SUNKEN, BF_RECT);
end;

Resultat: Et vindue med ramme inderst. Uden brug af komponenter, og med kun 4 liniers kode. Og så videre til noget mere avanceret igen: Tegn din egen vinduesramme på formen. Her er en komplet unit fyldt med kommentarer:

Eksempel: En ny type ramme til vinduet

unit CustomBorderFormUnit;

interface

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

type
  TCustomBorderForm = class(TForm)
    // Husk at sætte OnPaint til FormPaint.
    // Det er også en god ide at sætte OnResize til OnPaint.
    procedure FormPaint(Sender: TObject);
  private
    { Private declarations }
    // En "handler" til Windows messagen  WM_NCPAINT, eller
    // "Window Message Non-Client Paint"
    procedure WMNCPaint(var Msg: TMessage); message WM_NCPAINT;
  public
    { Public declarations }
  end;

var
  CustomBorderForm: TCustomBorderForm;

implementation

{$R *.DFM}

procedure TCustomBorderForm.WMNCPaint(var Msg: TMessage);
var
  // NCCanvas = Non-CLient canvas
  NCCanvas: TCanvas;
  // DrawEdge kræver at det er en variabel du giver den.
  // Den vil ikke nøjes med at property.
  BR: TRect;
begin
  // Lav et nyt canvas
  NCCanvas := TCanvas.Create;
  // Tildel det hele vinduets HDC (Handle Drawing Canvas)
  NCCanvas.Handle := GetWindowDC(Handle);
  // BoundsRect kan ikke bruges, den giver koordinter i forhold
  // til skærmen
  BR := Rect(0, 0, Width, Height);
  // Tegn en kant rundt om formen, en lidt speciel kant
  DrawEdge(NCCanvas.Handle, BR, EDGE_ETCHED, BF_RECT);
  // Gør rektangler 2 pixels mindre i alle retninger
  InflateRect(BR, -2, -2);
  // Tegn to Button Face farvede raktangler inden i rammen
  NCCanvas.Brush.Color := clBtnFace;
  NCCanvas.FrameRect(BR);
  InflateRect(BR, -1, -1);
  NCCanvas.FrameRect(BR);
  // Tegn en linie under titellinien
  NCCanvas.Pen.Color := clBtnFace;
  NCCanvas.PenPos := Point(2, GetSystemMetrics(SM_CYSIZE)+4);
  NCCanvas.LineTo(Width - 2, NCCanvas.PenPos.y);
  // Lav et rektangel der er lidt højere end den normale
  // titellinie
  BR := Rect(2, 2, Width-2, GetSystemMetrics(SM_CYSIZE)+4);
  // Tegn rektanglet med InfoTip baggrunds farven. Specielt udseende.
  NCCanvas.Brush.Color := clInfoBk;
  NCCanvas.FillRect(BR);
  // Også en anderledes font. Verdana med InfoText farve.
  NCCanvas.Font.Color := clInfoText;
  NCCanvas.Font.Style := [fsBold];
  NCCanvas.Font.Name := 'Verdana';
  // Dette er i stedet for at sætte Font.Size. Giver et bedre resultat.
  NCCanvas.Font.Height := GetSystemMetrics(SM_CYSIZE);
  // Endnu mere API. Denne gang til at tegne tekst på en speciel måde.
  DrawText(NCCanvas.Handle, PChar(Caption), Length(Caption), BR,
    DT_CENTER or DT_END_ELLIPSIS);
  // Frigiv tegneoverfladen og kanvasset
  ReleaseDC(Handle, NCCanvas.Handle);
  NCCanvas.Free;
  // Fortæl Windows at vi har behandlet beskeden
  Msg.Result := 0;
end;

procedure TCustomBorderForm.FormPaint(Sender: TObject);
begin
  // Ekstra forsikring for at vores egen ramme bliver tegnet rigtigt
  Perform(WM_NCPAINT, 0, 0);
end;

end.


Resultat: Et helt anderledes vindue. Denne metode kan bruges til alle kontroller der har et vindue. For at vise det, har jeg lavet et TPanel med en ny slags ramme. Det hedder TStrangePanel og ligger i Download sektionen her på Udvikleren. Det bruger denne metode til at få en lignende ramme når det er sat til Ctl3D=True og BorderStyle=bsSingle.


Hvad synes du om denne artikel? Giv din mening til kende ved at stemme via pilene til venstre og/eller lægge en kommentar herunder.

Del også gerne artiklen med dine Facebook venner:  

Kommentarer (1)

User
Bruger #5519 @ 23.01.06 13:56
God artikel - men , men , men ...
Der måtte gerne være lidt mere forklaring -
Ellers flot og god til at 'gejle' ens forme med.
Du skal være logget ind for at skrive en kommentar.
t