Reputation:
I have an application written in C# which takes continuously commands from the user via a while(true) loop and Console.ReadLine.
I also do have various background operations which report back via the console. View this simple example:
class Program
{
static void Main(string[] args)
{
new Messager(1000 * 2.5f);
new Messager(1000 * 3);
while (true)
{
Console.ReadLine();
}
}
}
public class Messager
{
Timer Timer;
public Messager(double interval)
{
this.Timer = new Timer(interval);
this.Timer.Elapsed += (sender, e) =>
{
Console.WriteLine("message from " + this.Timer.Interval);
};
this.Timer.Start();
}
}
The problem is, when I enter a command and a Messager reports back before I have pressed Enter the message from the Messager will be appended to my current line and the cursor will be placed into the next line. See this screenshot:
As you'd guess I don't want that. I want the current inputting of a command to remain always in the last line and have all the messages coming placed above that line. Just as you'd expect from such an application. How do I achieve that?
Upvotes: 2
Views: 5803
Reputation:
Here's the answer to my problem:
class Program
{
static void Main(string[] args)
{
new Messager(1000 * 2.5f);
new Messager(1000 * 3);
while (true)
{
Console.ReadLine();
}
}
}
public class Messager
{
Timer Timer;
public Messager(double interval)
{
this.Timer = new Timer(interval);
this.Timer.Elapsed += (sender, e) =>
{
int currentTopCursor = Console.CursorTop;
int currentLeftCursor = Console.CursorLeft;
Console.MoveBufferArea(0, currentTopCursor, Console.WindowWidth, 1, 0, currentTopCursor + 1);
Console.CursorTop = currentTopCursor;
Console.CursorLeft = 0;
Console.WriteLine("message from " + this.Timer.Interval);
Console.CursorTop = currentTopCursor +1;
Console.CursorLeft = currentLeftCursor;
};
this.Timer.Start();
}
}
Upvotes: 3
Reputation: 3455
Just an idea. You can use the Console.CursorTop
and Console.CursorLeft
properties.
class Program
{
static void Main(string[] args)
{
new Messager(1000 * 2.5f);
new Messager(1000 * 3);
while (true)
{
Console.CursorTop = 0;
Console.ReadLine();
}
}
}
public class Messager
{
Timer Timer;
static int position = 1;
public Messager(double interval)
{
this.Timer = new Timer(interval);
this.Timer.Elapsed += (sender, e) =>
{
lock (this)
{
var cursorLeft = Console.CursorLeft;
Console.CursorLeft = 0;
Console.CursorTop = position++;
Console.WriteLine("message from " + this.Timer.Interval);
Console.CursorTop = 0;
Console.CursorLeft = cursorLeft;
}
};
this.Timer.Start();
}
}
Upvotes: 0
Reputation: 133950
This is very, very difficult to do. You end up writing your own console input and output methods that every thread has to call. If any thread doesn't cooperate, it just doesn't work right. But it's possible using the Console API.
The basic idea is to have your common output function write to the console screen buffer, and scroll the buffer in code rather than letting the text flow onto the last line and scroll automatically. As I recall, you'll have to parse the output for newlines and other control characters, and handle them correctly.
You might be able to get away with using Console.ReadLine
on the last line, although in doing so you risk problems if the user enters more text than will fit on a single line. Also, the user hitting Enter at the end of the line might cause it to scroll up. Probably best in this situation to use raw console input.
You'll want to become very familiar with Windows consoles.
Be forewarned: it's a lot of work. And probably not worth the effort.
Upvotes: 5