March 25, 2019
Hot Topics:

Redirect I/O to a TextBoxWriter in .NET

  • April 17, 2006
  • By Paul Kimmel
  • Send Email »
  • More Articles »

A data stream is like a river where you set data adrift. You put data on the river and it floats along, waiting to be plucked out at some other location. As with streams in nature, you can move up or down the stream, see what's there, and put in or take out things. Streams in .NET work that way too. In .NET, the console is represented by the stream class/metaphor. When you write a statement such as Console.WriteLine, the argument to WriteLine is put on the stream and it usually shows up in a command window.

Sometimes, though, you want basic output to go somewhere else besides the console (or DOS prompt). For example, System.Diagnostics sends Debug statements to the Output window in Visual Studio, and NUnit redirects Console.WriteLine statements to an internal window in its NUnit GUI. Well, you can redirect console output for your applications too, and this article shows you how. Specifically, you'll learn how to redirect Console output streams to a TextBox instead of the command window.

Creating a Custom TextWriter

The System.Console class has an Out property. The Out property is an instance of a TextWriter. To redirect Console output statements, you have to define a class that inherits from System.IO.TextWriter and replaces the default value of Out to an instance of your custom class. After you define and create an instance of your custom TextWriter, you replace the value of Out by calling Console.SetOut.

After defining the custom TextWriter class, you need to define two things:

  1. A control that will be the new output locus
  2. An event handler that is fired when the Windows handle of the new output control is created. (You need the Windows handle because you can't perform tasks such as sending text to a TextBox until that TextBox's Windows handle is created.)

Listing 1 shows the custom TextWriter import statements, class header, and fields.

Listing 1: The Stub for the Custom TextWriter

Imports System.Windows.Forms
Imports System.Text
Public Class TextBoxWriter
   Inherits System.IO.TextWriter
   Private control As TextBoxBase
   Private Builder As StringBuilder
End Class

Implementing the Constructor

You add the constructor to the code in Listing 1. The constructor accepts a TextBox as an argument to the constructor and wires up the HandleCreated event handler for the TextBox. Listing 2 is the code for passing a textbox control to the Sub New constructor and wiring up an event handler for the TextBox's HandleCreated event. (The entire TextBoxWriter class is listed at the end of the article.)

Listing 2: Pass a TextBox Control and Wire Up an Event Handler

Public Sub New(ByVal control As TextBox)
   Me.control = control
   AddHandler control.HandleCreated, _
      New EventHandler(AddressOf OnHandleCreated)
End Sub

You don't create the StringBuilder field in the constructor because you need it only temporarily: when data is sent to the TextBoxWriter and until the TextBox's HandleCreated event fires.

Buffering I/O Until the TextBox Is Created

You can send text to the Console by using Console.Write or Console.WriteLine before a TextBox can actually handle the text. So, you need to implement a buffering scheme that can store text sent to the TextBoxWriter until the TextBox's handle is created. Once the handle is created, you can flush the buffer and forward any additional text directly to the control. Listing 3 contains a combination of Write and WriteLine methods and private subroutines for buffering text. The Public Write and WriteLine methods support basic output behaviors, and the private methods manage buffering text until the real output target is created. (You add the code in Listing 3 to the TextBoxWriter class from Listing 1.)

Listing 3: Public Write and WriteLine Methods, and Private Methods

Public Overrides Sub Write(ByVal ch As Char)
End Sub
Public Overrides Sub Write(ByVal s As String)
   If (control.IsHandleCreated) Then
   End If
End Sub
Public Overrides Sub WriteLine(ByVal s As String)
   Write(s + Environment.NewLine)
End Sub
Private Sub BufferText(ByVal s As String)
   If (Builder Is Nothing) Then
      Builder = New StringBuilder()
   End If
End Sub
Private Sub AppendText(ByVal s As String)
   If (Builder Is Nothing = False) Then
      Builder = Nothing
   End If
End Sub

All Write invocations end up at Write(ByVal s as String). This Write statement checks the control to see whether the handle has been created. If the TextBox's handle is created, it forwards the text to the control by using AppendText. If the control's handle hasn't been created yet, it buffers the text in the StringBuilder using the BufferText method. (The StringBuilder is created on demand, if needed, when BufferText is called.)

Knowing When the TextBox's Handle Is Created

The last bit of code is wired to the TextBox's HandleCreated event property. Listing 4 demonstrates how you can flush the buffer and release the StringBuilder when the TextBox's handle is created.

Listing 4: OnHandleCreated Responds When the TextBox's Handle Is Created

Private Sub OnHandleCreated(ByVal sender As Object, _
   ByVal e As EventArgs)
   If (Builder Is Nothing = False) Then
      Builder = Nothing
   End If
End Sub

Page 1 of 2

Comment and Contribute


(Maximum characters: 1200). You have characters left.



Enterprise Development Update

Don't miss an article. Subscribe to our newsletter below.

Thanks for your registration, follow us on our social networks to keep up-to-date