views:

1056

answers:

4

I need to ask for input in console and wait for few sec may be a min after that I need to default to some value. How can basically time our on console input in c# and proceed with default settings? If you have sample code that would be great.

A: 

One way would be to loop, calling Console.In.Peek(), waiting for input or for enough time to go by.

Doug McClean
A: 

See here, it uses a nice little polling technique on the console, which although a bit crude, very effective.

Mark
+3  A: 

You could use the timer object in System.Timers.Timer and set it to 60 secs, enable it and if someone enters something into the console just disable it, if not then just handle the what happens in the Timer.Elapsed event.

static void Main(string[] args)
        {
            System.Timers.Timer timer = new System.Timers.Timer(60000);
            timer.Elapsed += new System.Timers.ElapsedEventHandler(T_Elapsed);
            timer.Start();
            var i = Console.ReadLine();
            if (string.IsNullOrEmpty(i)) 
            {
                timer.Stop();
                Console.WriteLine("{0}", i);
            }
        }

        static void T_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
        {
            Console.WriteLine("Defult Values Used");
            var T = (Timer)sender;
            T.Stop;

        }

Not sure if this is the best way. I have tested this but as I said it may not be the best way.

Nathan W
The problem with this model is that ReadLine is a blocking call that doesn't return until the line has been entered, so whilst the default value may be sent when the timer is elapsed the program won't continue until the text has been entered.
ICR
mmm yeah didn't really think of that. Bugger
Nathan W
+1  A: 

You may want to consider doing something like adding a command line argument to act as a switch between an automatic mode using defaults and the full user input mode.

If you use a specific interpretation of your request it becomes quite trivial. In this model the user is prompted for input. If they type nothing after a timeout period the default value is used. If they start typing something then no timeout is used. This also deals with the usability issue of giving up and using a default value when they're taking a long time to type something in.

    static void Main(string[] args)
    {
        Console.WriteLine("Please enter your name ");
        string input;

        if (TryReadLine(out input, 10000, true))
        {
            Console.WriteLine(input);
        }
        else
        {
            Console.WriteLine("[DEFAULT]");
        }

        Console.ReadKey(true);
    }

    const string timerString = "[{0} seconds until default value is used]";

    public static bool TryReadLine(out string s, double timeout, bool showTimer)
    {
        DateTime timeoutDateTime = DateTime.Now.AddMilliseconds(10000);
        DateTime nextTimer = DateTime.Now;
        while (DateTime.Now < timeoutDateTime)
        {
            if (Console.KeyAvailable)
            {
                ClearTimer(timeoutDateTime);
                s = Console.ReadLine();
                return true;
            }

            if (showTimer && DateTime.Now > nextTimer)
            {
                WriteTimer(string.Format(timerString, (timeoutDateTime - DateTime.Now).Seconds));
                nextTimer = DateTime.Now.AddSeconds(1);
            }
        }

        ClearTimer(timeoutDateTime);
        s = null;
        return false;
    }

    private static void ClearTimer(DateTime timeoutDateTime)
    {
        WriteTimer(new string(' ', string.Format(timerString, (timeoutDateTime - DateTime.Now).Seconds).Length));
    }

    private static void WriteTimer(string s)
    {
        int cursorLeft = Console.CursorLeft;
        Console.CursorLeft = 0;
        Console.CursorTop += 1;
        Console.Write(s);
        Console.CursorLeft = cursorLeft;
        Console.CursorTop -= 1;
    }
}

