Windows Develop Bookmark and Share   
 index > Windows Forms General > Control.DoubleBuffered should NOT be used?
 

Control.DoubleBuffered should NOT be used?

It seems to me that we should never use Control.DoubleBuffered.

My reasoning is thus:

(1) You set Control.DoubleBuffered to true in a control's constructor.
(2) Control.DoubleBuffered is a virtual property.
(3) You should never call virtual members from a constructor of a non-sealed class.
(4) Therefore you should never use Control.DoubleBuffered.

You can call this.SetStyle(ControlStyles.OptimizedDoubleBuffer | ControlStyles.AllPaintingInWmPaint, true) to achieve the same effect.
I'm thinking about advising our programming team to avoid the use of Control.DoubleBuffered for the aforementioned reason.

Anyone agree/disagree/have other thoughts?

There is a complicating factor:

Look at the ListView class. It overrides Control.DoubleBuffered so that when it's called, UpdateExtendedStyles() is called. That means you have to remember to call that after calling this.SetStyle().

Does it seem to anyone else that the introduction of a virtual property that is intended to be set in a constructor is somewhat of a design error?

Matthew Watson  Monday, September 21, 2009 2:43 PM
That's C++ reasoning. Managed code can call virtual members in the constructor, the CLR doesn't patch the v-table like C++ does. That can have unintended side-effects but that will never be the case with the DoubleBuffered property. It doesn't actually get used until the window handle is created. ListView.UpdateExtendedStyles() does its job when OnHandleCreated() runs.

Hans Passant.
nobugz  Monday, September 21, 2009 4:07 PM
It's certainly NOT C++ reasoning!

Suppose this happens:

(1) I call a virtual method in the constructor for a class.
(2) Someone derives a new class from that class.
(3) In the derived class they override the virtual method and do something that assumes that the derived class' constructor has been called.
(4) BANG! It blows up.

The following code demonstrates the problem:

(1) The "Base" class introduces a virtual bool property called "DoubleBuffered".
(2) The "NastyDerived" class sets DoubleBuffered to true in its constructor. That's all it does.
(3) The InnocentDerived class derives from NastyDerived.
It overrides DoubleBuffered, and as part of its implementation uses a value ("NonZeroValue") that should have been set in the constructor.
Seems ok, right? After all, it's been set to 1 in the constructor. So how could it possibly be 0 inside DoubleBuffered?

Anyways, compile and run the program, and you'll see that it blows up with a divide-by-zero exception!

So by setting DoubleBuffered in the constructor, I have caused DoubleBuffered in a derived class to be called before the derived class's constructor has been run. Nasty nasty nasty! Which is why I think it should be avoided.

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            InnocentDerived d = new InnocentDerived();
        }
    }

    class Base
    {
        public virtual bool DoubleBuffered
        {
            get;
            set;
        }
    }

    class NastyDerived: Base
    {
        public NastyDerived()
        {
            DoubleBuffered = true;
        }
    }

    class InnocentDerived: NastyDerived
    {
        public InnocentDerived()
        {
            NonZeroValue = 1;  // Ensure that this can never be zero.
        }

        public int NonZeroValue  // Can never be zero. Set to 1 in the ctor.
        {                        // ...or so he thinks.
            get;
            private set;
        }

        public override bool DoubleBuffered
        {
            get
            {
                return base.DoubleBuffered;
            }
            set
            {
                base.DoubleBuffered = value;
                _specialValue = 100/NonZeroValue;
            }
        }

        private int _specialValue;
    }

}


Matthew Watson  Monday, September 21, 2009 4:38 PM
Yah, that's the unintended side-effect I mentioned. Again, this is not an issue for DoubleBuffered, it doesn't get used until the window handle is created. You'll need to be consistent if you worry about DoubleBuffered, you'll need to create a new WF designer. The AllowDrop, Anchor, AutoSize, BackColor and many other properties are virtual too and assigned in the constructor by the designer.

Hans Passant.
nobugz  Monday, September 21, 2009 5:20 PM
You're missing the point...

It IS an issue if I call it from the constructor of a class and someone else derives from that class and overrides it!

That's what my code above demonstrates. It doesn't matter one bit how the .Net libraries use DoubleBuffered. If I call it from a constructor of a class, and someone derives from that class, then as soon as they override DoubleBuffered their override will be being called BEFORE THEIR CONSTRUCTOR IS EXECUTED.

I think you haven't understood what happens yet. Try running the code above - it crashes! Even though the Base class doesn't do anything with DoubleBuffered at all.

To reiterate: It doesn't matter HOW Base uses it; that's completely irrelevant. In the same way, it doesn't matter how .Net uses DoubleBuffered; the problem is caused by derived classes.

So when you say "that will never be the case with the DoubleBuffered property", that's not true - the problem exists, and I can easily write code that will crash because of it.
Matthew Watson  Monday, September 21, 2009 7:34 PM
As I said, this is by design. The C++ language has specific runtime behavior built-in to avoid this problem. It builds two v-tables for a class, one that is only used while the constructor runs, designed to prevent a derived virtual method in a derived class from being called. It gets redirected to the nearest virtual method instead. When the constructor ends, it swaps another v-table in place that enables virtual method calls to be dispatched to overrides in the derived class.

The C# language does not have that feature. More accurately, the CLR does not support it. And that allows the kind of problem you describe. This behavior is well documented in the C# spec. Getting it wrong, allowing a derived method to do something that relies on the partially constructed object state is a bug.

This is not specific to Windows Forms, any managed class has this potential problem.

Hans Passant.
nobugz  Monday, September 21, 2009 8:48 PM
Indeed, of course all managed classes have this potential problem. I think we are in agreement. :)

That's why the advice is to never call a virtual method in the constructor of a non-sealed class.

Which is my entire point! We shouldn't access a virtual member from a constructor, and therefore we should not design virtual members that are intended to be called from constructors, in any class. But DoubleBuffered is exactly such a virtual member - which is why I am saying that it is not a good design.

As Microsoft themselves state here http://msdn.microsoft.com/en-us/library/ms229060.aspx :

Do not call virtual members on an object inside its constructors.

Calling a virtual member causes the most-derived override to be called regardless of whether the constructor for the type that defines the most-derived override has been called. The following code example demonstrates this issue. As the base class constructor executes, it calls the derived class member, even though the derived class constructor has not been called. This example prints BadBaseClass to show that the state field has not been updated by the DerivedFromBad constructor.

This reinforces my position from my first post.
Matthew Watson  Monday, September 21, 2009 11:03 PM

You can use google to search for other answers

Custom Search

More Threads

• Registering microsoft jet OLE DB 4.0 provider on cilent machine
• Initialdirectory in runtime
• PropertyGrid - IDataErrorInfo
• ComboBox / DataGridView Problem
• creating a form to edit elements of an object.
• Weird e.Graphics.DrawImage comportation
• Window Service
• POS system
• How To: Set the Name property of a custom control that inherits from a .NET common control when it’s Text property is changed?
• Get Selected Text