locked
DrawToBitmap z-order issue RRS feed

  • Question

  • Hi

    To come to the point - imagine we have a UserControl on which we place a number of TextBoxes and Labels.  Lay these controls out partially over one another.

    Now place an instance of this UserControl on a Form with a button next to it. On clicking this button take a snapshot of the UserControl instance using the DrawToBitmap method that this instance exposes (via the Control object).

    This creates a Bitmap where the z-order of all controls seems to have been reversed i.e. if a textbox was on top of a label on the original control then on the newly created Bitmap it is placed behind the label.

    There are a few more inconsistencies regarding using this DrawToBitmap method (like colourings) but this probably is the main one.

    This method is in theory incredibly powerful since it allows a snapshot to be taken of UserControls that are hidden (such as on different Tabs in a TabControl).  Another way (I have been forced to take now) is to use a BitBlt which unfortunately requires me to make the control visible. 

    Any help much appreciated !!

    Marcel

     

    Tuesday, June 27, 2006 1:40 PM

Answers

  •  Marcel Heeremans wrote:
    If this is a known MS issue then is there a workaround
    This is apparently a known issue with MS, see http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=311255&SiteId=1.  The lad from MS mentions in that post there is no workaround.

    This is an issue with Windows, not with the .NET framework...

    The issue is further mentioned here http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=39463&SiteId=1, with no comments from MS.

    While the lad from MS says there's no workaround, you could simply change the z-order of the controls in the UserControl, for example:


    public partial class UserControl1 : UserControl
    {
     public UserControl1 ( )
     {
      InitializeComponent();
     }

     bool printing;
     bool donePrinting;

     /// <summary>
     /// synchronizes our painting invariant encapsulated
     /// in <i>printing</i> and <i>donePrinting</i>
     /// </summary>
     object paintLock = new Object();

     const int WM_PRINT = 0x317;

     protected override void WndProc(ref Message m)
     {
      if (m.Msg == WM_PRINT)
      {
       lock (paintLock)
       {
        donePrinting = false;
        printing = true;
       }
      }
      base.WndProc(ref m);
     }

     protected override void OnPaint(PaintEventArgs e)
     {
      lock (paintLock)
      {
       if (printing)
       {
        Control[] controls = new Control[this.Controls.Count];
        this.Controls.CopyTo(controls, 0);

        foreach (Control c in controls)
         c.BringToFront();
       }

       base.OnPaint(e);

       if (printing)
       {
        Control[] controls = new Control[this.Controls.Count];
        this.Controls.CopyTo(controls, 0);

        for (int i = controls.Length - 1; i >= 0; i--)
        {
         controls[ i ].BringToFront();
        }
        if (donePrinting)
        {
         printing = false;
         donePrinting = false;
        }
        else
        {
         donePrinting = true;
        }
       }
      }
     }
    }

     

    Wednesday, June 28, 2006 3:12 PM

