?
Solved

How do I allow my UserControl to take dropped child controls and place them in container within the control? (2005 BETA 2)

Posted on 2005-04-06
14
Medium Priority
?
1,415 Views
Last Modified: 2008-01-09
I have a custom panel control that I wrote that draws dynamic borders.  The problem is that docking overwrites these borders, so what I need to do is create a container within the control that receives the child controls and controls the docking to be within the borders.

The problem is I cannot seem to manage this.  Even overriding the CreateControl and AddControl functions doesn't seem to work (I was trying to set the .Parent).

So the question is this - how do I have dropped controls on my UserControl become children of the Panel control within my user control?

Thanks!
0
Comment
Question by:Floobie
  • 7
  • 6
14 Comments
 
LVL 8

Expert Comment

by:RomanPetrenko
ID: 13720746
use ParentControlDesigner Class and Designer Attribute
ParentControlDesigner defined in System.Design.dll(System.Windows.Forms.Design namespace).

Add System.Design.dll as reference into your project.

====
using System.Windows.Forms.Design;

namespace MyControls
{
[Designer(typeof(ParentControlDesigner))]
public class UserControl1 : System.Windows.Forms.UserControl
{
...///Your class here
...
}
}

====
0
 
LVL 1

Author Comment

by:Floobie
ID: 13720780
This sets the control itself up to be a control container, but does not allow a control to be dropped INTO another control within the user control.  I am already doing this.
0
 
LVL 8

Expert Comment

by:RomanPetrenko
ID: 13721471
Take a look here:
http://www.experts-exchange.com/Programming/Programming_Languages/C_Sharp/Q_20861404.html
I think you have to hook events of IComponentChangeService sevice.
0
Concerto Cloud for Software Providers & ISVs

Can Concerto Cloud Services help you focus on evolving your application offerings, while delivering the best cloud experience to your customers? From DevOps to revenue models and customer support, the answer is yes!

Learn how Concerto can help you.

 
LVL 8

Expert Comment

by:RomanPetrenko
ID: 13721924
I think you don't need this stuff with putting dropped into another control - all that you have to do is to respond on WM_NCCALCIZE message and tell that client area of your panel smaller than whole window and drawing border procedure move into WM_NCPAINT message handler.

What do you think? Is this way better? If you need I could give you a sample for such implementation.
0
 
LVL 1

Author Comment

by:Floobie
ID: 13722806
Well the issue is making sure that docked controls don't overwrite the borders, which can be 5-10 pixels in width.  I can drop controls into the main usercontrol just fine; if I can somehow change the internal docking RECT size, that would solve the problem.  Is this possible?
0
 
LVL 8

Expert Comment

by:RomanPetrenko
ID: 13724967
Try to use this code in WndProc of your Control.
switch((Win32.WindowsMessages)m.Msg)
{

case Win32.WindowsMessages.WM_NCCALCSIZE:
{
      RECT r;
      r = (RECT)Marshal.PtrToStructure( m.LParam,
            typeof(RECT));
      //Make ClientSize smaller 3 pixel from each side. Can do anything you want.
      r.top += 3;
      r.bottom -= 3;
      r.left +=3;
      r.right -=3;
      
      Marshal.StructureToPtr( r, m.LParam, false );
      break;
}
case WindowsMessages.WM_NCPAINT:
{
      IntPtr hDC = User32.GetWindowDC( m.HWnd );
      Graphics g = Graphics.FromHdc( hDC );
      if (m.WParam.ToInt32() != 1)
      {
            Region rgn = Region.FromHrgn(m.WParam);
            Rectangle rec = RectangleToClient(Rectangle.Round( rgn.GetBounds(g) ));
            g.IntersectClip(rec);
      }
      YourDrawBordersProcedure(g);
      User32.ReleaseDC( m.HWnd, hDC );
      break;
}
}
0
 
LVL 8

Expert Comment

by:rajaloysious
ID: 13725265
i hope....Subclassing from the panel control should do the trick.

cheers
0
 
LVL 1

Author Comment

