Question

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.

Was it helpful?

Solution

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();

        }

    }
}

OTHER TIPS

There is nothing like this built into the framework. Instead of doing it the hard way, simply add a static helper method (available anywhere) that you will call whenever you want to output something:

public static void Output(string message)
{
    if ( IsConsoleApp() )
    {
        // process Console input and output
    }
    else
    {
        // process Windows input and output
    }
}

Then just have one line when you want output to show:

Utils.Output("Hello, World!");

To identify whether running as Console or not, you can use such code:

private static bool? IsConsole = null;
public static void Output(string message)
{
    if (IsConsole == null)
    {
        int width;
        try
        {
            width = Console.WindowWidth;
        }
        catch
        {
            width = 0;
        }
        IsConsole = (width > 0);
    }

    if (IsConsole.Value == true)
    {
        // process Console input and output
    }
    else
    {
        // process Windows input and output
    }
}

Not super elegant, but should work.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top