1

Sorry for my poor English.

I have a user control which includes two text boxes. I wanna draw a circle over that.

I tried to use a transparent panel like below. (This code is from Drawing circles on top of a form)

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }

    private void DrawCircle(int x, int y, int transparency, Graphics graphics)
    {
        if (transparency < 0)
            transparency = 0;
        else if (transparency > 255)
            transparency = 255;

        Pen pen = new Pen(Color.Red, 5)

        graphics.DrawEllipse(pen, new Rectangle(x, y, 90, 90));
        pen.Dispose();
        graphics.Dispose();
    }

    private void TransparentPanel1_Paint(object sender, PaintEventArgs e)
    {
        DrawCircle(10, 10, 255, e.Graphics);
    }

    private void Form1_Load(object sender, EventArgs e)
    {
        transparentPanel1.Enabled = false;
        transparentPanel1.Paint += TransparentPanel1_Paint;
        transparentPanel1.BringToFront();
    }
}

public class TransparentPanel : Panel
{
    protected override CreateParams CreateParams
    {
        get
        {
            CreateParams cp = base.CreateParams;
            cp.ExStyle |= 0x00000020; // WS_EX_TRANSPARENT
            return cp;
        }
    }
    protected override void OnPaintBackground(PaintEventArgs e)
    {
        //base.OnPaintBackground(e);
    }
}

However, it doesn't work.

When I use normal panel rather than transparent panel, the background color covers the entire textbox so I can't see the text. I don't want that.

I don't need editing text when the circle appeared, so this textbox can be replaced with label. (But I still need editing text when the circle doesn't exist.)

How can i draw a circle on a textbox? (Circle can be replaced with 'Circle Image file'. But the background of circle still need to be transparent.)

John
  • 13
  • 3
  • TextBox is a legacy control which doesn't like being drawn upon. You can add a Label/Panel etc with the image and the background will show through but the text will not. Do you really need textboxes, ie will they actually receive user input? – TaW Jul 20 '20 at 08:22

2 Answers2

0

I have had a similiar problem and the best solution I came up with was to make a screenshot from the parent panel and show the image in a another panel. This panel was made visible while the other one containing all controls was made invisible. I used this to show a loading screen without using a modal form.

I found the code in an old VB.NET application and hope that the translated code works:

class NativeMethods
{
    [DllImport("user32.dll", SetLastError = true)]
    [return: MarshalAs(UnmanagedType.Bool)]
    static extern bool PrintWindow(IntPtr hwnd, IntPtr hDC, uint nFlags);

    internal static Image PrintControl(Control ctrl)
    {
        using (Graphics controlGraphics = ctrl.CreateGraphics())
        {
            Bitmap bmp = new Bitmap(ctrl.Size.Width, ctrl.Size.Height, controlGraphics);

            using (Graphics bmpGraphics = Graphics.FromImage(bmp))
            {
                IntPtr dc = bmpGraphics.GetHdc();
                PrintWindow(ctrl.Handle, dc, 0);
                bmpGraphics.ReleaseHdc(dc);
                return bmp;
            }
        }
    }
}

Given you have Panel1 which contains the TextBox controls and Panel2 as a overlay (that contains the screenshot with the red circle) you could use a code like this:

        private void ShowRedCircle()
        {
            Image bmp = NativeMethods.PrintControl(this.panel1);

            using (Graphics bmpGraphics = Graphics.FromImage(bmp))
            using (Pen sPen = new Pen(Color.Red))
            {
                bmpGraphics.DrawEllipse(sPen, new Rectangle(10, 10, 90, 90));
                this.panel2.BackgroundImage = bmp;
            }

            this.panel2.Visible = true;
            this.panel1.Visible = false;
        }

When you want to remove the circle just change the visibility of the panels again. You should consider to dispose the BackgroundImage of panel2 in this case.

keco
  • 136
  • 9
0

You can do this by setting the Region property as a ring.

using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;

namespace WinForm
{
    public partial class Form1 : Form
    {
        TextBox textBox1;
        TextBox textBox2;
        Panel circle;

        public Form1()
        {
            //InitializeComponent();
            textBox1 = new TextBox { Parent = this, Width = 100, Left = 20, Top = 20, Height = 80, AutoSize = false };
            textBox2 = new TextBox { Parent = this, Width = 100, Left = 20, Top = textBox1.Bottom };

            ShowCircle();
        }
        void ShowCircle()
        {
            circle = new Panel
            {
                Parent = this,
                BackColor = Color.Red,
                Top = textBox1.Top,
                Left = textBox1.Left,
                Width = textBox1.Width,
                Height = textBox1.Height + textBox2.Height
            };

            using (var path = new GraphicsPath())
            {
                var rect = new Rectangle(0, 0, circle.Width, circle.Height);
                path.AddEllipse(rect);
                rect.Inflate(-5, -5);
                path.AddEllipse(rect);

                circle.Region = new Region(path);
                circle.BringToFront();
            }
        }
    }
}
Alexander Petrov
  • 13,457
  • 2
  • 20
  • 49