Reputation: 5157
In a console application, I have a class called MyPolling which when started, monitors an external resource until halted. Inside of the monitoring loop, I use an injected implementation of ILogger, which the service uses to write a message about the status of the polling class.
Sometimes I will want an implementation of ILogger to write to the console, other times I may want to show a window and also write the output to that window in addition to the console.
However when I run the following with showLog = true, the visualLog window is not being updated (spinning "wait" icon). For both showLog = {true,false} the console logger is being updated correctly.
Obviously I have some sort of thread issue, but I'm not sure where I went wrong.
static void Main(string[] args)
{
bool showLog = true; //obviously will set this from args
VisualLogger visualLogger = null;
ILogger logger;
if (showLog)
{
//create the visual logger, and use that to create the debug logger
visualLogger = new VisualLogger();
logger = new DebugLogger(visualLogger);
}
else
{
//create the "normal" debug logger
logger = new DebugLogger();
}
//create the polling class and inject the logger and then start the thread
var svc = new MyPolling(logger);
var pollThread = new Thread(new ThreadStart(svc.BeginPolling));
pollThread.Start();
//if the visualLogger wasnt created, don't show it.
if (visualLogger!=null)
visualLogger.Show();
Console.ReadLine();
svc.Dispose();
}
public interface ILogger
{
void Write(string message);
}
public class DebugLogger : ILogger
{
private ILogger _baseLogger;
public DebugLogger(ILogger logger = null)
{
_baseLogger = logger;
}
public void Write(string message)
{
if (_baseLogger != null)
_baseLogger.Write(message);
Console.WriteLine(message);
}
}
And my implementation of VisualLogger:
public partial class VisualLogger : Form, ILogger
{
public VisualLogger()
{
InitializeComponent();
txtLog.Clear();
}
public void Write(string message)
{
txtLog.Text += message + Environment.NewLine;
}
}
Upvotes: 1
Views: 55
Reputation: 761
It comes from the fact that Form needs a message loop to process events. Form.ShowDialog performs its own message loop, whereas Show does not. If you call Show method from another form then there is a message loop for that but in your case which you call it from a console app, you need to use ShowDialog instead. Also you cannot edit UI controls from other threads, and you need the Invoke method. so you need these changes:
public partial class VisualLogger : Form, ILogger
{
public VisualLogger()
{
InitializeComponent();
txtLog.Clear();
}
private void WriteInternal(string message)
{
txtLog.Text += message + Environment.NewLine;
}
public void Write(string message)
{
txtLog.Invoke(new Action<string>(WriteInternal), message);
// or simpler remove the WriteInternal function and use the next line:
// txtLog.Invoke(new Action(() => txtLog.Text += message + Environment.NewLine));
}
}
in Main:
if (visualLogger != null)
visualLogger.ShowDialog();
Or if you want to continue your main thread:
Thread t = new Thread(new ThreadStart(
() =>
{
if (visualLogger != null)
visualLogger.ShowDialog();
}));
t.Start();
Upvotes: 2
Reputation: 1479
You have to update the form control from the UI thread. Something like this should do it:
txtLog.Invoke((sender, args) => (sender as TextBox).Text = "text");
Upvotes: 1