Because I spend so long on it before I realised there was a better way, here is some code I just knocked up to read a string from the console, with a timeout. It also has the option to be able to print the current time left to the console. It's not tested very thoroughly, so is likely to have a lot of bugs. The callback feature uses the .NET 3.0 Action, though if this is for C# 2.0 you can just turn it into a delegate.

    static void Main(string[] args)
    {
        string input;
        Console.Write("Please enter your name (");
        int timerPromptStart = Console.CursorLeft;
        Console.Write("    seconds left): ");
        if (TryReadLine(out input, 10000, delegate(TimeSpan timeSpan)
                                          {
                                              int inputPos = Console.CursorLeft;
                                              Console.CursorLeft = timerPromptStart;
                                              Console.Write(timeSpan.Seconds.ToString("000"));
                                              Console.CursorLeft = inputPos;
                                          },
                                          1000))
        {
            Console.WriteLine(input);
        }
        else
        {
            Console.WriteLine("DEFAULT");
        }
        while (true) { }
    }

    /// <summary>
    /// Tries to read a line of input from the Console.
    /// </summary>
    /// <param name="s">The string to put the input into.</param>
    /// <param name="timeout">The time in milliseconds before the attempt fails.</param>
    /// <returns>Whether the user inputted a line before the timeout.</returns>
    public static bool TryReadLine(out string s, double timeout)
    {
        return TryReadLine(out s, timeout, null, 0);
    }

    /// <summary>
    /// Tries to read a line of input from the Console.
    /// </summary>
    /// <param name="s">The string to put the input into.</param>
    /// <param name="timeout">The time in milliseconds before the attempt fails.</param>
    /// <param name="timerCallback">A function to call every callbackInterval.</param>
    /// <param name="callbackInterval">The length of time between calls to timerCallback.</param>
    /// <returns>Whether the user inputted a line before the timeout.</returns>
    public static bool TryReadLine(out string s, double timeout, Action<TimeSpan> timerCallback, double callbackInterval)
    {
        const int tabLength = 6;

        StringBuilder inputBuilder = new StringBuilder();
        int readStart = Console.CursorLeft;
        int lastLength = 0;
        bool isInserting = true;
        DateTime endTime = DateTime.Now.AddMilliseconds(timeout);
        DateTime nextCallback = DateTime.Now;
        while (DateTime.Now < endTime)
        {
            if (timerCallback != null && DateTime.Now > nextCallback)
            {
                nextCallback = DateTime.Now.AddMilliseconds(callbackInterval);
                timerCallback((endTime - DateTime.Now));
            }

            if (Console.KeyAvailable)
            {
                ConsoleKeyInfo key = Console.ReadKey(true);
                switch (key.Key)
                {
                    case ConsoleKey.Enter:
                        Console.WriteLine();
                        s = inputBuilder.ToString();
                        return true;

                    case ConsoleKey.Backspace:
                        if (Console.CursorLeft > readStart)
                        {
                            Console.CursorLeft -= 1;
                            inputBuilder.Remove(Console.CursorLeft - readStart, 1);
                        }
                        break;

                    case ConsoleKey.Delete:
                        if (Console.CursorLeft < readStart + inputBuilder.Length)
                        {
                            inputBuilder.Remove(Console.CursorLeft - readStart, 1);
                        }
                        break;

                    case ConsoleKey.Tab:
                        // Tabs are very difficult to handle properly, so we'll simply replace it with spaces.
                        AddOrInsert(inputBuilder, new String(' ', tabLength), isInserting, readStart);
                        Console.CursorLeft += tabLength;
                        break;

                    case ConsoleKey.Escape:
                        Console.CursorLeft = readStart;
                        inputBuilder = new StringBuilder();
                        break;

                    case ConsoleKey.Insert:
                        isInserting = !isInserting;
                        // This may be dependant on a variable somewhere.
                        if (isInserting)
                        {
                            Console.CursorSize = 25;
                        }
                        else
                        {
                            Console.CursorSize = 50;
                        }
                        break;

                    case ConsoleKey.Home:
                        Console.CursorLeft = readStart;
                        break;

                    case ConsoleKey.End:
                        Console.CursorLeft = readStart + inputBuilder.Length;
                        break;

                    case ConsoleKey.LeftArrow:
                        if (Console.CursorLeft > readStart)
                        {
                            Console.CursorLeft -= 1;
                        }
                        break;

                    case ConsoleKey.RightArrow:
                        if (Console.CursorLeft < readStart + inputBuilder.Length)
                        {
                            Console.CursorLeft += 1;
                        }
                        break;

                    case ConsoleKey.UpArrow:
                        // N.B. We can't handle Up like we normally would as we don't know the last console input.
                        //      You might want to handle this so it works appropriately within your own application.
                        break;

                    case ConsoleKey.PageUp:
                    case ConsoleKey.PageDown:
                    case ConsoleKey.PrintScreen:
                    case ConsoleKey.LeftWindows:
                    case ConsoleKey.RightWindows:
                    case ConsoleKey.Sleep:
                    case ConsoleKey.F1:
                    case ConsoleKey.F2:
                    case ConsoleKey.F3:
                    case ConsoleKey.F4:
                    case ConsoleKey.F5:
                    case ConsoleKey.F6:
                    case ConsoleKey.F7:
                    case ConsoleKey.F8:
                    case ConsoleKey.F9:
                    case ConsoleKey.F10:
                    case ConsoleKey.F11:
                    case ConsoleKey.F12:
                    case ConsoleKey.F13:
                    case ConsoleKey.F14:
                    case ConsoleKey.F15:
                    case ConsoleKey.F16:
                    case ConsoleKey.F17:
                    case ConsoleKey.F18:
                    case ConsoleKey.F19:
                    case ConsoleKey.F20:
                    case ConsoleKey.F21:
                    case ConsoleKey.F22:
                    case ConsoleKey.F23:
                    case ConsoleKey.F24:
                    case ConsoleKey.BrowserBack:
                    case ConsoleKey.BrowserForward:
                    case ConsoleKey.BrowserStop:
                    case ConsoleKey.BrowserRefresh:
                    case ConsoleKey.BrowserSearch:
                    case ConsoleKey.BrowserFavorites:
                    case ConsoleKey.BrowserHome:
                    case ConsoleKey.VolumeMute:
                    case ConsoleKey.VolumeUp:
                    case ConsoleKey.VolumeDown:
                    case ConsoleKey.MediaNext:
                    case ConsoleKey.MediaPrevious:
                    case ConsoleKey.MediaStop:
                    case ConsoleKey.MediaPlay:
                    case ConsoleKey.LaunchMail:
                    case ConsoleKey.LaunchMediaSelect:
                    case ConsoleKey.LaunchApp1:
                    case ConsoleKey.LaunchApp2:
                    case ConsoleKey.Play:
                    case ConsoleKey.Zoom:
                    case ConsoleKey.NoName:
                    case ConsoleKey.Pa1:
                        // These keys shouldn't do anything.
                        break;

                    case ConsoleKey.Clear:
                    case ConsoleKey.Pause:
                    case ConsoleKey.Select:
                    case ConsoleKey.Print:
                    case ConsoleKey.Execute:
                    case ConsoleKey.Process:
                    case ConsoleKey.Help:
                    case ConsoleKey.Applications:
                    case ConsoleKey.Packet:
                    case ConsoleKey.Attention:
                    case ConsoleKey.CrSel:
                    case ConsoleKey.ExSel:
                    case ConsoleKey.EraseEndOfFile:
                    case ConsoleKey.OemClear:
                        // I'm not sure what these do.
                        break;

                    default:
                        Console.Write(key.KeyChar);
                        AddOrInsert(inputBuilder, key.KeyChar.ToString(), isInserting, readStart);
                        break;
                }

                // Write what has current been typed in back out to the Console.
                // We write out everything after the cursor to handle cases where the current input string is shorter than before
                // (i.e. the user deleted stuff).
                // There is probably a more efficient way to do this.
                int oldCursorPos = Console.CursorLeft;
                Console.CursorLeft = readStart;
                Console.Write(inputBuilder.ToString());
                if (lastLength > inputBuilder.Length)
                {
                    Console.Write(new String(' ', lastLength - inputBuilder.Length));
                }
                lastLength = inputBuilder.Length;
                Console.CursorLeft = oldCursorPos;
            }
        }

        // The timeout period was reached.
        Console.WriteLine();
        s = null;
        return false;
    }

    // This is a rather ugly helper method to add text to the inputBuilder, either inserting or appending as appropriate.
    private static void AddOrInsert(StringBuilder inputBuilder, string s, bool insert, int readStart)
    {
        if (Console.CursorLeft < readStart + inputBuilder.Length + (insert ? -1 : 1))
        {
            if (!insert)
            {
                inputBuilder.Remove(Console.CursorLeft - 1 - readStart, 1);
            }
            inputBuilder.Insert(Console.CursorLeft - 1 - readStart, s);
        }
        else
        {
            inputBuilder.Append(s);
        }
    }
}
ICR