FaceNumeric.cs 12.9 KB
using System;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;

namespace FaceControl
{
    [DefaultProperty("Value")]
    [DefaultEvent("ValueChanged")]
    public partial class FaceNumeric : ControlBase
    {
        private TextBox txt;
        private bool[] mouseDown = new bool[2];
        private bool[] buttonOver = new bool[2];
        private bool borderOver = false;
        private GraphicsPath borderPath = new GraphicsPath();
        private RectangleF[] buttonRect = new RectangleF[2];
        private RectangleF[] textRect = new RectangleF[2];

        private float _min = 0;
        private float _max = 100;
        private float _value = 0;
        private int _decimalPlaces = 2;

        private const int BTN_WIDTH = 30;
        private readonly string[] BTN_TEXT = new string[] { "-", "+" };

        public FaceNumeric()
        {
            InitializeComponent();
          
            txt = new TextBox
            {
                BorderStyle = BorderStyle.None,
                BackColor = BackColor,
                ForeColor = ForeColor,
                ImeMode = ImeMode.Disable
            };
            txt.TextChanged += Txt_TextChanged;
            txt.MouseEnter += Txt_MouseEnter;
            txt.MouseLeave += Txt_MouseLeave;
            txt.KeyPress += Txt_KeyPress;
            Controls.Add(txt);

            _decimalPlaces = 2;
            Increment = 1;
            ShowNumeric();
            FaceNumeric_Resize(null, EventArgs.Empty);
        }





        #region 事件
        [Browsable(true), Category("操作"), Description("控件中的值更改时发生")]
        public event EventHandler ValueChanged;
        #endregion


