Comparar eventos de teclado con strings (C#)

Iniciado por Alejovsq, 7 Agosto 2019, 04:23 AM

0 Miembros y 1 Visitante están viendo este tema.

Alejovsq

Saludos, amigos!

Estoy intentando realizar un pequeño programa que genera letras aleatorias y con el teclado debo presionar las teclas correspondientes. Esto debe hacerse en un rango de tres (3) segundos, si no, la letra cambiará por otra.

Si la tecla presionada es igual a la letra generada, el texto debe cambiar a color verde, de lo contrario debe cambiar a color rojo.

El problema es que no se capturar muy bien los eventos del teclado para compararlos.


public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

//tmr.Temporizador.Start();

         private void tmrTemporizador_Tick(object sender, EventArgs e)
        {
            string letra= GeneradorLetra();
            lblEtiqueta.Text = letra;   //---------- Cómo llamar un evento de teclado desde aquí, para luego compararlo?
           
        }

        private string GeneradorLetra()
        {
            Random numero = new Random();
            int i = numero.Next(0, 7);

            string[] cadenaDeLetras = { "A", "B", "C", "D", "E", "F", "G" };

            return cadenaDeLetras[i];
        }
    }
}


Alejovsq

Empecé nuevamente desde cero, quité el ¨Timer¨para ir resolviendo el problema de a poco.

Así va quedando el nuevo código...

Código (csharp) [Seleccionar]

public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void btnIniciar_Click(object sender, EventArgs e)
        {
            letra = GeneradorLetra();
            lblEtiqueta.Text = letra;

            /*
             * Como puedo llamar al metodo Form1_Keypress(object, KeyPressEventArgs) desde aqui??
             * Me pide dos argumentos y no se como pasarlos desde este punto.
             */
        }

        private string GeneradorLetra()
        {
            Random aleatorio = new Random();
            int i = aleatorio.Next(0, 7);
            string[] seleccion = { "A", "B", "C", "D", "E", "F", "G" };

            return seleccion[i];
        }

        private void Form1_KeyPress(object sender, KeyPressEventArgs e) //Aun sin funcionar
        {
           
            string teclaOprimida = e.KeyChar.ToString();

            if (letra == teclaOprimida)
            {
                lblEtiqueta.ForeColor = Color.Green;
            }
            else
            {
                lblEtiqueta.ForeColor = Color.Red;
            }
        }

        string letra;
    }


Me gustaría llamar al método Form1_KeyPress() desde el método btnIniciar_Click(), no entiendo como podría pasar los argumentos (creo que se les denomina de ¨Control¨) desde ese punto.  Supongo que la solución sería parecida para implementar los métodos del Timer.

Eleкtro

#2
Cita de: Alejovsq en  7 Agosto 2019, 22:06 PM
Me gustaría llamar al método Form1_KeyPress() desde el método btnIniciar_Click(), no entiendo como podría pasar los argumentos (creo que se les denomina de ¨Control¨) desde ese punto.

Hacer eso carecería de sentido, puesto que estarías llamando a un controlador de evento (método Form1_KeyPress) con datos de evento (clase KeyPressEventArgs) artificiales. Dicho de otra forma, estarías simulando una pulsación del teclado artificial en el Form, y no creo que realmente desees hacer eso...

Cita de: Alejovsq en  7 Agosto 2019, 22:06 PM
Supongo que la solución sería parecida para implementar los métodos del Timer.

En realidad, la solución óptima y sofisticada sería utilizar la clase/componente BackgroundWorker para aplicar el método de invocación asincrónica en los controles de la interfaz de usuario. Pero como te veo con poca experiencia, y como tampoco se si esto que pides es para un trabajo de clase donde te pidan aplicar cosas más basicas, pues aquí tienes un ejemplo donde utilizo prácticas más básicas:



Código (csharp) [Seleccionar]
using System;
using System.Collections.Generic;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.Linq;
using System.Threading;
using System.Windows.Forms;

namespace WindowsFormsApp1 {

    public partial class Form1 : Form {

        private Random RNG = new Random(Environment.TickCount);

        private char nextChar;

        private bool isSuccess;
        private int successCount;
        private int failCount;

        public Form1() {
           this.InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e) {
            this.KeyPreview = true;
        }
       
        private void btnIniciar_Click(object sender, EventArgs e) {

            this.Reset();

            Stopwatch watch = new Stopwatch();
            watch.Start();

            while (watch.Elapsed <= TimeSpan.FromSeconds(3)) {
                this.nextChar = this.GeneradorLetra("ABCDEFG".ToCharArray(), this.nextChar);
                this.lblEtiqueta.Text = this.nextChar.ToString();

                while (!((this.isSuccess) || (watch.Elapsed >= TimeSpan.FromSeconds(3)))) {
                    this.LabelStopWatch.Text = (3 - watch.Elapsed.Seconds).ToString();
                    Application.DoEvents();
                }

                if (this.isSuccess) {
                    Thread.Sleep(500);
                    this.successCount += 1;
                } else {
                    this.failCount += 1;
                }
                this.isSuccess = false;

                this.lblEtiqueta.ForeColor = Label.DefaultForeColor;
                this.LabelSuccessful.Text = this.successCount.ToString();
                this.LabelFailed.Text = this.failCount.ToString();

                watch.Restart();
                Thread.CurrentThread.Join(0);
            }

        }

