S
Simon Tamman
I was trying to double buffer a control while drawing on CE devices. The code
below is compiled under the compact framework but you can also run it on the
desktop and it produces the same problem.
The basic problem is that my controls don't seem to correctly process an
invalidate call after a window is dropped on top on them, mainly the "cheat"
windows such as a combobox drop down or a loading symbol or volume control
(which you get in CE)
Example below, please let me know if you have ideas, the issue seems to be
the override Paint in the DoubleBufferedControl. What is wrong with this
code? It looks good to me.
1.
using System;
2.
using System.Collections.Generic;
3.
using System.Windows.Forms;
4.
using System.Text;
5.
using System.Drawing;
6.
7.
8.
namespace DeviceApplication9
9.
{
10.
static class Program
11.
{
12.
/// <summary>
13.
/// The main entry point for the application.
14.
/// </summary>
15.
[MTAThread]
16.
static void Main()
17.
{
18.
Application.Run(new Form1());
19.
}
20.
21.
22.
23.
24.
}
25.
26.
class CustomControl : DoubleBufferedControl
27.
{
28.
29.
protected override void DoPaint(PaintEventArgs e)
30.
{
31.
using (SolidBrush back = new
SolidBrush(Color.Red))
32.
{
33.
e.Graphics.FillRectangle(back,
e.ClipRectangle);
34.
}
35.
using (SolidBrush fore = new
SolidBrush(Color.White))
36.
{
37.
e.Graphics.DrawString(Text, Font,
fore, e.ClipRectangle);
38.
}
39.
}
40.
41.
}
42.
43.
class CustomLabel : Label
44.
{
45.
46.
protected override void
OnPaintBackground(PaintEventArgs pevent)
47.
{
48.
// do nothing, prevent flicker
49.
}
50.
51.
protected override void OnPaint(PaintEventArgs e)
52.
{
53.
using (SolidBrush back = new
SolidBrush(Color.Red))
54.
{
55.
e.Graphics.FillRectangle(back,
e.ClipRectangle);
56.
}
57.
using (SolidBrush fore = new
SolidBrush(Color.White))
58.
{
59.
e.Graphics.DrawString(Text, Font,
fore, e.ClipRectangle);
60.
}
61.
}
62.
}
63.
64.
/// <summary>
65.
/// <para>Provides double buffering functionality from any
control that inherits from this one.</para>
66.
/// <para>Override the DoPaint method to add your custom paint
code</para>
67.
/// </summary>
68.
public class DoubleBufferedControl : Control
69.
{
70.
#region Fields
71.
72.
private Graphics m_BackGraphics;
73.
private Bitmap m_BackBuffer;
74.
75.
#endregion
76.
77.
#region Ctor
78.
79.
/// <summary>
80.
/// Constructor
81.
/// </summary>
82.
protected DoubleBufferedControl()
83.
{
84.
}
85.
86.
#endregion
87.
88.
#region Private Impl
89.
90.
/// <summary>
91.
/// <para>Overrides the paint background event and
does nothing.</para>
92.
/// <para>If this isn't overidden then double
buffering wont work</para>
93.
/// </summary>
94.
/// <param name="e"></param>
95.
protected override void
OnPaintBackground(PaintEventArgs e)
96.
{
97.
// do nothing
98.
}
99.
100.
/// <summary>
101.
/// Overrides the paint event and performs double
buffering
102.
/// </summary>
103.
/// <param name="e"></param>
104.
protected override void OnPaint(PaintEventArgs e)
105.
{
106.
if (e.ClipRectangle.Width > 0 &&
e.ClipRectangle.Height > 0)
107.
{
108.
using (m_BackBuffer = new
Bitmap(e.ClipRectangle.Width, e.ClipRectangle.Height))
109.
{
110.
using (m_BackGraphics =
Graphics.FromImage(m_BackBuffer))
111.
{
112.
DoPaint(new
PaintEventArgs(m_BackGraphics, e.ClipRectangle));
113.
}
114.
e.Graphics.DrawImage(m_BackBuffer, 0, 0);
115.
}
116.
}
117.
}
118.
119.
/// <summary>
120.
/// Performs the paint event in the subclass
121.
/// </summary>
122.
/// <param name="e"></param>
123.
protected virtual void DoPaint(PaintEventArgs e)
124.
{
125.
// do nothing, for overrides only
126.
}
127.
128.
/// <summary>
129.
/// Overrides the dispose method to dispose the
backgraphics and
130.
/// back buffer if they have not yet been disposed.
131.
/// </summary>
132.
/// <param name="disposing"></param>
133.
protected override void Dispose(bool disposing)
134.
{
135.
base.Dispose(disposing);
136.
if (disposing)
137.
{
138.
if (m_BackGraphics != null)
139.
{
140.
m_BackGraphics.Dispose();
141.
m_BackGraphics = null;
142.
}
143.
if (m_BackBuffer != null)
144.
{
145.
m_BackBuffer.Dispose();
146.
m_BackBuffer = null;
147.
}
148.
}
149.
}
150.
151.
#endregion
152.
}
153.
154.
public partial class Form1 : Form
155.
{
156.
public Form1()
157.
{
158.
InitializeComponent();
159.
CustomControl control = new CustomControl();
160.
control.Text =
"hthttehstehrjgsjgsfkgdgjmrknvrgnrkgrvnsgrkrgdsjgrdhjn";
161.
control.Bounds = new Rectangle(100, 100, 100,
50);
162.
CustomLabel label = new CustomLabel();
163.
label.Text =
"hthttehstehrjgsjgsfkgdgjmrknvrgnrkgrvnsgrkrgdsjgrdhjn";
164.
label.Bounds = new Rectangle(100, 200, 100, 50);
165.
Controls.Add(control);
166.
Controls.Add(label);
167.
}
168.
169.
private void button1_Click(object sender, EventArgs e)
170.
{
171.
Close();
172.
}
173.
}
174.
175.
partial class Form1
176.
{
177.
/// <summary>
178.
/// Required designer variable.
179.
/// </summary>
180.
private System.ComponentModel.IContainer components =
null;
181.
private System.Windows.Forms.MainMenu mainMenu1;
182.
183.
/// <summary>
184.
/// Clean up any resources being used.
185.
/// </summary>
186.
/// <param name="disposing">true if managed resources
should be disposed; otherwise, false.</param>
187.
protected override void Dispose(bool disposing)
188.
{
189.
if (disposing && (components != null))
190.
{
191.
components.Dispose();
192.
}
193.
base.Dispose(disposing);
194.
}
195.
196.
#region Windows Form Designer generated code
197.
198.
/// <summary>
199.
/// Required method for Designer support - do not modify
200.
/// the contents of this method with the code editor.
201.
/// </summary>
202.
private void InitializeComponent()
203.
{
204.
this.mainMenu1 = new
System.Windows.Forms.MainMenu();
205.
this.button1 = new
System.Windows.Forms.Button();
206.
this.SuspendLayout();
207.
//
208.
// button1
209.
//
210.
this.button1.Location = new
System.Drawing.Point(77, 233);
211.
this.button1.Name = "button1";
212.
this.button1.Size = new
System.Drawing.Size(72, 20);
213.
this.button1.TabIndex = 0;
214.
this.button1.Text = "Close";
215.
this.button1.Click += new
System.EventHandler(this.button1_Click);
216.
//
217.
// Form1
218.
//
219.
this.AutoScaleDimensions = new
System.Drawing.SizeF(96F, 96F);
220.
this.AutoScaleMode =
System.Windows.Forms.AutoScaleMode.Dpi;
221.
this.AutoScroll = true;
222.
this.ClientSize = new System.Drawing.Size(240,
268);
223.
this.Controls.Add(this.button1);
224.
this.Menu = this.mainMenu1;
225.
this.Name = "Form1";
226.
this.Text = "Form1";
227.
this.ResumeLayout(false);
228.
229.
}
230.
231.
#endregion
232.
233.
private System.Windows.Forms.Button button1;
234.
}
235.
}
below is compiled under the compact framework but you can also run it on the
desktop and it produces the same problem.
The basic problem is that my controls don't seem to correctly process an
invalidate call after a window is dropped on top on them, mainly the "cheat"
windows such as a combobox drop down or a loading symbol or volume control
(which you get in CE)
Example below, please let me know if you have ideas, the issue seems to be
the override Paint in the DoubleBufferedControl. What is wrong with this
code? It looks good to me.
1.
using System;
2.
using System.Collections.Generic;
3.
using System.Windows.Forms;
4.
using System.Text;
5.
using System.Drawing;
6.
7.
8.
namespace DeviceApplication9
9.
{
10.
static class Program
11.
{
12.
/// <summary>
13.
/// The main entry point for the application.
14.
/// </summary>
15.
[MTAThread]
16.
static void Main()
17.
{
18.
Application.Run(new Form1());
19.
}
20.
21.
22.
23.
24.
}
25.
26.
class CustomControl : DoubleBufferedControl
27.
{
28.
29.
protected override void DoPaint(PaintEventArgs e)
30.
{
31.
using (SolidBrush back = new
SolidBrush(Color.Red))
32.
{
33.
e.Graphics.FillRectangle(back,
e.ClipRectangle);
34.
}
35.
using (SolidBrush fore = new
SolidBrush(Color.White))
36.
{
37.
e.Graphics.DrawString(Text, Font,
fore, e.ClipRectangle);
38.
}
39.
}
40.
41.
}
42.
43.
class CustomLabel : Label
44.
{
45.
46.
protected override void
OnPaintBackground(PaintEventArgs pevent)
47.
{
48.
// do nothing, prevent flicker
49.
}
50.
51.
protected override void OnPaint(PaintEventArgs e)
52.
{
53.
using (SolidBrush back = new
SolidBrush(Color.Red))
54.
{
55.
e.Graphics.FillRectangle(back,
e.ClipRectangle);
56.
}
57.
using (SolidBrush fore = new
SolidBrush(Color.White))
58.
{
59.
e.Graphics.DrawString(Text, Font,
fore, e.ClipRectangle);
60.
}
61.
}
62.
}
63.
64.
/// <summary>
65.
/// <para>Provides double buffering functionality from any
control that inherits from this one.</para>
66.
/// <para>Override the DoPaint method to add your custom paint
code</para>
67.
/// </summary>
68.
public class DoubleBufferedControl : Control
69.
{
70.
#region Fields
71.
72.
private Graphics m_BackGraphics;
73.
private Bitmap m_BackBuffer;
74.
75.
#endregion
76.
77.
#region Ctor
78.
79.
/// <summary>
80.
/// Constructor
81.
/// </summary>
82.
protected DoubleBufferedControl()
83.
{
84.
}
85.
86.
#endregion
87.
88.
#region Private Impl
89.
90.
/// <summary>
91.
/// <para>Overrides the paint background event and
does nothing.</para>
92.
/// <para>If this isn't overidden then double
buffering wont work</para>
93.
/// </summary>
94.
/// <param name="e"></param>
95.
protected override void
OnPaintBackground(PaintEventArgs e)
96.
{
97.
// do nothing
98.
}
99.
100.
/// <summary>
101.
/// Overrides the paint event and performs double
buffering
102.
/// </summary>
103.
/// <param name="e"></param>
104.
protected override void OnPaint(PaintEventArgs e)
105.
{
106.
if (e.ClipRectangle.Width > 0 &&
e.ClipRectangle.Height > 0)
107.
{
108.
using (m_BackBuffer = new
Bitmap(e.ClipRectangle.Width, e.ClipRectangle.Height))
109.
{
110.
using (m_BackGraphics =
Graphics.FromImage(m_BackBuffer))
111.
{
112.
DoPaint(new
PaintEventArgs(m_BackGraphics, e.ClipRectangle));
113.
}
114.
e.Graphics.DrawImage(m_BackBuffer, 0, 0);
115.
}
116.
}
117.
}
118.
119.
/// <summary>
120.
/// Performs the paint event in the subclass
121.
/// </summary>
122.
/// <param name="e"></param>
123.
protected virtual void DoPaint(PaintEventArgs e)
124.
{
125.
// do nothing, for overrides only
126.
}
127.
128.
/// <summary>
129.
/// Overrides the dispose method to dispose the
backgraphics and
130.
/// back buffer if they have not yet been disposed.
131.
/// </summary>
132.
/// <param name="disposing"></param>
133.
protected override void Dispose(bool disposing)
134.
{
135.
base.Dispose(disposing);
136.
if (disposing)
137.
{
138.
if (m_BackGraphics != null)
139.
{
140.
m_BackGraphics.Dispose();
141.
m_BackGraphics = null;
142.
}
143.
if (m_BackBuffer != null)
144.
{
145.
m_BackBuffer.Dispose();
146.
m_BackBuffer = null;
147.
}
148.
}
149.
}
150.
151.
#endregion
152.
}
153.
154.
public partial class Form1 : Form
155.
{
156.
public Form1()
157.
{
158.
InitializeComponent();
159.
CustomControl control = new CustomControl();
160.
control.Text =
"hthttehstehrjgsjgsfkgdgjmrknvrgnrkgrvnsgrkrgdsjgrdhjn";
161.
control.Bounds = new Rectangle(100, 100, 100,
50);
162.
CustomLabel label = new CustomLabel();
163.
label.Text =
"hthttehstehrjgsjgsfkgdgjmrknvrgnrkgrvnsgrkrgdsjgrdhjn";
164.
label.Bounds = new Rectangle(100, 200, 100, 50);
165.
Controls.Add(control);
166.
Controls.Add(label);
167.
}
168.
169.
private void button1_Click(object sender, EventArgs e)
170.
{
171.
Close();
172.
}
173.
}
174.
175.
partial class Form1
176.
{
177.
/// <summary>
178.
/// Required designer variable.
179.
/// </summary>
180.
private System.ComponentModel.IContainer components =
null;
181.
private System.Windows.Forms.MainMenu mainMenu1;
182.
183.
/// <summary>
184.
/// Clean up any resources being used.
185.
/// </summary>
186.
/// <param name="disposing">true if managed resources
should be disposed; otherwise, false.</param>
187.
protected override void Dispose(bool disposing)
188.
{
189.
if (disposing && (components != null))
190.
{
191.
components.Dispose();
192.
}
193.
base.Dispose(disposing);
194.
}
195.
196.
#region Windows Form Designer generated code
197.
198.
/// <summary>
199.
/// Required method for Designer support - do not modify
200.
/// the contents of this method with the code editor.
201.
/// </summary>
202.
private void InitializeComponent()
203.
{
204.
this.mainMenu1 = new
System.Windows.Forms.MainMenu();
205.
this.button1 = new
System.Windows.Forms.Button();
206.
this.SuspendLayout();
207.
//
208.
// button1
209.
//
210.
this.button1.Location = new
System.Drawing.Point(77, 233);
211.
this.button1.Name = "button1";
212.
this.button1.Size = new
System.Drawing.Size(72, 20);
213.
this.button1.TabIndex = 0;
214.
this.button1.Text = "Close";
215.
this.button1.Click += new
System.EventHandler(this.button1_Click);
216.
//
217.
// Form1
218.
//
219.
this.AutoScaleDimensions = new
System.Drawing.SizeF(96F, 96F);
220.
this.AutoScaleMode =
System.Windows.Forms.AutoScaleMode.Dpi;
221.
this.AutoScroll = true;
222.
this.ClientSize = new System.Drawing.Size(240,
268);
223.
this.Controls.Add(this.button1);
224.
this.Menu = this.mainMenu1;
225.
this.Name = "Form1";
226.
this.Text = "Form1";
227.
this.ResumeLayout(false);
228.
229.
}
230.
231.
#endregion
232.
233.
private System.Windows.Forms.Button button1;
234.
}
235.
}