在WPF中实现Console.ReadLine

本文关键字:Console ReadLine 实现 WPF | 更新日期: 2023-09-27 18:12:31

我正试图用C#WPF创建一个应用程序来模拟Windows的命令提示符,但它具有更大的灵活性和输出选项(如显示图像或表单(。我最近一直在尝试模拟Console.ReadLine()。我需要让GUI保持完全响应,允许用户键入输入。同时,我需要能够用同样的方法return的答案。

我已经尝试通过使用事件来解决这个问题,但我不知道如何以不返回void的方式使用它们。我研究了async/await和一个关于它的问题,但不太清楚如何使用这些信息。我考虑了一个事件驱动的解决方案,其中结果将存储在所有输入的永久列表变量中,我可以读取其中的最后一个以获得最新的输入,但我认为这对于我模拟的内容来说还不够好。

我计划在应用程序启动后立即在主线程中创建控制台GUI。然而,我将在另一个线程中使用它的逻辑,这将是我代码的核心(我知道这不是一种专业的编程方式,但这毕竟是一种个人项目/学习体验。(然后,我想使用某种自定义的ReadLine()方法,等待用户提交文本,然后返回。如果可能的话,如何在WPF中完成?

在WPF中实现Console.ReadLine

以下快速而肮脏的代码应该让您知道如何实现您想要的:

public partial class MainWindow : Window {
    public MainWindow() {
        InitializeComponent();
        var console = new MyConsole();
        this.Content = console.Gui;
        Task.Factory.StartNew(() => {
            var read = console.ReadLine();
            console.WriteLine(read);
        });
    }
}
public class MyConsole {
    private readonly ManualResetEvent _readLineSignal;
    private string _lastLine;        
    public MyConsole() {
        _readLineSignal = new ManualResetEvent(false);
        Gui = new TextBox();
        Gui.AcceptsReturn = true;
        Gui.KeyUp += OnKeyUp;
    }
    private void OnKeyUp(object sender, KeyEventArgs e) {
        // this is always fired on UI thread
        if (e.Key == Key.Enter) {
            // quick and dirty, but that is not relevant to your question
            _lastLine = Gui.Text.Split(new string[] { "'r'n"}, StringSplitOptions.RemoveEmptyEntries).Last();
            // now, when you detected that user typed a line, set signal
            _readLineSignal.Set();
        }
    }        
    public TextBox Gui { get; private set;}
    public string ReadLine() {
        // that should always be called from non-ui thread
        if (Gui.Dispatcher.CheckAccess())
            throw new  Exception("Cannot be called on UI thread");
        // reset signal
        _readLineSignal.Reset();
        // wait until signal is set. This call is blocking, but since we are on non-ui thread - there is no problem with that
        _readLineSignal.WaitOne();
        // we got signalled - return line user typed.
        return _lastLine;
    }
    public void WriteLine(string line) {
        if (!Gui.Dispatcher.CheckAccess()) {
            Gui.Dispatcher.Invoke(new Action(() => WriteLine(line)));
            return;
        }
        Gui.Text += line + Environment.NewLine;
    }
}

使用BlockingCollection类。

以下是我认为可以做到的方法:

public class MyConsole
{
    private readonly BlockingCollection<string> m_Lines = new BlockingCollection<string>();
    public string ReadLine()
    {
        return m_Lines.Take();
    }
    private void AddNewLine(string new_line)
    {
        m_Lines.Add(new_line);
    }
    //...
}

AddNewLine方法是一个私有方法,当用户写入内容并点击回车键时,必须调用它。这种情况发生在GUI线程中。

ReadLine方法是一个公共方法,其他线程将调用它来获取新行。

请注意,如果有存储的项,此调用将返回该项,或者将等待AddNewLine方法添加新项。这种阻塞是BlockingCollection类的一个特性。