Club Builder - Foros de discusión Club Builder
Foro de C++Builder en español
 
 FAQFAQ   BuscarBuscar   MiembrosMiembros   Grupos de UsuariosGrupos de Usuarios   RegistrarseRegistrarse 
 PerfilPerfil   Entra para ver tus mensajes privadosEntra para ver tus mensajes privados   EntrarEntrar 

Componente Buscador en BD

 
Publicar nuevo tema   Responder al tema    Foros de discusión -> Tips & Tricks
Ver tema anterior :: Ver tema siguiente  
Autor Mensaje
Mensaje    Asunto: Componente Buscador en BD Responder citando

Hola!!

No sé si este el lugar adecuado para poner este código, pero he estado de visita en lo del Open Source y me he perdido. Así que pongo aquí este código y vosotros mismos.

Esto sirve para hacer una búsqueda en un campo de una tabla de una BD. Se hace mediante una consulta SQL; "Select Distinct Campo from Tabla where Campo like 'loquesea%'.
La información que se obtiene se muestra al usuario en un TListBox. He usado para derivar el componente un TCustomEdit.
Es muy sencillito lo que hace. Y me imagino que encontrareis a faltar muchas cosas, así que el que quiera opinar que opine, que será bien recibido.
Esto, por supuesto, hay que agradecérselo a _Leo y a César que son los que me han sacado de todos los apuros.

El .h:
Código:

//---------------------------------------------------------------------------
#ifndef BuscadorH
#define BuscadorH
//---------------------------------------------------------------------------
#include <SysUtils.hpp>
#include <Classes.hpp>
#include <Controls.hpp>
#include <StdCtrls.hpp>

#include <ADODB.hpp>

//El BEGIN_MESSAGE_MAP da un warning. Esto lo quita
#pragma warn -inl
//---------------------------------------------------------------------------
class PACKAGE TBuscador : public TCustomEdit
{
private:

int cuenta;
bool ClickLista;

//esta sustituye al doble click de la lista
void __fastcall ListaDblClick(TObject* Sender);
//Idem para el Keypress
void __fastcall ListaKeyPress(TObject* Sender,char &Key);
//esta permite repintar de dos colores distintos cada linea de la lista
void __fastcall ListaDrawItem(TWinControl *Control, int Index,
      const TRect &Rect, TOwnerDrawState State);

int FLeftMas; //para desplazar la lista
int FHeightMas; //para lo mismo
TADOQuery *FConsulta; //se necesita para las consultas un TQuery CONECTADO!
AnsiString FTabla; //La tabla en la que se busca
AnsiString FAlias; //si esta propiedad tiene algo se usa en lugar del nombre del componente
AnsiString FSQL; //si esta propiedad tiene algo se usa en la clausula where
int FCuantos; //máximos resultados a displayar por defecto 10
int FLimite; //máximos resultados a cargar por defecto 20

protected:

/*
Esto que sigue es para evitar el TAB. En este pedacito de código se le dice al
sistema que este control, además de lo que habitualmente manipula, va a usarse
el tabulador. Al decir esto cuando llegue al control en el formulario donde
se coloque. el TAB no funcionará por mucho que le des. (gracias _Leo)
*/
/*
La estructura TWMGetDlgCode es un typedef de TWMNoParams (sacado de Messages.h
struct TWMNoParams
{
unsigned Msg;
Word Unused[4];
int Result;
} ;
*/
//Gracias a _Leo. Esto me lo enseño él.
void __fastcall WmGetDlgCode(TWMGetDlgCode& msg)
{
// Llamamos al evento en la clase base, tiene
// que estar "inherited" correctamente declarado
inherited::Dispatch( &msg );
// Le decimos que nos interesa el tabulador ademas de
// lo que haya añadido la llamada anterior a la clase base
msg.Result |= DLGC_WANTTAB;
}
BEGIN_MESSAGE_MAP
        MESSAGE_HANDLER(WM_GETDLGCODE, TWMGetDlgCode, WmGetDlgCode)
END_MESSAGE_MAP(inherited)

//esta es la lista que se desplegará y se creará en tiempo de ejecución
TListBox *Lista;

//se redefine el Change para que cree el TListBox y lo vaya cargando
DYNAMIC void __fastcall Change();

DYNAMIC void __fastcall KeyPress(char &Key);
DYNAMIC void __fastcall KeyDown(WORD &Key,TShiftState Shift);


DYNAMIC void __fastcall DoExit(void);

bool __fastcall CargarLista();

public:

