Solved

Paint events - determine when a paint event has finished.

Posted on 2012-03-19
22
1,147 Views
Last Modified: 2012-08-14
I'd like to know if one can determine programmatically when a paint event has just completed.
0
Comment
Question by:David L. Hansen
  • 11
  • 4
  • 4
  • +1
22 Comments
 

Expert Comment

by:TwynFeyr
Comment Utility
You can always override the OnPaint kind like:
protected override void OnPaint(PaintEventArgs e) {    
base.OnPaint(e);
//Do your stuff here
} 

Open in new window

base.OnPaint(e) does it's thing and then you do your afterwards

here's a link to MSDN
Control.OnPaint method
0
 
LVL 39

Expert Comment

by:Kyle Abrahams
Comment Utility
Easiest way that comes to mind is the following:

Inherit a class, say TextBox

then create an After_Paint Event

Override the OnPaint event, raising After_Paint when done.



eg:

Class MyTextBox : TextBox
{
Public Event After_Paint

Protected void Overrides OnPaint(e as System.EventArgs)
{  
   base.OnPaint(e);
   RaiseEvent After_Paint;
}

}

NOTE:  Code was done by hand, may have syntax error but displays general idea.
0
 
LVL 15

Author Comment

by:David L. Hansen
Comment Utility
TwynFeyr: It seems that OnPaint just allows me to force the paint event to fire. But thanks for the suggestion.

Here is some context: I have a datagridview and that flicker bug is becoming a problem (for those of you not aware of the bug...a datagridview placed on a form with a background image will often cause horrible flicker of the screen when the datagridview is painting itself - there is a lot of documentation of this if you search for it - affecting VS 2005 and older).  I have minimized this issue but it is still a problem.  I want to just tell the grid's paint event to set the form's background to nothing (done that), then - when the grid's paint event is finished, reset the form's backgroundimage to what it was (here in is my problem).
0
 

Expert Comment

by:TwynFeyr
Comment Utility
Hi again,

overide onpaint is fired everytime a paint event is raised.  If you put it directly in a winform it'll fire for the form's events.  If you do as ger325 suggested it'll fire when paint events are raised for the control class you have inherited (which in this case after the question had further context added) is a better suggestion.

however, I'd be wary as alot of paint events can be raised in a very short amount of time depending on the circumstances.  resizing a form usually forces all the controls to repaint if they are docked or anchored to resize on form resize.  Also when the form becomes smaller and then bigger than the controls on the form there is alot of repainting.  you may end up replacing one flicker, for a different kind.

i look forward to hearing about your progress.

Cheers!
TwynFeyr
0
 
LVL 15

Author Comment

by:David L. Hansen
Comment Utility
Yes, that really is the root of the problem.  I need to know when the grid is beginning to paint itself so I can disable the background, then have an event fire when it is completed with all of its interior paint events.  This may not be possible, but that is what I'm after.

Setting the background = nothing for each descrete paint event is not a big deal.  There is no flicker issue there.  So, if all I can get is the "FinishedPaintingTheWholeGrid" event, I'm in good shape.
0
 
LVL 83

Expert Comment

by:CodeCruiser
Comment Utility
I think the grid paint event may be fired again when you change the background color.

Have you tried putting the grid into a panel of same size as grid? This may not solve the problem but worth the try.
0
 
LVL 39

Expert Comment

by:Kyle Abrahams
Comment Utility
Inherit the datagrid:


implement this for your paint:

Boolean _painting = false;
 
Protected void Overrides OnPaint(e as System.EventArgs)
{  
 

  if (!_painting)
   {
    _painting = true;
     //set background image to nothing
    base.OnPaint(e);
    //set background image back.  
   _painting = false;
   }
}


