On and off MSDN forums, there have been questions and comments raised about flickering due to SplitContainer.
The repeated official answer from MSDN is the same - setStyle for DoubleBuffer, UserPaint and AllPaintingInWmPaint to true.
All the while the official answer does not andress this one obvious phenomenon:
-
The child SplitterPanels, panel1 and panel2, do not expose their setStyle methods.
-
SetStyle methods are protected. Therefore in order to apply setStyle to child SplitterPanels, the programmer needs to create asubclass of SplitterPanel.
-
But SplitContainer does not allow you to replace the child SplitterPanels referenced by the variables panel1 and panel2 with our own SplitterPanel instances.
Therefore, there is no way to enable doublebuffering on the individual SplitterPanels. There is no point in enabling doublebuffering for SplitContainer.
In fact, having doublebuffering feature available to SplitContainer is nearly a waste of namespace real-estate, because what really matters is doublebuffering the child SplitterPanels not the SplitContainer itself.
Another answer given to solve this problem is - have the contents of the SplitterPanels absorb theSplitterPanel'sirresistableurge toflicker.Which would require the programmer to create contents that have complex manual painting strategies that may or may not work. Which the programmer may not able to implement due to class protection of the content being use.
Therefore, the only effective answer I have found on the internet is - create your own SplitContainer class.
I urge MSDN to carefully inspect this issue and admit that there is a bug (aka non-deliberate feature)here:
-
Please do not tell us to use doublebuffering unless
-
You can tell us a way to propagate the double buffering to the child SplitterPanels.
Perhaps, the issue would be solved in .NET 3.x?
| | Blessed Geek Sunday, September 28, 2008 9:11 AM | The SetStyle() method of the panels are not accessible. You can hack around that with Reflection:
using System; using System.Windows.Forms; using System.Reflection;
public class MySplitContainer : SplitContainer { public MySplitContainer() { MethodInfo mi = typeof(Control).GetMethod("SetStyle", BindingFlags.NonPublic | BindingFlags.Instance); object[] args = new object[] { ControlStyles.OptimizedDoubleBuffer, true }; mi.Invoke(this.Panel1, args); mi.Invoke(this.Panel2, args); } } Hans Passant.- Marked As Answer bynobugzMVP, ModeratorSaturday, January 10, 2009 9:09 PM
-
| | nobugz Friday, January 02, 2009 6:33 PM | OK, I encountered someone suggesting that we should have a panel subclassed to use its protected setStyle method to enable double buffering.
Then attach and dock-fill the subclassed panel to the intendedSplitterPanel as a child control.
Then proceed todeposit stuffs intothe subclassed panel rather than to the SplitterPanel.
I will experiment with that and feedback my findings.
I don't quite like the idea of piling up layers of control unnecessarily. Wouldn't that slow the application down? Would the compiler optimiserbe smart enough to remove unnecessary layers (I guess not)?
But then, I am still curious:
Microsoft needs to reconsiderthe SplitContainer to make it more functional, usable and customisable. Microsoft should remove these paternalistic/fascistic restrictions and trust that a programmer knows what to do with thefreedom.
I might be deviating but I need to rub it in. Definition of a fascistic programming language/system - One that implements excessive or annoyingrestrictions and protectionsagainst the freedom of programming in hope of preventing programmers from getting interesting orultra-curricularwork done and to compel potential hackers from deviating from the intended architected use of the system.
Let's say we are all keen supporters of fascistic systems - I would like to know how having or not havingthose tworestrictions would prevent or allowa programmer from deviating from the intended architecture of .NET. And, how would a programmer be able to write write ultra-curricular applications beyond what is already available in .NET?
| | Blessed Geek Sunday, September 28, 2008 10:33 AM | Wow! Like somepeople would say -Works like a charm.
But I also overrode OnPaint.
I have a boolean flag PanelContainer.SuspendPainting which I set to true at start of a series of component updates and set it back to false at end of those updates. Which, basically, is similar to beginUpdate and endUpdate found in some controls. I tested it with the most nastyflicker-tendencycontrol ever - the webbrowser control.
Code Snippet
public class ContainerPanel : Panel
{
public ContainerPanel(Container tabContainer)
{
this._TabContainer = tabContainer;
this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
this.SetStyle(ControlStyles.CacheText, true);
this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
this.SetStyle(ControlStyles.ResizeRedraw, true);
this.SetStyle(ControlStyles.UserPaint, true);
}
private PanelContainer _PanelContainer;
public PanelContainer PanelContainer
{
get{return this._PanelContainer;}
}
protected override void OnPaint(PaintEventArgs e)
{
if (this.PanelContainer == null || this.PanelContainer.SuspendPainting)
return;
base.OnPaint(e);
}
}
At the SplitContainer
Code Snippet
this._ContentPanel2 = new ContainerPanel(this);
this._ContentPanel2.SuspendLayout();
this.Panel1.SuspendLayout();
this.Panel2.SuspendLayout();
this.SuspendLayout();
this._ContentPanel2.Dock = DockStyle.Fill;
this.Panel2.Controls.Add(this._ContentPanel2);
As usual for voluntary semaphores, the SuspendPainting flag may need to be treated for concurrent update.
I avoided that complexity by having only one thread update the flag and designing my code to perform anymultiplecomponent updatessynchronised toone single sequence of updatesand hence having definitebegin and end.
| | Blessed Geek Sunday, September 28, 2008 6:33 PM | I'm facing the same problem as you. I have not tested your solution so far, but I have a question.
How to you make it, that SplitContainer uses your new ContainerPanel? | | wannabe_2 Thursday, January 01, 2009 9:03 PM | So, now I have tested it. Here's my code:
| publicrefclassmyPanel:publicPanel{ |
| public: |
| myPanel(){ |
| SetStyle(ControlStyles::DoubleBuffer,true); |
| SetStyle(ControlStyles::UserPaint,true); |
| SetStyle(ControlStyles::AllPaintingInWmPaint,true); |
|
| }; |
| }; |
|
| publicrefclassmySplitContainer:publicSplitContainer{ |
| public: |
| mySplitContainer(){ |
| Panel1 =gcnewmyPanel; // this does not work? |
| Panel2=gcnewmyPanel; // this does not work? |
| }; |
| }; |
| But two questions arise: 1. How can I use my new panel-class inside the Form-Designer? 2. Is there a way to define 2 new myPanels for the derived mySplitContainer class? And one more question: How Do you handle a TreeView-control? It's content is flickering as the content of the original Panel-control. | | wannabe_2 Thursday, January 01, 2009 10:37 PM | The SetStyle() method of the panels are not accessible. You can hack around that with Reflection:
using System; using System.Windows.Forms; using System.Reflection;
public class MySplitContainer : SplitContainer { public MySplitContainer() { MethodInfo mi = typeof(Control).GetMethod("SetStyle", BindingFlags.NonPublic | BindingFlags.Instance); object[] args = new object[] { ControlStyles.OptimizedDoubleBuffer, true }; mi.Invoke(this.Panel1, args); mi.Invoke(this.Panel2, args); } } Hans Passant.- Marked As Answer bynobugzMVP, ModeratorSaturday, January 10, 2009 9:09 PM
-
| | nobugz Friday, January 02, 2009 6:33 PM | This code is what I need, butwhat is the VB.NET equivalent? Thanks!
- Jon | | Agent Z5q Wednesday, January 28, 2009 3:51 PM | Thank you to nobugz. That code worked *however* I had to add two extra control styles before it worked, as follows:
| //mustusereflectiontosetdoublebufferingonthechildpanelcontrols |
| MethodInfomi=typeof(Control).GetMethod("SetStyle",BindingFlags.NonPublic|BindingFlags.Instance); |
| object[]args=newobject[]{ControlStyles.UserPaint|ControlStyles.AllPaintingInWmPaint|ControlStyles.OptimizedDoubleBuffer,true}; |
| mi.Invoke(this.Panel1,args); |
| mi.Invoke(this.Panel2,args); |
| I'm not sure if that will work for everyone's situation, but I hope it is helpful to someone else out there. - Proposed As Answer byNate Cook Tuesday, March 10, 2009 4:33 PM
-
| | Nate Cook Tuesday, March 10, 2009 4:32 PM |
|