Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations SkipVought on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

GUI not updating

Status
Not open for further replies.

1DMF

Programmer
Jan 18, 2005
8,795
0
0
GB
I have a form with a rich textbox on it, when I append text, it doesn't update the GUI until the program ends?

Why doesn't WPF update the GUI and how do I make it as Application.DoEvents doesn't exist?

Thanks,
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
 
Well I don't know if this is right or what but WPF is starting to get on my nerves.

I simply use this and now my GUI updates
Code:
                [highlight #FCE94F]Dispatcher.Invoke(new Action(() => { }), DispatcherPriority.ContextIdle, null);[/highlight]
this.Progress.AppendText(msg);

Why do I need to do this before I update a control on the GUI?

Why won't the GUI reflect changes to it's controls by itself?

"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
 
updates are handled by background methods in wpf, are they not? you first invalidate the control and let the background process update it? assuming the busy code is deployed to a separate thread as it should do.

i guess you could kludge a refresh extension method to the control manually if you are running single threaded.

Code:
public static void updateControl(this UIElement el){
      el.Dispatcher.Invoke(DispatcherPriority.Render, EmptyDelegate);
}
 
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
 
i don't program in ms languages so i'm just going on things i learn osmotically. for wpf you have a busy code thread running in parallel. it is that thread that redraws the screen. then in your main thread you invalidate the control after updating its content. simple as that. the background process sorts the redraw out.

if you are running mono-threaded then you need to do what i posted above, or something similar to it. not a great solution for an app but ok for prototyping.

Code:
public static class ExtensionMethods {
   private static Action EmptyDelegate = delegate() { };
 
   public static void updateObj(this UIElement el){
     el.Dispatcher.Invoke(DispatcherPriority.Render, EmptyDelegate);
   }
}

then call updateObj on whatever you're looking to repaint.

sorry i cannot be more help.
 
Hi Justin,

This doesn't make any sense to me, why are you defining a blank method to a delegate and then taking a GUI control and invoking an empty method pointer against it?

To me that code says, invoke this method that does absolutely nothing?

I guess I need to find a WPF course, as I'm clearly doing something wrong and can't fathom it out, all I can find is a plethora of forum threads all asking the same question about why their GUI doesn't update.



"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
 
I think that's the way extensionmethods work.

Have you tried it this way?

Eg control_1.updateObj();
 
Well I wrapped my click event in

Code:
new Thread(() => 
{
    Thread.CurrentThread.IsBackground = true; 

... my code here

}).Start();
And now the progress form won't even instantiate because of
The calling thread must be STA, because many UI components require this.

So I tried wrapping it in
Code:
Application.Current.Dispatcher.Invoke((Action)delegate
{
.... my code here
});
And that does absolutely nothing and the GUI still doesn't update.

So I gave up and replaced the code I had with the refresh method

In a helper static class...
Code:
        private static Action EmptyDelegate = delegate() { };

        public static void Refresh(this UIElement el)
        {
            el.Dispatcher.Invoke(DispatcherPriority.Render, EmptyDelegate);
        }

In my progress form
Code:
        // add messages to form
        public override void AddMessage(string msg)
        {
            if (msg != null && msg != "")
            {
                this.Progress.AppendText(msg);
                this.Progress.Refresh();
           }
        }

        // clear form messages
        public override void ClearMessage()
        {
            this.Progress.Document.Blocks.Clear();
            this.Progress.Refresh();
        }
As per your example taken from
I don't understand any of this, if I try to run a background worker, the form doesn't even open and errors, if I don't the GUI doesn't update, I don't know whether to go back to Win Forms or carry on with what I have, I'm getting too frustrated and demoralised for this to be fun anymore [sad].


"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
 
I do see the similarities but I hadn't read that site. I will try to re-find the article that I read but it is quite probable that you have found the genesis of that code snip/solution.

i think your experiments don't work because you are trying to run the usual foreground thread as the background.
You should be running the 'busy' code thread as the background. but i'm in danger of going way beyond my experience in this language framework. Silly - but i have always hated the MS bound controls framework. likewise Objective C for the same reason. I guess i was brought up drawing and filling and changing controls by code rather than checkboxes and better the devil you know. i don't like MS Access for the same reason although I sometimes wish the query builder was available for other languages. i do like being able to define joins visually.

have a look at this website for some more examples of background processes
 
You should be running the 'busy' code thread as the background.

I'm lost to identify how I do this against what code?

Is it in the Email class? If so is it each method and I have to ensure the AddProgress calls are outside of this worker?

Let's break this down to say one of the many methods in my email class that takes time to run....

Code:
        // Check outlook is open helper
        private bool CheckOutlook()
        {

            bool check_outlook = false;
            int cnt = 0;
            try
            {
                [highlight #FCE94F]while (!check_outlook && cnt < this.TimeOut)
                {
                    [highlight #EF2929]this.ProgressMsg.AddProgress("Checking Outlook is open.");[/highlight]

                    Process[] aProcess = Process.GetProcesses();
                    for (int ProcessIndex = 0; ProcessIndex < aProcess.Length; ProcessIndex++)
                    {
                        if (aProcess[ProcessIndex].ProcessName.ToUpper() == "OUTLOOK")
                        {
                            check_outlook = true;
                            break;
                        }
                    }

                    if (!check_outlook)
                    {
                        cnt++;
                        [highlight #EF2929]this.ProgressMsg.AddProgress("Trying to open Outlook.");[/highlight]
                        Process.Start("OUTLOOK.EXE");
                        Thread.Sleep(10000);
                    }
                }[/highlight]
            }
            catch (Exception e)
            {
                this.AddError(99, e.Message);
            }
            finally
            {
                if (!check_outlook && this.OK)
                {
                    this.AddError(1);
                }
                else if (check_outlook && this.OK)
                {
                    this.ProgressMsg.AddProgress("Outlook open.");
                }
            }

            return check_outlook;
        }

How can this possibly be wrapped in a worker process, the while loop contains two updates to the GUI for one?

The code also needs to wait until the while condition ends, if I put it in an asynchronous call the code would continue wouldn't it returning an invalid value of false to outlook having been successfully opened?

... so I tried it...

Code:
 private bool CheckOutlook()
        {

            bool check_outlook = false;
            BackgroundWorker worker = new BackgroundWorker();

            int cnt = 0;
            try
            {   
                worker.DoWork += delegate(object s, DoWorkEventArgs args)
                {
                    while (!check_outlook && cnt < this.TimeOut)
                    {
                        this.ProgressMsg.AddProgress("Checking Outlook is open.");

                        Process[] aProcess = Process.GetProcesses();
                        for (int ProcessIndex = 0; ProcessIndex < aProcess.Length; ProcessIndex++)
                        {
                            if (aProcess[ProcessIndex].ProcessName.ToUpper() == "OUTLOOK")
                            {
                                check_outlook = true;
                                break;
                            }
                        }

                        if (!check_outlook)
                        {
                            cnt++;
                            this.ProgressMsg.AddProgress("Trying to open Outlook.");
                            Process.Start("OUTLOOK.EXE");
                            Thread.Sleep(10000);
                        }
                    }
                };

                worker.RunWorkerAsync();
            }
            catch (Exception e)
            {
                this.AddError(99, e.Message);
            }
            finally
            {
                if (!check_outlook && this.OK)
                {
                    this.AddError(1);
                }
                else if (check_outlook && this.OK)
                {
                    this.ProgressMsg.AddProgress("Outlook open.");
                }
            }

            return check_outlook;
        }

Now outlook isn't opened and my email code fails with the error that it couldn't open outlook, because the catch clause is invoked due to the AddProgress attempting to update the GUI from the worker thread.

If I comment out the AddProgress calls, then the code runs, outlook opens, however, it runs asynchronous, so while the code is running to open outlook the return value from the method is also executed, which of course is currently false so the method returns failure that it opened outlook which is incorrect.

And of course the user messages aren't showing defeating the whole point of what I'm trying to achieve.

It's not the processing of the code I want to run asynchronously, it's the real time update of the progress window that refuses to update without that empty delegate extension method.

I am at a total loss how to code the above on a worker thread that doesn't run asynchronously and allows the GUI free to update without the issue of the update not being on the UI thread?











"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
 
In the end I added this in the Progress class

Code:
// add message to form
if (this.Msg.Count > 0)
{
    [highlight #FCE94F]this.Frm.Dispatcher.Invoke(new Action(() => { }), DispatcherPriority.Render);[/highlight]
    this.AddMsg(this.Msg.Last() + "\n");
}

// Clear progress messages
if (this.Frm != null)
{
    [highlight #FCE94F]this.Frm.Dispatcher.Invoke(new Action(() => { }), DispatcherPriority.Render);[/highlight]
    this.ClearMsg();
}

The problem with the extension method is it then requires any form that derives from ProgressForm to use that extension method in the abstracted methods for updating and clearing the GUI control, which I don't wont as it's binding explicit required functionality to the form, defeating the point of the abstractness of the ProgressForm UI.

The progress form is meant to be able to be any form derived from ProgressForm and only has the requirement to implement two abstract methods to update or clear its GUI control. Forcing the use of the extension method in the form means the loose coupling is lost and specific functionality hard-coded into the form is then required.

The Win Forms version updated the GUI fine without any DoEvents requirements in the code, I'm not sure why WPF is giving me so much grief?





"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
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top