将Console.WriteLine()重定向到文本框

38

我正在使用C#在Visual Studio 2010中构建此应用程序。

基本上有两个文件,form1.cs(窗体)和program.cs(包含所有逻辑的位置)。

//form1.cs
public partial class Form1 : Form
{
    //runButton_click function
}

//program.cs
class Program
{
    static void Main()
    {
        while(blah-condition)
        {
            //some calculation
            Console.WriteLine("Progress " + percent + "% completed.");
        }
    }
}

有一个“运行”按钮和一个空白的文本框

当用户点击“运行”按钮时,program.cs将执行一些任务,并使用Console.WriteLine()在控制台(命令提示符)上不断打印进度。

问题:我如何将内容打印到form1上的文本框中,而不是打印到命令提示符中? 我需要不断地打印进度,而不需要任何用户操作。

提前致谢!

顺便说一下,它不一定非得是文本框,它可以是标签或其他能够接受文本的东西。我选择文本框是因为这对我来说更有意义。


1
虽然您的原始问题似乎已得到解答,但请注意,如果您向文本框发送大量更新,则可能会淹没您的消息队列,导致表单变得非常不响应。表单的响应性还取决于“某些计算”的强度。您可能需要考虑使用后台工作线程,将进度报告回传给表单。 - Rick Davin
1
请注意,这样的替代方案需要具备将所有控制台写入更改为其他内容的能力。如果他无法控制该代码(即它是库代码),那么这可能不是一个选择,或者可能只是不切实际的。@RickDavin - Servy
4个回答

76

首先创建一个新的TextWriter,它能够向文本框中写入内容。 它只需要重写接受char参数的Write方法,但这样做效率非常低,所以最好至少覆盖使用字符串参数的该方法。

public class ControlWriter : TextWriter
{
    private Control textbox;
    public ControlWriter(Control textbox)
    {
        this.textbox = textbox;
    }

    public override void Write(char value)
    {
        textbox.Text += value;
    }

    public override void Write(string value)
    {
        textbox.Text += value;
    }

    public override Encoding Encoding
    {
        get { return Encoding.ASCII; }
    }
}
在这种情况下,我已经让它接受一个“控件”,可以是一个“文本框”,一个“标签”或任何其他控件。如果您想将其更改为仅使用“标签”,那也可以。
然后,只需将控制台输出设置为指向某个文本框或标签的新编写器实例:
Console.SetOut(new ControlWriter(textbox1));

如果您希望输出不仅写入文本框,而且也写入控制台,则可以使用此类创建一个编写器,该编写器将写入多个编写器:

public class MultiTextWriter : TextWriter
{
    private IEnumerable<TextWriter> writers;
    public MultiTextWriter(IEnumerable<TextWriter> writers)
    {
        this.writers = writers.ToList();
    }
    public MultiTextWriter(params TextWriter[] writers)
    {
        this.writers = writers;
    }

    public override void Write(char value)
    {
        foreach (var writer in writers)
            writer.Write(value);
    }

    public override void Write(string value)
    {
        foreach (var writer in writers)
            writer.Write(value);
    }

    public override void Flush()
    {
        foreach (var writer in writers)
            writer.Flush();
    }

    public override void Close()
    {
        foreach (var writer in writers)
            writer.Close();
    }

    public override Encoding Encoding
    {
        get { return Encoding.ASCII; }
    }
}

然后,我们可以这样做:

Console.SetOut(new MultiTextWriter(new ControlWriter(textbox1), Console.Out));

我应该将 "Console.SetOut(new TextboxWriter(textbox1));" 放在 program.cs 中的 Main() 函数内吗?program.cs 无法识别 form1.cs 中的 textbox1,因为它不存在于 program.cs 中。 - sora0419
@sora0419 不,你需要将它放在表单的定义中。 - Servy
当我创建ControlWriter时,出现错误提示“'ControlWriter'未实现继承的抽象成员'System.IO.TextWriter.Encoding.get'”,我错过了什么? - sora0419
@sora0419 只需添加一个编码的实现。 - Servy
如果代码是从非 UI 线程调用的,它可能会因为试图从非 UI 线程访问表单而生成错误。如果是这种情况,您可能需要调用到 UI 线程,但如果需要这样做,那将会有显著的性能后果。 - Servy
显示剩余5条评论

2
我使用类似这样的代码来创建列表框:

    public class ListBoxWriter : TextWriter //this class redirects console.writeline to debug listbox
{
    private readonly ListBox _list;
    private StringBuilder _content = new StringBuilder();

    public ListBoxWriter(ListBox list)
    {
        _list = list;
    }

    public override Encoding Encoding
    {
        get { return Encoding.UTF8; }
    }
    public override void Write(char value)
    {
        base.Write(value);
        _content.Append(value);

        if (value != '\n') return;
        if (_list.InvokeRequired)
        {
            try
            {
                _list.Invoke(new MethodInvoker(() => _list.Items.Add(_content.ToString())));
                _list.Invoke(new MethodInvoker(() => _list.SelectedIndex = _list.Items.Count - 1));
                _list.Invoke(new MethodInvoker(() => _list.SelectedIndex = -1));
            }
            catch (ObjectDisposedException ex)
            {
                Console.WriteLine(Resources.Exception_raised + " (" + ex.Message + "): " + ex);
            }
        }
        else
        {
            _list.Items.Add(_content.ToString());
            _list.SelectedIndex = _list.Items.Count - 1;
            _list.SelectedIndex = -1;
        }
        _content = new StringBuilder();
    }
}

在我的主应用程序中:
_writer = new ListBoxWriter(DebugWin); // DebugWin is the name og my listbox

Console.SetOut(_writer);


1

不确定是否有效,但您可以尝试重定向控制台输出。

使用Console.SetOut()并创建TextWriter的派生类,覆盖WriteLine()方法,并将方法参数简单地分配给您的TextBox.Text。 应该可以工作。


-3
文本框放在表单上(启用多行),然后您可以在循环中执行操作。
txtOutput.Text += "Progress " + percent + "% completed." + Environment.NewLine();

问题在于,循环在program.cs中,它无法识别textbox,而textbox只存在于form1.cs中。 - sora0419
不要使用控制台应用程序...只需使用简单的 GUI 应用程序来完成。 - Dan Hunex

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接