and also turn on double buffering:  (modified from http://bitmatic.com/c/fixing-a-slow-scrolling-datagridview)

new()
{
  Type dgvType = this.GetType();
        PropertyInfo pi = dgvType.GetProperty("DoubleBuffered",
            BindingFlags.Instance | BindingFlags.NonPublic);
        pi.SetValue(this, true, null);

}
0
 
LVL 15

Author Comment

by:David L. Hansen
Comment Utility
CodeCruiser,
The panel didn't help.  Too bad, it would have been the easiest solution.

ged325,
I really like your idea and am trying to implement it.  I'm in VB, so I've converted the code.  Two things: First, the following code causes constant flicker when the form loads...the flicker never stops (it paints the background as nothing then the image, then nothing, then the image, etc., etc. very very fast).  Secondly, I don't see how I can generically set the background of the form to nothing within the custom datagridview class.  I certainly can force it through "my.Forms.mySpecificFrom.BackgroundImage = Nothing" but then I can't reuse the code without rewriting that line of code each time I drop a grid on a form.  Is there a way I can generically point to the parent form?

Here is the code:
Public NotInheritable Class DatagridViewPlus
    Inherits DataGridView

    Dim _painting As Boolean = False

    Private Sub New()
        Dim dgvType As Type = Me.[GetType]()
        Dim pi As PropertyInfo = dgvType.GetProperty("DoubleBuffered", BindingFlags.Instance Or BindingFlags.NonPublic)
        pi.SetValue(Me, True, Nothing)
    End Sub

    Protected Overrides Sub OnPaint(ByVal e As System.Windows.Forms.PaintEventArgs)
        _painting = True
        My.Forms.frmMain.BackgroundImage = Nothing  '<******** Too specific
        MyBase.OnPaint(e)
        Dim resources As System.ComponentModel.ComponentResourceManager = New               System.ComponentModel.ComponentResourceManager(GetType(frmBuyer))
        My.Forms.frmMain.BackgroundImage = CType(resources.GetObject("$this.BackgroundImage"), System.Drawing.Image)
        _painting = False
    End Sub
End Class

Open in new window


To make it really reuasable I think I'll put in a test to see if a background image exists.
0
 
LVL 39

Assisted Solution

by:Kyle Abrahams
Kyle Abrahams earned 250 total points
Comment Utility
Try this:


  Protected Overrides Sub OnPaint(ByVal e As System.Windows.Forms.PaintEventArgs)

    if not _Painting then
         _painting = True
        Dim frm as Form = me.Getform()
        Dim img as Image
         if frm isnot nothing andalso frm.BackgroundImage isnot nothing then
                 img = frm.BackgroundImage
                 frm.BackgroundImage  = nothing
        end if

        My.Forms.frmMain.BackgroundImage = Nothing  '<******** Too specific
        MyBase.OnPaint(e)

        if frm isnot nothing  then
            frm.BackgroundImage = img
       end if

        _painting = False

end if

    End Sub
0
 
LVL 15

Author Comment

by:David L. Hansen
Comment Utility
There is no "GetForm" method for DatagridViews.  But there is a "Parent" property which I've not tried.  

Also, getting the image is not a problem. The "Dim resources ..." line and the "...$this.BackgroundImage" line work wonderfully.  I've tested those.  I think perhaps the sequencing is funny...not sure, but there is definitely an infinite loop going on.
0
 
LVL 39

Expert Comment

by:Kyle Abrahams
Comment Utility
the infinite loop comes from setting the background to null.  Which is intern triggering the paint event again . . . Hence the mutex like options.



Once it's in the on_paint, it needs to skip it for all other calls.


Regarding the .Parent . . . I've had issues with that where it was nothing even after being loaded.

You may want to create another property and set the property from the control.

As for the resources line . . . that may work for this app but was trying to make it as generic as possible.  If you're okay with it though that's what counts.
0
How to improve team productivity

Quip adds documents, spreadsheets, and tasklists to your Slack experience
- Elevate ideas to Quip docs
- Share Quip docs in Slack
- Get notified of changes to your docs
- Available on iOS/Android/Desktop/Web
- Online/Offline

 
LVL 15

Author Comment

by:David L. Hansen
Comment Utility
Cruiser, Ged, and Twyn,

Truly sorry for the long delay guys (and through the E-E-Derby too)!  I've been trying to duplicate the flicker, in a test project, and fix it with the custom grid.  The problem is, I've been getting inconsistent results.  Without determining all the variables contributing to the problem, I'm reluctant to close this post (and give out an incomplete answer).  I'll continue testing with this and won't neglect this post; it may take some time however (as I do have a band-aid fix, which my boss is happy with).  I will definitely work on it as I have time to do so -- I really do want to pin this down.
0
 

Accepted Solution

by:
TwynFeyr earned 250 total points
Comment Utility
How's aboot using a Timer to help ignore rapidly fired paint events with an interval that's big enough to reduce flicker but small enough so it won't be too obvious?

So,
Inherit the control
Create a timer and setup the interval and the event handler for a "tick"
Override the onPaint and have it "reset" the timer every time it fires
When the timer "ticks" do your background changes there (and stop the timer).

What i'm hoping is going to happen, is this will give you a "finished painting" kind of event as any rapid paint events will only restart the timer and when the timer ticks after a predetermined wait period then it will do the background update as it's assuming all paint events are completed.

The idea (I'm hoping) is the same as a spell checker.  The word processor usually waits until you've finished typing for a while before it tries to check your spelling... once you start typing it resets it's timer and waits for you to finish typing again.


Here's some very rough code using a form and listbox.  There's a delegate in there to update the listbox as events are occuring on the form.


public partial class MainForm : Form
	{
		Timer _ticker = new Timer();
		
		public MainForm()
		{
			InitializeComponent();
			_ticker.Interval = 150;
			_ticker.Tick += new EventHandler(ticker_Tick);
		}

		void ticker_Tick(object sender, EventArgs e)
		{
			SetText("Tick!  Update background!");
			_ticker.Stop();
			this.BackColor = Color.Gray;
		}
		
		protected override void OnPaint(PaintEventArgs e)
		{  
			_ticker.Stop();
		    _ticker.Start();
		    SetText("ticker restart");
		}

		delegate void TextCallback(string text);
		void SetText(string text)
		{
			if (this.InvokeRequired){
				TextCallback callback = new TextCallback(SetText);
				this.Invoke(callback, new object[] { text });
			}
			else{
				listBox1.Items.Add(text);
				listBox1.SelectedIndex = listBox1.Items.Count -1;
			}
		}
		
	}

Open in new window



This might might not solve your problem, but it's the only way i can think of to get a "paint complete" sort of event.


Again, I look forward to hearing about your progress :)

Cheers!
Twynfeyr
0
 
LVL 15

Author Comment

by:David L. Hansen
Comment Utility
I'll definitely give your idea a shot and let you know the results :)
0
 
LVL 83

Expert Comment

by:CodeCruiser
Comment Utility
>and through the E-E-Derby too

Curious on the use of Derby word as its the city I live in.
0
 
LVL 15

Author Comment

by:David L. Hansen
Comment Utility
I was just referring to the Grand-Prix competition Experts-Exchange was having at the time.
0
 
LVL 83

Expert Comment

by:CodeCruiser
Comment Utility
Oh ok.
0
 
LVL 15

Author Comment

by:David L. Hansen
Comment Utility
The Derby in England?
0
 
LVL 83

Expert Comment

by:CodeCruiser
Comment Utility
Yes
0
 
LVL 15

Author Comment

by:David L. Hansen
Comment Utility
I'm working on this today.  I hope I can finish this off and that it will work...thanks for your help and patients.
0
 
LVL 15

Author Comment

by:David L. Hansen
Comment Utility
I need to just tie this up.  I believe your approach will work TwynFyer; I'm going to award the points without actually testing because I just don't know when I'll be able to.  I was basically told me to forget the "Pretty backgrounds" and finish the job.  Too bad really.
0
 
LVL 15

Author Closing Comment

by:David L. Hansen
Comment Utility
Thank you very very much.
0

Featured Post

How your wiki can always stay up-to-date

Quip doubles as a “living” wiki and a project management tool that evolves with your organization. As you finish projects in Quip, the work remains, easily accessible to all team members, new and old.
- Increase transparency
- Onboard new hires faster
- Access from mobile/offline

Join & Write a Comment

Suggested Solutions

Summary: Persistence is the capability of an application to store the state of objects and recover it when necessary. This article compares the two common types of serialization in aspects of data access, readability, and runtime cost. A ready-to…
Exception Handling is in the core of any application that is able to dignify its name. In this article, I'll guide you through the process of writing a DRY (Don't Repeat Yourself) Exception Handling mechanism, using Aspect Oriented Programming.
Excel styles will make formatting consistent and let you apply and change formatting faster. In this tutorial, you'll learn how to use Excel's built-in styles, how to modify styles, and how to create your own. You'll also learn how to use your custo…
You have products, that come in variants and want to set different prices for them? Watch this micro tutorial that describes how to configure prices for Magento super attributes. Assigning simple products to configurable: We assigned simple products…

728 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

8 Experts available now in Live!

Get 1:1 Help Now