        //__property TabOrder;
        __fastcall TBuscador(TComponent* Owner);

__published:

__property int LeftMas = {write=FLeftMas,read=FLeftMas};
__property int HeightMas = {write=FHeightMas,read=FHeightMas};
__property TADOQuery *Consulta = {write=FConsulta,read=FConsulta};
__property AnsiString Tabla = {write=FTabla,read=FTabla};
__property AnsiString SQL = {write=FSQL,read=FSQL};
__property AnsiString Alias = {write=FAlias,read=FAlias};
__property int Cuantos = {write=FCuantos,read=FCuantos};
__property int Limite = {write=FLimite,read=FLimite};
};
//---------------------------------------------------------------------------
#endif


El .cpp:

Código:

//---------------------------------------------------------------------------
//Para que esto funcione hay que poner el KeyPreview del form a true y en el
//evento KeyPress del form:
// if(Key == VK_RETURN) Perform(WM_NEXTDLGCTL, 0, 0);
//---------------------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop

#include "Buscador.h"
#pragma package(smart_init)
//---------------------------------------------------------------------------
// ValidCtrCheck is used to assure that the components created do not have
// any pure virtual functions.
//---------------------------------------------------------------------------
static inline void ValidCtrCheck(TBuscador *)
{
        new TBuscador(NULL);
}

//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
__fastcall TBuscador::TBuscador(TComponent* Owner)
        : TCustomEdit(Owner)
{
Text = "";
Color = TColor(0x00F1E4CF);
Cuantos = 10;
Limite = 20;
LeftMas = 10;
HeightMas = 0;
}
//---------------------------------------------------------------------------
// Cuando se hace doble clic se le pasa el valor de la lista al textbox
// y se oculta la lista. Se hace con doble clic por que si se hace con clic
// no funcionan los eventos del teclado del componente TListBox
//---------------------------------------------------------------------------
void __fastcall TBuscador::ListaDblClick(TObject* Sender)
{
if(Lista->ItemIndex >= 0) Text = Lista->Items->Strings[Lista->ItemIndex];
SetFocus();
Lista->Visible = false;
}
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
void __fastcall TBuscador::ListaKeyPress(TObject* Sender,char &Key)
{
if(Key == VK_RETURN)
  {
  if(Lista->ItemIndex >= 0) Text = Lista->Items->Strings[Lista->ItemIndex];
  SetFocus();
  Lista->Visible = false;
  }
}
//---------------------------------------------------------------------------
// Se repinta de dos colores para diferenciar cada línea
// Gracias César
//---------------------------------------------------------------------------
void __fastcall TBuscador::ListaDrawItem(TWinControl *Control, int Index,
      const TRect &Rect, TOwnerDrawState State)
{
TListBox *Temp = dynamic_cast<TListBox*>(Control);

//Cada una de las líneas de diferente color
if ((Index%2)==0) Temp->Canvas->Brush->Color = (TColor) 0x00EFFAEF;
else Temp->Canvas->Brush->Color = (TColor) 0x00D5EDC9;

//Esta es la línea seleccionada
if(State.Contains(odSelected))
  {
  Temp->Canvas->Brush->Color = TColor(0x00F1E4CF);
  Temp->Canvas->FillRect(Rect);
  Temp->Canvas->Font->Color = clBlack;
  }
else Temp->Canvas->FillRect(Rect);

Temp->Canvas->TextOut(Rect.Left,Rect.Top,((TListBox *)Control)->Items->Strings[Index]);
}
//---------------------------------------------------------------------------
// Carga la lista con lo que salga de la busqueda SQL
// Cuantos es el nº máximo de Items que se desea mostrar en la pantalla
// Límite es el nº máximo de Items que se desean cargar en el ListBox. Puede
// haber ocasiones en las que el campo tenga tantos datos que no sea operativo
// cargarlos todos (al fin y al cabo esto es una ayuda a la búsqueda de un dato
// dentro de la BD). Limite marca cuantos se deben cargar.
// Por ejemplo: Ubicacion tiene 2689 registros que empiezan por 0. Si se teclea
// 0 y no existiera Limite (por defecto 20), cargaría 2689 ubicaciones en el
// ListBox con lo que el propio sentido de este componente se va a la mierda

