Redirecting a Stream to a Text Box in real-time

Intrepid picture Intrepid · Sep 12, 2013 · Viewed 7.8k times · Source

I have got an interesting dilemma where my application can run as a Console App or a Windows Forms App.

Since I do not want to write a shed load of code like this all over my application:

If ( IsConsoleApp() )
{
    // process Console input and output
}
else
{
    // process Windows input and output
}

To prevent this, I have decided to create two methods where I can pass in a TextReader and TextWriter instance and subsequently use these to process input and output, e.g.

public void SetOutputStream( TextWriter outputStream )
{
    _outputStream = outputStream;
}

public void SetInputStream( TextReader inputStream )
{
    _inputStream = inputStream;
}

// To use in a Console App:
SetOutputStream( Console.Out );
SetInputStream( Console.In );

To display some text in the Console window I just need to do something like this:

_outputStream.WriteLine( "Hello, World!");

And the text is magically redirected to the Console.

Now, my issue is how do I do something similar for a Windows application? I have created a form with a read-only Text Box control on it and I want the contents of the _outputStream to be redirected to this text box in real-time.

Also, I want the _inputStream to contain the contents of another Text Box control so that my App can read from this stream instead of the Text Box directly.

Thanks in advance.

Answer

Intrepid picture Intrepid · Sep 13, 2013

I have managed to resolve this by creating a ConcurrentStreamWriter class that inherits StreamWriter and uses a ConcurrentQueue backed up by BackgroundWorker to process the queue's contents.

This is the solution I have come up with:

using System;
using System.Collections.Concurrent;
using System.ComponentModel;
using System.IO;
using System.Threading;
using System.Windows.Forms;

namespace Quest.Core.IO
{
    public class ConcurrentStreamWriter : StreamWriter
    {
        private ConcurrentQueue<String> _stringQueue = new ConcurrentQueue<String>();
        private Boolean _disposing;
        private RichTextBox _textBox;

        public ConcurrentStreamWriter( Stream stream )
            : base( stream )
        {
            CreateQueueListener();
        }

        public ConcurrentStreamWriter( Stream stream, RichTextBox textBox )
            : this( stream )
        {
            _textBox = textBox;
        }

        public override void WriteLine()
        {
            base.WriteLine();
            _stringQueue.Enqueue( Environment.NewLine );
        }

        public override void WriteLine( string value )
        {
            base.WriteLine( value );
            _stringQueue.Enqueue( String.Format( "{0}\n", value ) );
        }

        public override void Write( string value )
        {
            base.Write( value );
            _stringQueue.Enqueue( value );
        }

        protected override void Dispose( bool disposing )
        {
            base.Dispose( disposing );

            _disposing = disposing;
        }

        private void CreateQueueListener()
        {
            var bw = new BackgroundWorker();

            bw.DoWork += ( sender, args ) =>
            {
                while ( !_disposing )
                {
                    if ( _stringQueue.Count > 0 )
                    {
                        string value = string.Empty;
                        if ( _stringQueue.TryDequeue( out value ) )
                        {
                            if ( _textBox != null )
                            {
                                if ( _textBox.InvokeRequired )
                                {
                                    _textBox.Invoke( new Action( () =>
                                    {
                                        _textBox.AppendText( value );
                                        _textBox.ScrollToCaret();
                                    } ) );
                                }
                                else
                                {
                                    _textBox.AppendText( value );
                                    _textBox.ScrollToCaret();
                                }
                            }
                        }
                    }
                }
            };

            bw.RunWorkerAsync();

        }

    }
}