// ============================================================================ ' // NumericUpDownEx - v.1.3 ' // ============================================================================ ' // Author: Claudio Nicora ' // WebSite: http://coolsoft.altervista.org ' // CodeProject: http://www.codeproject.com/KB/edit/NumericUpDownEx.aspx ' // License: CodeProject Open License ' // http://www.codeproject.com/info/cpol10.aspx ' // Feel free to contribute here: http://coolsoft.altervista.org ' // ============================================================================ ' using System; using System.Reflection; using System.Collections; using System.Collections.Generic; using System.Data; using System.Diagnostics; using System.Drawing; using System.ComponentModel; using System.Windows.Forms; namespace CodeImp.DoomBuilder.BuilderEffects { [DesignerCategory("code")] public class NumericUpDownEx : NumericUpDown { // reference to the underlying TextBox control private TextBox _textbox; // reference to the underlying UpDownButtons control private Control _upDownButtons; /// /// object creator /// public NumericUpDownEx() : base() { // extract a reference to the underlying TextBox field _textbox = GetPrivateField(this, "upDownEdit"); if (_textbox == null) { throw new ArgumentNullException(this.GetType().FullName + ": Can't find internal TextBox field."); } // extract a reference to the underlying UpDownButtons field _upDownButtons = GetPrivateField(this, "upDownButtons"); if (_upDownButtons == null) { throw new ArgumentNullException(this.GetType().FullName + ": Can't find internal UpDown buttons field."); } // add handlers (MouseEnter and MouseLeave events of NumericUpDown // are not working properly) _textbox.MouseEnter += _mouseEnterLeave; _textbox.MouseLeave += _mouseEnterLeave; _upDownButtons.MouseEnter += _mouseEnterLeave; _upDownButtons.MouseLeave += _mouseEnterLeave; base.MouseEnter += _mouseEnterLeave; base.MouseLeave += _mouseEnterLeave; } /// /// Extracts a reference to a private underlying field /// protected static internal T GetPrivateField(NumericUpDownEx ctrl, string fieldName) where T : Control { // find internal TextBox FieldInfo fi = typeof(NumericUpDown).GetField(fieldName, BindingFlags.FlattenHierarchy | BindingFlags.NonPublic | BindingFlags.Instance); // take some caution... they could change field name in the future! if (fi == null) { return null; } else { return fi.GetValue(ctrl) as T; } } protected override void OnPaint(System.Windows.Forms.PaintEventArgs e) { if (_upDownButtons.Visible == false) { e.Graphics.Clear(this.BackColor); } base.OnPaint(e); } /// /// WndProc override to kill WN_MOUSEWHEEL message /// protected override void WndProc(ref System.Windows.Forms.Message m) { const int WM_MOUSEWHEEL = 0x20a; if (m.Msg == WM_MOUSEWHEEL) { switch (_interceptMouseWheel) { case InterceptMouseWheelMode.Always: // standard message base.WndProc(ref m); break; case InterceptMouseWheelMode.WhenMouseOver: if (_mouseOver) { // standard message base.WndProc(ref m); } break; case InterceptMouseWheelMode.Never: // kill the message return; } } else { base.WndProc(ref m); } } #region New properties [DefaultValue(false)] [Category("Behavior")] [Description("Automatically select control text when it receives focus.")] public bool AutoSelect { get { return _autoSelect; } set { _autoSelect = value; } } private bool _autoSelect = false; [Browsable(false)] [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public int SelectionStart { get { return _textbox.SelectionStart; } set { _textbox.SelectionStart = value; } } [Browsable(false)] [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public int SelectionLength { get { return _textbox.SelectionLength; } set { _textbox.SelectionLength = value; } } [Browsable(false)] [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public string SelectedText { get { return _textbox.SelectedText; } set { _textbox.SelectedText = value; } } [DefaultValue(typeof(InterceptMouseWheelMode), "Always")] [Category("Behavior")] [Description("Enables MouseWheel only under certain conditions.")] public InterceptMouseWheelMode InterceptMouseWheel { get { return _interceptMouseWheel; } set { _interceptMouseWheel = value; } } private InterceptMouseWheelMode _interceptMouseWheel = InterceptMouseWheelMode.Always; public enum InterceptMouseWheelMode { /// MouseWheel always works (defauld behavior) Always, /// MouseWheel works only when mouse is over the (focused) control WhenMouseOver, /// MouseWheel never works Never } [DefaultValue(typeof(ShowUpDownButtonsMode), "Always")] [Category("Behavior")] [Description("Set UpDownButtons visibility mode.")] public ShowUpDownButtonsMode ShowUpDownButtons { get { return _showUpDownButtons; } set { _showUpDownButtons = value; // update UpDownButtons visibility UpdateUpDownButtonsVisibility(); } } private ShowUpDownButtonsMode _showUpDownButtons = ShowUpDownButtonsMode.Always; public enum ShowUpDownButtonsMode { /// UpDownButtons are always visible (defauld behavior) Always, /// UpDownButtons are visible only when mouse is over the control WhenMouseOver } /// /// If set, incrementing value will cause it to restart from Minimum /// when Maximum is reached (and viceversa). /// [DefaultValue(false)] [Category("Behavior")] [Description("If set, incrementing value will cause it to restart from Minimum when Maximum is reached (and viceversa).")] public bool WrapValue { get { return _wrapValue; } set { _wrapValue = value; } } private bool _wrapValue = false; #endregion #region Text selection // select all the text on focus enter protected override void OnGotFocus(System.EventArgs e) { if (_autoSelect) { _textbox.SelectAll(); } base.OnGotFocus(e); } // MouseUp will kill the SelectAll made on GotFocus. // Will restore it, but only if user have not made a partial text selection. protected override void OnMouseUp(System.Windows.Forms.MouseEventArgs mevent) { if (_autoSelect && _textbox.SelectionLength == 0) { _textbox.SelectAll(); } base.OnMouseUp(mevent); } #endregion #region Additional events // these events will be raised correctly, when mouse enters on the textbox public new event EventHandler MouseEnter; public new event EventHandler MouseLeave; // Events raised BEFORE value decrement(increment public event CancelEventHandler BeforeValueDecrement; public event CancelEventHandler BeforeValueIncrement; // flag to track mouse position private bool _mouseOver = false; // this handler is called at each mouse Enter/Leave movement private void _mouseEnterLeave(object sender, System.EventArgs e) { Rectangle cr = RectangleToScreen(ClientRectangle); Point mp = MousePosition; // actual state bool isOver = cr.Contains(mp); // test if status changed if (_mouseOver ^ isOver) { // update state _mouseOver = isOver; if (_mouseOver) { if (MouseEnter != null) { MouseEnter(this, EventArgs.Empty); } } else { if (MouseLeave != null) { MouseLeave(this, EventArgs.Empty); } } } // update UpDownButtons visibility if (_showUpDownButtons != ShowUpDownButtonsMode.Always) { UpdateUpDownButtonsVisibility(); } } #endregion #region Value increment/decrement management // raises the two new events public override void DownButton() { CancelEventArgs e = new CancelEventArgs(); if (BeforeValueDecrement != null) { BeforeValueDecrement(this, e); } if (e.Cancel) return; if (_wrapValue && Value - Increment < Minimum) { Value = Maximum; } else { base.DownButton(); } } public override void UpButton() { CancelEventArgs e = new CancelEventArgs(); if (BeforeValueIncrement != null) { BeforeValueIncrement(this, e); } if (e.Cancel) return; if (_wrapValue && Value + Increment > Maximum) { Value = Minimum; } else { base.UpButton(); } } #endregion #region UpDownButtons visibility management /// /// Show or hide the UpDownButtons, according to ShowUpDownButtons property value /// public void UpdateUpDownButtonsVisibility() { // test new state bool newVisible = false; switch (_showUpDownButtons) { case ShowUpDownButtonsMode.WhenMouseOver: newVisible = _mouseOver; break; default: newVisible = true; break; } // assign only if needed if (_upDownButtons.Visible != newVisible) { if (newVisible) { _textbox.Width = this.ClientRectangle.Width - _upDownButtons.Width; } else { _textbox.Width = this.ClientRectangle.Width; } _upDownButtons.Visible = newVisible; OnTextBoxResize(_textbox, EventArgs.Empty); this.Invalidate(); } } /// /// Custom textbox size management /// protected override void OnTextBoxResize(object source, System.EventArgs e) { if (_textbox == null) return; if (_showUpDownButtons == ShowUpDownButtonsMode.Always) { // standard management base.OnTextBoxResize(source, e); } else { // custom management // change position if RTL bool fixPos = this.RightToLeft == RightToLeft.Yes ^ this.UpDownAlign == LeftRightAlignment.Left; if (_mouseOver) { _textbox.Width = this.ClientSize.Width - _textbox.Left - _upDownButtons.Width - 2; if (fixPos) _textbox.Location = new Point(16, _textbox.Location.Y); } else { if (fixPos) _textbox.Location = new Point(2, _textbox.Location.Y); _textbox.Width = this.ClientSize.Width - _textbox.Left - 2; } } } #endregion } }