Creating a static Messages Viewer

S

superseed

Hi,

I'm pretty new to C#, and I'm quite stuck on the following problem.

I would like to add to my application a Windows.Form (singleton) on
which I could display a message of one of the following type :
Exception, Error, Warning, Infos (with differents colors, etc).

I would like to use it like the Console.WriteLine("My Message"); on
everywhere in my application.

Something like that :

Code:
using MyApp.Tools;
//popup (if not already displayed) and display the error
LogViewer.WriteLine(MessageType.Error, "Something bad happenend");

My application is multithreaded.

I already tried many things but I always have problems ("Windows Handle
Already Exists", form not refreshing (aka no messages loop), form not
displaying, etc.)

Sorry but I'm out of ideas. So if any one is ready to give me a very
useful help it would be really appreciated !

Here is my test class, lot's of things are commented because I made a
lot's of test.

Main questions and remarks are :

- Is the delegate way of doing ok ?
- Since this code can be called before the main Application.Run(new
MainForm) should I call an Application.Run() for this form ?
- How to cleanly prevent the component of the form not to be released
when closing the form


Ok here we go :


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

namespace Tools
{
/// <summary>
/// A singleton to display message in a windows form
/// </summary>
public class LogViewer : System.Windows.Forms.Form
{
//a delegate to update the rich text box from everywhere (another
thread)
private delegate void UpdateLogRtb(MessageType _Type, string _Text);

public enum MessageType
{
Info,
Error,
Exception
}

/// <summary>
/// Required designer variable.
/// </summary>
///

//common to all instances

private static bool m_IsDisplayed;
static private int m_CurrentLine;

private static System.Windows.Forms.RichTextBox richTextBox = new
RichTextBox();
private static System.Windows.Forms.Button btnSaveAs = new
System.Windows.Forms.Button();
private static System.Windows.Forms.Button btnClearLog = new
System.Windows.Forms.Button();
private static System.Threading.Thread m_UiThread;
static private LogViewer m_LogViewer = new LogViewer();

static LogViewer()
{
InitializeComponent();

m_IsDisplayed = false;
m_CurrentLine = 0;
}

/// <summary>
/// Private constructor, singleton
/// </summary>
private LogViewer()
{
//new thread ready to start
//m_UiThread = new System.Threading.Thread(new
System.Threading.ThreadStart(UIThread));
//m_UiThread.Start();
}

//private void UIThread()
//{
// Application.Run(Instance);
//}

public static LogViewer Instance
{
get
{
return m_LogViewer;
}
}

public static void UpdateLog(MessageType _Type, string _Text)
{
if ( Instance.InvokeRequired )
{
UpdateLogRtb delUpdate = new UpdateLogRtb(UpdateLog);
Instance.BeginInvoke(delUpdate, new object[]{_Type, _Text});
}
else
{
lock(richTextBox)
{
if(!m_IsDisplayed)
{
Instance.Show();
m_IsDisplayed = true;
}

Instance.SetColor(_Type);
richTextBox.AppendText(_Text);
}
}
}

void SetColor(MessageType _Type)
{
switch(_Type)
{
case MessageType.Error : richTextBox.SelectionColor =
System.Drawing.Color.Fuchsia;
break;
case MessageType.Exception : richTextBox.SelectionColor =
System.Drawing.Color.Red;
break;
case MessageType.Info : richTextBox.SelectionColor =
System.Drawing.Color.Blue;
break;
}
}

public static void WriteLine(MessageType _Type ,string _Text,
object[] _Parameters)
{
IEnumerator iter = _Parameters.GetEnumerator();
Write(_Type, "[" + DateTime.Now.ToShortDateString() + " " +
DateTime.Now.ToLongTimeString() + "] "+ _Text + " ");
while(iter.MoveNext())
{
Write(_Type, iter.Current.ToString());
Write(_Type, " ");
}
Write(_Type, "\n");
}

public static void WriteLine(MessageType _Type ,string _Text, object
_Parameter)
{
Write(_Type, "[" + DateTime.Now.ToShortDateString() + " " +
DateTime.Now.ToLongTimeString() + "] "+ _Text + " ");
Write(_Type, _Parameter.ToString());
Write(_Type,"\n");
}

