Inicio
 
Creación de un componente paso a paso. V

Imágenes.

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.