        #region 属性
        [Browsable(true), Category("数据"), Description("控件的最小值"), DefaultValue(0)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
        public float Minimum
        {
            get => _min;
            set
            {
                _min = value;
                if (_value < _min) _value = _min;
                ShowNumeric();
                CalcSize();
                Refresh();
            }
        }

        [Browsable(true), Category("数据"), Description("控件的最大值"), DefaultValue(100)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
        public float Maximum
        {
            get => _max;
            set
            {
                _max = value;
                if (_value > _max) _value = _max;
                ShowNumeric();
                CalcSize();
                Refresh();
            }
        }

        [Browsable(true), Category("外观"), Description("控件的当前值"), DefaultValue(0)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
        public float Value
        {
            get => _value;
            set
            {
                if (_value == value) return;
                _value = value;
                if (_value < _min)
                    _value = _min;
                else if (_value > _max)
                    _value = _max;
                ShowNumeric();
                CalcSize();
                Refresh();
                ValueChanged?.Invoke(this, new EventArgs());
            }
        }

        [Browsable(true), Category("数据"), Description("指示要显示的小数位数"), DefaultValue(2)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
        public int DecimalPlaces
        {
            get => _decimalPlaces;
            set
            {
                _decimalPlaces = value;
                ShowNumeric();
            }
        }

        [Browsable(true), Category("数据"), Description("增加或减少的数量"), DefaultValue(1)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
        public float Increment { set; get; }

        [Browsable(true), Category("行为"), Description("控制能否更改编辑控件中的文本"), DefaultValue(false)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
        public bool ReadOnly { set => txt.ReadOnly = value; get => txt.ReadOnly; }

        [Browsable(true), Category("外观"), Description("指示应该如何对齐编辑控件的文本"), DefaultValue(HorizontalAlignment.Left)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
        public new HorizontalAlignment TextAlign { set => txt.TextAlign = value; get => txt.TextAlign; }

        [Browsable(true), Category("外观"), Description("用于显示控件中文本的字体")]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
        public override Font Font
        {
            get => base.Font;
            set
            {
                base.Font = value;
                FaceNumeric_Resize(null, EventArgs.Empty);
            }
        }
        #endregion





        protected override void CalcSize()
        {
            base.CalcSize();
            CalcTextLocation();
            CalcShape();
        }

        protected override void DrawEnabled(Graphics g)
        {
            base.DrawEnabled(g);

            //控件边框
            if (CursorInside && borderOver)
            {
                if (mouseDown[0])
                    g.FillRectangle(new SolidBrush(theme.DOWN_COLOR), buttonRect[0]);  //按下
                else if (buttonOver[0])
                    g.FillRectangle(new SolidBrush(theme.OVER_COLOR), buttonRect[0]);  //经过

                if (mouseDown[1])
                    g.FillRectangle(new SolidBrush(theme.DOWN_COLOR), buttonRect[1]);  //按下
                else if (buttonOver[1])
                    g.FillRectangle(new SolidBrush(theme.OVER_COLOR), buttonRect[1]);  //经过

                if (BorderWidth > 0)
                    g.DrawPath(borderInsidePen, borderPath);
            }
            else
            {
                g.FillPath(new SolidBrush(BackColor), borderPath);
                if (BorderWidth > 0)
                    g.DrawPath(borderOutsidePen, borderPath);
            }

            g.DrawString(BTN_TEXT[0], new Font("宋体", Font.Size), new SolidBrush(ForeColor), textRect[0]);
            g.DrawString(BTN_TEXT[1], new Font("宋体", Font.Size), new SolidBrush(ForeColor), textRect[1]);

        }

        protected override void DrawDisabled(Graphics g)
        {
            base.DrawDisabled(g);

            //控件边框
            if (BorderWidth > 0)
                g.DrawPath(borderOutsidePen, borderPath);
            g.DrawString(BTN_TEXT[0], new Font("宋体", Font.Size), new SolidBrush(theme.LOST_FOCUS_COLOR), textRect[0]);
            g.DrawString(BTN_TEXT[1], new Font("宋体", Font.Size), new SolidBrush(theme.LOST_FOCUS_COLOR), textRect[1]);

        }


        private void CalcShape()
        {
            borderPath = new GraphicsPath();
            float n;

            switch (FaceShape)
            {
                case Model.ButtonShape.Rectangle:
                    borderPath.AddRectangle(borderRect);
                    break;
                case Model.ButtonShape.RoundRectangle:
                    n = borderRect.Height / 8f;
                    borderPath.AddLine(borderRect.X + n, borderRect.Y, borderRect.Right - n, borderRect.Y);
                    borderPath.AddArc(borderRect.Right - n - n, borderRect.Y, n + n, n + n, 270, 90);
                    borderPath.AddLine(borderRect.Right, borderRect.Y + n, borderRect.Right, borderRect.Bottom - n);
                    borderPath.AddArc(borderRect.Right - n - n, borderRect.Bottom - n - n, n + n, n + n, 0, 90);
                    borderPath.AddLine(borderRect.Right - n, borderRect.Bottom, borderRect.X + n, borderRect.Bottom);
                    borderPath.AddArc(borderRect.X, borderRect.Bottom - n - n, n + n, n + n, 90, 90);
                    borderPath.AddLine(borderRect.X, borderRect.Bottom - n, borderRect.X, borderRect.Y + n);
                    borderPath.AddArc(borderRect.X, borderRect.Y, n + n, n + n, 180, 90);
                    break;
                case Model.ButtonShape.Ellipse:
                    borderPath.AddEllipse(borderRect);
                    break;
                case Model.ButtonShape.EllipseRectangle:
                    n = borderRect.Height / 2f;
                    borderPath.AddLine(borderRect.X + n, borderRect.Y, borderRect.Right - n, borderRect.Y);
                    borderPath.AddArc(borderRect.Right - n - n, borderRect.Y, n + n, n + n, 270, 180);
                    borderPath.AddLine(borderRect.Right - n, borderRect.Bottom, borderRect.X + n, borderRect.Bottom);
                    borderPath.AddArc(borderRect.X, borderRect.Y, n + n, n + n, 90, 180);
                    break;
                case Model.ButtonShape.Circle:
                    if (borderRect.Width > borderRect.Height)
                        borderPath.AddEllipse((borderRect.Width - borderRect.Height) / 2f, borderRect.Y, borderRect.Height, borderRect.Height);
                    else
                        borderPath.AddEllipse(borderRect.X, (borderRect.Height - borderRect.Width) / 2f, borderRect.Width, borderRect.Width);
                    break;
            }
        }

        private void CalcTextLocation()
        {
            buttonRect[0] = new RectangleF(borderRect.X, borderRect.Y, BTN_WIDTH, borderRect.Height);
            buttonRect[1] = new RectangleF(borderRect.Right - BTN_WIDTH, borderRect.Y, BTN_WIDTH, borderRect.Height);
            Graphics g = CreateGraphics();

            SizeF sf = g.MeasureString(BTN_TEXT[0], new Font("宋体", Font.Size));
            float x = buttonRect[0].X + (buttonRect[0].Width - sf.Width) / 2f;
            float y = buttonRect[0].Y + (buttonRect[0].Height - sf.Height) / 2f;
            textRect[0] = new RectangleF(x, y, sf.Width, sf.Height);

            sf = g.MeasureString(BTN_TEXT[1], new Font("宋体", Font.Size));
            x = buttonRect[1].X + (buttonRect[1].Width - sf.Width) / 2f;
            y = buttonRect[1].Y + (buttonRect[1].Height - sf.Height) / 2f;
            textRect[1] = new RectangleF(x, y, sf.Width, sf.Height);

        }

        private void ShowNumeric()
        {
            txt.Text = string.Format("{0:N"+ _decimalPlaces + "}", _value);
            txt.SelectionStart = txt.Text.Length;
        }




        private void Txt_TextChanged(object sender, EventArgs e)
        {
            //TextChanged?.Invoke(this, new EventArgs());
        }

        private void Txt_KeyPress(object sender, KeyPressEventArgs e)
        {
            if (e.KeyChar == 13)
            {
                bool rtn = float.TryParse(txt.Text, out float result);
                if (rtn) _value = result;
                ShowNumeric();
            }
            else
            {
                if ((e.KeyChar < 48 || e.KeyChar > 57) &&  //0-9
                    (e.KeyChar != 8) &&                    //Backspace
                    (e.KeyChar != 46) &&                   //.
                    (e.KeyChar != 45))                     //-
                {
                    e.Handled = true;
                }
            }
        }

        private void Txt_MouseLeave(object sender, EventArgs e)
        {
            CursorInside = false;
            borderOver = false;
            Refresh();
        }

        private void Txt_MouseEnter(object sender, EventArgs e)
        {
            CursorInside = true;
            borderOver = true;
            Refresh();
        }

        private void FaceNumeric_MouseDown(object sender, MouseEventArgs e)
        {
            if (!Enabled) return;
            if (e.Button == MouseButtons.Left)
            {
                if (buttonOver[0])
                {
                    mouseDown[0] = true;
                    Value -= Increment;
                }
                else if (buttonOver[1])
                {
                    mouseDown[1] = true;
                    Value += Increment;
                }
                Refresh();
            }
        }

        private void FaceNumeric_MouseMove(object sender, MouseEventArgs e)
        {
            buttonOver[0] = buttonOver[1] = false;
            if (buttonRect[0].Contains(e.Location))
                buttonOver[0] = true;
            else if (buttonRect[1].Contains(e.Location))
                buttonOver[1] = true;

            borderOver = borderPath.IsVisible(e.Location);
            Refresh();
        }

        private void FaceNumeric_MouseUp(object sender, MouseEventArgs e)
        {
            if (!Enabled) return;
            if (e.Button == MouseButtons.Left)
            {
                mouseDown[0] = mouseDown[1] = false;
                Refresh();
            }
        }

        private void FaceNumeric_Resize(object sender, EventArgs e)
        {
            txt.Left = BorderWidth + BTN_WIDTH + 3;
            txt.Width = Width - (BorderWidth + BTN_WIDTH + 3) * 2;
            txt.Top = BorderWidth + (Height - BorderWidth - BorderWidth - txt.Height) / 2;
        }
    }
}