Código en Delphi 6, válido para Delphi for win32 y CodeGear RAD Studio (Delphi win32) | ||
En este apartado vamos a ver como tratar imágenes en nuestro componente.
En Delphi hay varios objetos que guardan (o hacen referencia a) imágenes : TPicture, TBitmap,..., dependiendo de nosotros seleccionar aquel que más se ajuste a nuestro interés, o bien podemos optar por crear alguno nuevo. En nuestro componente vamos a utilizar dos de estos objetos, un Picture y un Bitmap (este último lo veremos más adelante, hacia el final de esta serie de artículos).
Picture nos permitirá cargar imágenes de varios tipos : bmp, jpg,ico,..., de esta manera ofrecemos al programador que utilice el componente una mayor libertad para seleccionar el tipo de gráfico que quiere mostrar.
Como hemos dicho, las imágenes se guardarán en objetos que nuestro componente debe gestionar (crear, manipular y destruir) por lo que se deben definir variables que referencien dichos objetos. Como siempre, estas variables se definirán en la parte privada y serán las propiedades, las encargadas de interactuar con ellas.
Como se ha dicho, al ser objetos, se deben crear en algún lugar del código de nuestro componente y deberán ser destruidos antes de que se destruya nuestro control. Crear el objeto se hará, normalmente, en el constructor (Create) del componente y para destruirlo deberemos hacerlo justo antes de la destrucción de nuestro propio objeto. Hay un procedimiento especial que se ejecuta siempre antes de la liberación de memoria del objeto, este método es el 'destructor', se llama Destroy y deberemos sobreescribirlo:
Destructor Destroy; begin //acciones anteriores a la liberación de memoria inherited; end;
Este procedimiento se debe definir en el apartado Public de nuestro componente.
Resumiendo, para guardar la imagen que queremos mostrar en nuestro componente debemos : (a) Definir una variable que referencie al objeto que contendrá la imagen, (b) crear el objeto imagen y hacer que la variable definida contenga su referencia, (c) si queremos tener una propiedad que refleje y maneje a la variable deberemos definir esa propiedad, (d) tratar el objeto en cualquier sitio del código del componente y (e) destruir/liberar el objeto antes de liberar el control creado con nuestro componente.
private FPicture:TPicture; ... procedure SetPicture(Value:TPicture); ... public constructor Create(AOwner:TComponent);override; destructor Destroy;override; ... published property Picture:TPicture read FPicture Write SetPicture; ... implementation constructor TPanelSel.Create(AOwner:TComponent); begin inherited; ... ... FPicture:=TPicture.Create; end; destructor TPanelSel.Destroy; begin FPicture.Free; inherited; end; ... procedure TPanelSel.SetPicture(Value:TPicture);
begin
FPicture.Assign(Value);
repaint;
end;
...
Hasta aquí tenemos la imagen guardada pero todavía no hemos hecho que se dibuje en pantalla dentro del control, para ello iremos al método Paint y allí dibujaremos la posible imagen.
procedure TPanelSel.Paint; var X, Y, W, H: Integer; begin with Canvas do begin setbkmode(Handle,TRANSPARENT); Pen.Width:=BorderWidth; Pen.Color:=BorderColor; Brush.Style:=bsSolid; Brush.Color:=Color; X := Pen.Width div 2; Y := X; W := Width - Pen.Width + 1; H := Height - Pen.Width + 1; if Focused then begin Pen.Color:=FocusedBorderColor; Brush.Color:=FocusedColor; end; if FOver then begin Pen.Color:=OverBorderColor; Brush.Color:=OverColor; end; FillRect(ClientRect); Brush.Style:=bsClear; if Assigned(Picture.Graphic) then Draw(BorderWidth,((Height-Picture.Graphic.Height) div 2),Picture.Graphic); if Border then Rectangle(X, Y, X + W, Y + H); end; end;
El resultado en pantalla es :
Figura 5
A la hora de dibujar la imagen hemos hecho que ésta aparezca, verticalmente, en el medio y horizontalmente justo después del borde. Nótese que antes de dibujar, nos hemos asegurado de que el objeto picture contiene algo (una imagen). La funciónAssigned(puntero) sólo comprueba que este no sea Nil
Observamos en la figura anterior un relleno blanco sobre el dibujo que a nosotros nos interesa, el círculo con la letra 'A'. La propiedad Graphic del objeto Picture de nuestro componente, es a la vez un objeto que tiene una propiedad denominada Transparent, esta propiedad cuando tiene el valor True hace que sea transparente el color de la imagen que coincida con el color del pixel (0,0) de la misma. Hay otras propiedades dentro de objetos como Bitmap que hacen referencia a esto mismo y se puede elegir el color que deseamos que sea el transparente, pero esto queda fuera del objetivo de este artículo.
... Brush.Style:=bsClear; if Assigned(Picture.Graphic) then begin Picture.Graphic.Transparent:=true; Draw(BorderWidth,((Height-Picture.Graphic.Height) div 2),Picture.Graphic); end; if Border then Rectangle(X, Y, X + W, Y + H); ...
Vamos a dar la posibilidad de que el programador elija la coordenada X a partir de la cual dibujar la imagen, para ello añadimos una variable nueva, una propiedad y hacemos que cualquier cambio en el valor de esta propiedad se refleje inmediatamente en pantalla (como hemos hecho en otras ocasiones) :
El código de nuestro componente hasta el momento es :
unit PanelSel; interface uses Windows, Messages, SysUtils, Classes, Controls, Graphics; type TPanelSel = class(TCustomControl) private FPicture:TPicture; FColors:array[0..5] of TColor; FBorder:Boolean; FBorderWidth:Integer; FOver:Boolean; FPosXPicture:Word; procedure SetPicture(Value:TPicture); procedure SetColors(Index:Integer;Value:TColor); function GetColors(Index:integer):TColor; procedure SetBorder(Value:Boolean); procedure SetBorderWidth(Value:integer); procedure SetPosXPicture(Value:Word); { Private declarations } protected procedure WMSetFocus(var Message: TWMSetFocus); message WM_SETFOCUS; procedure WMKillFocus(var Message: TWMSetFocus); message WM_KILLFOCUS; procedure CMMouseEnter(var Message: TMessage); message CM_MOUSEENTER; procedure CMMouseLeave(var Message: TMessage); message CM_MOUSELEAVE; procedure Paint; override; procedure Click;override; { Protected declarations } public constructor Create(AOwner:TComponent);override; destructor Destroy;override; property Colors[Index:Integer]:TColor read GetColors Write SetColors; { Public declarations } published property Picture:TPicture read FPicture Write SetPicture; property Border:Boolean read FBorder Write SetBorder default True; property BorderWidth:integer read FBorderWidth Write SetBorderWidth default 1; property Color:TColor Index 0 read GetColors Write SetColors default clBtnFace; property BorderColor:TColor Index 1 read GetColors Write SetColors default clBlack; property FocusedColor:TColor Index 2 read GetColors Write SetColors default clBtnHighlight; property FocusedBorderColor:TColor Index 3 read GetColors Write SetColors default clBlack; property OverColor:TColor Index 4 read GetColors Write SetColors default clBtnShadow; property OverBorderColor:TColor Index 5 read GetColors Write SetColors default clBlack; property PosXPicture:Word read FPosXPicture Write SetPosXPicture default 10; property Font; property Tabstop; { Published declarations } end; procedure Register; implementation constructor TPanelSel.Create(AOwner:TComponent); begin inherited; FOver:=False; Tabstop:=True; FBorder:=True; FBorderWidth:=1; FColors[0]:= clBtnFace; FColors[1]:=clBlack; FColors[2]:=clBtnHighlight; FColors[3]:=clBlack; FColors[4]:= clBtnShadow; FColors[5]:=clBlack; FPicture:=TPicture.Create; FPosXPicture:=10; end; destructor TPanelSel.Destroy; begin FPicture.Free; inherited; end; procedure TPanelSel.WMSetFocus(var Message: TWMSetFocus); begin inherited; Invalidate; end; procedure TPanelSel.WMKillFocus(var Message: TWMSetFocus); begin inherited; Invalidate; end; procedure TPanelSel.CMMouseEnter(var Message: TMessage); begin inherited; FOver:=True; Invalidate; end; procedure TPanelSel.CMMouseLeave(var Message: TMessage); begin inherited; FOver:=False; Invalidate; end; procedure TPanelSel.SetPicture(Value:TPicture); begin FPicture.Assign(Value); repaint; end; procedure TPanelSel.SetPosXPicture(Value:Word); begin if FPosXPicture<>Value then // Sólo permitimos valores mayores que cero if value>0 then begin FPosXPicture:=Value; invalidate; end; end; procedure TPanelSel.SetBorder(Value:Boolean); begin if FBorder<>Value then begin FBorder:=Value; Invalidate; end; end; procedure TPanelSel.SetBorderWidth(Value:integer); begin if FBorderWidth<>Value then begin if Value>0 then FBorderWidth:=Value; Invalidate; end; end; procedure TPanelSel.SetColors(Index:Integer;Value:TColor); begin if FColors[Index]<>Value then begin FColors[Index]:=Value; Invalidate; end; end; Function TPanelSel.GetColors(Index:Integer):TColor; begin Result:=FColors[Index]; end; procedure TPanelSel.Click; begin inherited; SetFocus; end; procedure TPanelSel.Paint; var X, Y, W, H: Integer; begin with Canvas do begin setbkmode(Handle,TRANSPARENT); Pen.Width:=BorderWidth; Pen.Color:=BorderColor; Brush.Style:=bsSolid; Brush.Color:=Color; X := Pen.Width div 2; Y := X; W := Width - Pen.Width + 1; H := Height - Pen.Width + 1; if Focused then begin Pen.Color:=FocusedBorderColor; Brush.Color:=FocusedColor; end; if FOver then begin Pen.Color:=OverBorderColor; Brush.Color:=OverColor; end; FillRect(ClientRect); Brush.Style:=bsClear; Picture.Graphic.Transparent:=true; if Assigned(Picture.Graphic) then Draw(BorderWidth+PosXPicture,((Height-Picture.Graphic.Height) div 2),Picture.Graphic); if Border then Rectangle(X, Y, X + W, Y + H); end; end; procedure Register; begin RegisterComponents('Ejemplo', [TPanelSel]); end; end.