        private void Form1_KeyPress(object sender, KeyPressEventArgs e) {

            if (!(this.isSuccess) && (char.ToLower(this.nextChar) == char.ToLower(e.KeyChar))) {
                this.lblEtiqueta.ForeColor = Color.Green;
                this.isSuccess = true;

            } else {
                this.lblEtiqueta.ForeColor = Color.Red;

            }

        }

        private char GeneradorLetra(char[] charSet, char previousChar) {
            IEnumerable<char> tmpCol = from char c in charSet
                                       where (c != previousChar)
                                       select c;
            return tmpCol.ElementAtOrDefault(this.RNG.Next(0, tmpCol.Count()));
        }

        private void Reset() {
            this.successCount = 0;
            this.failCount = 0;
            this.LabelSuccessful.Text = this.successCount.ToString();
            this.LabelFailed.Text = this.failCount.ToString();
        }
       
    }
}


Código original escrito en VB.NET:
Código (vbnet) [Seleccionar]
Imports System.Threading

Public Class Form1

   Private RNG As New Random(Environment.TickCount)

   Private nextChar As Char

   Private isSuccess As Boolean
   Private successCount As Integer
   Private failCount As Integer

   Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
       Me.KeyPreview = True
   End Sub

   Private Sub btnIniciar_Click(ByVal sender As Object, ByVal e As EventArgs) Handles btnIniciar.Click

       Me.Reset()

       Dim watch As New Stopwatch()
       watch.Start()

       Do While (watch.Elapsed <= TimeSpan.FromSeconds(3))
           Me.nextChar = Me.GeneradorLetra("ABCDEFG".ToCharArray(), Me.nextChar)
           Me.lblEtiqueta.Text = Me.nextChar

           Do Until (Me.isSuccess) OrElse (watch.Elapsed >= TimeSpan.FromSeconds(3))
               Me.LabelStopWatch.Text = CStr(3 - watch.Elapsed.Seconds)
               Application.DoEvents()
           Loop

           If (Me.isSuccess) Then
               Thread.Sleep(500)
               Me.successCount += 1
           Else
               Me.failCount += 1
           End If
           Me.isSuccess = False

           Me.lblEtiqueta.ForeColor = Label.DefaultForeColor
           Me.LabelSuccessful.Text = CStr(Me.successCount)
           Me.LabelFailed.Text = CStr(Me.failCount)

           watch.Restart()
           Thread.CurrentThread.Join(0)
       Loop

   End Sub

   Private Sub Form1_KeyPress(ByVal sender As Object, ByVal e As KeyPressEventArgs) Handles Me.KeyPress

       If Not (Me.isSuccess) AndAlso (Char.ToLower(Me.nextChar) = Char.ToLower(e.KeyChar)) Then
           Me.lblEtiqueta.ForeColor = Color.Green
           Me.isSuccess = True

       Else
           Me.lblEtiqueta.ForeColor = Color.Red

       End If

   End Sub

   Private Function GeneradorLetra(charSet As Char(), previousChar As Char) As Char
       Dim tmpCol As IEnumerable(Of Char) = From c As Char In charSet Where (c <> previousChar)
       Return tmpCol(Me.RNG.Next(0, tmpCol.Count))
   End Function

   Private Sub Reset()

       Me.successCount = 0
       Me.failCount = 0
       Me.LabelSuccessful.Text = CStr(Me.successCount)
       Me.LabelFailed.Text = CStr(Me.failCount)

   End Sub

End Class


Puedes trasladar parte del código a un Timer si lo prefieres para evitar el búcle o llamada bloqueante junto a la llamada Application.DoEvents(), pero para ponerte a usar un Timer sería más eficiente que apliques el uso de la clase BackgroundWorker como ya mencioné.

Caracterísiticas del código/programa de arriba:
- Cuando el usuario presiona la tecla que imprime el caracter correcto, se visualiza en verde durante 500 ms y seguidamente se muestra el siguiente caracter.
- Se considera un fallo solamente si la cuenta atrás llega a 0 sin que el usuario haya conseguido presionar la tecla que imprime el caracter correcto, indiferentemente de la cantidad de fallos que el usuario haya tenido durante la cuenta atrás. Dicho de otra forma, se permiten fallos "infinitos" de pulsación de tecla durante la cuenta atrás.
- La aleatoriedad de muestra de caracteres consiste en que el caracter actual nunca se repetirá en la siguiente "prueba", pero si que podrá volver a mostrarse en las "pruebas" posteriores a esta.

Todo esto es así, por que tampoco especificaste como se supone que debe funcionar exactamente el programa. Pero ya con ese código puedes modificarlo para tus requisitos.

Saludos.








Alejovsq

Es exactamente el funcionamiento que deseaba!!

No es para un trabajo de clase. Soy principiante y son ideas propias para ir practicando lo que ya he aprendido del lenguaje.

Tu respuesta ha sido excelente, tengo muchas cosas que estudiar y aprender de ella. Investigaré sobre la clase/componente BackgroundWorker.

Muchas gracias, Elektro!!!