views:

337

answers:

4

I want to make a panel have a thick border. Can I set this somehow?

PS, I am using C#. VS 2008.

+2  A: 

Jim,

I've made a user control and given is a ParentControlDesigner. As I indicated in my comment it's not a perfect solution to what you're asking for. But it should be a good starting point. Oh any FYI, I've got it with a customizable border color too. I was inspired by another SO post to pursue this... It was trickier than I expected. To get things to rearrange correctly when setting the border size a call to PerformLayout is made. The override to DisplayRectangle and the call to SetDisplayRectLocation in OnResize cause the proper repositioning of the child controls. As well the child controls don't have the expected "0,0" when in the upper left most... unless border width is set to 0... And OnPaint provides the custom drawing of the border.

Best of luck to ya! Making custom controls that are parents is tricky, but not impossible.

[Designer(typeof(ParentControlDesigner))]
public partial class CustomPanel : UserControl
{
    Color _borderColor = Color.Blue;
    int _borderWidth = 5;

    public int BorderWidth
    {
        get { return _borderWidth; }
        set { _borderWidth = value; 
            Invalidate();
            PerformLayout();
        }
    }

    public CustomPanel()  { InitializeComponent(); }

    public override Rectangle DisplayRectangle
    {
        get 
        { 
            return new Rectangle(_borderWidth, _borderWidth, Bounds.Width - _borderWidth * 2, Bounds.Height - _borderWidth * 2); 
        }
    }

    public Color BorderColor
    {
        get { return _borderColor; }
        set { _borderColor = value; Invalidate(); }
    }

    new public BorderStyle BorderStyle
    {
        get { return _borderWidth == 0 ? BorderStyle.None : BorderStyle.FixedSingle; }
        set  { }
    }

    protected override void OnPaint(PaintEventArgs e)
    {
        base.OnPaintBackground(e);
        if (this.BorderStyle == BorderStyle.FixedSingle)
        {
            using (Pen p = new Pen(_borderColor, _borderWidth))
            { 
                Rectangle r = ClientRectangle; 
                // now for the funky stuff...
                // to get the rectangle drawn correctly, we actually need to 
                // adjust the rectangle as .net centers the line, based on width, 
                // on the provided rectangle.
                r.Inflate(-Convert.ToInt32(_borderWidth / 2.0 + .5), -Convert.ToInt32(_borderWidth / 2.0 + .5));
                e.Graphics.DrawRectangle(p, r);
            }
        }
    }

    protected override void OnResize(EventArgs e)
    {
        base.OnResize(e);
        SetDisplayRectLocation(_borderWidth, _borderWidth);
    }
}
Jason D
Wow. Looks complicated. . . I will try the code later today.
jim
hmmm... not quite what I was hoping for. I will wait to see if better answers are given. If not I will accept this one.
jim
A: 

This is kind of rigging it but I've always just used a label for each side border. You'll have to set the autosize property to false and dock one to each side (left, right, top, bottom). Then just set the width/height/background color to do what you want.

You could easily make this a user control and just expose some custom public properties to set the width/height for you and the background color of all the labels to change the color.

Miles
What happens with your solution if I my control has a "min size" bigger than my custom panel (user control?).
jim
well, since all the labels are docked internally on the panel it will all depend on how the panel is displayed.Can you explain a little more on what you mean by "min size" bigger than the panel? I can't think of a good example in my head...
Miles
@Miles, I'm guessing what he means is what I described to him using almost those same words... Basically if you set a child controls minimum size to say, 100x100; then dock it to fill. Now size the parent panel to lets say 50x50. In my "solution" the child control is still placed correctly relative to the upper left corner, but the painting of the right and bottom get clipped by the child control... i'm still working how to fix that... Of course I could be wrong... he might mean something else.
Jason D
@miles and @jason. jason is right about what i mean.
jim
ahhhhhh gotcha. Mine couldn't probably cause a problem then. Nobugs's answer looks like the best then. probably alot cleaner too
Miles
+1  A: 

Just implement the panel's Paint event and draw a border. For example:

using System;
using System.Drawing;
using System.Windows.Forms;
using System.Windows.Forms.VisualStyles;

namespace WindowsFormsApplication1 {
  public partial class Form1 : Form {
    public Form1() {
      InitializeComponent();
      panel1.Paint += panel1_Paint;
    }
    VisualStyleRenderer renderer = new VisualStyleRenderer(VisualStyleElement.Button.PushButton.Normal);

    private void panel1_Paint(object sender, PaintEventArgs e) {
      renderer.DrawEdge(e.Graphics, panel1.ClientRectangle,
        Edges.Bottom | Edges.Left | Edges.Right | Edges.Top,
        EdgeStyle.Raised, EdgeEffects.Flat);
    }
  }
}

Play around with the arguments to find something you like. You ought to add code to fallback to ControlPaint.DrawBorder if visual styles aren't enabled. Meh.

Hans Passant
@nobugz, try putting a control in your panel, then docking it to fill. You'll see that the child control muddles the drawing of the border. I tried this approach initially as it seemed appropriate. However, without the display rectangle being relocated and resized a docked--fill control will prevent the custom border from being seen.
Jason D
Use the panel's Padding property to keep enough space for the border to be visible.
Hans Passant
nobugs, can I accept your answer instead?
jim
nvm. it has the same problem as Jason Ds stuff. Only it takes a property setting. So its simpler, but no better.
jim
It's complicated?
Hans Passant
sorry. yours is simpler. i used his code everywhere first. So no point changing all my code to use your approach when it has the same basic problem. and no point changing which i accept.
jim
+1  A: 

If this is just about presentation, put a panel that fills the form with a background color of the border color you want and a Dock style of Fill. Place another panel inside this one with the standard background color and a Dock style of Fill. Play with Padding and Margin of the two panels to get the border size you wish (I forget which param applies correclty to the inner panel and the outer panel). Place your controls on the interior panel. With both panels set to Dock=Fill, form resizing is automatically handled for you. You may need to experiment with some of the controls, but I have done this many times with no issues for both application main windows and popup forms.

cdkMoose