by:Floobie
ID: 13732011
Is this really the best way to do this?  I'd prefer to use the .NET framework and not to have to revert back to the old WM_X style of handling messages while using 2005 if possible.

Don't get me wrong, I'll try this and if it works, I'll close this; it just seems like a rather ugly fix (no offense to those trying to help, if that's the case it's a MS issue :) )
0
 
LVL 1

Author Comment

by:Floobie
ID: 13741458
OK, I've converted it all over, and I believe the code is incomplete.  Right now, it's not displaying the graphics properly...

I am thinking now that the best route is to figure out how to make it so dropped/created controls in the main control become children of a panel within the main control...

But here's the code as I have it in .NET.

      public class MyFilter: IMessageFilter
      {
            [DllImport("user32.dll")]
            static extern IntPtr GetWindowDC(IntPtr hWnd);
            [DllImport("user32.dll")]
            static extern int ReleaseDC(IntPtr hWnd, IntPtr hDC);

            private EnigmaPanel _Panel;

            public MyFilter(EnigmaPanel oPanel)
            {
                  _Panel = oPanel;
            }

            const int WM_NCCALCSIZE = 0x0083;
            const int WM_NCPAINT = 0x0085;

            public bool PreFilterMessage(ref Message aMessage)
            {

                  // Listen for operating system messages.
                  switch (aMessage.Msg)
                  {
                        case WM_NCCALCSIZE:
                              {

                                    Rectangle r;
                                    r = (Rectangle)Marshal.PtrToStructure(aMessage.LParam, typeof(Rectangle));
                                    r.Inflate(new Size(3,3));

                                    Marshal.StructureToPtr(r, aMessage.LParam, false);
                                    return true;
                              }

                        case WM_NCPAINT:
                              {
                                    IntPtr hDC = GetWindowDC(aMessage.HWnd);
                                    Graphics g = System.Drawing.Graphics.FromHdc(hDC);
                                    if (aMessage.WParam.ToInt32() != 1)
                                    {
                                          Region rgn = Region.FromHrgn(aMessage.WParam);
                                          Rectangle rec = _Panel.RectangleToClient(Rectangle.Round(rgn.GetBounds(g)));
                                          g.IntersectClip(rec);
                                    }

                                    // Draw the transparent background
                                    //base.OnPaintBackground(pe);

                                    // Draw the background Gradient
                                    Point oPoint = new Point(0, 0);
                                    Size oSize = new Size(_Panel.Width, _Panel.Height);

                                    if (_Panel.GradientBitmap == null)
                                    {_Panel.Rebuild();}
                                    
                                    g.DrawImage(_Panel.GradientBitmap, new Rectangle(oPoint, oSize));
                                    
                                    ReleaseDC(aMessage.HWnd, hDC);
                                    return true;
                              }

                        default:
                              {
                                    break;
                              }
                  }                  
                  
                  // This can be either true of false
                  // false enables the message to propagate to all other
                  // listeners
                  return false;
            }
      }
0
 
LVL 1

Author Comment

by:Floobie
ID: 13758017
Anyone?
0
 
LVL 8

Expert Comment

by:RomanPetrenko
ID: 13760129
Sorry for delay, here is the sample
May be it's not perfect(You have to calculate fill region more precise) but it works.
===
using System;
using System.Collections;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Windows.Forms.Design;
namespace WindowsApplication2
{
      public class User32
      {
            [DllImport("user32.dll")]
            public static extern int ReleaseDC(IntPtr hWnd, IntPtr hDC);
            [DllImport("user32.dll")]
            public static extern IntPtr GetWindowDC(IntPtr hWnd);
            [DllImport("user32.dll")]
            public static extern bool RedrawWindow(IntPtr hWnd, [In] ref RECT lprcUpdate,
                  IntPtr hrgnUpdate, uint flags);
      }
      [StructLayout(LayoutKind.Sequential)]
      public struct RECT
      {
            public int left;
            public int top;
            public int right;
            public int bottom;


