This tip is targeted for a specific task in vb.net. The problem, not actually a problem but the task is to edit the content in the RichTextBox of our Windows application through Wordpad and just update our control with that formatted text.
Well, the simple way to do this job is 1. Open the Editor – wordpad. 2. Edit the textcontent 3. Copy and paste it to the textbox.
Pretty Simple.
But, let’s look into it with a different approach. And I did it something like this – 1. On Edit button click, a. Save the content of RichTextBox to a temporary “.rtf” file. b. Start a wordpad process to open this file 2. Edit the wordpad, and just close the window, saving the file.
3. As the process exits, raise the exit event of process and call a method to update the text.
And for this the code looks something like this :
Private WithEvents WordpadProcess As New Process Public strTempFileName As String = "C:TempDemo.rtf" Private Delegate Sub UpdateRichTextBox(ByVal strFileName As String) Private Sub btnEdit_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnEdit.Click 'Save the rich textbox content to temporary file rtbTextArea.SaveFile(strTempFileName, RichTextBoxStreamType.RichText) 'Open the temporary file in Wordpad OpenEditor(strTempFileName) End Sub Private Sub OpenEditor(ByVal strFileName As String) Try 'Open the wordpad window WordpadProcess.EnableRaisingEvents = True WordpadProcess.StartInfo.FileName = strFileName WordpadProcess.Start() Catch ex As System.Exception MsgBox(ex.Message, MsgBoxStyle.Information) End Try End Sub Private Sub WordpadProcess_Exited(ByVal sender As Object, ByVal e As System.EventArgs) Handles WordpadProcess.Exited Try 'Invoke method to update the Rich Text Box content Dim delUpdateRichText As UpdateRichTextBox = AddressOf UpdateTextBox Me.Invoke(delUpdateRichText, New Object() {"C:TempDemo.rtf"}) Catch ex As System.Exception MsgBox(ex.Message, MsgBoxStyle.Information) End Try End Sub Private Sub UpdateTextBox(ByVal strFileName As String) 'Update the Rich Text Box content with temporary file content rtbTextArea.LoadFile(strFileName, RichTextBoxStreamType.RichText) End Sub
So here I just want to raise two important points through this tip :
1. How can we do cross-thread update?
2. How can we track another process’ exit?
Let’s first go to the first point:
Often for a newbie, one exception is obvious for at least once while working with multithreading – “Crossthread operation not valid : Control ‘rtbTextArea’ accessed from a thread other than the thread it was created on.”
And it occurs when we try something like this in demo presented above
Private Sub WordpadProcess_Exited(ByVal sender As Object, ByVal e As System.EventArgs) Handles WordpadProcess.Exited Try 'Update the Richtextbox on Wordpad's exited event rtbTextArea.LoadFile("C:TempDemo.rtf", RichTextBoxStreamType.RichText) Catch ex As System.Exception MsgBox(ex.Message, MsgBoxStyle.Information) End Try End Sub
The cause is that WordpadProcess runs in a separate thread, and we want to update the control in another running thread, i.e. our windows application. So, it simply raises a cross thread exception. There are different ways to handle this[1], either by using BackgroundWorker or Delegates.
Here delegate approach is used to make thread safe call.
1. Define a delegate to update the textbox Private Delegate Sub UpdateRichTextBox(ByVal strFileName As String)
2. Invoke the method through delegate
Dim delUpdateRichText As UpdateRichTextBox = AddressOf UpdateTextBox
Me.Invoke(delUpdateRichText, New Object() {“C:TempDemo.rtf”})
Now let’s think about the second point: What if we want to perform some operations that depends on some other process. The example is clear here, we want to edit the text in Wordpad and on closing the wordpad we want to update that changes to richtextbox in our application.
So, in .Net, System.Diagnostics.Process[2] class provides access and control(start/stop) to local and remote processes. We can start any process from within our application by simply calling Process.Start() method and kill the process by calling Process.Kill(). And for our purpose we can use the Exited[3] event of Process class. This event is raised whenever the target process is exited. But, it is mandatory to assign EnableRaisingEvents= true[4] for that process.
So the summerized steps are :
1. Get the process
Private WithEvents WordpadProcess As New Process WordpadProcess.StartInfo.FileName = strFileName
2. Set EnableRaisingEvents property to True
WordpadProcess.EnableRaisingEvents = True
3. Start the Process
WordpadProcess.Start()
4. Define the Handler method to handle the exited event
Private Sub WordpadProcess_Exited(ByVal sender As Object, ByVal e As System.EventArgs) Handles WordpadProcess.Exited
References :
[1]Make Thread safe calls : http://msdn.microsoft.com/en-us/library/ms171728(v=VS.80).aspx
[2]Process Class : http://msdn.microsoft.com/en-us/library/ccf1tfx0.aspx
[3]Process.Exited Event : http://msdn.microsoft.com/en-us/library/system.diagnostics.process.exited.aspx
[4]EnableRaisingEvents : http://msdn.microsoft.com/en-us/library/system.diagnostics.process.enableraisingevents.aspx