可能还有很多需要改进的地方,但我想我已经用这个解决方案涵盖了大部分基础知识:
此代码处理:
- 超出控制台窗口边缘写入
- 在现有文本后调用方法(使用 Console.Write)
- 调整控制台窗口的大小
- Ctrl 修饰符
此代码无法处理:
- Tab 键(Normal Console.ReadLine 在 Tab 键超出窗口大小时会出现错误,因此选择将其排除在外,因为它很少需要...)
我的代码基于 oleg wx 和 Chris Dunaway's 的答案,因此也归功于他们。
我建议尝试根据这些答案构建自己的,但有时您只需要一些工作即可,在这种情况下,当我在我的应用程序中使用该代码时,该代码对我有用:
只需新建一个静态类,例如:
public static class XConsole
{
}
并在其中粘贴以下方法:
public static string CancelableReadLine(out bool isCancelled)
{
var cancelKey = ConsoleKey.Escape;
var builder = new StringBuilder();
var cki = Console.ReadKey(true);
int index = 0;
(int left, int top) startPosition;
while (cki.Key != ConsoleKey.Enter && cki.Key != cancelKey)
{
if (cki.Key == ConsoleKey.LeftArrow)
{
if (index < 1)
{
cki = Console.ReadKey(true);
continue;
}
LeftArrow(ref index, cki);
}
else if (cki.Key == ConsoleKey.RightArrow)
{
if (index >= builder.Length)
{
cki = Console.ReadKey(true);
continue;
}
RightArrow(ref index, cki, builder);
}
else if (cki.Key == ConsoleKey.Backspace)
{
if (index < 1)
{
cki = Console.ReadKey(true);
continue;
}
BackSpace(ref index, cki, builder);
}
else if (cki.Key == ConsoleKey.Delete)
{
if (index >= builder.Length)
{
cki = Console.ReadKey(true);
continue;
}
Delete(ref index, cki, builder);
}
else if (cki.Key == ConsoleKey.Tab)
{
cki = Console.ReadKey(true);
continue;
}
else
{
if (cki.KeyChar == '\0')
{
cki = Console.ReadKey(true);
continue;
}
Default(ref index, cki, builder);
}
cki = Console.ReadKey(true);
}
if (cki.Key == cancelKey)
{
startPosition = GetStartPosition(index);
ErasePrint(builder, startPosition);
isCancelled = true;
return string.Empty;
}
isCancelled = false;
startPosition = GetStartPosition(index);
var endPosition = GetEndPosition(startPosition.left, builder.Length);
var left = 0;
var top = startPosition.top + endPosition.top + 1;
Console.SetCursorPosition(left, top);
var value = builder.ToString();
return value;
}
private static void LeftArrow(ref int index, ConsoleKeyInfo cki)
{
var previousIndex = index;
index--;
if (cki.Modifiers == ConsoleModifiers.Control)
{
index = 0;
var startPosition = GetStartPosition(previousIndex);
Console.SetCursorPosition(startPosition.left, startPosition.top);
return;
}
if (Console.CursorLeft > 0)
Console.CursorLeft--;
else
{
Console.CursorTop--;
Console.CursorLeft = Console.BufferWidth - 1;
}
}
private static void RightArrow(ref int index, ConsoleKeyInfo cki, StringBuilder builder)
{
var previousIndex = index;
index++;
if (cki.Modifiers == ConsoleModifiers.Control)
{
index = builder.Length;
var startPosition = GetStartPosition(previousIndex);
var endPosition = GetEndPosition(startPosition.left, builder.Length);
var top = startPosition.top + endPosition.top;
var left = endPosition.left;
Console.SetCursorPosition(left, top);
return;
}
if (Console.CursorLeft < Console.BufferWidth - 1)
Console.CursorLeft++;
else
{
Console.CursorTop++;
Console.CursorLeft = 0;
}
}
private static void BackSpace(ref int index, ConsoleKeyInfo cki, StringBuilder builder)
{
var previousIndex = index;
index--;
var startPosition = GetStartPosition(previousIndex);
ErasePrint(builder, startPosition);
builder.Remove(index, 1);
Console.Write(builder.ToString());
GoBackToCurrentPosition(index, startPosition);
}
private static void Delete(ref int index, ConsoleKeyInfo cki, StringBuilder builder)
{
var startPosition = GetStartPosition(index);
ErasePrint(builder, startPosition);
if (cki.Modifiers == ConsoleModifiers.Control)
{
builder.Remove(index, builder.Length - index);
Console.Write(builder.ToString());
GoBackToCurrentPosition(index, startPosition);
return;
}
builder.Remove(index, 1);
Console.Write(builder.ToString());
GoBackToCurrentPosition(index, startPosition);
}
private static void Default(ref int index, ConsoleKeyInfo cki, StringBuilder builder)
{
var previousIndex = index;
index++;
builder.Insert(previousIndex, cki.KeyChar);
var startPosition = GetStartPosition(previousIndex);
Console.SetCursorPosition(startPosition.left, startPosition.top);
Console.Write(builder.ToString());
GoBackToCurrentPosition(index, startPosition);
}
private static (int left, int top) GetStartPosition(int previousIndex)
{
int top;
int left;
if (previousIndex <= Console.CursorLeft)
{
top = Console.CursorTop;
left = Console.CursorLeft - previousIndex;
}
else
{
var decrementValue = previousIndex - Console.CursorLeft;
var rowsFromStart = decrementValue / Console.BufferWidth;
top = Console.CursorTop - rowsFromStart;
left = decrementValue - rowsFromStart * Console.BufferWidth;
if (left != 0)
{
top--;
left = Console.BufferWidth - left;
}
}
return (left, top);
}
private static void GoBackToCurrentPosition(int index, (int left, int top) startPosition)
{
var rowsToGo = (index + startPosition.left) / Console.BufferWidth;
var rowIndex = index - rowsToGo * Console.BufferWidth;
var left = startPosition.left + rowIndex;
var top = startPosition.top + rowsToGo;
Console.SetCursorPosition(left, top);
}
private static (int left, int top) GetEndPosition(int startColumn, int builderLength)
{
var cursorTop = (builderLength + startColumn) / Console.BufferWidth;
var cursorLeft = startColumn + (builderLength - cursorTop * Console.BufferWidth);
return (cursorLeft, cursorTop);
}
private static void ErasePrint(StringBuilder builder, (int left, int top) startPosition)
{
Console.SetCursorPosition(startPosition.left, startPosition.top);
Console.Write(new string(Enumerable.Range(0, builder.Length).Select(o => ' ').ToArray()));
Console.SetCursorPosition(startPosition.left, startPosition.top);
}
你现在可以这样称呼它:
Console.WriteLine("Calling at start of screen");
string text = XConsole.CancelableReadLine(out bool isCancelled);
if (isCancelled)
{
//Do what you want in here, for instance:
return;
}
你也可以在 Console.Write 之后调用它:
Console.WriteLine("Calling after Console.Write");
Console.Write("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.");
string text = XConsole.CancelableReadLine(out bool isCancelled);
if (isCancelled)
return;
希望这会有所帮助!