Venia preparando esto desde hace tiempo, pero siempre aparecian algun problema de "cuentas" y se desfiguraba todo jaja
En fin, Tuve que usar threads para una aplicacion que hice, y decidi hacerlo en consola (uff... para que )
En fin, el programa hacia lo que queria que haga, peeero... necesitaba ejecutar "comandos" introducidos por el usuario, y a la vez el programa debia reportar algunos datos al usuario. Como era de suponerse hacia un ReadLine y en otro thread usaba WriteLine cuando era necesario. Ahi surgio un problema, se cruzaba todo haciendo todo ilegible al volver a leer, les pongo un ejemplo grafico:
Escenario: Un servidor de juegos... Al estilo Half-Life
Código:
Log:
Nivel 1 iniciado
esperando respuesta....
_
si yo pongo un comando ("agregar bot 1") pero en medio de la escritura entra un jugador .... se reporta como "Nuevo jugador: -xDMan-", se pone justo apenas terminamos de escribir la letra en la que estabamos, se escribe el reporte y despues nosotros seguimos escribiendo. Queda horrible!!!
Empezamos a escribir el comando:
Código:
Log:
Nivel 1 iniciado
esperando respuesta....
agregar b_
Entra el usuario, interrumpiendo nuestro comando
Código:
Log:
Nivel 1 iniciado
esperando respuesta....
agregar bNuevo jugador: -xDMan-_
Si seguimos escribiendo (supongamos que no nos dimos cuenta que salio eso)
Código:
Log:
Nivel 1 iniciado
esperando respuesta....
agregar bNuevo jugador: -xDMan-ot 1_
y el ReadLine devuelve bien: agregar bot 1
pero cuando lees la consola directa, es total mente ilegible, todo mezclado
y aca idee los metodos necesarios para resolverlo
Son dos funciones que reemplazan a Console.WriteLine y Console.ReadLine
ReadLine: es muy similar al Console.ReadLine, pero tiene un buffer en el que se guarda el string escrito
WriteLine: lo que hace es "borrar" de la consola lo escrito y poner lo que se quiere introducir, y luego abajo poner exactamente lo que habiamos puesto (desde el buffer ) y todo haciendo parecer que no paso nada.
Usando mis metodos ReadLine y WriteLine, quedaria asi
Empezamos a escribir el comando:
Código:
Log:
Nivel 1 iniciado
esperando respuesta....
agregar b_
Entra el usuario, interrumpiendo nuestro comando
Código:
Log:
Nivel 1 iniciado
esperando respuesta....
Nuevo jugador: -xDMan-
agregar b_
seguimos escribiendo como si nada hubiera pasado
Código:
Log:
Nivel 1 iniciado
esperando respuesta....
Nuevo jugador: -xDMan-
agregar bot 1_
y el ReadLine devuelve bien (como era de esperarse): agregar bot 1
Como les dije, es una consola al estilo quake (porque en quake se vio la primera consola asi, de ahi el nombre)
Sin mas, aca esta el codigo, A mas de uno le puede servir
(no se necesita ninguna referencia, nada, esta todo listo para servir )
Código
// Si uno hace un WriteLine mientras se espera un ReadLine pasa esto: // "probando // Si antes de que ponga la ultima comilla pongo un Console.WriteLine("Hola") queda asi // "progandoHola // " // // La siguentes funciones estan hechas para evitar eso // By Raul338 (proximamente: raul338.com.ar) static string buffer = ""; /// <summary>Escribe un texto sin "cortar" o interrumpir un Console.ReadLine()</summary> /// <param name="Text">Texto a escribir</param> static void WriteLine(string Text) { // Guarda la ubicacion actual (la linea se le suma 1, porque es la que sigue) int lastLeftPos = Console.CursorLeft; int lastTopPos = Console.CursorTop +1; // Lineas de lo que se va a escribir y lo escrito a mano int lineas = ObtenerLineas(Text); int lineasEscritas = ObtenerLineas(buffer); // Esto equilibra algunas cuentas if (lastTopPos != 0) if (lineasEscritas < 1) lastTopPos -= 1; // Ponemos el cursor al principio de la linea que empezamos a escribir y borramos lo escrito Console.SetCursorPosition(0, lastTopPos - lineasEscritas); Console.Write("".PadLeft(buffer.Length)); // Aca vaciamos todo :P Console.SetCursorPosition(0, lastTopPos - lineasEscritas); // Escribimos el texto Console.WriteLine(Text); // Ponemos en el cursor en el nuevo lugar (lugarAnterior + las nuevas lineas) Console.SetCursorPosition(0, lastTopPos + lineas - lineasEscritas); // Ponemos lo que el usuario habia escrito anteriormente Console.Write(buffer); } /// <summary>Obtienes las lineas que ocupa el texto introducido en consola</summary> /// <param name="input">Texto a contar las lineas</param> /// <returns>Lineas que ocupa en consola</returns> /// <!-- By Raul338 --> static int ObtenerLineas(string input) { int lineas = 0; // Separamos el texto en lineas System.Text.RegularExpressions.Match matchResult = System.Text.RegularExpressions.Regex.Match(input, @"[^\r\n]*(?:\r\n)?"); while (matchResult.Success) { // Por alguna razon se incluyen capturas vacias o_O if (matchResult.Value != "") { // La expreision regular tambien cuenta los cierre de lineas, asi que los separamos int enter = matchResult.Value.IndexOf(System.Environment.NewLine); string final = matchResult.Value; if (enter >= 0) final = matchResult.Value.Substring(0, enter); // Hacemos la division, sera una linea, pero en consola no entra como tal y se dividen en lineas lineas += Convert.ToInt32(final.Length / Console.WindowWidth) + 1; } matchResult = matchResult.NextMatch(); } return lineas; } /// <summary>ReadLine 2.0 para poder Escribir a la vez</summary> /// <returns>El texto escrito</returns> static string ReadLine() { string result = ""; // Lee cada tecla presionada ConsoleKeyInfo t = Console.ReadKey(false); while (t.Key != ConsoleKey.Enter) { // En caso de que borre, se quita el ultimo caracter if (t.Key == ConsoleKey.Backspace && buffer.Length > 0) { Console.Write(" "); Console.SetCursorPosition(Console.CursorLeft - 1, Console.CursorTop); buffer = buffer.Substring(0, buffer.Length - 1); } else { // Es un caracter cualquiera, se agrega al string buffer += t.KeyChar.ToString(); } t = Console.ReadKey(false); } Console.WriteLine(); // Se vacia el buffer y se devuelve lo escrito result = buffer; buffer = ""; return result; }
Y aca un pequeño ejemplo: Un thread con un bucle infinito que muestra numeros sucesivos en la pantalla, y ustedes pueden escribir lo que sea
Código
static void Main(string[] args) { // Empezamos el relleno t.Start(); string comando; do { // Al mismo tiempo que escribimos se separa lo que se va a mostrar de lo que escribimos comando = ReadLine(); WriteLine(DateTime.Now.ToString("HH:mm:ss") + System.Environment.NewLine +" - " + comando); } while (comando != "salir"); t.Abort(); } // Iteracion infinita para rellenar la consola static void Rellena() { int i = 0; while (true) { WriteLine(i.ToString()); if (i == int.MaxValue) i = 0; i++; Thread.Sleep(1000); } }
Espero que lo prueben, y les guste
Cualquier critica (constructiva) es recibida