Personalización de la programación de control VB.NET para interceptar pulsaciones de teclas
1. Introducción
En primer lugar, este artículo asume que ya está familiarizado con Visual
Diseñador de formularios de Windows.
Al desarrollar controles personalizados de Windows Forms, suele ser muy conveniente proporcionar nuestro propio editor de tipo de cuadro desplegable para manipular las propiedades del control. Un editor de tipos personalizado no solo proporciona una experiencia más rica en el momento del diseño, sino que también puede ser el factor decisivo para determinar si a los usuarios les gusta su control.
Si decide crear su propio editor de tipo desplegable, debe seguir un patrón similar al editor de tipo desplegable integrado. Tomemos la propiedad Anchor como ejemplo. Una interacción típica del usuario en tiempo de diseño para manipular este atributo se describe a continuación:
· El usuario selecciona el atributo Anchor en la cuadrícula de atributos y hace clic en el botón desplegable en el lado derecho de la cuadrícula de atributos.
· Un buen control gráfico es un cuadro desplegable que permite al usuario hacer clic en un borde con el mouse o usar las teclas de flecha para resaltar un borde y seleccionarlo/deseleccionarlo usando la barra espaciadora.
· El usuario puede recibir cambios presionando la tecla ENTER o haciendo clic fuera del control desplegable. Para cancelar este cambio, el usuario puede presionar la tecla ESC.
Ahora, analicemos la tecnología de implementación específica.
2. Implementación
Primero, construyamos un editor de tipo ResourceImageEditor que permita seleccionar un archivo de imagen del sistema de archivos actual (al igual que la clase ImageEditor incorporada) o desde Select. un recurso de imagen del archivo de manifiesto de un ensamblado. Además, en términos de experiencia de usuario, ResourceImageEditor debería comportarse de manera similar al editor de tipos integrado en el sistema. Aquí hay una descripción general de nuestros requisitos:
1. Cuando el usuario selecciona un atributo de la cuadrícula de atributos, se mostrará la cuadrícula, mostrando los atributos editables en forma de una interfaz de usuario de cuadro desplegable.
2. Cuando se hace clic en el botón desplegable, se mostrarán todos los recursos de imagen en el ensamblaje actual.
3. Cuando el usuario selecciona un elemento de recurso de imagen, la imagen correspondiente se puede cargar desde el ensamblaje.
4. Permite la selección de un archivo de imagen y el último elemento del cuadro de lista desplegable tendrá la etiqueta "Examinar...". Cuando el usuario hace clic en el elemento "Examinar...", se mostrará el clásico cuadro de diálogo de abrir archivo, desde el cual el usuario puede seleccionar un archivo de imagen.
5. Permita que el usuario seleccione un elemento de este cuadro de lista desplegable haciendo clic con el mouse o usando las teclas de flecha para resaltar un elemento y presionando la tecla Enter para seleccionarlo. Esta selección desplegable se puede cancelar presionando la tecla ESC.
ResourceImageEditor es un editor de tipos, por lo que se deriva directa o indirectamente de la clase System.Drawing.Design.UITypeEditor. Decidí derivar de la clase incorporada System.Drawing.Design.ImageEditor porque ya implementa la funcionalidad de selección de archivos de imagen. Es decir, la implementación ImageEditor.EditValue mostrará un cuadro de diálogo de apertura de archivo para permitir al usuario seleccionar un archivo de imagen del sistema de archivos. Luego, llamar a esta funcionalidad desde mi clase derivada simplemente llama a MyBase.EditValue.
Para cumplir con el primer requisito anterior (mostrar un botón de flecha desplegable en el panel de propiedades), debo anular el método GetEditStyle para devolver las constantes apropiadas de la enumeración UITypeEditorEditStyle:
Las sobrecargas públicas anulan la función GetEditStyle( _
Contexto ByVal como ITypeDescriptorContext) como UITypeEditorEditStyle
Devolver UITypeEditorEditStyle.DropDown
Finalizar función
Para mostrar la lista de recursos de imágenes, tengo que enumerar todos los recursos en un conjunto determinado y mostrar solo los recursos de imágenes en la lista. Para simplificar, decidí usar una convención simple: cuando el nombre de un recurso termina con una extensión de archivo de imagen válida (.bmp, .jpg, .gif...), consideramos que es un recurso de imagen y lo incluimos en el menú desplegable. cuadro de lista desplegable. Además, completo este control ListBox desplegable con una colección de nombres de recursos de imágenes, como se detalla a continuación.
Inicialmente, el ensamblado que se enumera para consultar recursos de imágenes es el ensamblado que contiene la clase ResourceImageEditor. Sin embargo, podemos cambiar esto configurando la propiedad ResourceImageEditor.ResourceAssembly en cualquier referencia System.Reflection.Assembly válida.
Cuando el usuario selecciona un nombre de recurso de imagen del cuadro de lista, la imagen debe cargarse desde el archivo de manifiesto en el ensamblaje dado. Esto se implementa dentro del método LoadResourceImage:
Función privada LoadResourceImage(ByVal ResourceName As String) As Image
Debug.Assert(Not ResourceName Is Nothing)
Dim ImageStream As System.IO.Stream = Me.ResourceAssembly.GetManifestResourceStream(resourceName)
Devolver System.Drawing.Bitmap.FromStream(ImageStream)
Función final
La interfaz de usuario desplegable se implementa creando y completando dinámicamente un control ListBox dentro del método EditValue sobrecargado. El editor también maneja los eventos Click y KeyDown generados por ListBox, ya que esto es necesario para interceptar las teclas ENTER y ESC. El siguiente pseudocódigo muestra la lógica de implementación en el método EditValue:
Sobrecargas públicas anula la función EditValue(...)
' Almacena información contextual para usar en el controlador de eventos ListBox desplegable .
'Cree y complete este ListBox con nombres de recursos de imágenes disponibles.
'Agregue nuestro elemento especial "Examinar...".
'Enlazar evento ListBox.
'Muestra el ListBox en una ventana desplegable.
Función final
3. Varios problemas y soluciones clave
Para desarrollar ResourceImageEditor, creé un MyPictureBox (derivado de System) que sobrecarga el atributo Imagen. .Windows.Forms.PictureBox) para especificar ResourceImageEditor como editor de tipos para la propiedad Imagen. Luego, compilo el código para este control. Luego, puede colocar el control MyPictureBox en un
formulario e invocar la interfaz de usuario desplegable...
La interfaz del mouse funciona bien. Sin embargo, cuando selecciono un elemento usando el teclado y luego presiono Enter, el cuadro desplegable desaparece y mi selección se pierde (es decir, la imagen de selección anterior no ha cambiado). Rápidamente descubrí que ListBox no generaba un evento KeyDown cuando se presionaba la tecla Enter.
Aunque la tecla ESC también genera un evento KeyDown, esto no es un problema porque el cuadro de lista desplegable se cierra automáticamente y no tengo que lidiar con la selección actual.
Obviamente, este cuadro de atributos "protege" las teclas ENTER y ESC antes de que el control ListBox pueda procesarlas.
Para simplificar y solucionar el problema, voy a utilizar el método ProcessDialogKey. Este método se llama durante el preprocesamiento del mensaje (procesamiento de caracteres de diálogo como TAB, RETURN, ESCAPE y teclas de flecha). Este método se declara dentro de la clase System.Windows.Forms.Control; simplemente delega la llamada al padre del control (si lo hay). He subclasificado el control ListBox y sobrecargué el método ProcessDialogKey para interceptar la tecla Enter de la siguiente manera:
Función de anulación protegida ProcessDialogKey(ByVal keyData As Keys) Como booleano
If keyData = System. Windows.Forms.Keys.Return Then
RaiseEvent EntERPressed(Me, EventArgs.Empty)
Return True 'True significa que hemos procesado la clave correspondiente
Else
Devolver MyBase.ProcessDialogKey(keyData)
Finalizar si
Finalizar función
No de ProcessDialogKey Para implementar la generación interna de KeyDown evento, decidí utilizar un método más directo: el evento EnterPressed. Para hacer esto, modifiqué la implementación de ResourceImageEditor.EditValue para manejar este evento (en lugar del evento KeyDown) y todo funcionó perfectamente.
Puedes usar esta técnica para interceptar la tecla ENTER en cualquier clase derivada de Control
que uses para implementar la interfaz de usuario desplegable en tu editor de tipos.