I'm a complete newbie to WPF/C# and have a project which I'm trying to implement a way to output information to a textbox labelled LogText live from a Process instance. I will detail how I currently have it setup and after, what issues I'm facing:
In MainWindow.xaml.cs just to show the form which works:
private void Button_Click(object sender, RoutedEventArgs e) {
xLogger.Instance.Show();
}
I'm pulling it statically from the LoggerView.xaml.cs:
public static xLogger _Instance;
public static xLogger Instance { get { return _Instance; } set { _Instance = value; } }
I also have a small (probably useless) method for now inside this:
public void AddEntry(string entry) { LogText.AppendText(entry); }
The process I'm running for tests is dism.exe CMD tool with a DataReceivedEventHandler:
public static void Run() {
Process dProcess = new Process();
dProcess.StartInfo.FileName = "dism.exe";
dProcess.StartInfo.Arguments = "/online /cleanup-image /restorehealth";
dProcess.StartInfo.UseShellExecute = false;
dProcess.StartInfo.RedirectStandardOutput = true;
dProcess.OutputDataReceived += new DataReceivedEventHandler(OutputHandler);
dProcess.Start();
dProcess.BeginOutputReadLine();
}
public static void OutputHandler(object sender, DataReceivedEventArgs e) {
xLogger.Instance.AddEntry(e.Data);
}
And now if we go back to the MainWindow.xaml.cs
private async void runDismScanButton_Click(object sender, RoutedEventArgs e) {
await Task.Run(() => Dism.Run());
}
So, this actually all runs just fine. In the OutputHandler method, if I replace the Instance.AddEntry() call to Console.WriteLine() then it works perfect, this was my first test before anything and it seemed to have worked. Now, when I use it as displayed above with Instance.AddEntry() I get the following error
Object reference not set to an instance of an object
Which in this case is self explanatory. So what I did here is define the object inside MainWindow.xaml.cs which when I come to think about it, I'm probably using or interacting with the object inside the UI thread? I found out after some reading that this won't work, and makes sense.
Either way, I did it this way and I get the error:
The calling thread cannot access this object because a different thread owns it
Which confirms that I am probably accessing it on the wrong thread, in this case the UI one, and I've learned I shouldn't do that and should use background workers and be sure to use the new async await features.
When I received that error initially, I started reading about BeginInvoke() in order to do things like this however I don't understand how I'm supposed to use it in this context or how it's supposed to work fully. I've read the documentation, I can't piece it together for this case admittedly.
Finally, my questions are this:
- Am I doing this correctly? If I want to update a textbox live with information as a command pushes out the information. Is there a preferable way to do this?
- How do I use this xLogger in other classes simultaneously without instantiating an object every time? I know
staticmethods help with this, but again I'm not sure how it's done. - The instance of the logger will always be null until an object is created, so where do I initially create the object to use for reuse? Or am I incorrect?
- Is my understanding of this situation wrong in any way?