Home > Back-end >  NumericUpDown ValueChanged event not firing while typing
NumericUpDown ValueChanged event not firing while typing

Time:03-31

I have a bound NumericUpDown control.

I want the ValueChanged Event to fire when either the up or down arrow is clicked or if the user changes the number.

When the up or down arrow is clicked the ValueChanged event does fire, but not when I just type into the control.

// numericUpDownDiscountRate
// 
this.numericUpDownDiscountRate.DecimalPlaces = 4;
this.numericUpDownDiscountRate.Location = new System.Drawing.Point(133, 344);
this.numericUpDownDiscountRate.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
this.numericUpDownDiscountRate.Name = "numericUpDownDiscountRate";
this.numericUpDownDiscountRate.Size = new System.Drawing.Size(115, 23);
this.numericUpDownDiscountRate.TabIndex = 78;
this.numericUpDownDiscountRate.ValueChanged  = new System.EventHandler(this.numericUpDownDiscountRate_ValueChanged);

I found a question that advises to use the KeyPress event However the keypress event happens before the value is updated.

How do I get the value of the control from within the key press?

private void numericUpDownDiscountPercent_KeyPress(object sender, KeyPressEventArgs e)
{
    if (loadingForm) return;
    try
    {
        loadingForm = true;
        var updn = sender as NumericUpDown;

        // updn.value does not include the key press and I don't know how to construct the value 
        MyCalculateRate(updn?.Value ?? 0);

        loadingForm = false;
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.ToString(), "Discount rate Key Press");
        loadingForm = false;
    }
}

CodePudding user response:

Since the Value is changed only after validation, which occurs when the input is validated when the internal Edit Control loses focus or when the Value is retrieved, I suggest to introduce a custom value that works alongside the default Value property.

Note that when the Value Property is read, the input is not just validated but also the Text is re-set, so reading the Value Property while editing becomes quite difficult, since the caret is moved when the Text is reset.

When the Up/Down Buttons are pressed, also the Text is formatted, hence OnTextChanged() is called and the custom value is set.

Implemented in a Custom Control here, but you can do ~the same using the event handlers of a standard NumericUpDown Control.
Don't read the Value property in OnValueChanged(), for the reasons previously explained.
Use CurrentEditValue while the Text is being edited.

using System.ComponentModel;
using System.Windows.Forms;

[ToolboxItem(true), DesignerCategory("code")]
public class NumericUpDownEx : NumericUpDown {

    private static readonly object Event_CurrentEditValueChanged = new object();
    private decimal m_CurrentEditValue = 0M;

    public NumericUpDownEx() { }

    [Bindable(true), Browsable(false), EditorBrowsable(EditorBrowsableState.Always)]
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
    public virtual decimal CurrentEditValue { 
        get => m_CurrentEditValue;
        internal set { 
            if (value != m_CurrentEditValue) {
                m_CurrentEditValue = value;
                OnCurrentEditValueChanged(this, EventArgs.Empty);
            }
        } 
    }

    protected override void OnTextChanged(EventArgs e)
    {
        Console.WriteLine("NumericUpDownEx OnTextChanged");
        base.OnTextChanged(e);
        if (decimal.TryParse(Text, out decimal v)) {
            CurrentEditValue = v;
            OnValueChanged(e);
        }
    }

    protected override void OnValueChanged(EventArgs e)
    {
        Console.WriteLine($"NumericUpDownEx OnValueChanged: CurrentEditValue={CurrentEditValue}");
        base.OnValueChanged(e);
    }

    public event EventHandler CurrentEditValueChanged {
        add {
            Events.AddHandler(Event_CurrentEditValueChanged, value);
        }
        remove {
            Events.RemoveHandler(Event_CurrentEditValueChanged, value);
        }
    }

    protected virtual void OnCurrentEditValueChanged(object sender, EventArgs e)
    {
        if (Events[Event_CurrentEditValueChanged] is EventHandler evth) evth(this, e);
    }
}

Example of direct (One-Way) Binding to the CurrentEditValue Property:

[SomeTextBox].DataBindings.Add("Text", numericUpDpwnEx1, "CurrentEditValue", true, DataSourceUpdateMode.OnPropertyChanged);

CodePudding user response:

It's a bit "hocus pocus" but (in .netfw472 - haven't tried in netcore) if you bind an event handler to KeyUp that accesses the Value:

    private void numericUpDown1_KeyUp(object sender, KeyEventArgs e)
    {
        var v = numericUpDown1.Value; //I know it looks useless, but it seems to make ValueChanged fire more often
    }

it causes your ValueChanged to fire on every keypress and accessing .Value within ValueChanged will give you the current value..

"But why?!" you might ask... "I don't know; I've never looked.. but NumericUpDown isn't regarded as the most reliable control.."

  • Related