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...
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:
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:
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.