Как создать пользовательский элемент управления с закругленными углами?

Я пытаюсь иметь элемент управления пользователя, который имеет закругленные углы. Он не имеет фиксированный размер, но обычно он не имеет ширины намного больше, чем 120 пикселей.

Мне нужен элемент управления пользователя и его содержимое (ярлык и таблица), чтобы иметь закругленные края и выглядеть круглой коробкой.

Я использовал этот код.

[DllImport("Gdi32.dll", EntryPoint = "CreateRoundRectRgn")] private static extern IntPtr CreateRoundRectRgn ( int nLeftRect, // x-coordinate of upper-left corner int nTopRect, // y-coordinate of upper-left corner int nRightRect, // x-coordinate of lower-right corner int nBottomRect, // y-coordinate of lower-right corner int nWidthEllipse, // height of ellipse int nHeightEllipse // width of ellipse ); public static System.Drawing.Region GetRoundedRegion(int controlWidth, int controlHeight) { return System.Drawing.Region.FromHrgn(CreateRoundRectRgn(0, 0, controlWidth - 5, controlHeight - 5, 20, 20)); } 

Это дает контроль округленным углам, но после того, как он работает несколько раз, и я добавил несколько элементов моего User Control в форму, которая вызовет утечку, и я получу белый ящик с красным крестом на моих пользовательских элементах управления.

Есть ли лучший способ сделать это?

Если вы хотите действительно круглый угол и не только прозрачный трюк, вы можете использовать этот пример:

 private int radius=20; [DefaultValue(20)] public int Radius { get { return radius; } set { radius = value; this.RecreateRegion(); } } private GraphicsPath GetRoundRectagle(Rectangle bounds, int radius) { GraphicsPath path = new GraphicsPath(); path.AddArc(bounds.X, bounds.Y, radius, radius, 180, 90); path.AddArc(bounds.X + bounds.Width - radius, bounds.Y, radius, radius, 270, 90); path.AddArc(bounds.X + bounds.Width - radius, bounds.Y + bounds.Height - radius, radius, radius, 0, 90); path.AddArc(bounds.X, bounds.Y + bounds.Height - radius, radius, radius, 90, 90); path.CloseAllFigures(); return path; } private void RecreateRegion() { var bounds = new Rectangle(this.ClientRectangle.Location, this.ClientRectangle.Size); bounds.Inflate(-1, -1); using (var path = GetRoundRectagle(bounds, this.Radius)) this.Region = new Region(path); this.Invalidate(); } protected override void OnSizeChanged(EventArgs e) { base.OnSizeChanged(e); this.RecreateRegion(); } 

И скриншот будет:

введите описание изображения здесь

Разница между этим подходом и прозрачностью:

  • Устанавливая круглую область, элемент управления имеет действительно круглые углы, и вы можете видеть, что находится за круглой частью, несмотря на прозрачность, вы увидите фон формы.
  • Окружающая область, когда вы нажимаете на удаленную закругленную часть, переходите через область и тяните ее позади, но если вы используете прозрачный трюк, щелчок на прозрачной области будет обрабатываться элементом управления.

Вы можете использовать любой из этих двух вариантов. Создание прозрачного или настраиваемого региона на основе ваших требований.

Скачать

Вы можете скачать код или клонировать repository здесь:

  • г-aghaei / RoundCornerControlExample

Настройка Region имеет смысл только в том случае, если вы хотите «щелкнуть» прозрачную область. Если закругленные углы не такие большие, и вы хотите просто сделать углы визуально прозрачными, вы можете сделать то же самое, что и Button .

Преимущество этого решения заключается в том, что здесь вы можете иметь красивый сглаженный закругленный угол, в то время как края области всегда острые. Не говоря уже о том, что экземпляр Region содержит неуправляемые ресурсы и должен быть каким-то образом удален.

 protected override void OnPaint(PaintEventArgs e) { PaintTransparentBackground(this, e); // TODO: Paint your actual content here with rounded corners } private static void PaintTransparentBackground(Control c, PaintEventArgs e) { if (c.Parent == null || !Application.RenderWithVisualStyles) return; ButtonRenderer.DrawParentBackground(e.Graphics, c.ClientRectangle, c); } 

Я ответил на свой вопрос.

После комментария Sinatr я обнаружил, что не смог использовать OnHandleCreated, поскольку мне нужно было рисовать объект, прежде чем я узнаю, каков его размер. По ссылке Sinatr предоставлено исключение GetRoundedRegion

Итак, что я сделал, добавлена ​​переменная IntPtr в мой UserControl, которая присваивается методу CreateRoundRectRgn каждой краской с помощью дескриптора. Перед этим триггером я использую DeleteObject для удаления старого дескриптора.

Не оптимальное решение, но, похоже, сейчас работает нормально.

Другие предложения, в то время как хорошие, не будут работать в моем случае.