Я пытаюсь иметь элемент управления пользователя, который имеет закругленные углы. Он не имеет фиксированный размер, но обычно он не имеет ширины намного больше, чем 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 здесь:
Настройка 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 для удаления старого дескриптора.
Не оптимальное решение, но, похоже, сейчас работает нормально.
Другие предложения, в то время как хорошие, не будут работать в моем случае.