Pues eso… que hace poco mandé al servicio técnico de LG la pantalla externa que conecto a mi ordenador portátil y estoy teniendo problemas cuando abro las aplicaciones que he estado usando con ese monitor, al menos las que tengo programadas para que al cambiar la posición o el tamaño se guardan en un fichero de configuración para cuando cargue de nuevo esa aplicación se muestre donde estuvo la vez anterior.
Una de las opciones por la que opté fue poner que siempre se mostrasen en el centro de la pantalla, pero con el tamaño indicado en el diseño del formulario.
La idea que se me ha ocurrido esta mañana es la de comprobar dónde está la posición Left y Top guardadas y si estarían fuera del rango de esos mismos valores de las propiedades de el WorkingArea de PrimaryScreen.
Los valores de alto y ancho (Height y Width) no los toco, ya que el usuario podrá cambiar esos tamaños a su antojo, ya que ahora sí que podrá ver la ventana de la aplicación.
Este es el código para Visual Basic con el que hago la comprobación que te he mencionado:
' Asignar el tamaño y última posición
' Comprobar que esté en la parte visible (24/Oct/20)
Dim l = cfg.GetValue("Ventana", "Left", Me.Left)
Dim t = cfg.GetValue("Ventana", "Top", Me.Top)
IfScreen.PrimaryScreen.WorkingArea.Left < l Then
Me.Left = cfg.GetValue("Ventana", "Left", Me.Left)
Else
Me.Left = 0
EndIf
IfScreen.PrimaryScreen.WorkingArea.Top < t Then
Me.Top = cfg.GetValue("Ventana", "Top", Me.Top)
Else
Me.Top = 0
EndIf
Me.Height = cfg.GetValue("Ventana", "Height", Me.Height)
Me.Width = cfg.GetValue("Ventana", "Width", Me.Width)
Los valores de la posición y tamaño del formulario los obtengo de un fichero de configuración y solo asigno el valor Left si la posición Left de WorkingArea es menor, ya que cuando está en el monitor externo (al menos en mi caso) el valor de Left suele ser negativo. Y con el valor Top hacemos lo mismo, solo asignarlo si no es menor que el valor Top del área de trabajo de la pantalla principal.
Y aquí tienes el mismo código para C#:
// Asignar el tamaño y última posición
// Comprobar que esté en la parte visible (24/Oct/20)
var l = cfg.GetValue("Ventana", "Left", this.Left);
var t = cfg.GetValue("Ventana", "Top", this.Top);
if (Screen.PrimaryScreen.WorkingArea.Left < l)
this.Left = cfg.GetValue("Ventana", "Left", this.Left);
else
this.Left = 0;
if (Screen.PrimaryScreen.WorkingArea.Top < t)
this.Top = cfg.GetValue("Ventana", "Top", this.Top);
else
this.Top = 0;
this.Height = cfg.GetValue("Ventana", "Height", this.Height);
this.Width = cfg.GetValue("Ventana", "Width", this.Width);
Y esto es todo, espero que te haya sido de utilidad.
Pues eso… necesitaba saber cómo detectar varias pulsaciones de teclas al estilo de Ctrl+K, Ctrl+C y similares, es decir, se pulsa la tecla Control seguida de la K y se pulsa la tecla Control seguida de la C (como la combinación de Visual Studio para poner comentarios en la selección que haya en el código). Así que… busqué en internet, pero… había ejemplos muy enrevesados… con temporizadores y demás monadas… así que… basándome en algunos ejemplos (seguramente del mismo autor o copiados unos de otros) he hecho algo que puede servir… al menos a mí me sirve, aunque se puede mejorar, como todo.
De la forma que lo he hecho da igual si se pulsa primero Ctrl+K que Ctrl+C, ya que lo que he intentado es que se sepa cuando se han hecho esas pulsaciones, y si entre cualquiera de las dos pulsaciones se ha pulsado otra tecla, no se tiene en cuenta esa combinación. Es decir si quieres detectar Ctrl+K seguida de Ctrl+C (que para el caso del código que te mostraré es lo mismo que si pulsas Ctrl+C seguida de Ctrl+K) pulsas otra tecla o combinación de teclas, no se dará como detectada esa doble pulsación.
Nota: Precisamente con esas teclas: Ctrl+C y otras automatizadas de edición: Ctrl+V, Ctrl+X, Ctrl+P, etc., habría que tener cuidado o hacerle un seguimiento distinto al que ahora hago para que no la detecte y, por ejemplo pegue el texto si es Ctrl+P).
¿Dónde se hará la comprobación de la tecla pulsada?
Las comprobaciones de qué tecla se está pulsando (o se ha pulsado) la hago en el evento KeyDown del formulario. Y como de forma predeterminada el formulario no intercepta las pulsaciones de las teclas, habrá que hacer una asignación de un valor verdadero (true) a la propiedad KeyPreview del formulario. Eso lo he puesto en el evento Load, con idea de que esté activado si por casualidad cambio el valor en el diseñador (esas cosas suelen ocurrir, y es complicado de saber porqué antes funcionaba y después no).
En la figura 1 tienes una captura del código de ejemplo en funcionamiento (en ese caso la aplicación de C# creada con .NET Framework 4.8).
Figura 1. La aplicación de ejemplo en funcionamiento.
¿Cómo saber si hay varias combinaciones de teclas?
Lo que yo he hecho es crear unas variables para asignarles un valor si la combinación que se quiere detectar se cumple. Esas variables (o campos) definidas a nivel de la clase, las he declarado de tipo entero, (en los ejemplos que vi en la web eran de tipo Boolean, bool en C#), ya que lo que me interesa es saber si algunas de las combinaciones se ha hecho más de una vez, ese es el caso de Ctrl+K, Ctrl+K, es decir: pulsar dos veces la tecla Control y la tecla K.
Si no quieres comprobar si hay una combinación que se haga más de una vez, lo mismo puedes usar variables de tipo Bolean (bool en C#), eso ya a tu discreción (o preferencia).
Aquí te muestro el código con la definición de esas variables (tanto para VB como para C#):
' Para doble pulsación de teclasPrivate CtrlK AsIntegerPrivate CtrlC AsIntegerPrivate CtrlU AsIntegerPrivate CtrlL AsIntegerPrivate ShiftAltL AsIntegerPrivate ShiftAltS AsInteger
// Para doble pulsación de teclasprivateint CtrlK;
privateint CtrlC;
privateint CtrlU;
privateint CtrlL;
privateint ShiftAltL;
privateint ShiftAltS;
Esas variables las usaremos en el evento KeyDown del formulario, incrementando el valor cuando se cumpla que se han pulsado las teclas indicadas, por ejemplo si queremos detectar la pulsación de Ctrl+K, tendremos que incrementar el valor de la variable CtrlK, ídem con el resto.
Como en el ejemplo hay varias combinaciones de teclas que detectar, puede ser un poco largo de ver, pero prefiero que lo veas completo para que no te líes demasiado. Ahí se comprueban las tres posibles teclas de «control«, es decir, Control, Shift y Alt. También hago comprobaciones para que, por ejemplo, si queremos detectar Ctrl+Shift se haga en un bloque de código diferente para cuando se detecta, por ejemplo Ctrl+Alt o Shift+Alt. Creo que el código está bastante claro y no tendrás complicaciones de ver el proceso que se hace.
Aquí tienes el código de VB y C#.
PrivateSubForm1_KeyDown(senderAsObject, e AsKeyEventArgs) HandlesMe.KeyDown' Comprobaciones para Ctrl+Shift' esta de forma simpleIf e.ControlAndAlso e.ShiftThenIf e.KeyCode = Keys.V Then
e.Handled = True'MostrarRecortes();txtPulsadas.Text = "Capturada: Ctrl+Shift+V" & vbCrLf & txtPulsadas.TextEndIf' Estas son con varias combinaciones' Comprobaciones para Shift+AltElseIf e.ShiftAndAlso e.AltThen' si se ha pulsado Shift+Alt+LIf e.KeyCode = Keys.L Then
e.Handled = True
ShiftAltL += 1
' si se ha pulsado Shift+Alt+SElseIf e.KeyCode = Keys.S Then
e.Handled = True
ShiftAltS += 1
EndIf' Si se ha pulsado Shitf+Alt+S, Shift+Alt+L' (en cualquier orden)If ShiftAltL = 1 AndAlso ShiftAltS = 1 Then
e.Handled = True'ClasificarSeleccion();txtPulsadas.Text = "Capturada: Shift+Alt+L, Shift+Alt+S" & vbCrLf & txtPulsadas.TextEndIf' Comprobaciones para solo la tecla Ctrl (sin Shift ni Alt)ElseIf e.ControlAndAlsoNot e.ShiftAndAlsoNot e.AltThen' Solo se ha pulsado la tecla Ctrl' comprobar el resto de combinaciones' Forma simple si se ha pulsado Ctrl+BIf e.KeyCode = Keys.B Then
e.Handled = True' Esta solo es para detectar' la combinación 'simple' de Ctrl+B' No es necesario llevar la cuenta de las pulsacionestxtPulsadas.Text = "Capturada: Ctrl+B" & vbCrLf & txtPulsadas.TextElseIf e.KeyCode = Keys.K Then
e.Handled = True
CtrlK += 1
txtPulsadas.Text = "Ctrl+K - " & txtPulsadas.TextElseIf e.KeyCode = Keys.C Then
e.Handled = True
CtrlC += 1
txtPulsadas.Text = "Ctrl+C - " & txtPulsadas.TextElseIf e.KeyCode = Keys.U Then
e.Handled = True
CtrlU += 1
txtPulsadas.Text = "Ctrl+U - " & txtPulsadas.TextElseIf e.KeyCode = Keys.L Then
e.Handled = True
CtrlL += 1
txtPulsadas.Text = "Ctrl+L - " & txtPulsadas.TextEndIf' Si se ha pulsado Ctrl+K, CtrlCIf CtrlK = 1 AndAlso CtrlC = 1 Then
e.Handled = True' Ctrl+K, Ctrl+C
CtrlK = 0
CtrlC = 0
'PonerComentarios(richTextBoxCodigo);txtPulsadas.Text = "Capturada: Ctrl+K, Ctrl+C" & vbCrLf & txtPulsadas.Text' Si se ha pulsado Ctrl+K, Ctrl+UElseIf CtrlK = 1 AndAlso CtrlU = 1 Then
e.Handled = True' Ctrl+K, Ctrl+U
CtrlK = 0
CtrlU = 0
'QuitarComentarios(richTextBoxCodigo);txtPulsadas.Text = "Capturada: Ctrl+K, Ctrl+U" & vbCrLf & txtPulsadas.Text' Si se ha pulsado Ctrl+K, Ctrl+LElseIf CtrlK = 1 AndAlso CtrlL = 1 Then
e.Handled = True' Ctrl+K, Ctrl+L
CtrlK = 0
CtrlL = 0
' preguntar'buttonEditorMarcadorQuitarTodos.PerformClick();txtPulsadas.Text = "Capturada: Ctrl+K, Ctrl+L" & vbCrLf & txtPulsadas.Text' Si se ha pulsado Ctrl+K, Ctrl+KElseIf CtrlK = 2 Then
e.Handled = True' Ctrl+K, Ctrl+K
CtrlK = 0
'MarcadorPonerQuitar();txtPulsadas.Text = "Capturada: Ctrl+K, Ctrl+K" & vbCrLf & txtPulsadas.TextEndIfElsetxtPulsadas.Text = $"{vbCrLf}No es una de las teclas comprobadas: {e.KeyCode} +{vbCrLf}" &
$" Ctrl: {e.Control}, Shift: {e.Shift}, Alt: {e.Alt}{vbCrLf}" & txtPulsadas.Text
CtrlK = 0
CtrlC = 0
CtrlU = 0
ShiftAltL = 0
ShiftAltS = 0
' Otras pulsaciones' No están detectadas explícitamenteEndIfEndSub
privatevoidForm1_KeyDown(objectsender, KeyEventArgs e)
{
// Comprobaciones para Ctrl+Shift// esta de forma simpleif (e.Control && e.Shift)
{
if (e.KeyCode == Keys.V)
{
e.Handled = true;
//MostrarRecortes();txtPulsadas.Text = "Capturada: Ctrl+Shift+V\r\n" + txtPulsadas.Text;
}
}
// Estas son con varias combinaciones// Comprobaciones para Shift+Altelseif (e.Shift && e.Alt)
{
// si se ha pulsado Shift+Alt+Lif (e.KeyCode == Keys.L)
{
e.Handled = true;
ShiftAltL += 1;
}
// si se ha pulsado Shift+Alt+Selseif (e.KeyCode == Keys.S)
{
e.Handled = true;
ShiftAltS += 1;
}
// Si se ha pulsado Shitf+Alt+S, Shift+Alt+L// (en cualquier orden)if (ShiftAltL == 1 && ShiftAltS == 1)
{
e.Handled = true;
//ClasificarSeleccion();txtPulsadas.Text = "Capturada: Shift+Alt+L, Shift+Alt+S\r\n" + txtPulsadas.Text;
}
}
// Comprobaciones para solo la tecla Ctrl (sin Shift ni Alt)elseif (e.Control && !e.Shift && !e.Alt)
{
// Solo se ha pulsado la tecla Ctrl// comprobar el resto de combinaciones// Forma simple si se ha pulsado Ctrl+Bif (e.KeyCode == Keys.B)
{
e.Handled = true;
// Esta solo es para detectar// la combinación 'simple' de Ctrl+B// No es necesario llevar la cuenta de las pulsacionestxtPulsadas.Text = "Capturada: Ctrl+B\r\n" + txtPulsadas.Text;
}
elseif (e.KeyCode == Keys.K)
{
e.Handled = true;
CtrlK += 1;
txtPulsadas.Text = "Ctrl+K - " + txtPulsadas.Text;
}
elseif (e.KeyCode == Keys.C)
{
e.Handled = true;
CtrlC += 1;
txtPulsadas.Text = "Ctrl+C - " + txtPulsadas.Text;
}
elseif (e.KeyCode == Keys.U)
{
e.Handled = true;
CtrlU += 1;
txtPulsadas.Text = "Ctrl+U - " + txtPulsadas.Text;
}
elseif (e.KeyCode == Keys.L)
{
e.Handled = true;
CtrlL += 1;
txtPulsadas.Text = "Ctrl+L - " + txtPulsadas.Text;
}
// Si se ha pulsado Ctrl+K, CtrlCif (CtrlK == 1 && CtrlC == 1)
{
e.Handled = true;
// Ctrl+K, Ctrl+C
CtrlK = 0;
CtrlC = 0;
//PonerComentarios(richTextBoxCodigo);txtPulsadas.Text = "Capturada: Ctrl+K, Ctrl+C\r\n" + txtPulsadas.Text;
}
// Si se ha pulsado Ctrl+K, Ctrl+Uelseif (CtrlK == 1 && CtrlU == 1)
{
e.Handled = true;
// Ctrl+K, Ctrl+U
CtrlK = 0;
CtrlU = 0;
//QuitarComentarios(richTextBoxCodigo);txtPulsadas.Text = "Capturada: Ctrl+K, Ctrl+U\r\n" + txtPulsadas.Text;
}
// Si se ha pulsado Ctrl+K, Ctrl+Lelseif (CtrlK == 1 && CtrlL == 1)
{
e.Handled = true;
// Ctrl+K, Ctrl+L
CtrlK = 0;
CtrlL = 0;
// preguntar//buttonEditorMarcadorQuitarTodos.PerformClick();txtPulsadas.Text = "Capturada: Ctrl+K, Ctrl+L\r\n" + txtPulsadas.Text;
}
// Si se ha pulsado Ctrl+K, Ctrl+Kelseif (CtrlK == 2)
{
e.Handled = true;
// Ctrl+K, Ctrl+K
CtrlK = 0;
//MarcadorPonerQuitar();txtPulsadas.Text = "Capturada: Ctrl+K, Ctrl+K\r\n" + txtPulsadas.Text;
}
}
else
{
txtPulsadas.Text = $"\r\nNo es una de las teclas comprobadas: {e.KeyCode} +\r\n"+
$" Ctrl: {e.Control}, Shift: {e.Shift}, Alt: {e.Alt}\r\n" + txtPulsadas.Text;
CtrlK = 0;
CtrlC = 0;
CtrlU = 0;
ShiftAltL = 0;
ShiftAltS = 0;
// Otras pulsaciones// No están detectadas explícitamente
}
}
Nota: En el código están comentadas las funciones / métodos a los que en el programa que uso esta forma de controlar las pulsaciones (múltiples) de teclas llama cuando se produce una. Así sabrás cuándo tienes que actuar cuando se produzca la pulsación esperada.
Una aclaración sobre la diferencia entre KeyCode y KeyValue
Hay gente que no se aclara entre los valores de esas dos propiedades del argumento KeyEventArgs del evento KeyDown (o KeyUp).
KeyCode contiene el código de la tecla pulsada y el del tipo Keys (enumeración). KeyValue contiene el código de la tecla pulsada y el de tipo Integer (int en C#).
En Visual Basic se puede usar indistintamente sin hacer nada especial, es decir, par saber si se ha pulsado la tecla B puedes hacerlo de estas dos formas: If e.KeyCode = Keys.B Then o If e.KeyValue = Keys.B
Pero en C# no te permite hacer la comparación del valor int con un valor de la enumeración Keys. Si así lo quieres hacer, tendrías que hacer un cast al tipo entero: if (e.KeyValue == (int)Keys.B)
Por tanto, es mejor usar e.KeyCode si la intención es compararla con un valor de Keys.
Ya solo me queda ponerte el código completo de esta aplicación de prueba, pero como últimamente estoy haciendo (creo que solo lo he hecho una vez) ese código (tanto el de Visual Basic como el de C#) está en mis repositorios en GitHub, concretamente en varias-pulsaciones-de-teclas.
Dicho código está creado en un proyecto para Visual Studio 2019 usando .NET Framework 4.8, pero también es válido para aplicaciones (de WinForms) creadas para .NET Core, al menos yo lo estoy usando en .NET 5.0 RC1.
Pues eso… Si usas el texto copiado de Visual Studio para después mostrarlo coloreado en una publicación en la WEB (un blog, etc.) este truco que te explico aquí te será de gran ayuda, ya que le indica que a demás de colorear el código «normal» (instrucciones, comentarios y cadenas entrecomilladas) también copie el color de los tipos.
Esto lo utilizo con mi utilidad de colorear el código (gsColorearCodigo), concretamente usando la opción Colorear desde RTF del menú contextual de la caja de textos donde se pega el código a colorear.
Y ( lo que decía ese párrafo anterior 🙂 ) antes, al copiar texto del editor de Visual Studio, éste me contenía los colores de los tipos de datos (verde azulado), pero últimamente no lo hacía, así que… esta mañana me puse a ver las opciones del editor de textos (realmente buscando otra cosa, todo hay que decirlo) y me topé con una opción que no era muy aclaratorio de que podía ser eso lo que lo solucionara: Usar clasificación precisa. Pero al estar justo debajo de Copiar texto enriquecido al copiar o cortar, pues… ¡tuve que probarlo! 😉
Y eso es… aquí te dejo la nota «adicional» que puse en el post anterior en el que explico sobre la nueva versión de la utilidad de colorear código.
Nota adicional sobre el coloreado de Visual Studio
Comenté en el párrafo anterior que Visual Studio no pasa los colores de las clases (verde azulado) y antes lo hacía. ¿Lo habrán quitado?
No, no lo han quitado.
Y es que el que lo haga o no depende de una opción en la configuración del editor de textos de Visual Studio.
Esa opción en español es: Usar clasificación precisa. En inglés es: Use accurate classification.
Y con esos nombres… ¿Quién lo iba a saber?
Para poder cambiar (activar) esa opción lo tienes que hacer en: (ver la captura) Herramientas>Opciones>Editor de textos>Avanzadas En inglés sería: Tools>Options>Text editor>Advanced
Opción para que al copiar el código en Visual Studio coloree también las definiciones de los tipos
Fíjate que también tiene que estar seleccionada la opción Copiar texto enriquecido al copiar o cortar (Copy rich text on copy/cut).
Al pegar el texto coloreado, quedaría así:
'''<summary>
''' Devuelve la versión de la DLL.
''' Si completa es True, se devuelve también el nombre de la DLL:
''' gsColorear v 1.0.7.0 (para .NET Framework 4.7.2 revisión del dd/MMM/yyyy)
'''</summary>
PublicSharedFunction Version(Optional completa AsBoolean = False) AsString
Dim res = ""
Dim ensamblado = System.Reflection.Assembly.GetExecutingAssembly
'Dim m_fvi = System.Diagnostics.FileVersionInfo.GetVersionInfo(ensamblado.Location)
'res = $"v {m_fvi.FileVersion}"
Dim versionAttr = ensamblado.GetCustomAttributes(GetType(System.Reflection.AssemblyVersionAttribute), False)
'DirectCast(DirectCast(versionAttr, System.Reflection.AssemblyVersionAttribute())(0), System.Reflection.AssemblyVersionAt
Dim vers = If(versionAttr.Length > 0, (TryCast(versionAttr(0), System.Reflection.AssemblyVersionAttribute)).Version,
"1.0.7.0")
Dim fileVerAttr = ensamblado.GetCustomAttributes(GetType(System.Reflection.AssemblyFileVersionAttribute), False)
Dim versF = If(fileVerAttr.Length > 0, (TryCast(fileVerAttr(0), System.Reflection.AssemblyFileVersionAttribute)).Version,
"1.0.7.1")
res = $"v {vers} ({versF})"
If completa Then
Dim prodAttr = ensamblado.GetCustomAttributes(GetType(System.Reflection.AssemblyProductAttribute), False)
Dim producto = If(prodAttr.Length > 0, (TryCast(prodAttr(0), System.Reflection.AssemblyProductAttribute)).Product,
"gsColorear")
res = $"{producto}{res} (para .NET Framework 4.7.2 revisión del 09/Sep/2020)"
EndIf
Return res
EndFunction