0

I use the OnPaint (c#) event to draw something in my form. I want to get the value of a variable during the OnPaint process. But I cant get it during, only before or after the OnPaint process...

In fact, the variable is like a counter that I want to get to increase the value of a ProgressBar.

I tried adding a Thread, a Timer and "ValueChanged" events, I still can't get the value. The code is quite long (it's for generating a HeatMap from some data).

I increase the value in some for loops during the event, and I call the OnPaint event by the "Invalidate()" function.

I hope to be clear without pasting my code (it's very long) ! Thanks.

With the code this is better : (Simplified)

public partial class HeatPainter : UserControl
{
    public long _progress = 0; //My counter

    public HeatPainter()
    {
        InitializeComponent();
    }

    public void DrawHeatMap(List<List<int>> Items, decimal Value, int MaxStacks, int Factor, string FileName)
    {
        if (_allowPaint) //If the control is ready to process
        {
            timer1.Start();
            _progress = 0;
            _allowPaint = false;
            Invalidate();
        }
    }


    protected override void OnPaint(PaintEventArgs e)
    {
        base.OnPaint(e);
        for (int Pass = _factor; Pass >= 0; Pass--)
        {
            //Some draw stuff
            //...
            _progress++;
        }
     }
     private void timer1_Tick(object sender, EventArgs e)
    {
        Console.WriteLine(_progress);
    }
}
Gafda
  • 13
  • 2
  • Unfortunately it is not clear at all (at least not to me). Can you try to create a [sscce](http://sscce.org/) or perhaps show some pseudo code? – default Jun 18 '14 at 13:20
  • What do you mean with "I cant get it during..". Where is your variable located(Form, other class) and what do you expect to see. And Windows Forms are singlethreaded environment(one operation at time), so if you painting code takes enormous amount of time you won't be able to change anything on that form during the repainting. – Eugene Podskal Jun 18 '14 at 13:27
  • You can't access it - does it give incorrect results, crashes or stops the app, does it compile... What exactly? – Eugene Podskal Jun 18 '14 at 13:29
  • My OnPaint function looks like this : `protected override void OnPaint(PaintEventArgs e) { for(int cnt=0;cnt – Gafda Jun 18 '14 at 13:29
  • You won't be able to monitor it with windows forms timer - http://msdn.microsoft.com/en-us/library/system.windows.forms.timer(v=vs.110).aspx. It adds message into the event queue of the form. So until the previous message is processed(Paint) it won't run. – Eugene Podskal Jun 18 '14 at 13:32
  • I've added a simplified code in my question. I guess this is the same for a thread ? – Gafda Jun 18 '14 at 13:38
  • What is the type of the Timer? And it is better to give him some good name like timer_RepaintingProgress or something similar. – Eugene Podskal Jun 18 '14 at 13:41
  • Why is there Console.WriteLine? It is WinForms app, isn't it? – Eugene Podskal Jun 18 '14 at 13:41
  • Console.WriteLine(string) is for printing the string or a value. It's for debugging. the timer is a basic WindowsForms Timer – Gafda Jun 18 '14 at 14:56
  • Your loop needs to be called in the DrawHeatMap routine, and inside that loop, you would call Invalidate(). In the paint event, you should *only* be painting the current state of that drawing. – LarsTech Jun 18 '14 at 15:04
  • The problem is that as I remember WinForms painting context does not store any previously painted elements. It is returned in pristinely new condition each time. – Eugene Podskal Jun 18 '14 at 16:59

1 Answers1

0

It looks like your repainting takes a lot of time. You are unable to change anything on the form during the repainting (it won't be changed until the end).

So you should look at the task from other point of view. What if you will create the image(like How to create a jpg image dynamically in memory with .NET?) in the parallel thread(or another parallelization construct http://www.dotnetperls.com/backgroundworker)? After it is painted you will set it as background or the .Image of some PictureBox. The form will be responsive all the time.

You will have to synchronize(to update your progress bar) but that isn't such a difficult task (Thread not updating progress bar control - C#, C# Windows Forms Application - Updating GUI from another thread AND class?).

And for the future: Threads and BackgroundWorkers are slowly going away from the.NET world. They are still used in .NET < 4.0 but .NET 4.0 and higher provide better abstractions for asynchronous operations. I recommend you to read about Tasks and Async Await. They are more appropriate for many launch-the-work-get-the-result-on-completion scenoarios.

You should use only one asyncronous construct(for example BackgroundWorker) that will paint the image. You user control should provide an event (actually it is better to refactor this functionality out of the user interface) like

public event EventHandler ProgressChanged;

and raise this event in the code that creates the image when you modify the property of Progress. Just don't forget about synchronization and dispatching(see above).

Community
  • 1
  • 1
Eugene Podskal
  • 10,270
  • 5
  • 31
  • 53
  • That's a good idea, I will try to create the image in the memory and then apply the final result in a pictureBox. – Gafda Jun 18 '14 at 15:15
  • I've tried to generate the image in memory, it's better, because I can save it into a file, it's more safe. But I can't access the value anyore. Anyway, I will try to skirt the problem. Thanks – Gafda Jun 18 '14 at 16:59
  • If it is so try http://stackoverflow.com/questions/4928195/c-sharp-windows-forms-application-updating-gui-from-another-thread-and-class – Eugene Podskal Jun 18 '14 at 17:02
  • In fact, I still have no access to the variable. But with the PictureBox method I succeed to add a progress bar directly in the usercontrol. So I can control te ProgressBar inside my loops. – Gafda Jun 18 '14 at 17:44
  • I am still baffled with 'no access to the variable'. – Eugene Podskal Jun 18 '14 at 17:46
  • Well, now I've got 2 backgroundWorker, one for the monitoring of my "_progress" variable. In this backgroundWorker, I only write the value in the VS exit prompt, and the value is 0 at the beginning and the next update is when the other backgroundWorker is ended. Also the InvokeDelegate seems don't update the picturebox with the generated image. – Gafda Jun 18 '14 at 17:56
  • Humm, I just removed a "Refresh()" in my parent class who contains the usercontrol with the picture box and it works. I really don't untersantand, that's weird ! – Gafda Jun 18 '14 at 18:02
  • The Refresh method adds a new repainting command into the message queue - till it is completed no other change to the Form elements can not be made. http://msdn.microsoft.com/en-us/library/windows/desktop/ms644927(v=vs.85).aspx - windows forms is basically a thin wrapper above windows functions of WinApi. – Eugene Podskal Jun 18 '14 at 18:13
  • And I suggest you to edit the title of your question as 'Why does Windows Forms control's property cannot be changed during the OnPaint event'. It is more appropriate and describes the problem(at least close enough). – Eugene Podskal Jun 18 '14 at 18:29