简介:
- 这是一个类库,正如标题所说,它具有这两个最基本而又强大的功能,有时候,我们可能会需要在ReadLine的过程中就访问已经输入了的内容,但.NET又没有提供这样的功能。
其实在之前已经写过一个文章,也是动态输入,但是太烂了地址:旧的动态输入
功能:
- 在ReadLine的时候就读取已经输入了的内容,提供了完整的封装
- 移动已经输入了的内容,你可以在输入时就将输入内容移动到控制台的任意位置
- 光标移动,插入和覆盖模式,HOME和END键的处理。
- 字符输入事件,在用户按下后,会有两个事件触发,可以通过这两个事件来过滤用户输入内容,例如,仅允许输入数字,只需要判断事件参数即可。
- 密码输入模式,与Linux的密码输入一致,不会显示任何内容,但功能按键以及输入事件仍然可用
- 历史记录功能,它模拟的是Linux系统的命令行输入历史记录功能,比Windows的历史记录功能好那么一丢丢。
- (后续会增加更多功能,如果我需要或者你需要的话。
原理:
- 其实就是对 Console.ReadKey() 的妙用
使用方式:
- 实例化一个DynamicScanner对象。
- 调用DynamicScanner的ReadLine()方法或者QuietReadLine()方法
- 读取正在输入的内容,请使用类实例的InputtingString属性
- 获取已输入内容的起始位置以及末端位置, 可使用StartLeft, StartTop, EndLeft, EndTop属性
- CharInput事件是当字符输入并且已经录入后触发的,PreviewCharInput是字符已经输入,但是并未录入的事件,CharInput要求返回值指定是否停止输入,PreviewCharInput要求返回值指定是否取消录入
如果代码有部分可以优化的部分,或者你有好的点子,欢迎私信我哦~
2021 / 1 / 5: 更新了源代码, 优化了一个小细节: 当此次输入为空时, 不保存历史记录
源代码:
- 创建一个名为Null.DynamicScanner.cs的文件,粘贴以下内容,添加到你的项目中,即可使用
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Null.Library
{
public class DynamicScanner
{
int startTop, startLeft, endTop, endLeft, currentTop, currentLeft, tempEndTop, tempEndLeft;
int inputIndex, historyIndex;
bool insertMode = true, inputting = false, cursorVisible;
private readonly object printting = false; // 用于互斥锁
ConsoleKeyInfo readedKey;
private readonly List<List<char>> inputHistory;
private List<char> inputtingChars;
private string promptText = string.Empty;
public delegate bool CharInputEventHandler(DynamicScanner sender, ConsoleKeyInfo c);
public delegate bool PreviewCharInputEventHandler(DynamicScanner sender, ConsoleKeyInfo c);
public event CharInputEventHandler CharInput;
public event PreviewCharInputEventHandler PreviewCharInput;
public DynamicScanner()
{
inputHistory = new List<List<char>>();
}
public string InputtingString
{
get
{
return inputtingChars == null ? string.Empty : new string(inputtingChars.ToArray());
}
}
public int StartTop { get => startTop; }
public int StartLeft { get => startLeft; }
public int EndTop { get => endTop; }
public int EndLeft { get => endLeft; }
public int CurrentTop { get => currentTop; }
public int CurrentLeft { get => currentLeft; }
public bool IsInputting { get => inputting; }
public string PromptText { get => promptText; set => promptText = value; }
public static bool IsControlKey(ConsoleKey k)
{
return k == ConsoleKey.Enter ||
k == ConsoleKey.UpArrow ||
k == ConsoleKey.DownArrow ||
k == ConsoleKey.LeftArrow ||
k == ConsoleKey.RightArrow ||
k == ConsoleKey.Insert ||
k == ConsoleKey.Backspace ||
k == ConsoleKey.Delete ||
k == ConsoleKey.Home ||
k == ConsoleKey.End;
}
private void InitReadLine()
{
startTop = Console.CursorTop;
startLeft = Console.CursorLeft;
inputIndex = 0;
if (inputHistory.Count == 0 || inputHistory[inputHistory.Count - 1].Count != 0)
{
historyIndex = inputHistory.Count;
inputHistory.Add(new List<char>());
}
else
{
historyIndex = inputHistory.Count - 1;
}
inputtingChars = inputHistory[historyIndex];
inputting = true;
}
private bool DealInputChar()
{
switch (readedKey.Key)
{
case ConsoleKey.Enter:
Console.WriteLine();
return true;
case ConsoleKey.UpArrow:
if (historyIndex > 0)
{
historyIndex--;
UpdateInputState();
}
break;
case ConsoleKey.DownArrow:
if (historyIndex < inputHistory.Count - 1)
{
historyIndex++;
UpdateInputState();
}
break;
case ConsoleKey.LeftArrow:
if (inputIndex > 0)
{
inputIndex--;
}
break;
case ConsoleKey.RightArrow:
if (inputIndex < inputtingChars.Count)
{
inputIndex++;
}
break;
case ConsoleKey.Insert:
insertMode = !insertMode;
break;
case ConsoleKey.Backspace:
if (inputIndex > 0)
{
inputtingChars.RemoveAt(inputIndex - 1);
inputIndex--;
}
break;
case ConsoleKey.Delete:
if (inputIndex < inputtingChars.Count)
{
inputtingChars.RemoveAt(inputIndex);
}
break;
case ConsoleKey.Home:
inputIndex = 0;
break;
case ConsoleKey.End:
inputIndex = inputtingChars.Count;
break;
default:
if (inputIndex == inputtingChars.Count)
{
inputtingChars.Add(readedKey.KeyChar);
}
else
{
if (insertMode)
{
inputtingChars.Insert(inputIndex, readedKey.KeyChar);
}
else
{
inputtingChars[inputIndex] = readedKey.KeyChar;
}
}
inputIndex++;
break;
}
return false;
}
private void PrintInputString()
{
lock(printting)
{
cursorVisible = Console.CursorVisible;
Console.CursorVisible = false;
Console.SetCursorPosition(startLeft, startTop);
Console.Write(promptText);
if (inputIndex == inputtingChars.Count)
{
for (int i = 0; i < inputtingChars.Count; i++)
{
Console.Write(inputtingChars[i]);
}
currentLeft = Console.CursorLeft;
currentTop = Console.CursorTop;
}
else
{
for (int i = 0; i < inputtingChars.Count; i++)
{
if (inputIndex == i)
{
currentLeft = Console.CursorLeft;
currentTop = Console.CursorTop;
}
Console.Write(inputtingChars[i]);
}
}
tempEndLeft = Console.CursorLeft;
tempEndTop = Console.CursorTop;
while (Console.CursorTop < endTop || Console.CursorLeft < endLeft)
{
Console.Write(' ');
}
endLeft = tempEndLeft;
endTop = tempEndTop;
Console.SetCursorPosition(currentLeft, currentTop);
Console.CursorVisible = cursorVisible;
}
}
private void UpdateInputState()
{
inputtingChars = inputHistory[historyIndex];
inputIndex = inputtingChars.Count;
}
public void ClearDisplayBuffer()
{
lock(printting)
{
cursorVisible = Console.CursorVisible;
Console.CursorVisible = false;
Console.SetCursorPosition(startLeft, startTop);
while (Console.CursorTop < endTop || Console.CursorLeft < endLeft)
{
Console.Write(' ');
}
Console.SetCursorPosition(startLeft, startTop);
Console.CursorVisible = cursorVisible;
}
}
public void DisplayTo(int cursorLeft, int cursorTop)
{
lock(printting)
{
cursorVisible = Console.CursorVisible;
Console.CursorVisible = false;
Console.SetCursorPosition(cursorLeft, cursorTop);
startLeft = cursorLeft; startTop = cursorTop;
Console.Write(promptText);
if (inputIndex == inputtingChars.Count)
{
for (int i = 0; i < inputtingChars.Count; i++)
{
Console.Write(inputtingChars[i]);
}
currentLeft = Console.CursorLeft;
currentTop = Console.CursorTop;
}
else
{
for (int i = 0; i < inputtingChars.Count; i++)
{
if (inputIndex == i)
{
currentLeft = Console.CursorLeft;
currentTop = Console.CursorTop;
}
Console.Write(inputtingChars[i]);
}
}
endLeft = Console.CursorLeft;
endTop = Console.CursorTop;
Console.CursorVisible = cursorVisible;
}
}
public void SetInputStart(int cursorLeft, int cursorTop)
{
lock(printting)
{
cursorVisible = Console.CursorVisible;
Console.CursorVisible = false;
Console.SetCursorPosition(startLeft, startTop);
while (Console.CursorTop < endTop || Console.CursorLeft < endLeft)
{
Console.Write(' ');
}
Console.SetCursorPosition(cursorLeft, cursorTop);
startLeft = cursorLeft; startTop = cursorTop;
Console.Write(promptText);
if (inputIndex == inputtingChars.Count)
{
for (int i = 0; i < inputtingChars.Count; i++)
{
Console.Write(inputtingChars[i]);
}
currentLeft = Console.CursorLeft;
currentTop = Console.CursorTop;
}
else
{
for (int i = 0; i < inputtingChars.Count; i++)
{
if (inputIndex == i)
{
currentLeft = Console.CursorLeft;
currentTop = Console.CursorTop;
}
Console.Write(inputtingChars[i]);
}
}
endLeft = Console.CursorLeft;
endTop = Console.CursorTop;
Console.CursorVisible = cursorVisible;
}
}
public string ReadLine()
{
InitReadLine();
PrintInputString();
while (true)
{
readedKey = Console.ReadKey(true);
if (PreviewCharInput != null && PreviewCharInput.Invoke(this, readedKey))
{
continue;
}
if (DealInputChar())
{
inputting = false;
return InputtingString;
}
PrintInputString();
if (CharInput != null && CharInput.Invoke(this, readedKey))
{
inputting = false;
return InputtingString;
}
}
}
public string QuietReadLine()
{
InitReadLine();
PrintInputString();
while (true)
{
readedKey = Console.ReadKey(true);
if (PreviewCharInput != null && PreviewCharInput.Invoke(this, readedKey))
{
continue;
}
if (DealInputChar())
{
inputting = false;
return InputtingString;
}
if (CharInput != null && CharInput.Invoke(this, readedKey))
{
inputting = false;
return InputtingString;
}
}
}
}
}
使用实例:
- 这是一个可以过滤输入的使用实例, 它仅允许用户输入数字, 并且支持使用WSAD按键控制已输入内容在控制台中的移动
using System;
using Null.Library;
namespace LibraryTest
{
class Program
{
static void Main(string[] args)
{
DynamicScanner scanner = new DynamicScanner();
scanner.PreviewCharInput += Scanner_PreviewCharInput;
scanner.CharInput += Scanner_CharInput;
while(true)
{
string temp = scanner.ReadLine();
}
}
private static bool Scanner_CharInput(DynamicScanner sender, ConsoleKeyInfo c)
{
Console.Title = $"Length: {sender.InputtingString.Length}, Inputed: {sender.InputtingString}";
return false;
}
private static bool Scanner_PreviewCharInput(DynamicScanner sender, ConsoleKeyInfo c)
{
if (c.KeyChar >= '0' && c.KeyChar <= '9' || DynamicScanner.IsControlKey(c.Key))
{
return false; // 表示不取消, 即:录入这个字符
}
else
{
switch (c.Key) // 通过WSAD按键可以控制输入内容移动
{
case ConsoleKey.W:
sender.SetInputStart(sender.StartLeft, sender.StartTop - 1);
break;
case ConsoleKey.S:
sender.SetInputStart(sender.StartLeft, sender.StartTop + 1);
break;
case ConsoleKey.A:
sender.SetInputStart(sender.StartLeft - 1, sender.StartTop);
break;
case ConsoleKey.D:
sender.SetInputStart(sender.StartLeft + 1, sender.StartTop);
break;
}
return true;
}
}
}
}
如果你觉得这些内容对你有帮助, 请一定要点个赞,或者收藏它,这是对我莫大的鼓励
也欢迎私信我,交个朋友也是非常棒的~