All replies

  • Check to see what order your controls are drawing in.  I don't think sibling controls are clipped when using DrawToBitmap.  Depending on the window flags this would cause the controls to appear to draw in the opposite order of their z-order.  If you trace through the paint for these controls when DrawToBitmap() is call you should be able to tell if siblings are clipped by looking at the clip bounds.

    DrawToBitmap is basically asking the control on which it was called to issue a WM_PRINT notification. 

    Tuesday, June 27, 2006 5:39 PM
  • Thanks for the quick reply but it don't think it addresses the problem.

    Perhaps you could build a quick sample project or allow me to upload a small sample project or maybe have a look at this link at Syncfusion.com that points at my correspondence with Syncfusion who are taking their hands of it since they say it is a Microsoft issue.

    Fact of the matter is we have a team of developers writing Business UserControls that the main hosting program sometimes need to take snapshots of using DrawToBitmap - I can't control how they contruct their UserControls all I want is to end up with a Bitmap representation of the control at certain points bearing in mind that that control often may not be visible (ie it is placed on multiple tab-pages).

    The problem remains that the z-order seems to have been affected but also sometimes when the UserControl is not visible calling DrawToBitmap seems to affect the background colour of Textboxes?

    If this is a known MS issue then is there a work-around available?

    Cheers

    Marcel

    Wednesday, June 28, 2006 8:07 AM
  • I'll have a quick look at the project.  I don't know if this is known with MS, as I don't work there.  You could peruse Visual Studio and .NET Framework Feedback to see if has been reported...
    Wednesday, June 28, 2006 2:24 PM
  •  Marcel Heeremans wrote:
    If this is a known MS issue then is there a workaround
    This is apparently a known issue with MS, see http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=311255&SiteId=1.  The lad from MS mentions in that post there is no workaround.

    This is an issue with Windows, not with the .NET framework...

    The issue is further mentioned here http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=39463&SiteId=1, with no comments from MS.

    While the lad from MS says there's no workaround, you could simply change the z-order of the controls in the UserControl, for example:


    public partial class UserControl1 : UserControl
    {
     public UserControl1 ( )
     {
      InitializeComponent();
     }

     bool printing;
     bool donePrinting;

     /// <summary>
     /// synchronizes our painting invariant encapsulated
     /// in <i>printing</i> and <i>donePrinting</i>
     /// </summary>
     object paintLock = new Object();

     const int WM_PRINT = 0x317;

     protected override void WndProc(ref Message m)
     {
      if (m.Msg == WM_PRINT)
      {
       lock (paintLock)
       {
        donePrinting = false;
        printing = true;
       }
      }
      base.WndProc(ref m);
     }

     protected override void OnPaint(PaintEventArgs e)
     {
      lock (paintLock)
      {
       if (printing)
       {
        Control[] controls = new Control[this.Controls.Count];
        this.Controls.CopyTo(controls, 0);

        foreach (Control c in controls)
         c.BringToFront();
       }

       base.OnPaint(e);

       if (printing)
       {
        Control[] controls = new Control[this.Controls.Count];
        this.Controls.CopyTo(controls, 0);

        for (int i = controls.Length - 1; i >= 0; i--)
        {
         controls[ i ].BringToFront();
        }
        if (donePrinting)
        {
         printing = false;
         donePrinting = false;
        }
        else
        {
         donePrinting = true;
        }
       }
      }
     }
    }

     

    Wednesday, June 28, 2006 3:12 PM
  •  

    That's brilliant Peter - thanks for your help!

    However, reading the answer of the MS chap who claims that the order is non-deterministic I am hesitant to simply rely on the reversal of the z-order especially since I have also spotted some color changes in text-boxes when taking the image so there may be other unknown forces at work.

    Having said that - I will implement your reversal solution and let our test team give it a battering and see what comes out...

    Regards

    Marcel

     

     

     

    Friday, June 30, 2006 9:04 AM
  • Yes, no matter how you look at it, WM_PAINT seems either unreliable or flawed (and, by association, DrawToBitmap).  But, it's the only alternative if you don't have a visible window.
    Friday, June 30, 2006 12:25 PM
  • hi,

     

    you can do something like this:

     

    Bitmap bitmap = new Bitmap(ParentControl.Width, ParentControl.Height);

     

    ParentControl.DrawToBitmap(bitmap, new Rectangle(0, 0, ParentControl.Width, ParentControl.Height));

     

    foreach (Control c in ParentControl.Controls)

    {

    c.DrawToBitmap(bitmap, new Rectangle(c.Location.X, c.Location.Y, c.Width, c.Height));

    }

     

     

    regards,

    calin

    Sunday, November 23, 2008 3:43 PM
  • Hi,
    I managed to fix this problem for our project which uses vb.net - I simply added the following code to our Base form class:

    Const WM_PRINT As Integer = &H317
            Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)
                Select Case m.Msg
                    Case WM_PRINT
                        Me.SuspendLayout()
                        Try
                            Me.ReverseControlsCollection(Me)
                        Catch ex As Exception
                            'dont throw an error on the control order failing
                            'If the order fails, the second call (below) will set the indexes back
                        End Try
                        MyBase.WndProc(m)
                        Try
                            Me.ReverseControlsCollection(Me)
                        Catch ex As Exception
                            'dont throw an error on the control order failing
                        End Try
                        Me.ResumeLayout(False)
                        Return
                End Select
                MyBase.WndProc(m)
            End Sub
    
            Sub ReverseControlsCollection(ByVal pControl As Control)
                If pControl.Controls.Count > 0 Then
                    For Each lControl As Control In pControl.Controls
                        ReverseControlsCollection(lControl)
                    Next
                End If
                Dim max As Integer = pControl.Controls.Count - 1
                For i As Integer = max To 1 Step -1
                    pControl.Controls.SetChildIndex(pControl.Controls(max), max - i)
                Next
            End Sub
    It works 99% of the time perfectly - the only time I experience any issues is with unusual 3rd party controls that don't like Setting the Child Index - but in thoses cases the try/catch simply ensures the indexes are set back to their original values.

    Regards
    Adrian
    Wednesday, September 16, 2009 3:13 AM
  • Solution here: http://social.msdn.microsoft.com/Forums/en/winforms/thread/158c552c-92c3-4aa4-be10-08448741eb48

    Works great!!

    Saturday, February 2, 2013 9:18 AM