El misterio de los 200…

Aunque ya he comentado el contenido de esta entrada en el foro de facebook, necesitaba colgar de algún lado las fuentes que había utilizado para revisar el tema y de paso, dar la oportunidad para quien todavía no forme parte del mismo pueda acceder a su contenido, o por lo menos a las cosas que parecen interesantes. Es por esa razón que he acabado añadiéndola.

En este caso concreto, el hilo de comentarios del foro se originaba cuando uno de los compañeros, siguiendo las indicaciones del código publicado en el blog de Jim Tierney, que forma parte de los blogs de Embarcadero,  se extrañaba de que al intentar llenar los items de un componente TListBox (en tiempo de ejecución) desde una fuente de datos (un clientdataset, el numero de items añadidos al TListBox era como máximo igual o menor a 200. Y eso sucedía aun cuando dicha fuente de datos contuviera una cantidad mayor.

consulta

Esta es la entrada en la que me he basado para reproducir el problema y comprenderlo.

LiveBindings: Code to create TBindLink and fill a Listbox

Creo que lo mas interesante de estas lineas no es ya la corrección que se ha hecho para solucionar el problema, que solo ha consistido en añadir la linea de asignación en el procedimiento FillList( ) de la unidad UMain.pas

LBindList.BufferCount:= ARecordCount;

sino en destacar el punto que originaba el problema:

constructor TBindScopeDBEnumerator.Create(ABindScope: TCustomBindScopeDB;
  const AMemberName: string; ABufferCount: Integer);
begin
  FBindScope := ABindScope;
  FMemberName := AMemberName;
  FSaveActiveRecord := FBindScope.FDataLink.ActiveRecord;
  FNextRecord := FSaveActiveRecord;
  if ABufferCount > 0 then
    FBindScope.FDataLink.BufferCount := ABufferCount
  else
    FBindScope.FDataLink.BufferCount := 200;  // default to max 200 records in buffer
end;

 Al final, ese era el motivo por el que, no estando definido el valor del campo BufferCount en TBindList, cualquier movimiento hacia adelante de la estructura del enumerador, comprobaba si habia llegado al ultimo registro por lo que aunque existiera una cantida mayor en el dataset, el enlace le indicaba que había llegado al último.

🙂

En fin… cosas de los valores por defecto que supongo que sería fijado por algún motivo, porque de hecho el comentario en la misma linea corrobora que se hizo por alguna razón que ahora mismo ciertamente no comprendo.

Lo mas gracioso de todo es que pienso que esto debería por la forma en que se ha planteado afectar en tiempo de diseño por lo que quizás deberíamos comprobar que al crear una relación TBindList desde el editor de expresiones, en tiempo de diseño, el valor del campo en cuestión es correcto. Podéis hacer la prueba y comentamos en el foro. Para probarlo, simplemente acceded a la propiedad LiveBindings del TListBox y cread un nuevo enlace de tipo TBindList. Y seguidamente definid para la propiedad Format un nuevo item con los valores indicados en la rutina FillList( ). En las pruebas que he hecho, también se reproduce el error.

Tened en cuenta este punto para no caer en el problema.

 fillTListBox

 

unit UMain;

interface

uses
  System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
  FMX.Types, FMX.Controls, FMX.Forms, FMX.Dialogs, Data.DB, Data.Win.ADODB,
  Datasnap.DBClient, FMX.Layouts, FMX.ListBox, FMX.Bind.Editors, Data.Bind.Components,
  Data.Bind.DBScope, Datasnap.Provider, Data.Bind.EngExt, Fmx.Bind.DBEngExt;

type
  TfrmFillListBox = class(TForm)
    lbxData: TListBox;
    bnFill: TButton;
    bnClear: TButton;
    cdsData: TClientDataSet;
    Conexion: TADOConnection;
    qData: TADOTable;
    dsData: TDataSource;
    dspData: TDataSetProvider;
    cdsDataOrderNo: TFloatField;
    cdsDataCustNo: TFloatField;
    BindScopeDB1: TBindScopeDB;
    lbRecordCount: TLabel;
    lbItemsCount: TLabel;
    BindingsList1: TBindingsList;
    procedure bnClearClick(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure bnFillClick(Sender: TObject);
  private
    { Private declarations }
    procedure FillLabelRecordCount;
    procedure FillLabelItemsCount;
  public
    { Public declarations }
  end;

var
  frmFillListBox: TfrmFillListBox;

implementation

{$R *.fmx}

//fuente del procedimiento: http://blogs.embarcadero.com/jimtierney
//         http://blogs.embarcadero.com/jimtierney/2011/10/03/31601
//
//  El procedimiento encapsula los pasos para rellenar distintos
//  tipos de controles, siguiendo lo que haria el usuario en tiempo de
//  diseño. Es util para el tiempo de ejecución
//
procedure FillList(AControl: TComponent; const AControlExpression: string;
  ASource: TBaseBindScopeComponent; const ASourceExpression: string; ARecordCount: Integer; const ASourceMemberName: string = '');
var
  LBindList: TBindList;
begin
  LBindList := TBindList.Create(nil);
  try
    // Turn off auto properties.
    LBindList.AutoFill := False;
    LBindList.AutoActivate := False;
    LBindList.ControlComponent := AControl;
    LBindList.SourceComponent := ASource;
    LBindList.SourceMemberName := ASourceMemberName;
    LBindList.BufferCount:= ARecordCount; //<- Linea añadida
    with LBindList.FormatExpressions.AddExpression do
    begin
      SourceExpression := ASourceExpression;
      ControlExpression := AControlExpression;
    end;
    LBindList.FillList;
  finally
    LBindList.Free;
  end;
end;

procedure TfrmFillListBox.bnClearClick(Sender: TObject);
begin
  lbxData.Clear;
  FillLabelItemsCount;
  FillLabelRecordCount;
end;

procedure TfrmFillListBox.bnFillClick(Sender: TObject);
begin
  FillList(lbxData, 'Text', BindScopeDB1, 'AsString', dsData.DataSet.RecordCount, 'OrderNo', );
  FillLabelItemsCount;
  FillLabelRecordCount;
end;

procedure TfrmFillListBox.FillLabelItemsCount;
begin
  lbItemsCount.Text:=  'Items.Count: '+IntToStr(lbxData.Items.Count);
end;

procedure TfrmFillListBox.FillLabelRecordCount;
begin
  lbRecordCount.Text:=  'RecordCount: '+IntToStr(cdsData.RecordCount);
end;

procedure TfrmFillListBox.FormCreate(Sender: TObject);

begin
  Conexion.ConnectionString:= 'Provider=Microsoft.Jet.OLEDB.4.0;'+
                              'Data Source=C:Program FilesCommon FilesCodeGear SharedDatadbdemos.mdb;'+
                              'Persist Security Info=False';
  cdsData.Open;

  FillLabelItemsCount;
  FillLabelRecordCount;
end;

end.

Nada mas por comentar. Si deseáis ver el ejemplo podéis acceder al siguiente enlace:

Descargar fuentes

Un comentario sobre “El misterio de los 200…

Agrega el tuyo

  1. Una nota aclaratoria:

    He visto hace un rato que Jim Terney respondía en uno de los comentarios de la entrada http://blogs.embarcadero.com/jimtierney/2011/09/30/31559 precisamente la aclaración sobre el valor del campo BufferCount.

    Lo que no acabo de comprender es por que no se ha seguido el patron que pienso sería habitual, que es considerar que el valor por defecto, -1, traería todos los registros. Hay muchas propiedades que se rigen por esta consideración (me viene a la mente la propiedad PackedRecords en el ClientDataSet como ejemplo de eso) o incluso el valor O (cero) si no recuerdo mal en la propiedad MaxRecords de los adoDataSets. Y sería natural que fuera así porque la misma fuente de datos del TBindScopeDB, ligada al datasource, tiene acceso a esa información.

    Por eso creo que se presta para pasar inadvertido y quizás no fuera descabellado que BufferCount accediera a todos los registros si el valor fuera -1 y en el caso de que fuera mayor que cero, habría sido fijado por el usuario.

    Y ya para acabar… notar que si es fijado el valor, posteriormente a la apertura del dataset, aunque hagamos la asignación del valor correcto de buffercount no lo tendría en cuenta. 🙂 Luego tal y como esta la cosa hay que leer el valor de la cantidad de registros previa a la apertura del dataset… 😉 Creo que entendéis la ironía de eso… Como habitualmente está marcada el valor AutoFill como True, al abrir el dataset se rellena automáticamente la lista y cualquier cambio posterior de BufferCount requeriría reabrir el dataset, para que fuera considerado.

    Bueno… si no me he equivocado creo que es como digo.

    Un saludo

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s

Blog de WordPress.com.

Subir ↑

Recetas y consejos nutricionales

Indicadas para personas con diabetes, recomendadas para todos.

¡Buen camino!

ANÉCDOTAS Y REFLEXIONES SOBRE UN VIAJE A SANTIAGO…

http://lfgonzalez.visiblogs.com/

Algunas reflexiones y comentarios sobre Delphi

It's All About Code!

A blog about Delphi and related technologies

The Podcast at Delphi.org

The Podcast about the Delphi programming language, tools, news and community.

Blog de Carlos G

Algunas reflexiones y comentarios sobre Delphi

The Road to Delphi

Delphi - Free Pascal - Oxygene

La web de Seoane

Algunas reflexiones y comentarios sobre Delphi

El blog de cadetill

Cosas de programación....... y de la vida

Delphi-losophy

A Lover of Delphi Wisdom

Delphi en Movimiento

Algunas reflexiones y comentarios sobre Delphi

marcocantu.blog

Algunas reflexiones y comentarios sobre Delphi

/*Prog*/ Delphi-Neftalí /*finProg*/

Blog sobre programación de Neftalí -Germán Estévez-

Press F9

Algunas reflexiones y comentarios sobre Delphi

El blog de jachguate

Un blog sobre tecnología y la vida en general

A %d blogueros les gusta esto: