Foros Club Delphi

Foros Club Delphi (https://www.clubdelphi.com/foros/index.php)
-   Varios (https://www.clubdelphi.com/foros/forumdisplay.php?f=11)
-   -   Un comportamiento más útil para ComboBox (https://www.clubdelphi.com/foros/showthread.php?t=78388)

jhonalone 15-04-2012 19:07:01

Un comportamiento más útil para ComboBox
 
Hola a todos. Gracias por leerme.

Me gustaría obtener un comportamiento más adaptado a mis pretensiones en un ComboBox, pero no sé cómo hacerlo. Llevo intentándolo y buscando en el foro unos días, pero nada. Agradecería si alguien me puede ayudar.

Esto es lo que pretendo:

Poder desplazarme con las flechas arriba/abajo sin que salte el OnChange ni el OnClck hasta que pulse Enter o haga Click en un item concreto.

Agradecería cualquier idea.

Saludos.

ecfisa 15-04-2012 20:14:53

Cita:

Esto es lo que pretendo:

Poder desplazarme con las flechas arriba/abajo sin que salte el OnChange ni el OnClck hasta que pulse Enter o haga Click en un item concreto.
Hola jhonalone.

Creo que no entiendo la lógica de lo que buscas, pero voy a responder estríctamente a tu consulta:

En realidad sería sólo hasta que se pulse Enter, ya que en teoría OnClick estaría desactivado y por lo tanto no debería ser capturado:
Código Delphi [-]
...
// Desactivar eventos OnClik y OnChange
procedure TForm1.FormCreate(Sender: TObject);
begin
  ComboBox1.OnClick:= nil;
  ComboBox1.OnChange:= nil;
end;
...
// Activar o desactivar eventos OnClick y OnChange
procedure TForm1.ComboBox1KeyPress(Sender: TObject; var Key: Char);
begin
  // si es Enter y es el item en concreto (2 por ej.) => activar eventos
  if (Key =#13)and(ComboBox1.ItemIndex = 2) then
  begin
    ComboBox1.OnClick:= ComboBox1Click;
    ComboBox1.OnChange:= ComboBox1Change
  end
  else  // si no => desactivar eventos
  begin
    ComboBox1.OnClick:= nil;
    ComboBox1.OnChange:= nil
  end;
end;
Pero no visualizo la aplicabilidad de ese código... Concretamente, ¿ Que es lo que tratas de hacer ?

Saludos.

jhonalone 15-04-2012 22:55:32

Gracias, Ecfisa, por estar siempre pendiende de ayudar.

Me voy a extender un poco más, a ver si consigo explicarme mejor.

Si en un Combo tienes código en el evento, OnClick, y mueves el ratón arriba y abajo por los items desplegados, el Combo va redibujando el item por el que pasa en azul oscuro, si haces Click sobre uno de ellos activa OnClick, pero si no haces click lo deja azul. Aunque parece que está seleccionado, si pulsas Enter no hace nada, aunque hayas puesto alguna instrucción en OnKeyDown.
Código Delphi [-]
procedure TFClien.ComboBox1KeyDown(Sender: TObject; var Key: Word;
  Shift: TShiftState);
begin
if Key=vk_RETURN
     then SpeedButton3.Click;
end;

Pero si intentas desplazarte con las flechas genera un evento ONClick, aplicado al item siguiente al marcado hacia arriba o hacia abajo, dependiendo de la flecha que utilices.

Curiosamente, si haces Click en la tecla Return o Enter, no la reconoce, he probado a incluir un Beep si pulsa enter y no lo hace, ni en OnKeyDown ni en OnkeyPress con #13.

He hecho tantas pruebas ya, que no se lo hace ni cuándo se activa cada evento.

Lo que pretendo es muy sencillo:
Si utilizo las teclas para desplazarme, hacerlo libremente y, si pulso Enter, seleccionar el item iluminado. Y si no me desplazo con las flechas, sino con el cursor, que el item iluminado si lo dejo y pulso enter lo seleccione y genere el código para OnKeyDown u OnKeyPress, me da igual. Y si hago Click sobre un item genere el OnClick corrrespondiente.

No estoy seguro si lo vas a entender, porque esta noche ya estoy muy cansado, y he hecho tantas pruebas... que lo voy a dejar.

Si no me he explicado bien, dímelo y mañana lo intentaré de nuevo más despejado.

Saludos muy cordiales.

ecfisa 16-04-2012 09:48:46

Hola jhonalone.

Ahora creo haber entendido.

En el evento OnClick podrías interceptar la tecla que se presionó y por ejemplo si es VK_UP o VK_DOWN salir:
Código Delphi [-]
procedure TForm1.ComboBox1Click(Sender: TObject);
begin
 if (GetKeyState(VK_DOWN)<0)or(GetKeyState(VK_UP)<0) then Exit;
  (* aquí el codigo del evento OnClick *)
  //...
  ShowMessage('OnClick')
end;

Del mismo modo en el evento OnChange:
Código Delphi [-]
procedure TForm1.ComboBox1Change(Sender: TObject);
begin
   if (GetKeyState(VK_DOWN)<0)or(GetKeyState(VK_UP)<0) then Exit;
  (* aquí el codigo del evento OnChange *)
  //...
  ShowMessage('OnChange')
end;
Seguramente quieras controlar otras teclas, revisá Virtual key codes en la ayuda de Delphi.

Saludos.

jhonalone 17-04-2012 14:55:53

Muchas gracias, Ecfisa.

¡LA PRIMERA MISION ESTÁ CUMPLIDA!

Al controlar las teclas en el evento OnClick, como me has indicado, puedo desplazarme sin problemas por los items del Combo con las teclas arriba y abajo. He observado que, además, el texto del Combo va cambiando, lo que me indica, si no me equivoco, que ha sido seleccionado correctamente el item al cambiar las flechas.

Ahora viene la segunda parte.

Una vez que tengo seleccionado el item correspondiente con las flechas, ¿como debo hacer para que al pulsar la tecla ENTER, INTRO o RETURN, que suelen causar el mismo efecto, el programa realice las instrucciones que yo le indique?

Tengo instrucciones en los eventos OnKeyDown y OnKeyPress, que copio a continuación, y ninguno realiza acción alguna cuando pulso ENTER o RETURN,
sin embargo, cuando pulso la tecla ESC sí me cierra el Combo.


Código Delphi [-]
procedure TFClien.ComboBox1KeyPress(Sender: TObject; var Key: Char);
begin
if Key=#13
     then SpeedButton3Click(nil);
if key = #27 then Combobox1.Visible:=False;
end;

Código Delphi [-]
procedure TFClien.ComboBox1KeyDown(Sender: TObject; var Key: Word;
  Shift: TShiftState);
begin
if Key=vk_RETURN
     then SpeedButton3Click(nil);
end;

Ninguno de los dos eventos, realiza la acción. Es como si no hubiera pulsado la tecla ENTER (RETURN en el Virtual Key Code)

Comprendo que estoy abusando mucho de tu sabiduría y buena disposición a ayudar en el foro, si alguna vez resulto pesado, por favor házmelo saber, y no te seguiré preguntando. Me siento tan mal, que parece que te pido que me hagas mis tareas.

Muchas gracias y recibe toda mi consideración y un afectuoso saludo.

jhonalone 17-04-2012 15:00:01

¡ Vaya |

Veo que el código no salió correctamente.

Mis disculpas.

Casimiro Notevi 17-04-2012 16:11:19

Cita:

Empezado por jhonalone (Mensaje 430199)
¡ Vaya |
Veo que el código no salió correctamente.
Mis disculpas.

Ocurre algunas veces, voy a intentar arreglarlo...

ecfisa 17-04-2012 19:56:54

Hola jhonalone.

Creo que podrías hacer:
Código Delphi [-]
procedure TForm1.ComboBox1Click(Sender: TObject);
begin
  if (GetKeyState(VK_DOWN)<0)or(GetKeyState(VK_UP)<0)
      or(GetKeyState(VK_RETURN) < 0) then Exit;
  ...
end;

procedure TForm1.ComboBox1Change(Sender: TObject);
begin
  if (GetKeyState(VK_DOWN)<0)or(GetKeyState(VK_UP)<0)
      or(GetKeyState(VK_RETURN) < 0) then Exit;
  ...
end;
procedure TForm1.ComboBox1KeyDown(Sender: TObject; var Key: Word;
  Shift: TShiftState);
var
   Mgs: TMsg;
begin
   if Key = VK_RETURN then
   begin
     // Aqui llamá a tu procedimiento o poné el código a ejecutar
     PeekMessage(Mgs, 0, WM_CHAR, WM_CHAR, PM_REMOVE); // quitar el molesto beep :mad:
     ShowMessage('Intro fué presionada');
   end;
end;

Saludos.

jhonalone 17-04-2012 21:47:37

Gracias de nuevo Ecfisa.

Te cuento en la situación que estoy:

El combo se rellena y se hace visible y desplegado con:
Código Delphi [-]
procedure TFClien.SpeedButton3Click(Sender: TObject);
var
   NoHayMas: Boolean;
begin
ClienDB.Filtered := False;
ClienDB.Filter := 'Nom= '+QuotedStr(LMDEdit6.Text+'*');
ClienDB.Filtered := True;
Combobox1.Clear;
NoHayMas := False;
if ClienDB.FindFirst
then Combobox1.Items.Add(ClienDBNom.AsString);
while NoHayMas=False
do begin
   if ClienDB.FindNext
   then Combobox1.Items.Add(ClienDBNom.AsString)
   else NoHayMas := True;
   end;
Combobox1.ItemIndex := -1;
ClienDB.Filtered := False;
if Combobox1.Items.Count > 0
then begin
      if (Combobox1.Items.Count = 1) and  // Es el mismo
         (Combobox1.Items.Strings[0] = LMDEdit6.Text)
      then begin
           Combobox1.Visible:=False;
           Beep;
           Exit;
           end;
     Combobox1.Visible:=True;
     Combobox1.ItemIndex := 0;
     ComboBox1.SetFocus;
     ComboBox1.Perform(CB_SHOWDROPDOWN, 1,0);// Despliega el Combo
     ComboBox1.SetFocus;
     end
else PonDatosCli;
end;

A este procedimiento lo llamo desde aquí:
Código Delphi [-]
procedure TFClien.LMDEdit6KeyDown(Sender: TObject; var Key: Word;
  Shift: TShiftState);
begin
if Button6.Enabled // Si no está en Altas
then begin
     if Key=vk_RETURN
     then begin
          if ValorOriginal<>LMDEdit6.Text
          then SpeedButton3.Click;
          end;
     end;
end;

Utilizo el procedimiento siquiente para bloquear OnClick al mover las flechas y utilizarlo cuando se pulse Click sobre un Item:
Código Delphi [-]
procedure TFClien.ComboBox1Click(Sender: TObject);
begin
if (GetKeyState(VK_DOWN)<0)or(GetKeyState(VK_UP)<0) then Exit;
ClienDB.SetKey;
ClienDB['Nom'] := Combobox1.Items.Strings[Combobox1.ItemIndex];
ClienDB.GotoNearest;
// Segunda Base Clien2DB
FClien.Clien2DB.SetKey;
FClien.Clien2DB['Nom'] := Combobox1.Items.Strings[Combobox1.ItemIndex];
FClien.Clien2DB.GotoNearest;
//Comprobamos que hemos leido el mismo cliente en las dos bases
if FClien.ClienDBNom.AsString <> FClien.Clien2DBNom.AsString
then begin
     ShowMessage('La Base de Clientes de reserva, puede no estar actualizada.'+#13+
                 'Conviene que salga del programa y entre de nuevo para corregir este error.');
     end;
PonDatosCli;
Combobox1.Visible:=False;
end;

En los eventos OnKeyDown y OnKeyPress del combo, no me reconece la tecla Enter:
Código Delphi [-]
procedure TFClien.ComboBox1KeyDown(Sender: TObject; var Key: Word;
  Shift: TShiftState);
begin
if Key=vk_RETURN
     then beep;
end;
No se oye el BEEP al pulsar Enter.

Tampoco se oye al pulsar enter en OnKeyPress:
Código Delphi [-]
procedure TFClien.ComboBox1KeyPress(Sender: TObject; var Key: Char);
begin
if Key=#13 then Beep;
if key = #27 then Combobox1.Visible:=False;
end;
Sin embargo, si pulso ESCAPE se cierra el combo.

He recurrido al evento ONKeyUp:
Código Delphi [-]
procedure TFClien.ComboBox1KeyUp(Sender: TObject; var Key: Word;
  Shift: TShiftState);
begin
if Key=vk_RETURN
     then begin
          ClienDB.SetKey;
          ClienDB['Nom'] := Combobox1.Items.Strings[Combobox1.ItemIndex];
          ClienDB.GotoNearest;
          // Segunda Base Clien2DB
          FClien.Clien2DB.SetKey;
          FClien.Clien2DB['Nom'] := Combobox1.Items.Strings[Combobox1.ItemIndex];
          FClien.Clien2DB.GotoNearest;
          //Comprobamos que hemos leido el mismo cliente en las dos bases
          if FClien.ClienDBNom.AsString <> FClien.Clien2DBNom.AsString
          then begin
               ShowMessage('La Base de Clientes de reserva, puede no estar actualizada.'+#13+
                           'Conviene que salga del programa y entre de nuevo para corregir este error.');
               end;
          PonDatosCli;
          Combobox1.Visible:=False;
          end;
end;

El mismo Código que en OnClick.

Bueno pues esto funciona así:

Antes de asignar el procedimiento OnKey press.
El combo se queda desplegado.
Si hago click en un item con el ratón, presenta bien los datos.
Muevo las teclas arriba y abajo y selecciona el item y actualiza el texto del Combo.
Si pulso Enter no hace nada y no se oye el BEEP.

Después de asignar el procedimiento a OnKeyPress
Se despliega el Combo, selecciona el primer item de la lista, pone los datos correctos de este item y cierra el Combo. No espera deplegado a que pulse ninguna tecla, es como si siguiera vigente el Enter de TFCliien.LMDEditKeyDown() (El procedimiento que llama a TFClien.SpeedButton3(), que es el procedimiento que rellena y presenta el combo)

Quizá me he extendido demasiado, pero quería dejarte clara la situación.

Espero que me digas cómo evito que quede en memoria el primer ENTER que llama al procemiento que rellena y despliega el Combo.

Gracias de antemano.

Un Saludo.

ecfisa 17-04-2012 23:13:56

Hola jhonalone.

Veo que en el evento OnClick de tu código figura:
Código Delphi [-]
...
  if (GetKeyState(VK_DOWN)<0)or(GetKeyState(VK_UP)<0) then Exit;
...

Pero si revisás el código de mi último mensaje verás que en los eventos OnClick y OnChange también evaluo:
Código Delphi [-]
...
  if (GetKeyState(VK_DOWN)<0) or (GetKeyState(VK_UP)<0)
      or (GetKeyState(VK_RETURN) < 0) then Exit;
...

El valor de la tecla presionada lo podés evaluar en el evento OnKeyDown sin ningún problema.

Saludos.

jhonalone 18-04-2012 11:59:55

Muchas gracias, de nuevo, Ecfisa.

¡¡¡ CONSEGUIDO !!!

Pero te informo cómo:

(GetKeyState(VK_RETURN) < 0) no ha sido necesario ponerlo en OnClick.

El evento OnChange está vacío.

Sigue sin reconocer la tecla ENTER ni en OnKeyDown ni en OnKeyPress.

He tenido que despreciar la primera pulsación de Enter que provenía del procedimiento TFClien.LMDEdit6KeyDown() que era la que provocaba la autoselección en el Combo.
Paraa ello he utilizado una variable boleana global, que se activa al pulsar el primer Enter en el procedimiento TFClien.LMDEdit6KeyDown() y se desactiva al autoseleccionarse el Combo por medio del procedimiento TFClien.ComboBox1KeyUp() al recibir el foco el Combo.

Sigo sin entender, ¿por qué no reconoce la tecla Enter en OnKeyDown ni en OnKeyPress?

Tampoco ha sido necesario desactivar el BEEP al pulsar ENTER, simplemente porque no sonaba, pero me guardo el código para cuando lo necesite, que es muy útil.

Dejo las modificaciones, para quien le pueda interesar:
Código Delphi [-]
var //Global a la Form
  PrimeraVezEnter: Boolean;
...............
// Procedimiento inductor de la llamada con Enter
procedure TFClien.LMDEdit6KeyDown(Sender: TObject; var Key: Word;
  Shift: TShiftState);
begin
if Button6.Enabled // Si no está en Altas
then begin
     if Key=vk_RETURN
     then begin
          if ValorOriginal<>LMDEdit6.Text
          then begin
               SpeedButton3.Click;
               PrimeraVezEnter := True;
               end;
          end;
     end;
end;

//En el procedimiento que rellena el Combo,( después de rellenarlo)
procedure TFClien.SpeedButton3Click(Sender: TObject);
begin
.......................     
  Combobox1.Visible:=True;     
  Combobox1.ItemIndex := 0;     
  ComboBox1.SetFocus;     
  ComboBox1.Perform(CB_SHOWDROPDOWN, 1,0);
  // Despliega el Combo    
  ComboBox1.SetFocus;
  ..............
end;

// En el evento OnKeyUp del Combo (OnkeyDown y OnKeyPress no reconecen ENTER)
procedure TFClien.ComboBox1KeyUp(Sender: TObject; var Key: Word;
  Shift: TShiftState);
beginif Key=vk_RETURN
     then begin
          if PrimeraVezEnter
          then begin
               PrimeraVezEnter := False;
               Exit;
               end;
          end;// Instrucciones al seleccionar item en el Combo.............
end;

Dejo el mecanismo de acción por si interesa a alguien.

Gracias una vez más, Ecfica, por tu ayuda y esfuerzo, sin ellos no lo hubiera conseguido. ¡Eres mi ídolo...|
¡A ver cúanto aguanto sin tener que molestarte!

Saludos...


La franja horaria es GMT +2. Ahora son las 03:43:35.

Powered by vBulletin® Version 3.6.8
Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
Traducción al castellano por el equipo de moderadores del Club Delphi