public static void WriteLine(MessageType _Type, string _Text)
{
Write(_Type, "[" + DateTime.Now.ToShortDateString() + " " +
DateTime.Now.ToLongTimeString() + "] "+ _Text + " ");
Write(_Type,"\n");
m_CurrentLine++;
}

public static void Write(MessageType _Type, string _Text)
{
UpdateLog(_Type, _Text);
}

/// <summary>
/// Clean up any resources being used.
/// </summary>
protected override void Dispose( bool disposing )
{
m_IsDisplayed = false;
}

#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private static void InitializeComponent()
{
Instance.SuspendLayout();
//
// richTextBox
//
richTextBox.Cursor = System.Windows.Forms.Cursors.Arrow;
richTextBox.Location = new System.Drawing.Point(0, 0);
richTextBox.Name = "richTextBox";
richTextBox.ReadOnly = true;
richTextBox.Size = new System.Drawing.Size(640, 250 );
richTextBox.TabIndex = 0;
richTextBox.Text = "";
richTextBox.WordWrap = false;
//
// btnSaveAs
//
btnSaveAs.Location = new System.Drawing.Point(446, 254);
btnSaveAs.Name = "btnSaveAs";
btnSaveAs.Size = new System.Drawing.Size(89, 23);
btnSaveAs.TabIndex = 1;
btnSaveAs.Text = "Save as...";
btnSaveAs.Click += new System.EventHandler(btnSaveAs_Click);
//
// btnClearLog
//
btnClearLog.Location = new System.Drawing.Point(538, 254);
btnClearLog.Name = "btnClearLog";
btnClearLog.Size = new System.Drawing.Size(100, 23);
btnClearLog.TabIndex = 2;
btnClearLog.Text = "Clear Log";
btnClearLog.Click += new System.EventHandler(btnClearLog_Click);
//
// LogViewer
//
//m_LogViewer.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
Instance.ClientSize = new System.Drawing.Size(640, 280);
Instance.Controls.Add(btnClearLog);
Instance.Controls.Add(btnSaveAs);
Instance.Controls.Add(richTextBox);
Instance.FormBorderStyle =
System.Windows.Forms.FormBorderStyle.FixedToolWindow;
Instance.Name = "LogViewer";
Instance.Text = "Events viewer";
Instance.Closing += new
System.ComponentModel.CancelEventHandler(LogViewer_Closing);
Instance.ResumeLayout(false);

}
#endregion


private static void LogViewer_Closing(object sender,
System.ComponentModel.CancelEventArgs e)
{
Instance.Hide();
}

private static void btnSaveAs_Click(object sender, System.EventArgs
e)
{
SaveFileDialog saveFile = new SaveFileDialog();
saveFile.FileName = "QueueMeEvents.log";
if (saveFile.ShowDialog() != DialogResult.Cancel)
{
StreamWriter mySaveFile = File.CreateText(@saveFile.FileName);
mySaveFile.AutoFlush = true;
mySaveFile.Write(richTextBox.Text);
mySaveFile.Close();
}
}

private static void btnClearLog_Click(object sender, System.EventArgs
e)
{
richTextBox.Clear();
m_CurrentLine = 0;
}

// protected override void OnPaint(PaintEventArgs e)
// {
// base.OnPaint (e);
// Application.DoEvents();
// Invalidate();
// System.Threading.Thread.Sleep(100);
// }
}
}


Thanks !
 
N

