• Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 1997
  • Last Modified:

Paint events - determine when a paint event has finished.

I'd like to know if one can determine programmatically when a paint event has just completed.
0
David L. Hansen
Asked:
David L. Hansen
  • 11
  • 4
  • 4
  • +1
2 Solutions
 
TwynFeyrCommented:
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
 
Kyle AbrahamsSenior .Net DeveloperCommented:
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
 
David L. HansenProgrammer AnalystAuthor Commented:
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
VIDEO: THE CONCERTO CLOUD FOR HEALTHCARE

Modern healthcare requires a modern cloud. View this brief video to understand how the Concerto Cloud for Healthcare can help your organization.

 
TwynFeyrCommented:
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
 
David L. HansenProgrammer AnalystAuthor Commented:
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
 
CodeCruiserCommented:
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
 
Kyle AbrahamsSenior .Net DeveloperCommented:
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
 
David L. HansenProgrammer AnalystAuthor Commented:
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
 
Kyle AbrahamsSenior .Net DeveloperCommented:
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
 
David L. HansenProgrammer AnalystAuthor Commented:
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
 
Kyle AbrahamsSenior .Net DeveloperCommented:
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
 
David L. HansenProgrammer AnalystAuthor Commented:
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
 
TwynFeyrCommented:
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
 
David L. HansenProgrammer AnalystAuthor Commented:
I'll definitely give your idea a shot and let you know the results :)
0
 
CodeCruiserCommented:
>and through the E-E-Derby too

Curious on the use of Derby word as its the city I live in.
0
 
David L. HansenProgrammer AnalystAuthor Commented:
I was just referring to the Grand-Prix competition Experts-Exchange was having at the time.
0
 
CodeCruiserCommented:
Oh ok.
0
 
David L. HansenProgrammer AnalystAuthor Commented:
The Derby in England?
0
 
CodeCruiserCommented:
Yes
0
 
David L. HansenProgrammer AnalystAuthor Commented:
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
 
David L. HansenProgrammer AnalystAuthor Commented:
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
 
David L. HansenProgrammer AnalystAuthor Commented:
Thank you very very much.
0

Featured Post

What does it mean to be "Always On"?

Is your cloud always on? With an Always On cloud you won't have to worry about downtime for maintenance or software application code updates, ensuring that your bottom line isn't affected.

  • 11
  • 4
  • 4
  • +1
Tackle projects and never again get stuck behind a technical roadblock.
Join Now