//devuelve un bool, que indica que hay que mostrar la lista o no
//---------------------------------------------------------------------------
bool __fastcall TBuscador::CargarLista()
{
bool cargar = false;

if(TBuscador::GetTextLen() > 0) //si hay cosas que buscar
  {
  Screen->Cursor = crHourGlass;
  Consulta->Close();
  Consulta->SQL->Clear();
  if(SQL == "") Consulta->SQL->Add("Select Distinct "+ Name + " from " + Tabla + " where " + Name + " like '" + Text + "%'");
  else Consulta->SQL->Add("Select Distinct " + Name +" from " + Tabla + " where " + SQL);
  Consulta->Open();

  if(Consulta->RecordCount > 0 && Consulta->RecordCount <= Limite)
    {
    cargar = true;
    Lista->Clear();
    Consulta->First();
    while(!Consulta->Eof)
         {
         if(Alias != "") Lista->Items->Add(Consulta->FieldValues[Alias]);
         else Lista->Items->Add(Consulta->FieldValues[Name]);
         Consulta->Next();
         }
    }
  Screen->Cursor = crDefault;
  }
return(cargar);
}
//---------------------------------------------------------------------------
// Cuando se produce un cambio en el texto se crea la lista directamente
// El evento OnChange es muy cachondo, cuando se crea el objeto en tiempo de
// diseño se produce el evento. Así que al producirse este evento y
// ser Lista == NULL intenta crear el TListBox, pero como aún no se ha creado
// el objeto sobre el formulario, no tiene ::Parent y da un error:
// "El Objeto '' no tiene parent".
// Para evitarlo se pone !ComponentState.Contains(csDesigning) (_Leo, deo gratias)
// que nos dice si se está en tiempo de diseño o en tiempo de ejecución.
//---------------------------------------------------------------------------
void __fastcall TBuscador::Change()
{
if(!ComponentState.Contains(csDesigning))
  {
  if(Lista == NULL)
    {
    Lista = new TListBox(TBuscador::Parent);
    Lista->Parent = TBuscador::Parent;
    Lista->Name = TBuscador::Name+"Lista";

    Lista->Left = Left+LeftMas;//+ 10 o 15 no está mál sino 0
    Lista->Top = Top+Height+HeightMas;
    Lista->Width = Width;
    Lista->Height = Cuantos * Lista->ItemHeight;
    Lista->IntegralHeight = true;
    Lista->Ctl3D = false;
    Lista->Font->Name = "Small Fonts";
    Lista->Font->Size = 7;
    Lista->Style = lbOwnerDrawVariable;
    Lista->OnDblClick = ListaDblClick;
    Lista->OnKeyPress = ListaKeyPress;
    Lista->OnDrawItem = ListaDrawItem;
    Lista->Clear();
    Lista->TabStop = false;
    Lista->Visible = false;
    }
  }
//si hay algo que enseñar se muestra la lista
if(CargarLista()) Lista->Visible = true;
TCustomEdit::Change();
}
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
void __fastcall TBuscador::KeyPress(char &Key)
{
if(Key == VK_RETURN)
  {
  if(!ComponentState.Contains(csDesigning) && Lista != NULL)
    {
    Lista->Visible = false; //en vez del delete
    }
  }
// No se permite la comilla para que no jodan el SQL.
if(Key == 39) Key = 0;

TCustomEdit::KeyPress(Key);
}
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
void __fastcall TBuscador::KeyDown(WORD &Key,TShiftState Shift)
{
if(Key == VK_DOWN)
  {
  if(!ComponentState.Contains(csDesigning) && Lista != NULL && Lista->Visible == True)
    {
    Lista->ItemIndex = 0;
    Lista->SetFocus();
    }
  }
TCustomEdit::KeyDown(Key,Shift);
}
//---------------------------------------------------------------------------
// Cuando se sale del buscador se puede salir haciendo clic en la lista que es
// propiedad del buscador, en cuyo caso no se hace nada o se puede hacer clic en
// otra parte del formulario. si es así se esconde la lista de este buscador
//---------------------------------------------------------------------------
void __fastcall TBuscador::DoExit(void)
{
int objeto;

//Tbuscador::Parent se supone que debe ser un formulario o algo así.
//Al loro con los Paneles
for(objeto=0;objeto < TBuscador::Parent->ControlCount; objeto++)
   {
   //"Casteo" dinamico para saber tiene foco. Sacado de la ayuda
   TWinControl *Temp = dynamic_cast<TWinControl *>(TBuscador::Parent->Controls[objeto]);
   if(Temp && Temp->Focused())
     {
     if(Temp->Name != (TBuscador::Name+"Lista"))
       {
       if(!ComponentState.Contains(csDesigning) && Lista != NULL)
         {
         Lista->Visible = false; //en vez del delete
         }
       }
     }
   }
}