Nicholas Paldino [.NET/C# MVP]

I wouldn't expose the form as a singleton. Rather, I would expose
static methods which would access a private static member which stores the
form in a field.

This form can not and should not be called before the call to
Application.Run. The message loop for the application hasn't been set up
yet, and it can cause unexpected side effects.

Basically, you would expose your method that will take your message
parameters. The first thing you would do is check the field to see if the
form is created (the check would be thread-safe, using a lock statement).
If it is, then you continue. Otherwise, you ignore the message (or cache it
for when the form is actually created).

Then, you would make a call to the InvokeRequired property on the form.
If it returns true, you would create a delegate with the signature of your
method, passing the delegate and the parameters to your method to the Invoke
method on the form (so that the calls are made on the correct thread). If
InvokeRequired is set to false, then you append or set the text elements to
the message.

You would also require a method which would be called when your main
form is run (through Application.Run). This method would check the field to
see if it is null (again, in a thread-safe way). If it is null, then you
create the new instance of your form, and store it in the field, otherwise,
you just exit your method.

If you created your form, and you cached messages, you would also
display them when your form is created (of course, you would make calls to
your method which would handle calling the method on the correct thread).

Hope this helps.


--
- Nicholas Paldino [.NET/C# MVP]
- (e-mail address removed)

Hi,

I'm pretty new to C#, and I'm quite stuck on the following problem.

I would like to add to my application a Windows.Form (singleton) on
which I could display a message of one of the following type :
Exception, Error, Warning, Infos (with differents colors, etc).

I would like to use it like the Console.WriteLine("My Message"); on
everywhere in my application.

Something like that :

Code:
using MyApp.Tools;
//popup (if not already displayed) and display the error
LogViewer.WriteLine(MessageType.Error, "Something bad happenend");

My application is multithreaded.

I already tried many things but I always have problems ("Windows Handle
Already Exists", form not refreshing (aka no messages loop), form not
displaying, etc.)

Sorry but I'm out of ideas. So if any one is ready to give me a very
useful help it would be really appreciated !

Here is my test class, lot's of things are commented because I made a
lot's of test.

Main questions and remarks are :

- Is the delegate way of doing ok ?
- Since this code can be called before the main Application.Run(new
MainForm) should I call an Application.Run() for this form ?
- How to cleanly prevent the component of the form not to be released
when closing the form


Ok here we go :


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

namespace Tools
{
/// <summary>
/// A singleton to display message in a windows form
/// </summary>
public class LogViewer : System.Windows.Forms.Form
{
//a delegate to update the rich text box from everywhere (another
thread)
private delegate void UpdateLogRtb(MessageType _Type, string _Text);

public enum MessageType
{
Info,
Error,
Exception
}

/// <summary>
/// Required designer variable.
/// </summary>
///

//common to all instances

private static bool m_IsDisplayed;
static private int m_CurrentLine;

private static System.Windows.Forms.RichTextBox richTextBox = new
RichTextBox();
private static System.Windows.Forms.Button btnSaveAs = new
System.Windows.Forms.Button();
private static System.Windows.Forms.Button btnClearLog = new
System.Windows.Forms.Button();
private static System.Threading.Thread m_UiThread;
static private LogViewer m_LogViewer = new LogViewer();

static LogViewer()
{
InitializeComponent();

m_IsDisplayed = false;
m_CurrentLine = 0;
}

/// <summary>
/// Private constructor, singleton
/// </summary>
private LogViewer()
{
//new thread ready to start
//m_UiThread = new System.Threading.Thread(new
System.Threading.ThreadStart(UIThread));
//m_UiThread.Start();
}

//private void UIThread()
//{
// Application.Run(Instance);
//}

public static LogViewer Instance
{
get
{
return m_LogViewer;
}
}

public static void UpdateLog(MessageType _Type, string _Text)
{
if ( Instance.InvokeRequired )
{
UpdateLogRtb delUpdate = new UpdateLogRtb(UpdateLog);
Instance.BeginInvoke(delUpdate, new object[]{_Type, _Text});
}
else
{
lock(richTextBox)
{
if(!m_IsDisplayed)
{
Instance.Show();
m_IsDisplayed = true;
}

Instance.SetColor(_Type);
richTextBox.AppendText(_Text);
}
}
}

void SetColor(MessageType _Type)
{
switch(_Type)
{
case MessageType.Error : richTextBox.SelectionColor =
System.Drawing.Color.Fuchsia;
break;
case MessageType.Exception : richTextBox.SelectionColor =
System.Drawing.Color.Red;
break;
case MessageType.Info : richTextBox.SelectionColor =
System.Drawing.Color.Blue;
break;
}
}

public static void WriteLine(MessageType _Type ,string _Text,
object[] _Parameters)
{
IEnumerator iter = _Parameters.GetEnumerator();
Write(_Type, "[" + DateTime.Now.ToShortDateString() + " " +
DateTime.Now.ToLongTimeString() + "] "+ _Text + " ");
while(iter.MoveNext())
{
Write(_Type, iter.Current.ToString());
Write(_Type, " ");
}
Write(_Type, "\n");
}

public static void WriteLine(MessageType _Type ,string _Text, object
_Parameter)
{
Write(_Type, "[" + DateTime.Now.ToShortDateString() + " " +
DateTime.Now.ToLongTimeString() + "] "+ _Text + " ");
Write(_Type, _Parameter.ToString());
Write(_Type,"\n");
}

public static void WriteLine(MessageType _Type, string _Text)
{
Write(_Type, "[" + DateTime.Now.ToShortDateString() + " " +
DateTime.Now.ToLongTimeString() + "] "+ _Text + " ");
Write(_Type,"\n");
m_CurrentLine++;
}

public static void Write(MessageType _Type, string _Text)
{
UpdateLog(_Type, _Text);
}

/// <summary>
/// Clean up any resources being used.
/// </summary>
protected override void Dispose( bool disposing )
{
m_IsDisplayed = false;
}

#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private static void InitializeComponent()
{
Instance.SuspendLayout();
//
// richTextBox
//
richTextBox.Cursor = System.Windows.Forms.Cursors.Arrow;
richTextBox.Location = new System.Drawing.Point(0, 0);
richTextBox.Name = "richTextBox";
richTextBox.ReadOnly = true;
richTextBox.Size = new System.Drawing.Size(640, 250 );
richTextBox.TabIndex = 0;
richTextBox.Text = "";
richTextBox.WordWrap = false;
//
// btnSaveAs
//
btnSaveAs.Location = new System.Drawing.Point(446, 254);
btnSaveAs.Name = "btnSaveAs";
btnSaveAs.Size = new System.Drawing.Size(89, 23);
btnSaveAs.TabIndex = 1;
btnSaveAs.Text = "Save as...";
btnSaveAs.Click += new System.EventHandler(btnSaveAs_Click);
//
// btnClearLog
//
btnClearLog.Location = new System.Drawing.Point(538, 254);
btnClearLog.Name = "btnClearLog";
btnClearLog.Size = new System.Drawing.Size(100, 23);
btnClearLog.TabIndex = 2;
btnClearLog.Text = "Clear Log";
btnClearLog.Click += new System.EventHandler(btnClearLog_Click);
//
// LogViewer
//
//m_LogViewer.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
Instance.ClientSize = new System.Drawing.Size(640, 280);
Instance.Controls.Add(btnClearLog);
Instance.Controls.Add(btnSaveAs);
Instance.Controls.Add(richTextBox);
Instance.FormBorderStyle =
System.Windows.Forms.FormBorderStyle.FixedToolWindow;
Instance.Name = "LogViewer";
Instance.Text = "Events viewer";
Instance.Closing += new
System.ComponentModel.CancelEventHandler(LogViewer_Closing);
Instance.ResumeLayout(false);

}
#endregion


private static void LogViewer_Closing(object sender,
System.ComponentModel.CancelEventArgs e)
{
Instance.Hide();
}

private static void btnSaveAs_Click(object sender, System.EventArgs
e)
{
SaveFileDialog saveFile = new SaveFileDialog();
saveFile.FileName = "QueueMeEvents.log";
if (saveFile.ShowDialog() != DialogResult.Cancel)
{
StreamWriter mySaveFile = File.CreateText(@saveFile.FileName);
mySaveFile.AutoFlush = true;
mySaveFile.Write(richTextBox.Text);
mySaveFile.Close();
}
}

private static void btnClearLog_Click(object sender, System.EventArgs
e)
{
richTextBox.Clear();
m_CurrentLine = 0;
}

// protected override void OnPaint(PaintEventArgs e)
// {
// base.OnPaint (e);
// Application.DoEvents();
// Invalidate();
// System.Threading.Thread.Sleep(100);
// }
}
}


Thanks !
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top