Reputation: 2831
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.
Upvotes: 3
Views: 9023
Reputation: 2831
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();
}
}
}
Upvotes: 2
Reputation: 66399
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.
Upvotes: -1