laremi400 Master

Registrado: 12 May 2004Mensajes: 179
|
Publicado: Vie 12 Ago, 2005 9:22 am Asunto: Componente Buscador en BD |
|
|
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!!! |
|