updates are handled by background methods in wpf, are they not?
Pass? Only just started with WPF and none of my courses covered it. I was told Win Forms applications are to no longer be supported and WPF replaces them, so I ported my Win Form app to WPF and am now having loads of problems.
I've never messed with threading before, so don't know what is doing what on what thread, or how I should be coding methods to run on a particular thread, it's never something I have had to consider before.
I've read there is a UI thread and as the code is run via a click event on a form I can only assume the main code in my class is running on the UI thread, isn't every interaction with a GUI? As they are all usually interactions from a user control event on a form?
Does this mean if I have a class that does some processing when I instantiate that class and call it's methods I need to wrap that in some code that makes it run on a different thread, so the UI thread is free to update the GUI when controls are updated such as an RTB?
I currently have a main form with a button, just to test my email class..
Code:
private void button1_Click(object sender, EventArgs e)
{
Email eml = new Email();
Dictionary<string, string> mergedata = new Dictionary<string, string> { { "Salutation", "John Smith" } };
EmailTemplate tmp = new EmailTemplate(mergedata, App.HLP_User);
tmp.TemplateName = "Test.html";
tmp.TemplatePath = Path.Combine(Utils.EMAIL_TEMPLATES, "TestEmail");
tmp.AttachmentPath = Utils.EMAIL_ATTACHMENTS;
eml.AddTO("me@mydomain.com");
eml.Subject = "Test Email!";
if (eml.AddEmailRef("MyUID"))
{
if (eml.AddBody(tmp))
{
eml.Create();
if (eml.OK)
{
eml.Display();
}
else
{
Utils.Msg(eml.ErrorDesc);
}
Utils.Msg("Click to continue once email sent!");
Utils.Msg("found = " + eml.CheckSentItems().ToString());
}
}
else
{
Utils.Msg("Failed : " + eml.StatusMsg);
}
//eml.Dispose();
}
The plan was to use the MVC paradigm, so eventually most of this code will be in a controller, so an onclick of a form control will call the method of the controller.
So lets say I have..
Code:
public partial class My_Window : Window
{
private My_Controller_Class my_controller = new My_Controller_Class();
private void button1_Click(object sender, EventArgs e)
{
... set up some args
this.my_controller.Do_Something(my_args);
}
}
If Do_Something() in my controller uses my Email class that instantiates a popup progress window to report progress information, where am I wrapping what with what to make the process run on a background thread so the UI thread isn't hung by the processing?
Is it the initial call to the controller Do_Something() in the onclick event, so anything in the controller runs on a background thread?
Would that mean the instantiation of the progress window from the Email class is running on a background thread?
The progress window is handled in a generic Progress class, that may or may not use a progress window, depending on whether I want to just use a collection of progress messages or report to a progress window that may or may not want to be a popup window.
Code:
public class Progress
{
private List<string> Msg = new List<string>();
private ProgressForm Frm { get; set; }
internal bool Close { get; set; }
internal bool Hide { get; set; }
private delegate void AddProgressMsg(string msg);
private AddProgressMsg AddMsg;
private delegate void ClearProgressMsg();
private ClearProgressMsg ClearMsg;
public bool ShowProgress { get; set; }
public Progress(ProgressForm progressForm, bool close, bool hide)
{
// set properties
this.Hide = hide;
this.Close = close;
// assign form
this.Frm = progressForm;
// show form
this.Frm.Show();
// assign delegates
this.AddMsg = this.Frm.AddMessage;
this.ClearMsg = this.Frm.ClearMessage;
// Hide form
this.Frm.Visibility = (!hide)?Visibility.Visible:Visibility.Hidden;
}
// default constructor when no ProgressForm is desired
public Progress()
{
// set properties
this.Hide = false;
this.Close = false;
this.ShowProgress = false;
this.Frm = null;
}
// Add progress message
public void AddProgress(string msg, bool bHide = false)
{
this.Msg.Add(msg);
if (this.ShowProgress && this.Frm != null)
{
this.ShowProgressMsg(bHide);
}
}
// Show progress message
private void ShowProgressMsg(bool bHide)
{
// Check if form visible
if (this.Frm.Visibility != Visibility.Visible)
{
this.Frm.Visibility = Visibility.Visible;
}
// add message to form
if (this.Msg.Count > 0)
{
this.AddMsg(this.Msg.Last() + "\n");
}
// focus window
this.Frm.Focus();
// Hide message window if required
if (bHide && this.Hide)
{
this.Frm.Visibility = Visibility.Hidden;
}
}
public void ClearProgress(bool bHide = false)
{
this.Msg.Clear();
if (this.Frm != null)
{
this.ClearMsg();
}
}
public List<string> GetProgress()
{
return new List<string>(this.Msg);
}
public void CloseProgress()
{
this.Frm.Close();
this.Frm = null;
}
}
My Progress class delegates the actual update to the progress form (if provided). Or it just stores a collection of progress messages that can be accessed.
In this case I have a generic Progress_Info form I use as the popup window most of the time, but it could be an existing visible form on a window panel.
I've highlighted the GUI update code where I have had to add that Dispatcher code to get the window to refresh each time it is updated.
Code:
public partial class Progress_Info : ProgressForm
{
public Progress_Info()
{
InitializeComponent();
}
// add messages to form
public override void AddMessage(string msg)
{
if (msg != null && msg != "")
{
[highlight #FCE94F] Dispatcher.Invoke(new Action(() => { }), DispatcherPriority.ContextIdle, null);
this.Progress.AppendText(msg);[/highlight]
}
}
// clear form messages
public override void ClearMessage()
{
[highlight #FCE94F] Dispatcher.Invoke(new Action(() => { }), DispatcherPriority.ContextIdle, null);
this.Progress.Document.Blocks.Clear();[/highlight]
}
}
here is the abstract ProgressForm all Progress forms must derive from if they want to be used as a progress form
Code:
public abstract class ProgressForm : Window
{
public abstract void AddMessage(string msg);
public abstract void ClearMessage();
}
So to recap...
I have a window that has a controller, the window has an onclick event which calls a method in my controller, the controller uses a class that uses a Progress class which takes a ProgressForm class to display progress information, the Progress class uses delegates to call the methods in the ProgressForm (in this case Progress_Info) to update the GUI (an RTB control) with the progress messages.
So what is running on what thread and where, and what am I wrapping in what to ensure the main processing doesn't affect the Progress class updating the ProgressForm via the delegate methods?
This was all working fine under Win Forms, and I am now very confused over what I'm meant to be doing and where to keep the processing work in the controller away from any window update?
Some help is really appreciated.
1DMF.
"In complete darkness we are all the same, it is only our knowledge and wisdom that separates us, don't let your eyes deceive you."
"If a shortcut was meant to be easy, it wouldn't be a shortcut, it would be the way!"
Free Electronic Dance Music