[英]Using Readline() and ReadKey() Simultaneously
Is there any way to detect both Readline and ReadKey, so that in most cases it behaves as a readline, except for some special key inputs that should be detected?有没有办法同时检测 Readline 和 ReadKey,以便在大多数情况下它的行为就像一个 readline,除了一些应该检测的特殊键输入?
I need some "parallel" implementation to introduce simultaneity.我需要一些“并行”实现来引入同时性。 The code below is synchronous and does not meet my need
下面的代码是同步的,不符合我的需要
while ((line = Console.ReadLine()) != "x")
{
if (line == "BLABLA")
{
//Stuff
}
else
{
//Stuff
}
ConsoleKeyInfo ki = Console.ReadKey(true);
if ((ki.Key == ConsoleKey.V) && (ki.Modifiers == ConsoleModifiers.Control))
{
//Stuff
}
}
Here's a function I just made to do this. 这是我刚才做的一个功能。
Right now it only handles backspace, enter, and Esc, but it could easily be modified to handle other keys if you deem them necessary. 现在它只处理退格,输入和Esc,但如果你认为有必要,可以很容易地修改它来处理其他键。
// returns null if user pressed Escape, or the contents of the line if they pressed Enter.
private static string ReadLineOrEsc()
{
string retString = "";
int curIndex = 0;
do
{
ConsoleKeyInfo readKeyResult = Console.ReadKey(true);
// handle Esc
if (readKeyResult.Key == ConsoleKey.Escape)
{
Console.WriteLine();
return null;
}
// handle Enter
if (readKeyResult.Key == ConsoleKey.Enter)
{
Console.WriteLine();
return retString;
}
// handle backspace
if (readKeyResult.Key == ConsoleKey.Backspace)
{
if (curIndex > 0)
{
retString = retString.Remove(retString.Length - 1);
Console.Write(readKeyResult.KeyChar);
Console.Write(' ');
Console.Write(readKeyResult.KeyChar);
curIndex--;
}
}
else
// handle all other keypresses
{
retString += readKeyResult.KeyChar;
Console.Write(readKeyResult.KeyChar);
curIndex++;
}
}
while (true);
}
No, not as such. 不,不是这样的。 Both methods block until the user enters something on the console.
两种方法都会阻塞,直到用户在控制台上输入内容。 So even if you would find a way to have both run in parallel, it will not be deterministic which one gets the first shot.
因此,即使你找到了一种让两者并行运行的方法,也不确定哪一个获得第一枪。
There is a (not obvious) similar problem: how to make Console.ReadLine()
abort/break after a certain amount of time without user input. 有一个(不明显的)类似问题:如何在没有用户输入的情况下在一定时间后使
Console.ReadLine()
中止/中断。
There have been multiple attempts for this problem here: 这里有多次尝试解决这个问题:
Most are modelled around either creating your own version of a ReadLine
function that adds a timeout (or in your case special handling for certain character (codes)) or the use some sort of threading. 大多数都建模为创建自己的
ReadLine
函数版本,增加超时(或在您的情况下特殊处理某些字符(代码))或使用某种线程。
Both ways are either non-trivial or have their own issues (make sure you review the comments, even for the accepted answers). 这两种方式都是非平凡的或有自己的问题(确保您审查评论,即使是已接受的答案)。
In short, I think you will need to roll your own version of ReadLine, based on Console.ReadKey
with your special handling included and that much of the genuine Console.ReadLine
behavior that you require. 简而言之,我认为您需要根据
Console.ReadKey
推出自己的ReadLine版本,包括您的特殊处理以及您需要的大部分真正的Console.ReadLine
行为。 Note that this even include such basic things as RETURN, ARROW KEYS, BACKSPACE handling, etc. 请注意,这甚至包括RETURN,ARROW KEYS,BACKSPACE处理等基本功能。
Update : There is the getline.cs Code from the Mono project , which implements a line editing capability like it was provided by some venerable UNIX shells (EMACS mode, if you care). 更新 : Mono项目中有getline.cs代码,它实现了一些行编辑功能,就像它由一些古老的UNIX shell(EMACS模式,如果你关心的话)提供的那样。 For that, I believe it will need to implement some sort of ReadLine replacement, although I haven't checked.
为此,我相信它需要实现某种ReadLine替换,尽管我还没有检查过。 Maybe you can use this as a starting point.
也许你可以用它作为起点。
In response to @Overlord Zurd , I improved the code provided by the user. 为响应@Overlord Zurd ,我改进了用户提供的代码。
public class ConsoleOutput
{
private ConsoleOutputType OutputType { get; set; }
private object MyObject { get; }
private static bool IsInserting { get; set; }
public string KeyName => IsKey() && (ConsoleKeyInfo)MyObject != null ? ((ConsoleKeyInfo)MyObject).Key.ToString() : "Null";
public string OutputString => !IsKey() && MyObject != null ? (string)MyObject : string.Empty;
public static event Action<string> ReadInput = delegate { };
public static event Action<ConsoleKeyInfo> ReadKey = delegate { };
private ConsoleOutput()
{
}
public ConsoleOutput(object obj)
{
MyObject = obj;
OutputType = obj is ConsoleKeyInfo ? ConsoleOutputType.Key : ConsoleOutputType.Value;
}
public bool IsKey()
{
return OutputType == ConsoleOutputType.Key;
}
public bool IsExitKey()
{
if (!IsKey())
return false;
var info = ((ConsoleKeyInfo)MyObject);
return (info.Modifiers & ConsoleModifiers.Control) != 0 && info.Key == ConsoleKey.B;
}
public string GetValue()
{
return (string)MyObject;
}
// returns null if user pressed Escape, or the contents of the line if they pressed Enter.
public static ConsoleOutput ReadLineOrKey()
{
string retString = "";
int curIndex = 0;
do
{
ConsoleKeyInfo readKeyResult = Console.ReadKey(true);
// handle Enter
if (readKeyResult.Key == ConsoleKey.Enter)
{
ReadInput?.Invoke(retString);
Console.WriteLine();
return new ConsoleOutput(retString);
}
// handle backspace
if (readKeyResult.Key == ConsoleKey.Backspace)
{
if (curIndex > 0)
{
retString = retString.Remove(retString.Length - 1);
Console.Write(readKeyResult.KeyChar);
Console.Write(' ');
Console.Write(readKeyResult.KeyChar);
--curIndex;
}
}
else if (readKeyResult.Key == ConsoleKey.Delete)
{
if (retString.Length - curIndex > 0)
{
// Store current position
int curLeftPos = Console.CursorLeft;
// Redraw string
for (int i = curIndex + 1; i < retString.Length; ++i)
Console.Write(retString[i]);
// Remove last repeated char
Console.Write(' ');
// Restore position
Console.SetCursorPosition(curLeftPos, Console.CursorTop);
// Remove string
retString = retString.Remove(curIndex, 1);
}
}
else if (readKeyResult.Key == ConsoleKey.RightArrow)
{
if (curIndex < retString.Length)
{
++Console.CursorLeft;
++curIndex;
}
}
else if (readKeyResult.Key == ConsoleKey.LeftArrow)
{
if (curIndex > 0)
{
--Console.CursorLeft;
--curIndex;
}
}
else if (readKeyResult.Key == ConsoleKey.Insert)
{
IsInserting = !IsInserting;
}
#if DEBUG
else if (readKeyResult.Key == ConsoleKey.UpArrow)
{
if (Console.CursorTop > 0)
--Console.CursorTop;
}
else if (readKeyResult.Key == ConsoleKey.DownArrow)
{
if (Console.CursorTop < Console.BufferHeight - 1)
++Console.CursorTop;
}
#endif
else
// handle all other keypresses
{
if (IsInserting || curIndex == retString.Length)
{
retString += readKeyResult.KeyChar;
Console.Write(readKeyResult.KeyChar);
++curIndex;
}
else
{
// Store char
char c = readKeyResult.KeyChar;
// Write char at position
Console.Write(c);
// Store cursor position
int curLeftPos = Console.CursorLeft;
// Clear console from curIndex to end
for (int i = curIndex; i < retString.Length; ++i)
Console.Write(' ');
// Go back
Console.SetCursorPosition(curLeftPos, Console.CursorTop);
// Write the chars from curIndex to end (with the new appended char)
for (int i = curIndex; i < retString.Length; ++i)
Console.Write(retString[i]);
// Restore again
Console.SetCursorPosition(curLeftPos, Console.CursorTop);
// Store in the string
retString = retString.Insert(curIndex, new string(c, 1));
// Sum one to the cur index (we appended one char)
++curIndex;
}
}
if (char.IsControl(readKeyResult.KeyChar) &&
readKeyResult.Key != ConsoleKey.Enter &&
readKeyResult.Key != ConsoleKey.Backspace &&
readKeyResult.Key != ConsoleKey.Tab &&
readKeyResult.Key != ConsoleKey.Delete &&
readKeyResult.Key != ConsoleKey.RightArrow &&
readKeyResult.Key != ConsoleKey.LeftArrow &&
readKeyResult.Key != ConsoleKey.Insert)
{
#if DEBUG
if (readKeyResult.Key == ConsoleKey.UpArrow || readKeyResult.Key == ConsoleKey.DownArrow)
continue;
#endif
ReadKey?.Invoke(readKeyResult);
Console.WriteLine();
return new ConsoleOutput(readKeyResult);
}
}
while (true);
}
}
As you can see, I implemented Insert, Arrow control, Delete, etc etc... (Insert was an important thing because if you write any text with this code you will see behavior as Insert key provides). 如您所见,我实现了插入,箭头控制,删除等等...(插入是一件很重要的事情,因为如果您使用此代码编写任何文本,您将看到插入键提供的行为)。
And example of use: 和使用的例子:
internal class Program
{
private static void Main(string[] args)
{
Console.Write("Write test string: ");
var test = ConsoleOutput.ReadLineOrKey();
if (test.IsKey())
Console.WriteLine(test.KeyName);
else
Console.WriteLine($"Output string: {test.OutputString}");
Console.Read();
}
}
You can keep updated on this link (is a link to my lib I'm currently working at). 你可以在这个链接上保持更新(这是我目前正在工作的lib的链接)。
Here is a method that I created that works great.这是我创建的一种非常有效的方法。 You do not have to press button twice in order for string to start appearing.
您不必按两次按钮即可开始出现字符串。 Basically this replaces
Console.ReadLine()
but it also looks for Esc key pressed.基本上这取代了
Console.ReadLine()
但它也寻找按下的Esc键。 Just look at return type of method if it is null
then you know Esc was pressed.只需查看方法的返回类型,如果它为
null
那么您就知道按下了Esc 。
private string ReadLineOrEscape()
{
ConsoleKeyInfo keyInfo = new ConsoleKeyInfo();
StringBuilder sb = new StringBuilder();
int index = 0;
while (keyInfo.Key != ConsoleKey.Enter)
{
keyInfo = Console.ReadKey(true);
if (keyInfo.Key == ConsoleKey.Escape)
{
return null;
}
if(keyInfo.Key == ConsoleKey.Backspace)
{
if (index > 0)
{
Console.CursorLeft = index - 1;
sb.Remove(index - 1, 1);
Console.Write(" \b");
index--;
}
}
if(keyInfo.KeyChar > 31 && keyInfo.KeyChar < 127)
{
index++;
Console.Write(keyInfo.KeyChar);
sb.Append(keyInfo.KeyChar);
}
}
return sb.ToString(); ;
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.