c# - form - deshabilitar evento clave cuando el foco está en el cuadro de autocompletado de cuadro de texto
evento keyup c# (4)
En mi proyecto hay un formulario mainForm
en el que hay dos textBoxes txtUserName
y txtPassword
y también un botón btnLogin
.
He dado las siguientes propiedades txtUserName
:
Propiedades de txtUserName
AutoCompleteCustomSource - Collection
--> Administrator
--> Clerk
AutoCompleteMode - Suggest
AutoCompleteSource - CustomSource
Evento btnLogin_Click
if (txtUserName.Text.Equals("Administrator") && txtPassword.Text.Equals("123"))
{
//function to access admin features
}
else if (txtUserName.Text.Equals("Clerk") && txtPassword.Text.Equals("123"))
{
//function to access clerk features
}
else
{
MessageBox.Show("Please Enter correct details", "Login Error");
}
He mainForm
keypreview
mainForm
keypreview
para la función true
e implementado para el evento mainForm
de mainForm
que se muestra en el siguiente código:
mainForm_KeyDownEvent
if (e.KeyCode.Equals(Keys.Enter)) //Invokes whenever Enter is pressed
{
btnLogin_Click(sender,e); //login
}
Ahora mi problema es que cada vez que el foco está en txtUserName
y presionando A
, se muestra el menú desplegable para seleccionar "Administrador" (que se define en las colecciones como se muestra en las propiedades anteriores). Cuando hago clic en Enter
en el teclado, aparece MessageBox en lugar de seleccionar "Administrador". Sé que está invocando el evento mainForm
de mainForm
. ¿Cómo desactivar el evento keyDown, cuando está en el menú desplegable de texto para que pueda presionar enter
?
EDITAR :
Probé el siguiente código en public form()
:( no funciona )
InitializeComponent();
if (txtUserName.AutoCompleteMode) { /* showing red scribbles */
this.KeyDown -= mainForm_KeyDown;
}
Debe anular el controlador de eventos de selección de teclas.
protected override void OnKeyDown(KeyEventArgs e)
{
//call original event handler. Remove it if you don''t need it at all.
base.OnKeyDown(e);
//Insert your code here....
}
En realidad, tienes dos problemas.
En primer lugar, establezca la propiedad AutoCompleteMode de txtUserName en "SuggestAppend" en lugar de simplemente "Sugerir". De esta forma, si el usuario escribe la primera letra o dos, la entrada correcta se agregará automáticamente a txtUSerName.Text.
A continuación, modifique su código de formulario de la siguiente manera:
void Form1_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode.Equals(Keys.Enter)) //Invokes whenever Enter is pressed
{
if (txtPassword.ContainsFocus)
{
btnLogin_Click(sender, e); //login
}
else
{
this.txtPassword.Focus();
}
}
}
private void btnLogin_Click(object sender, EventArgs e)
{
if (txtUserName.Text.Equals("Administrator") && txtPassword.Text.Equals("123"))
{
MessageBox.Show("Administrator");
}
else if (txtUserName.Text.Equals("Clerk") && txtPassword.Text.Equals("123"))
{
MessageBox.Show("Clerk");
}
else
{
MessageBox.Show("Please Enter correct details", "Login Error");
}
}
En lo anterior, el código de manejo de eventos Key Down prueba para ver si el cuadro de texto de la contraseña tiene el foco (lo que significa que el usuario ya ha ingresado un nombre de usuario y una contraseña, y está listo para enviar los datos). Si es así, se llama al evento btnLogin_Click. De lo contrario, (es decir, txtUserName probablemente tenga el foco) el control se pasa a txtPassword para continuar la entrada de datos.
ACTUALIZACIÓN: re - Su comentario:
Simplemente elimine la lógica en el controlador de eventos de Key Down de la siguiente manera:
Código revisado de manejo de eventos:
void Form1_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode.Equals(Keys.Enter)) //Invokes whenever Enter is pressed
{
btnLogin_Click(sender, e); //login
}
}
Tenga en cuenta que otra mejora menor (teniendo en cuenta la estructura general de su código) sería utilizar un cuadro combinado para la selección de Nombre de usuario y establecer el origen de autocompletar en "Artículos de lista", luego ingrese sus opciones del mismo modo que con el cuadro de texto. Esto requiere que el usuario seleccione de la lista predefinida. Esto todavía tiene problemas de escalabilidad similares a los anteriores, pero elimina un paso innecesario para el usuario si simplemente comete un error al escribir datos de Nombre de usuario.
Recuerde que a los usuarios les desagrada la interrupción innecesaria mediante mensajes emergentes. Permítales seleccionar el "nombre de usuario" apropiado de un menú desplegable, escriba la contraseña correcta y continúe.
Hay algunas formas mejores de hacer todo esto, pero esto debería afinar lo que tiene en funcionamiento.
En una nota final, déjenme observar que eventualmente querrán encontrar una forma más robusta de realizar este tipo de validación. Cada vez que necesite agregar usuarios (que, en su código, parecen definirse más como "grupos", deberá agregarlos a su árbol de manejo de eventos condicionales).
Puede verificar nombres de usuario y contraseñas persistentes en un archivo o base de datos cifrados, y cargarlos en un diccionario o algo en tiempo de ejecución. A continuación, realice una búsqueda de clave / valor en usuario / Contraseña.
O algo.
De todos modos, espero que ayude.
ACTUALIZACIÓN 2: El código completo, todo en una sola toma. Esto debería comportarse de la manera que estás preguntando:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
this.KeyDown +=new KeyEventHandler(Form1_KeyDown);
}
void Form1_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode.Equals(Keys.Enter)) //Invokes whenever Enter is pressed
{
btnLogin_Click(sender, e); //login
}
}
private void btnLogin_Click(object sender, EventArgs e)
{
if (txtUserName.Text.Equals("Administrator") && txtPassword.Text.Equals("123"))
{
MessageBox.Show("Administrator");
}
else if (txtUserName.Text.Equals("Clerk") && txtPassword.Text.Equals("123"))
{
MessageBox.Show("Clerk");
}
else
{
MessageBox.Show("Please Enter correct details", "Login Error");
}
}
}
}
No debe manejar la tecla Enter en absoluto. Puede eliminar su manejador KeyDown
y, en su lugar, usar la propiedad AcceptButton
del formulario para establecer el botón que se "presiona" cuando se presiona Intro. Se supone que esto ya no "hace clic" en el botón cuando otro control ya ha manejado la tecla Intro.
Eso no es suficiente para su situación, porque el comportamiento estándar de Windows es que la tecla Intro presione el botón predeterminado. Presione Win + R, por ejemplo, para obtener el diálogo Ejecutar ..., comience a escribir C: / Usar, presione Abajo para seleccionar C: / Usuarios, presione Entrar y vea qué sucede.
Para anular ese comportamiento, debe hacer que el cuadro de texto indique al formulario que manejará la tecla Intro, para que el formulario no lo envíe al botón predeterminado. Esto se puede hacer creando una clase derivada y anulando IsInputKey
:
public class MyTextBox : TextBox
{
protected override bool IsInputKey(Keys keyData)
{
return base.IsInputKey(keyData) || ((keyData & ~Keys.Shift) == Keys.Enter && IsDroppedDown);
}
}
Sin embargo, TextBox
implementa el autocompletado utilizando la función SHAutoComplete
, que crea automáticamente un objeto IAutoComplete
detrás de las escenas. No se puede acceder a ese objeto y, debido a eso, no se puede crear la propiedad IsDroppedDown
que utilicé en IsInputKey
. Se implementaría utilizando IAutoCompleteDropDown.GetDropDownStatus
, pero como el objeto no es accesible, no se puede determinar (confiablemente) si se muestra la lista desplegable.
&& IsDroppedDown
implementar la finalización automática sin utilizar las propiedades integradas de AutoComplete*
, o deberá suprimir siempre la tecla Intro (eliminar el && IsDroppedDown
en la IsInputKey
anterior).
Actualización : aquí se explica cómo crear un objeto IAutoComplete
manualmente. Las cuerdas Administrador y Secretario están codificadas. La función GetDropDownStatus se usa para suprimir el manejo de cualquier botón predeterminado de Enter cuando la lista desplegable está visible. Comentarios bienvenidos.
IAutoComplete.cs:
using System;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
[ComImport]
[Guid("00bb2762-6a77-11d0-a535-00c04fd7d062")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[CoClass(typeof(IAutoCompleteClass))]
interface IAutoComplete
{
void Init(HandleRef hwndEdit, IEnumString punkACL, string pwszRegKeyPath, string pwszQuickComplete);
void Enable(bool fEnable);
}
IAutoComplete2.cs:
using System;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
[Guid("EAC04BC0-3791-11d2-BB95-0060977B464C")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
interface IAutoComplete2
{
void Init(HandleRef hwndEdit, IEnumString punkACL, string pwszRegKeyPath, string pwszQuickComplete);
void Enable(bool fEnable);
void SetOptions(AutoCompleteOptions dwFlag);
AutoCompleteOptions GetOptions();
};
AutoCompleteOptions.cs:
using System;
[Flags]
enum AutoCompleteOptions : int
{
None = 0x00,
AutoSuggest = 0x01,
AutoAppend = 0x02,
Search = 0x04,
FilterPrefixes = 0x08,
UseTab = 0x10,
UpDownKeyDropsList = 0x20,
RtlReading = 0x40,
WordFilter = 0x80,
NoPrefixFiltering = 0x100,
}
IAutoCompleteDropDown.cs:
using System;
using System.Runtime.InteropServices;
using System.Text;
[Guid("3CD141F4-3C6A-11d2-BCAA-00C04FD929DB")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
interface IAutoCompleteDropDown
{
void GetDropDownStatus(out AutoCompleteDropDownFlags dwFlags, out StringBuilder wszString);
void ResetEnumerator();
}
AutoCompleteDropDownFlags.cs:
using System;
[Flags]
enum AutoCompleteDropDownFlags : int
{
None = 0x00,
Visible = 0x01
}
IAutoCompleteClass.cs:
using System;
using System.Runtime.InteropServices;
[ComImport]
[Guid("00BB2763-6A77-11D0-A535-00C04FD7D062")]
class IAutoCompleteClass
{
}
EnumString.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
class EnumString : IEnumString
{
const int E_INVALIDARG = unchecked((int)0x80070057);
const int S_OK = 0;
const int S_FALSE = 1;
int current;
string[] strings;
public EnumString(IEnumerable<string> strings)
{
this.current = 0;
this.strings = strings.ToArray();
}
public void Clone(out IEnumString ppenum)
{
ppenum = new EnumString(strings);
}
public int Next(int celt, string[] rgelt, IntPtr pceltFetched)
{
if (celt < 0)
return E_INVALIDARG;
int num = 0;
while (current < strings.Length && celt != 0)
{
rgelt[num] = strings[current];
current++;
num++;
celt--;
}
if (pceltFetched != IntPtr.Zero)
Marshal.WriteInt32(pceltFetched, num);
if (celt != 0)
return S_FALSE;
return S_OK;
}
public void Reset()
{
current = 0;
}
public int Skip(int celt)
{
if (celt < 0)
return E_INVALIDARG;
if (strings.Length - current > celt)
{
current = strings.Length;
return S_FALSE;
}
current += celt;
return S_OK;
}
}
MyTextBox.cs:
using System;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows.Forms;
public class MyTextBox : TextBox
{
IAutoComplete2 autoComplete;
IAutoCompleteDropDown autoCompleteDropDown;
public bool IsDroppedDown
{
get
{
if (autoCompleteDropDown == null)
return false;
AutoCompleteDropDownFlags dwFlags;
StringBuilder wszString;
autoCompleteDropDown.GetDropDownStatus(out dwFlags, out wszString);
return (dwFlags & AutoCompleteDropDownFlags.Visible) != AutoCompleteDropDownFlags.None;
}
}
protected override void CreateHandle()
{
base.CreateHandle();
autoComplete = (IAutoComplete2)new IAutoComplete();
autoCompleteDropDown = (IAutoCompleteDropDown)autoComplete;
autoComplete.SetOptions(AutoCompleteOptions.AutoSuggest);
autoComplete.Init(new HandleRef(this, this.Handle), new EnumString(new string[] { "Administrator", "Clerk" }), null, null);
}
protected override void DestroyHandle()
{
ReleaseAutoComplete();
base.DestroyHandle();
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
ReleaseAutoComplete();
}
base.Dispose(disposing);
}
protected override bool IsInputKey(Keys keyData)
{
return base.IsInputKey(keyData) || ((keyData & ~Keys.Shift) == Keys.Enter && IsDroppedDown);
}
void ReleaseAutoComplete()
{
if (autoComplete != null)
{
Marshal.ReleaseComObject(autoComplete);
autoComplete = null;
autoCompleteDropDown = null;
}
}
}
Prueba esto. Con suerte, no causará ningún problema al presionar Enter mientras su enfoque está en txtUsername o de otra manera donde
Si escribe a
en txtUserName
y presiona intro, su elección de Admministrator
se seleccionará de su autocompletecustomsource
utilizando regular expression
y el foco irá a txtPassword
. Mi expresión regular es muy flexible, puedes restringirla un poco como sigue para que coincida estrictamente desde el principio y también puedes eliminar ignorar el caso
Regex rg = new Regex("^" + txtUserName.Text);
private void mainForm_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode.Equals(Keys.Enter))// && !txtUserName.Focus())// && intFlag.Equals(0))
{
if (txtUserName.Text.Length > 0)
{
if (txtUserName.Focused)
{
Regex rg = new Regex(txtUserName.Text, RegexOptions.IgnoreCase);
for (int i = 0; i < txtUserName.AutoCompleteCustomSource.Count; i++)
{
if (rg.IsMatch(txtUserName.AutoCompleteCustomSource[i]))
{
txtUserName.Text = txtUserName.AutoCompleteCustomSource[i];
txtPassword.Focus();
return;
}
}
}
if (txtPassword.Text.Length > 0)
{
btnLogin_Click(null, null); //login
}
else
{
//MessageBox.Show("Please Give a Password");
txtPassword.Focus();
}
}
else
{
//MessageBox.Show("Please Give a username");
txtUserName.Focus();
}
}
//if (txtPassword.ContainsFocus)
//{
// btnLogin_Click(sender, e); //login
//}
//else
//{
// this.txtPassword.Focus();
//}
}