Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C# simultanous console input and output?

Tags:

c#

console

I am writing a server app and I want it to be console based. I need the user to be able to input different commands, but at the same time there is a possibility that something will be output to the console while the user is writing. This messes the buffer up. Is there any clean way of doing this?

Thanks.

like image 775
Alex Turpin Avatar asked May 11 '09 20:05

Alex Turpin


2 Answers

I started work on a test program to show how you could divide the console into an output area and an input area, where the input area is moved down as the output area expands with more output. It's not perfect yet, but you may be able to develop it into the answer you're looking for:

  static int outCol, outRow, outHeight = 10;

  static void Main(string[] args)
  {
     bool quit = false;
     System.DateTime dt = DateTime.Now;
     do
     {
        if (Console.KeyAvailable)
        {
           if (Console.ReadKey(false).Key == ConsoleKey.Escape)
              quit = true;
        }
        System.Threading.Thread.Sleep(0);
        if (DateTime.Now.Subtract(dt).TotalSeconds > .1)
        {
           dt = DateTime.Now;
           WriteOut(dt.ToString(" ss.ff"), false);
        }            
     } while (!quit);
  }

  static void WriteOut(string msg, bool appendNewLine)
  {
     int inCol, inRow;
     inCol = Console.CursorLeft;
     inRow = Console.CursorTop;

     int outLines = getMsgRowCount(outCol, msg) + (appendNewLine?1:0);
     int outBottom = outRow + outLines;
     if (outBottom > outHeight)
        outBottom = outHeight;
     if (inRow <= outBottom)
     {
        int scrollCount = outBottom - inRow + 1;
        Console.MoveBufferArea(0, inRow, Console.BufferWidth, 1, 0, inRow + scrollCount);
        inRow += scrollCount;
     }
     if (outRow + outLines > outHeight)
     {
        int scrollCount = outRow + outLines - outHeight;
        Console.MoveBufferArea(0, scrollCount, Console.BufferWidth, outHeight - scrollCount, 0, 0);
        outRow -= scrollCount;
        Console.SetCursorPosition(outCol, outRow);
     }
     Console.SetCursorPosition(outCol, outRow);
     if (appendNewLine)
        Console.WriteLine(msg);
     else
        Console.Write(msg);
     outCol = Console.CursorLeft;
     outRow = Console.CursorTop;
     Console.SetCursorPosition(inCol, inRow);
  }

  static int getMsgRowCount(int startCol, string msg)
  {
     string[] lines = msg.Split('\n');
     int result = 0;
     foreach (string line in lines)
     {
        result += (startCol + line.Length) / Console.BufferWidth;
        startCol = 0;
     }
     return result + lines.Length - 1;
  }
like image 167
BlueMonkMN Avatar answered Oct 11 '22 05:10

BlueMonkMN


Personally i would use event handlers to managed a console that handles both input and outup at the same time, create a class ScreenManager or whatever, inside that class add a void RunProgram() mthod, create an event with handler and required variables for reading the input key "Console.ReadKey(bool).key".

static Consolekey newKey;

on your main program, creat an instance of your class "whatev you called it", then create a thread of that instances internal method, Thread coreThread = new Thread(delegate() {myinstance.myProgramMrthod()});

loop in your main until the threads up and running. while (!Thread.IsAlive) ;

then create the main program loop.

while (true)
{
}

then for safty, join your custom thread so the main program doesnt continue until the custom thread is closed/disposed.

customThread.Join();

you now have two threads running seperatly.

back to your class, create a switch inside your event handler method.

switch (newkey)
{
case Consolekey.enter
Console.WriteLine("enter pressed");
break;

ect, ect.

default:
Console.write(newkey); // writes character key that dont match above conditions to the screen. 
break;
}

stick allyour logic inhere with how you want to handle keys. How to use multiple modifier keys in C# might be of some help.

inside your instance's method RunProgram() or whatev you choose to call it, after you've done whatever code you need to, create an infinite loop to check for key change.

while (true)
{
newKey = Console.ReadKey(true).Key;
if (newKey != oldKey)
{
KeyChange.Invoke();
}
}

this loop stores any key pressed and then checks to see if theres a new key, if true fires the event.

you now have the core of what your looking for, one string that loops askng for a new key, whilst the main loop is free to display whatever text you wish to display.

two fixable bugs with this that i can think of, one is "default" inside switch will print to console in caps or strings. and the other is any text added to the console is added at the cursor point so it adds to the text the user has just input.

hwoever i will, since i've just made it, how you have to manager the text been added to the console. again im using an event. i could use methods and functions throughout but events add move flexability to the program, i think.

okay so we want to be able to add text to the console, without it upsetting the input we enter. keeping the input at the bottom;

create a new delegate that has a signiture with a string argument, void delegate myDelegate(string Arg). then create an event with this delegate, call it newline, newinput, whatev you like.

the events handler will take a string argument (repersenting the console update text: what you want to insert into the console above the users input) it will grab the text the user has been entering into the console, store it, then print out the paramiter string onto the console, then print out the users input underneith.

personally i chose to create a static string at the top outside the method, initialise it to empty, cos its going to be frequently used and you dont want to be creating a new identifyer and then initialising the variable everytime the method is called, then dispose of it at the end of the method, only to recreate a new one again, and again.

call the string "input" or whatever.

in the default area of the keychange event handle add input +=newkey.

in the Consolekey.enter section console writline input then input = string.empty Or string = "".

in the event handler add some logic.

public void OnInsert(string Argument)
        {
            Console.CursorTop -= 1;

    // moves the cursor to far left so new input overwrites the old.



    // if arg string is longer, then print arg string then print input  // string.

            if (Argument.Length > input.Length)
            {
                Console.WriteLine(Argument);
                Console.WriteLine(input);
            }
            else
            {

    // if the users input if longer than the argument text then print
    // out the argument text, then print white spaces to overwrite the
    // remaining input characters still displayed on screen.


                for (int i = 0; i < input.Length;i++ )
                {
                    if (i < Argument.Length)
                    {
                        Console.Write(Argument[i]);
                    }
                    else
                    {
                        Console.Write(' ');
                    }
                }
                Console.Write(Environment.NewLine);
                Console.WriteLine(input);
            }
        }

hope this helps some of you, its not perfect, a quick put together test that works enough to be built on.
like image 26
Gorlykio Avatar answered Oct 11 '22 05:10

Gorlykio