            public RECT(int left, int top, int right, int bottom)
            {
                  this.left = left;
                  this.top = top;
                  this.right = right;
                  this.bottom = bottom;
            }
            public RECT(Rectangle rect)
            {
                  this.left = rect.Left;
                  this.top = rect.Top;
                  this.right = rect.Right;
                  this.bottom = rect.Bottom;
            }

            // Handy method for converting to a System.Drawing.Rectangle
            public Rectangle Rectangle
            {
                  get { return new Rectangle(this.left, this.top, this.right - this.left, this.bottom - this.top); }
            }
      }


      /// <summary>
      /// Summary description for DockCtrl.
      /// </summary>
      [Designer(typeof(ParentControlDesigner))]
      public class DockCtrl : System.Windows.Forms.UserControl
      {
            /// <summary>
            /// Required designer variable.
            /// </summary>
            private System.ComponentModel.Container components = null;

            public DockCtrl()
            {
                  // This call is required by the Windows.Forms Form Designer.
                  InitializeComponent();

                  SetStyle(ControlStyles.Selectable, true);
                  SetStyle(ControlStyles.DoubleBuffer, true);
                  SetStyle(ControlStyles.AllPaintingInWmPaint, true);
            }

            /// <summary>
            /// Clean up any resources being used.
            /// </summary>
            protected override void Dispose( bool disposing )
            {
                  if( disposing )
                  {
                        if(components != null)
                        {
                              components.Dispose();
                        }
                  }
                  base.Dispose( disposing );
            }

            #region Component Designer generated code
            /// <summary>
            /// Required method for Designer support - do not modify
            /// the contents of this method with the code editor.
            /// </summary>
            private void InitializeComponent()
            {
                  components = new System.ComponentModel.Container();
            }
            #endregion
            
            protected override void WndProc(ref Message m)
            {
                  switch(m.Msg)
                  {
                              #region WM_NCCALCSIZE
                        case 0x0083:
                        {
                              RECT r;
                              r = (RECT)Marshal.PtrToStructure( m.LParam,
                                    typeof(RECT));
                              r.top += 3;
                              r.bottom -= 3;
                              r.left += 3;
                              r.right -=3;
                              Marshal.StructureToPtr( r, m.LParam, false );
                              break;
                        }
                              #endregion
                              #region WM_NCPAINT
                        case 0x0085:
                        {
                              IntPtr hDC = User32.GetWindowDC( m.HWnd );
                              Graphics g = Graphics.FromHdc( hDC );
                              if (m.WParam.ToInt32() != 1)
                              {
                                    Region rgn = Region.FromHrgn(m.WParam);
                                    Rectangle rec = RectangleToClient(Rectangle.Round( rgn.GetBounds(g) ));
                                    g.IntersectClip(rec);
                              }
                              g.FillRectangle(Brushes.Blue,g.ClipBounds);
                              InvalidateWindow(Rectangle.Round(g.VisibleClipBounds));
                              User32.ReleaseDC( m.HWnd, hDC );
                              break;
                        }
                              #endregion
                  }
                  base.WndProc (ref m);
            }
            private void InvalidateWindow(Rectangle rect)
 
            {
                  RECT r = new RECT(rect);
 
                  User32.RedrawWindow(this.Handle, ref r, IntPtr.Zero,
 
                        0x0400/*RDW_FRAME*/ | 0x0100/*RDW_UPDATENOW*/
 
                        | 0x0001/*RDW_INVALIDATE*/);
 
            }

            private new Rectangle RectangleToClient(Rectangle rect)
            {
                  Rectangle r = base.RectangleToClient(rect);
                  r.Offset(3,3);
                  return r;
            }

            private new Point PointToClient(Point pt)
            {
                  Point p = base.PointToClient(pt);
                  p.Offset(3,3);
                  return p;
            }
      }
}
===
0
 
LVL 1

Author Comment

by:Floobie
ID: 13768542
Roman, thank you for the code; I've got it working just the way I want except for one thing - when I draw the border, for some reason, the transparent pixels aren't coming through when I paint in WM_NCPAINT.  Any ideas?  I've set the bitmap as transparent, and even tried filling it with a transparent brush before I build it, but no go.

