0

I would like to ask for some advice. I don't have enough experience in windows forms programming so I don't know what is the appropriate way of dealing with this task.

I am currently creating a rectangle from four panels. (these rectangles do not represent the code below, they are using different sizing) enter image description here

private System.Windows.Forms.Panel rectangleLeftVertical;
private System.Windows.Forms.Panel rectangleRightVertical;
private System.Windows.Forms.Panel rectangleTopHorizontal;
private System.Windows.Forms.Panel rectangleBottomHorizontal;

// ...

this.rectangleLeftVertical.Location = new System.Drawing.Point(100, 100);
this.rectangleLeftVertical.Size = new System.Drawing.Size(3, 100);

this.rectangleRightVertical.Location = new System.Drawing.Point(200, 100);
this.rectangleRightVertical.Size = new System.Drawing.Size(3, 100);

this.rectangleTopHorizontal.Location = new System.Drawing.Point(100, 100);
this.rectangleTopHorizontal.Size = new System.Drawing.Size(100, 3);

this.rectangleBottomHorizontal.Location = new System.Drawing.Point(100, 200);
this.rectangleBottomHorizontal.Size = new System.Drawing.Size(103, 3);

It is working exactly as I want it to, I would just like to encapsulate everything into a custom Windows Control. The custom component size should of course resize the panels. It will also have a property that dictates border size.

I do not want to change anything, I don't want to do the drawing differently (using graphics.paint solutions). It is not appropriate for my use case.

I tried using a UserControl, however, it is not appropriate because it paints the entire inside of the rectangle - which is what I am trying to avoid.

What would be even better is if it could also be used in the Windows Forms Designer - but this is very unnecessary. It would be really nice though.

How do you guys recommend I tackle this? Any help would be much appreciated. It is likely not a difficult problem, I just lack experience. Thanks!

Naltamer14
  • 187
  • 3
  • 10
  • When you say paint, do you mean the Panel is repainted in response to a resize event? – Ross Bush Nov 05 '20 at 15:02
  • What will you do if you need to have a line of different color? Drawing lines like you do is *very* inefficient, I doubt you have arguments to support your "user case" and you already running into problem to deal with backgrounds. – Sinatr Nov 05 '20 at 15:02
  • @RossBush I'm not sure what you're referring to. I need the inside of the rectangle to be entirely transparent because I have another process docked to the panel beneath it - therefore using any sort of Color.Transparent will not work (it will hide the contents inside the borders). – Naltamer14 Nov 05 '20 at 15:05
  • @Sinatr My solution does not need to be efficient, it will be used for debugging purposes. The lines will always be the same color. – Naltamer14 Nov 05 '20 at 15:06
  • @Naltamer14 - You can make the control inherent the parent color. – Ross Bush Nov 05 '20 at 15:06
  • You don't need custom control nor designer support for "debugging purposes". Don't be afraid of graphics.paint, it's [easy](https://stackoverflow.com/a/20997144/1997232). – Sinatr Nov 05 '20 at 15:07
  • @Sinatr Thanks for the feedback. It is meant for debugging purposes for my users, not for me (the developer). I'm saying graphics.paint is inappropriate because I have been using it until now. I won't go into why it doesn't do what I need. It's a long discussion. In short, I had to keep calling it every second to repaint - which was slow and very flickery). As I said, I have a process under these rectangles that is redrawing the screen. I don't need suggestions for other solutions, I'd just like someone with some background to tell me how to do what I wrote in the thread. Thanks – Naltamer14 Nov 05 '20 at 15:12
  • 2
    The controls repaint themselves using exactly same "slow and very flickery" technique. In other words instead of quickly painting line you are creating another window, with all overheads (event loops, memory, resources) to .. paint the line. No offence, I am trying to convince you to write custom control (like the one in my previous comment), add properties to it and do it *properly*. – Sinatr Nov 05 '20 at 15:14
  • @Sinatr You are probably correct, it would be more proper. But let's not argue, the way I want to do it is easier (for me). Everything is already written and the performance is not an issue. I just need to refactor it. – Naltamer14 Nov 05 '20 at 15:23
  • 1
    This question may lead to a [xy problem](https://meta.stackexchange.com/questions/66377/what-is-the-xy-problem) – Mat Nov 05 '20 at 15:28
  • "As I said, I have a process under these rectangles that is redrawing the screen." Is that other process running in the same app/form?...or is it literally a different process with its own window? In other words, it sounds like you want an OVERLAY that only consists of the borders, and is literally transparent (non-existent) in all other areas. – Idle_Mind Nov 05 '20 at 17:18
  • @Idle_Mind Precisely. It is a completely different process with its own window. – Naltamer14 Nov 05 '20 at 17:26

1 Answers1

1

You can achieve this by creating a GraphicsPath object representing the areas of the form you want to keep/discard. Start with a Rectangle that covers the entire form, then remove the middles of the panels, leaving just the borders. Then build a Region from that GraphicsPath and assign it to the Region property of your Form. This will result in a Form that only exists where the borders are. The middle areas that were removed from the GraphicsPath will literally NOT EXIST in your form, thus anything below your overlay will show right on through.

Here's a quick example that makes a four pane "window" that can be dragged around by the borders of the panes:

public partial class Form1 : Form
{

    public Form1()
    {
        InitializeComponent();
        this.TopMost = true;
        this.BackColor = Color.Red; // just so you can see it better
        this.FormBorderStyle = FormBorderStyle.None;
    }

    private void Form1_Shown(object sender, EventArgs e)
    {
        GraphicsPath path = new GraphicsPath();
        // add the main rectangle:
        path.AddRectangle(new Rectangle(new Point(0, 0), this.Size));
        // punch some holes in our main rectangle
        // this will make a standard "windowpane" with four panes
        // and a border width of ten pixels
        Size sz = new Size((this.Width - (3 * 10))/2, (this.Height - (3 * 10))/2);
        path.FillMode = FillMode.Alternate;
        path.AddRectangle(new Rectangle(new Point(10, 10), sz));
        path.AddRectangle(new Rectangle(new Point(20 + sz.Width, 10), sz));
        path.AddRectangle(new Rectangle(new Point(10, 20 + sz.Height), sz));
        path.AddRectangle(new Rectangle(new Point(20 + sz.Width, 20 + sz.Height), sz));
        // build a region from our path and set the forms region to that:
        this.Region = new Region(path);
    }

    public const int HTCAPTION = 0x2;
    public const int WM_NCHITTEST = 0x84;
    public const int HTCLIENT = 1;
    protected override void WndProc(ref Message m)
    {
        base.WndProc(ref m);

        if (m.Msg == WM_NCHITTEST)
        {
            if (m.Result.ToInt32() == HTCLIENT)
                m.Result = (IntPtr)HTCAPTION;
        }
    }

}

Example of it running on top of this page as I edit it:

enter image description here

If your overlay can be resized and/or adjusted, just rebuild a new GraphicsPath, change up the rectangles and re-assign the Region built from the new GraphicsPath.

You could also drop the .Opacity of the main form and you'd be able to partially see through your borders.

Idle_Mind
  • 38,363
  • 3
  • 29
  • 40
  • Brilliant! This was even better than what I wanted. Thank you very much! I'm using it in a UserControl instead of a separate form, which of course also works with the same logic. Huge thumbs up – Naltamer14 Nov 06 '20 at 15:33