我正在尝试创建Bootstrap 4 Radio Button Group熟悉的用户体验。
我知道我可以改变Appearance=Button
,但这不会给我带来预期的效果。
这就是我得到的:
这就是我想要的:
我找到similar question,但它使用WPF,我需要WinForms。 Second one建议使用列表视图,但该问题列表是垂直的,但我需要水平对齐。
我正在寻找一个控件,左边第一个按钮,右边第一个按钮(就像在第二个图像上一样)
我的问题是:有谁知道这种控制是否存在?如果是的话,我会感激anu链接,如果没有,那么欢迎任何有关如何创建此类控件的提示(我知道如何创建自定义控件并绘制它们,因此不需要如何开始使用用户控件的链接)
修改
因为我的问题被投票,我正在添加我编写的代码来创建这样的控件。
这是我得到的结果:
这是代码:
using System;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;
namespace UserControls
{
public sealed class RadioGroupBox: UserControl
{
public RadioGroupBox()
{
DoubleBuffered = true;
ResizeRedraw = true;
Padding = new Padding(2);
CalculateItemWidth();
}
public event EventHandler SelectedIndexChanged;
protected override Size DefaultSize => new Size(200, 30);
private int _cornerRadius = 2;
public int CornerRadius
{
get => _cornerRadius;
set
{
if(value==_cornerRadius) return;
_cornerRadius = value;
Invalidate();
}
}
private string[] _items = {"A", "B", "C"};
[Category("Data")]
//[DefaultValue(null)]
[Description("Items")]
public string[] Items
{
get => _items;
set
{
if(value==_items) return;
_items = value;
CalculateItemWidth();
Invalidate();
}
}
private int _itemWidth;
private void CalculateItemWidth()
{
if (_items == null || _items.Length == 0)
{
_itemWidth = 0;
return;
}
var width = ClientRectangle.Width - Padding.Horizontal;
_itemWidth = width / _items.Length;
}
protected override void OnMouseMove(MouseEventArgs e)
{
base.OnMouseMove(e);
CalculateItemWidth();
//Debug.WriteLine(_itemWidth);
CalculateSelectedItem(e.Location);
}
private int _selectedIndex = -1;
public int SelectedIndex
{
get => _selectedIndex;
set
{
if(value==_selectedIndex) return;
_selectedIndex = value;
Invalidate();
}
}
protected override void OnMouseDown(MouseEventArgs e)
{
base.OnMouseDown(e);
_click = true;
if (_hoverPos > -1)
{
if (_selectedIndex != _hoverPos)
{
_selectedIndex = _hoverPos;
SelectedIndexChanged?.Invoke(this, e);
}
}
Invalidate();
}
protected override void OnMouseUp(MouseEventArgs e)
{
base.OnMouseUp(e);
_click = false;
Invalidate();
}
private int _hoverPos = -1;
private bool _click;
private void CalculateSelectedItem(Point mouseLocation)
{
var clientRect = GetPaddedRectangle();
int pos;
if (!clientRect.Contains(mouseLocation))
{
pos = -1;
}
else
{
pos = mouseLocation.X / _itemWidth;
if (pos > _items.Length - 1)
{
pos = -1;
}
}
if (pos != _hoverPos)
{
_hoverPos = pos;
Invalidate();
}
//Debug.WriteLine(_hoverPos);
}
protected override void OnResize(EventArgs e)
{
base.OnResize(e);
CalculateItemWidth();
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
var rect = GetPaddedRectangle();
LinearGradientBrush normalBrush = new LinearGradientBrush(rect,
Color.White,
Color.LightBlue,
LinearGradientMode.Vertical);
LinearGradientBrush hoverBlush = new LinearGradientBrush(rect,
Color.RoyalBlue,
Color.MediumBlue,
LinearGradientMode.Vertical);
LinearGradientBrush selectedBrush = new LinearGradientBrush(rect,
Color.DodgerBlue,
Color.Blue,
LinearGradientMode.Vertical);
//e.Graphics.FillRectangle(Brushes.Aqua, this.ClientRectangle);
using (GraphicsPath path = RoundedRect(rect, _cornerRadius))
{
e.Graphics.FillPath(normalBrush, path);
e.Graphics.DrawPath(Pens.DodgerBlue, path);
}
if (_items == null || _items.Length == 0) return;
if (_hoverPos > -1 || _selectedIndex>-1)
{
var flags = RectangleCorners.None;
if (_hoverPos == 0) flags = RectangleCorners.TopLeft | RectangleCorners.BottomLeft;
if(_hoverPos==_items.Length-1) flags = RectangleCorners.TopRight | RectangleCorners.BottomRight;
var rect2 = new Rectangle(rect.X + _hoverPos * _itemWidth, rect.Y, _itemWidth, rect.Height);
if (_hoverPos == _items.Length - 1)
{
rect2 = new Rectangle(rect.X + _hoverPos * _itemWidth, rect.Y, rect.Width-_hoverPos*_itemWidth, rect.Height);
}
if (_hoverPos > -1 && _selectedIndex != _hoverPos)
{
using (GraphicsPath path = RoundedRect(rect2, _cornerRadius, flags))
{
e.Graphics.FillPath(_click ? Brushes.SteelBlue : hoverBlush, path);
}
}
if (_selectedIndex > -1)
{
rect2 = new Rectangle(rect.X + _selectedIndex * _itemWidth, rect.Y, _itemWidth, rect.Height);
if (_selectedIndex == _items.Length - 1)
{
rect2 = new Rectangle(rect.X + _selectedIndex * _itemWidth, rect.Y, rect.Width - _selectedIndex * _itemWidth, rect.Height);
}
flags = RectangleCorners.None;
if (_selectedIndex == 0) flags = RectangleCorners.TopLeft | RectangleCorners.BottomLeft;
if (_selectedIndex == _items.Length - 1) flags = RectangleCorners.TopRight | RectangleCorners.BottomRight;
using (GraphicsPath path = RoundedRect(rect2, _cornerRadius, flags))
{
e.Graphics.FillPath(selectedBrush, path);
}
}
}
//pionowe linie
for (int i = 1; i <= _items.Length-1; i++)
{
e.Graphics.DrawLine(Pens.DodgerBlue,rect.X+i*_itemWidth,rect.Y, rect.X + i * _itemWidth, rect.Y+rect.Height);
}
StringFormat sf = new StringFormat
{
LineAlignment = StringAlignment.Center,
Alignment = StringAlignment.Center
};
for (var i = 0; i < _items.Length; i++)
{
string item = _items[i];
e.Graphics.DrawString(item, Font, i==_hoverPos?Brushes.White:Brushes.DodgerBlue, new Rectangle(rect.X+i* _itemWidth, rect.Y, _itemWidth, rect.Height), sf);
}
//Debug.WriteLine(MousePosition);
}
private Rectangle GetPaddedRectangle()
{
var rect = ClientRectangle;
var pad = Padding;
return new Rectangle(rect.X + pad.Left,
rect.Y + pad.Top,
rect.Width - pad.Horizontal,
rect.Height - pad.Vertical);
}
[Flags]
public enum RectangleCorners
{
None = 0, TopLeft = 1, TopRight = 2, BottomLeft = 4, BottomRight = 8,
All = TopLeft | TopRight | BottomLeft | BottomRight
}
public static GraphicsPath RoundedRect(Rectangle bounds, int radius, RectangleCorners corners = RectangleCorners.All)
{
int diameter = radius * 2;
GraphicsPath path = new GraphicsPath();
if (radius == 0)
{
path.AddRectangle(bounds);
return path;
}
// Make a GraphicsPath to draw the rectangle.
PointF point1, point2;
// Upper left corner.
if ((RectangleCorners.TopLeft & corners) == RectangleCorners.TopLeft)
{
RectangleF corner = new RectangleF(bounds.X, bounds.Y,diameter, diameter);
path.AddArc(corner, 180, 90);
point1 = new PointF(bounds.X + radius, bounds.Y);
}
else point1 = new PointF(bounds.X, bounds.Y);
// Top side.
if ((RectangleCorners.TopRight & corners) == RectangleCorners.TopRight)
point2 = new PointF(bounds.Right - radius, bounds.Y);
else
point2 = new PointF(bounds.Right, bounds.Y);
path.AddLine(point1, point2);
// Upper right corner.
if ((RectangleCorners.TopRight & corners) == RectangleCorners.TopRight)
{
RectangleF corner = new RectangleF(bounds.Right - diameter, bounds.Y,diameter, diameter);
path.AddArc(corner, 270, 90);
point1 = new PointF(bounds.Right, bounds.Y + radius);
}
else point1 = new PointF(bounds.Right, bounds.Y);
// Right side.
if ((RectangleCorners.BottomRight & corners) == RectangleCorners.BottomRight)
point2 = new PointF(bounds.Right, bounds.Bottom - radius);
else
point2 = new PointF(bounds.Right, bounds.Bottom);
path.AddLine(point1, point2);
// Lower right corner.
if ((RectangleCorners.BottomRight & corners) == RectangleCorners.BottomRight)
{
RectangleF corner = new RectangleF(bounds.Right - diameter,bounds.Bottom - diameter,diameter, diameter);
path.AddArc(corner, 0, 90);
point1 = new PointF(bounds.Right - radius, bounds.Bottom);
}
else point1 = new PointF(bounds.Right, bounds.Bottom);
// Bottom side.
if ((RectangleCorners.BottomLeft & corners) == RectangleCorners.BottomLeft)
point2 = new PointF(bounds.X + radius, bounds.Bottom);
else
point2 = new PointF(bounds.X, bounds.Bottom);
path.AddLine(point1, point2);
// Lower left corner.
if ((RectangleCorners.BottomLeft & corners) == RectangleCorners.BottomLeft)
{
RectangleF corner = new RectangleF(bounds.X, bounds.Bottom - diameter,diameter, diameter);
path.AddArc(corner, 90, 90);
point1 = new PointF(bounds.X, bounds.Bottom - radius);
}
else point1 = new PointF(bounds.X, bounds.Bottom);
// Left side.
if ((RectangleCorners.TopLeft & corners) == RectangleCorners.TopLeft)
point2 = new PointF(bounds.X, bounds.Y + radius);
else
point2 = new PointF(bounds.X, bounds.Y);
path.AddLine(point1, point2);
// Join with the start point.
path.CloseFigure();
return path;
}
}
}
它没有经过优化,只是证明了它的概念。
这个问题的重点是找到现有的控制而不需要重新发明轮子。
如果我无法找到这样的控件,我将创建一个并共享它。