Reputation: 808
I'm trying to learn my way around C# threading by making a simple little game. I've run into a bit of an issue that I could certainly use help with.
My desire is to have simultaneous input and output. The output from the threads would appear at the top of the screen, while the user's input could be typed to the bottom of the screen. The problem I am running into is that in order to refresh the screen, when I use Console.Clear()
, it also wipes out whatever the user is typing! Below I have attached an extremely simplified version of what I am trying to do (in order to avoid unnecessary code getting in the way of the actual issue).
Please note: While in this example I am only updating a single character at the top of the screen, my actual program would have LOTS of text all over the top and middle of the screen that would be constantly changing with every tick (which I plan to use at 1.5 seconds).
Any ideas would be great! I'm still new to C# programming and would be thrilled for any help you could give :) The only thing here that I am attached to is the design. System output at the top, user input at the very bottom preceeded by ">". If the way I am doing it is wrong, I have no issue with tossing it all out the window and doing it a different way.
EDIT: My goal here is to have the output text at the top of the screen update every 1.5 seconds (every run of the count thread) while allowing the user to type at the bottom of the screen. However, the method I am using to do that (clearing and then writing new content to the screen) is also wiping out the user's input! It makes it so that every 1.5 seconds whatever input the user is typing just disappears, and rightfully so since that's exactly what Console.Clear does. So I'm looking for a new method to accomplish this task.
In short: How do I update text at the top/middle of the screen while allowing the user to continue typing at the bottom of the screen?
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
namespace ConsoleApplication2
{
class Program
{
static int i = 0;
static void Main(string[] args)
{
Thread tickThread = new Thread(new ThreadStart(CountThread));
Thread userThread = new Thread(new ThreadStart(UserInput));
tickThread.Start();
Thread.Sleep(1);
userThread.Start();
Thread.Sleep(20000);
tickThread.Abort();
userThread.Abort();
}
static void UserInput()
{
string input = "";
while (true)
{
input = Console.ReadLine();
}
}
static void CountThread()
{
while (true)
{
Console.Clear();
Console.SetCursorPosition(0, 0);
Console.WriteLine(i);
i++;
Console.SetCursorPosition(0, Console.WindowHeight - 1);
Console.Write("> ");
Thread.Sleep(1500);
}
}
}
}
Upvotes: 3
Views: 4348
Reputation: 2135
Here is an approach that has the main thread read console input, and a 2nd thread to write console output:
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
namespace ConsoleReadWriteTest
{
class Program
{
private static ConsoleInfo consoleInfo = new ConsoleInfo();
static void Main(string[] args)
{
Thread consoleWriter = new Thread(new ThreadStart(ConsoleWriter));
consoleWriter.Start();
consoleInfo.outputBuffer.Add("Running.");
consoleInfo.outputBuffer.Add(".. status of foo = good");
consoleInfo.outputBuffer.Add(".. status of bar = bad");
while (true)
{
var key = Console.ReadKey(true);
lock (consoleInfo)
{
if (key.Key == ConsoleKey.Enter)
{
consoleInfo.commandReaty = true;
}
else
{
consoleInfo.sbRead.Append(key.KeyChar.ToString());
}
}
}
}
static void ConsoleWriter()
{
while (true)
{
lock(consoleInfo)
{
Console.Clear();
if (consoleInfo.outputBuffer[0].Length > 20)
{
consoleInfo.outputBuffer[0] = "Running.";
}
else
{
consoleInfo.outputBuffer[0] += ".";
}
foreach (var item in consoleInfo.outputBuffer)
{
Console.WriteLine(item);
}
Console.WriteLine("--------------------------------------------------------------");
if (consoleInfo.commandReaty)
{
consoleInfo.commandReaty = false;
consoleInfo.lastCommand = consoleInfo.sbRead.ToString();
consoleInfo.sbRead.Clear();
consoleInfo.lastResult.Clear();
switch (consoleInfo.lastCommand)
{
case "command1":
consoleInfo.outputBuffer[2] = ".. status of bar = good";
consoleInfo.lastResult.Append("command #1 performed");
break;
case "command2":
consoleInfo.outputBuffer[2] = ".. status of bar = bad";
consoleInfo.lastResult.Append("command #2 performed");
break;
case "?":
consoleInfo.lastResult.AppendLine("Available commands are:");
consoleInfo.lastResult.AppendLine("command1 sets bar to good");
consoleInfo.lastResult.AppendLine("command1 sets bar to bad");
break;
default:
consoleInfo.lastResult.Append("invalid command, type ? to see command list");
break;
}
}
Console.WriteLine(consoleInfo.lastCommand);
Console.WriteLine(consoleInfo.lastResult);
Console.WriteLine();
Console.Write(">");
Console.WriteLine(consoleInfo.sbRead.ToString());
Console.WriteLine();
Console.WriteLine();
Console.WriteLine();
}
Thread.Sleep(250);
}
}
private class ConsoleInfo
{
public bool commandReaty { get; set; }
public StringBuilder sbRead { get; set; }
public List<string> outputBuffer { get; set; }
public string lastCommand { get; set; }
public StringBuilder lastResult { get; set; }
public ConsoleInfo()
{
sbRead = new StringBuilder();
outputBuffer = new List<string>();
commandReaty = false;
lastResult = new StringBuilder();
}
}
}
}
Upvotes: 0
Reputation: 79
You can read input character by charcter using Console.ReadKey
. If you store the input, you can rebuild the entire screen, including the input typed before the console was cleared. I suggest to do all I/O related in one single thread. You can keep an internal representation of the console that is updated by multiple threads, and then print it from one thread.
Do this!
Upvotes: 1
Reputation: 2958
Did you consider using a Window Form for that, where you have one panel for the output and one for the input?
Upvotes: 0
Reputation: 1972
You can set cursor position in a loop for each cell in the console except those, which are designed for user's input, and write a space symbol. This will, essentially, clear a part of a console window. Also, you can use the same method to partially render output to the console.
Upvotes: 1