//---------------------------------------------------------------------------
// Se crea el componente y se registra.
//---------------------------------------------------------------------------
namespace Buscador
{
        void __fastcall PACKAGE Register()
        {
       
        TComponentClass classes[1] = {__classid(TBuscador)};
       
        RegisterComponents("Propios", classes, 0);
        }
}
//---------------------------------------------------------------------------


Está sin probar a fondo. Eso lo haré poco a poco mientras voy haciendo una aplicación. Así que si encuentras algún fallo te agradeceré que lo comentes. Y si se te ocurre alguna mejora pues lo mismo.
Gracias!!!
Volver arriba
Ver perfil de usuario Enviar mensaje privado
Mensaje    Asunto: Responder citando

Hola!!

El caso es que había un error en el código. el evento OncChange es así:

Código:

void __fastcall TBuscador::Change()
{
if(!ComponentState.Contains(csDesigning))
  {
  if(Lista == NULL)
    {
    Lista = new TListBox(TBuscador::Parent);
    Lista->Parent = TBuscador::Parent;
    Lista->Name = TBuscador::Name+"Lista";

    Lista->Left = Left+LeftMas;//+ 10 o 15 no está mál sino 0
    Lista->Top = Top+Height+HeightMas;
    Lista->Width = Width;
    Lista->Height = Cuantos * Lista->ItemHeight;
    Lista->IntegralHeight = true;
    Lista->Ctl3D = false;
    Lista->Font->Name = "Small Fonts";
    Lista->Font->Size = 7;
    Lista->Style = lbOwnerDrawVariable;
    Lista->OnDblClick = ListaDblClick;
    Lista->OnKeyPress = ListaKeyPress;
    Lista->OnDrawItem = ListaDrawItem;
    Lista->Clear();
    Lista->TabStop = false;
    Lista->Visible = false;
    }
   //si hay algo que enseñar se muestra la lista
   if(CargarLista()) Lista->Visible = true;
}
TCustomEdit::Change();
}


Las prisas no son buenas y de vez en cuando a uno se le va la olla. Lo siento...
Volver arriba
Ver perfil de usuario Enviar mensaje privado
Mostrar mensajes de anteriores:   
Cambiar a:  
No puedes publicar temas nuevos en este foro
No puedes responder a temas en este foro
No puedes editar tus mensajes en este foro
No puedes borrar tus mensajes en este foro
No puedes votar en las encuestas de este foro


Hosting WordPress Indedmedia desde 3,99 €
© 2002–2026 Club Builder · Funciona con phpBB.