I can tell it's working because when I fill with a black brush, the edges are black, but when I remove the fill, the edges are white.  Same when I try filling with Transparent...

Also, when I 'drag' the control around, the border is painting without the graphic border - only as a solid color.  Odd...
0
 
LVL 1

Author Comment

by:Floobie
ID: 13783926
Roman - you there?  I'm ready to award the points to you, even if you can't answer the above (since you provided the solution, which I appreciate).

If you do know why the border won't paint a transparent bitmap properly, I'd like to hear your solution.

Thanks!
0
 
LVL 8

Accepted Solution

by:
RomanPetrenko earned 800 total points
ID: 13789050
Sorry,  I was very busy at work

here is some links for you about transparent drawing:
http://www.codeproject.com/cs/miscctrl/customcheckbox.asp
http://support.microsoft.com/default.aspx?scid=kb;en-us;316963
http://www.bobpowell.net/transcontrols.htm
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vbcon/html/vbtskcreatingnon-standardshapedwindowsforms.asp
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dv_vstechart/html/vbtchShapedWindowsFormsControlsInVisualStudioNET.asp
http://addressof.com/blog/articles/293.aspx

and some improved code (But still not perfect :( ) for NC area drawing:
====
using System;
using System.Diagnostics;
using System.Collections;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Windows.Forms.Design;
namespace WindowsApplication2
{
      public class User32
      {
            [DllImport("user32.dll")]
            public static extern int ReleaseDC(IntPtr hWnd, IntPtr hDC);
            [DllImport("user32.dll")]
            public static extern IntPtr GetWindowDC(IntPtr hWnd);
            [DllImport("user32.dll")]
            public static extern bool RedrawWindow(IntPtr hWnd, [In] ref RECT lprcUpdate,
                  IntPtr hrgnUpdate, uint flags);
      }
      [StructLayout(LayoutKind.Sequential)]
      public struct RECT
      {
            public int left;
            public int top;
            public int right;
            public int bottom;


            public RECT(int left, int top, int right, int bottom)
            {
                  this.left = left;
                  this.top = top;
                  this.right = right;
                  this.bottom = bottom;
            }
            public RECT(Rectangle rect)
            {
                  this.left = rect.Left;
                  this.top = rect.Top;
                  this.right = rect.Right;
                  this.bottom = rect.Bottom;
            }

            // Handy method for converting to a System.Drawing.Rectangle
            public Rectangle Rectangle
            {
                  get { return new Rectangle(this.left, this.top, this.right - this.left, this.bottom - this.top); }
            }
      }


      /// <summary>
      /// Summary description for DockCtrl.
      /// </summary>
      [Designer(typeof(ParentControlDesigner))]
      public class DockCtrl : System.Windows.Forms.UserControl
      {
            /// <summary>
            /// Required designer variable.
            /// </summary>
            private System.ComponentModel.Container components = null;

            public DockCtrl()
            {
                  // This call is required by the Windows.Forms Form Designer.
                  InitializeComponent();

                  SetStyle(ControlStyles.Selectable, true);
                  SetStyle(ControlStyles.DoubleBuffer, true);
                  SetStyle(ControlStyles.AllPaintingInWmPaint, true);
                  SetStyle(ControlStyles.Opaque, false);
                  SetStyle( ControlStyles.UserPaint, true );
                  SetStyle(ControlStyles.SupportsTransparentBackColor,true);
            }
            protected override CreateParams CreateParams
            {
                  get
                  {
                        CreateParams cp=base.CreateParams;
                        //cp.ExStyle|=0x00000020; //WS_EX_TRANSPARENT
                        return cp;
                  }
            }

            /// <summary>
            /// Clean up any resources being used.
            /// </summary>
            protected override void Dispose( bool disposing )
            {
                  if( disposing )
                  {
                        if(components != null)
                        {
                              components.Dispose();
                        }
                  }
                  base.Dispose( disposing );
            }

            #region Component Designer generated code
            /// <summary>
            /// Required method for Designer support - do not modify
            /// the contents of this method with the code editor.
            /// </summary>
            private void InitializeComponent()
            {
                  components = new System.ComponentModel.Container();
            }
            #endregion
         
            private Brush b = new SolidBrush(Color.FromArgb(50,Color.Blue));
            protected override void WndProc(ref Message m)
            {
                  switch(m.Msg)
                  {
                              #region WM_NCCALCSIZE
                        case 0x0083:
                        {
                              RECT r;
                              r = (RECT)Marshal.PtrToStructure( m.LParam,
                                    typeof(RECT));
                              r.top += 3;
                              r.bottom -= 3;
                              r.left += 3;
                              r.right -=3;
                              Marshal.StructureToPtr( r, m.LParam, false );
                              break;
                        }
                              #endregion
                              #region WM_NCPAINT
                        case 0x0085:
                        {
                              IntPtr hDC = User32.GetWindowDC( m.HWnd );
                              Graphics g = Graphics.FromHdcInternal( hDC );
                              Rectangle rec = Rectangle.Empty;
                              if (m.WParam.ToInt32() != 1)
                              {
                                    //TODO: improve Clip region calculation
                                    Region rgn = Region.FromHrgn(m.WParam);
                                    rec = RectangleToClient(Rectangle.Round( rgn.GetBounds(g) ));
                                    Debug.WriteLine(rec);
                                    g.SetClip(rec,System.Drawing.Drawing2D.CombineMode.Replace);
                              }
                              Rectangle qr = rec;
                              if (qr == Rectangle.Empty)
                                    qr = Bounds;
                              Region r = new Region(qr);

                              r.Exclude(Rectangle.Inflate(qr,-3,-3));
                              RectangleF boundingRect = r.GetBounds(g);
                              Parent.Invalidate(r);
                              g.FillRegion(b, r);
                              User32.ReleaseDC( m.HWnd, hDC );
                              return;
                        }
                              #endregion
                  }
                  base.WndProc (ref m);
            }
            private void InvalidateWindow(Rectangle rect)
 
            {
                  RECT r = new RECT(rect);
 
                  User32.RedrawWindow(this.Handle, ref r, IntPtr.Zero,
 
                        0x0400/*RDW_FRAME*/ | 0x0100/*RDW_UPDATENOW*/
 
                        | 0x0001/*RDW_INVALIDATE*/ | 0x0080/*RDW_ALLCHILDREN*/);
 
            }

            private new Rectangle RectangleToClient(Rectangle rect)
            {
                  Rectangle r = base.RectangleToClient(rect);
                  r.Offset(3,3);
                  return r;
            }

            private new Point PointToClient(Point pt)
            {
                  Point p = base.PointToClient(pt);
                  p.Offset(3,3);
                  return p;
            }
      }
}
====

PS: I was trying to make transparent border but unsuccessfully(Transparency is new stuff for me), the parent window has not drawn under NC area. May be there is some way to make the parent draw itself in this case - I don't know.
0

Featured Post

Free Tool: Subnet Calculator

The subnet calculator helps you design networks by taking an IP address and network mask and returning information such as network, broadcast address, and host range.

One of a set of tools we're offering as a way of saying thank you for being a part of the community.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

This article describes a simple method to resize a control at runtime.  It includes ready-to-use source code and a complete sample demonstration application.  We'll also talk about C# Extension Methods. Introduction In one of my applications…
We all know that functional code is the leg that any good program stands on when it comes right down to it, however, if your program lacks a good user interface your product may not have the appeal needed to keep your customers happy. This issue can…
This lesson discusses how to use a Mainform + Subforms in Microsoft Access to find and enter data for payments on orders. The sample data comes from a custom shop that builds and sells movable storage structures that are delivered to your property. …
Despite its rising prevalence in the business world, "the cloud" is still misunderstood. Some companies still believe common misconceptions about lack of security in cloud solutions and many misuses of cloud storage options still occur every day. …
Suggested Courses

829 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question