Ver Mensaje Individual
  #1  
Antiguo 24-04-2018
bucanero bucanero is offline
Miembro
 
Registrado: nov 2013
Ubicación: Almería, España
Posts: 137
Reputación: 6
bucanero Va por buen camino
HELPER: Insertar ProgressBar en LISTITEM

Hola a todos,

Hace algún tiempo descubrí los HELPER como una posibilidad muy interesante que incluye el lenguaje PASCAL, aunque realmente no se en que momento se introdujo en el lenguaje.

Básicamente lo que permite es añadirle funcionalidades que no existen o que podamos necesitar, a classes u otras estructuras de datos, con la ventaja de que se puede utilizar directamente sin necesidad de modificar nada de lo que ya hay creado por debajo. No necesita llamar a create/destroy, ni hacer nada especial con lo que ya hay creado o funcionando.

Tiene algunas limitaciones como no poder utilizar nuevas estructuras de datos dentro del propio HELPER, siempre y cuando sean necesarias crearlas junto con el propio componente, por la imposibilidad de sobreescribir los procedimientos constructor/destructor del método original sobre el que se aplica el HELPER y porque si no esta incluida nuestra nueva unidad con el HELPER en el resto de código entonces todas las modificaciones existentes es como si no existieran.

Como ejemplo simple pero bastante interesante es el típico código para insertar un ProgressBar a un LISTITEM de un LISTVIEW y aquí como quedaría insertado como un HELPER de un LISTITEM en una unidad independiente:

Código Delphi [-]
unit ListItemHelpers;

interface

uses Windows, ComCtrls;

type
  TListItemHelper = class helper for TListItem
  private
    function GetRect(AColumn: Integer): TRect;
    function GetProgressBar: TProgressBar;
  public
    procedure AdjustProgressBar;
    function AddPRogressBar(AColumn: integer = 1): TProgressBar;
    procedure DeleteProgressBar;
    property ProgressBar: TProgressBar read GetProgressBar;
  end;

implementation

uses
  Dialogs, System.SysUtils;

procedure Error(E:Exception; const msgTxt:String);
begin
  MessageDlg(E.Message + #13 + msgTxt, mtError, [mbOK], 0);
end;


{ TListItemHelper }

function TListItemHelper.AddPRogressBar(AColumn: integer): TProgressBar;
/// Si AColumn es igual 0 entonces se situa en la ultima columna
begin
  if assigned(Data) then begin
    Result := data;
    exit;
  end;
  ///  se le asigna como OWNER del PROGRESSBAR el listView propietario del LISTITEM
  ///  en donde queremos el PROGRESSBAR, para que asi, se libere automaticamente
  ///  tambien al liberar el listView
  result := TProgressBar.Create(ListView);
  /// se le asigna como padre el listView
  result.parent := ListView;
  /// El dobleBuffered evita el parpadeo
  result.DoubleBuffered := true;
  if (AColumn <= 0) then
    AColumn := 1;
  /// El tag del progresBass va a ser el numero de columna
  /// dentro del listItem donde se situa el propio ProgressBar
  result.Tag := AColumn;
  /// se guarda el puntero al ProgressBar en el campo DATA del ListItem
  Data := result;
  AdjustProgressBar;
end;

procedure TListItemHelper.AdjustProgressBar;
var
  pb: TProgressBar;
begin
  pb := Data;  //<-- listItem.data
  if assigned(pb) then
    /// El tag del progresBass va a ser el numero de columna
    /// dentro del listItem donde se situa el propio ProgressBar
    pb.BoundsRect := GetRect(pb.Tag);
end;

procedure TListItemHelper.DeleteProgressBar;
var
  pb: TProgressBar;
begin
  /// al liberar el ProgressBar no se por que da un error,
  /// asi que en principio lo dejo sin liberar.
  /// Aunque queda pendiente de ver por que lo hace.
  /// Tampoco supone un gran problema porque se creo con padre
  /// y ya se encarga el proceso de eliminarlo automaticamente
  try
    pb := Data;
    if assigned(pb) then
    try
      freeAndNil(pb);
      data := Nil;
    except
      on E: Exception do
        error(e, 'TListItemHelper.DeleteProgressBar');
    end;
  except
    on E: Exception do
      error(E, 'TListItemHelper.DeleteProgressBar');
  end;
end;

function TListItemHelper.GetProgressBar: TProgressBar;
begin
  try
    Result := nil;
    if not Assigned(Data) then
      AddPRogressBar;
    result := TObject(Data) as TProgressBar
  except
    on E: Exception do
      Error(E, 'TListItemHelper.GetProgressBar');
  end;
end;

function TListItemHelper.GetRect(AColumn: Integer): TRect;
var
  i: longint;
begin
  result := DisplayRect(drBounds);
  // last column is to take progress bar
  with TListView(ListView) do begin
    if AColumn = 0 then
      AColumn := columns.Count - 1;
    for i := 1 to AColumn do
      result.left := result.left + columns[i - 1].Width;
    result.right := result.Left + columns[AColumn].Width;
  end;
end;

end.


Para usarlo solamente es necesario añadir la unidad con el HELPER a nuestro formulario y listo, tendremos las nuevas funciones disponibles para su uso:

Código Delphi [-]
uses ..., ListItemHelpers;

...

procedure TForm1.AddProgressBarClick(Sender: TObject);
begin
  ListView1.Selected.addProgressBar;
end;

procedure TForm1.DeleteProgressBarClick(Sender: TObject);
begin
  ListView1.Selected.DeleteProgressBar;
end;

procedure TForm1.DeleteItemClick(Sender: TObject);
begin
  /// IMPORTANTE:
  ///     el progressBar no se elimina de forma automatica al
  ///     eliminar el LISTITEM y hay que hacerlo a mano, 
  ///     aunque si se elimina al eliminar el LISTVIEW
  ListView1.selected.DeleteProgressBar;
  ListView1.DeleteSelected;
end;

procedure TForm1.ListView1CustomDrawItem(Sender: TCustomListView;
  Item: TListItem; State: TCustomDrawState; var DefaultDraw: Boolean);
begin
  /// esta llamada es para que el tamaño de los progressBar insertados, se ajusten automáticamente 
  /// al cambiar la columna sobre la que esta insertado su tamaño
  Item.AdjustProgressBar;
end;

Esta técnica sobre todo la veo interesante por su simplicidad de uso,
Espero les sea util

Un saludo
Responder Con Cita