在使用数字增减控件时,如何在数值后面添加文本?

17

在WinForms中,能否在NumericUpDown控件内显示文本?例如,我想在我的numericupdown控件中显示值为微安的文本,应该是"1 uA"。

谢谢。


控件旁边加一个标签怎么样? - DarkSquirrel42
那是可能的,但我想把它放在控件内部。 - Dumbo
你可以尝试将标签放置在控件上方,否则我想不到任何属性来将字符串附加到数字增减控件的末尾。 - lpd
2
绝对不要尝试将标签定位在控件上方。这将很难正确处理,并且会一直困扰着你。在控件旁边(在表单本身上)放置一个标签通常是足够好的解决方案。如果你需要更强大的工具,请参见我下面的答案。 - Cody Gray
5个回答

30

标准控件中没有这样的功能。但是,可以通过创建一个继承自 NumericUpDown 类并重写 UpdateEditText 方法 以相应地格式化数字的自定义控件来轻松实现。

例如,您可能会有以下类定义:

public class NumericUpDownEx : NumericUpDown
{
    public NumericUpDownEx()
    {
    }

    protected override void UpdateEditText()
    {
        // Append the units to the end of the numeric value
        this.Text = this.Value + " uA";
    }
}

或者,对于更完整的实现,请参阅此示例项目:带单位度量的NumericUpDown


@greg 我不知道。但是对于任何 .NET 开发人员来说,VB.NET 和 C# 应该同样容易阅读。如果失败了,总还有自动翻译工具。 - Cody Gray
刚刚发现这个功能很好用。我添加了一个属性并在设计器中设置为可见,但由于某种原因,追加的文本在设计器中没有显示出来。这只是一个小问题,它仍然可以正常工作,但有人有什么提示如何让它可见吗? - jason.kaisersmith
很可能,在设计师中从未调用UpdateEditText函数。这并不是特别不寻常的。设计师只是旨在为您提供事物外观的预览。它不应该是完全功能的。很难想象为什么在设计时看到这一点非常重要。按运行以查看它。 - Cody Gray
2
这很好,但是当您编辑文本时会出现问题,因为ValidateEditText()的默认实现如果文本中有后缀则会失败。您还必须覆盖ValidateEditText()以将文本转换为数字值。 - Fabio

3

使用CodeGray的答案,Fabio对其无法通过ValidateEditText的评论以及NumericUpDown文档,我创建了一个简单的带单位的NumericUpDown组件。您可以直接复制粘贴如下:

using System;
using System.Globalization;
using System.Text.RegularExpressions;
using System.Windows.Forms;

public class NumericUpDownWithUnit : NumericUpDown
{
    #region| Fields |

    private string unit = null;
    private bool unitFirst = true;

    #endregion

    #region| Properties |

    public string Unit
    {
        get => unit;
        set
        {
            unit = value;

            UpdateEditText();
        }
    }

    public bool UnitFirst
    {
        get => unitFirst;
        set
        {
            unitFirst = value;

            UpdateEditText();
        }
    }

    #endregion

    #region| Methods |

    /// <summary>
    /// Method called when updating the numeric updown text.
    /// </summary>
    protected override void UpdateEditText()
    {
        // If there is a unit we handle it ourselfs, if there is not we leave it to the base class.
        if (Unit != null && Unit != string.Empty)
        {
            if (UnitFirst)
            {
                Text = $"({Unit}) {Value}";
            }
            else
            {
                Text = $"{Value} ({Unit})";
            }
        }
        else
        {
            base.UpdateEditText();
        }
    }

    /// <summary>
    /// Validate method called before actually updating the text.
    /// This is exactly the same as the base class but it will use the new ParseEditText from this class instead.
    /// </summary>
    protected override void ValidateEditText()
    {
        // See if the edit text parses to a valid decimal considering the label unit
        ParseEditText();
        UpdateEditText();
    }

    /// <summary>
    /// Converts the text displayed in the up-down control to a numeric value and evaluates it.
    /// </summary>
    protected new void ParseEditText()
    {
        try
        {
            // The only difference of this methods to the base one is that text is replaced directly
            // with the property Text instead of using the regex.
            // We now that the only characters that may be on the textbox are from the unit we provide.
            // because the NumericUpDown handles invalid input from user for us.
            // This is where the magic happens. This regex will match all characters from the unit
            // (so your unit cannot have numbers). You can change this regex to fill your needs
            var regex = new Regex($@"[^(?!{Unit} )]+");
            var match = regex.Match(Text);

            if (match.Success)
            {
                var text = match.Value;

                // VSWhidbey 173332: Verify that the user is not starting the string with a "-"
                // before attempting to set the Value property since a "-" is a valid character with
                // which to start a string representing a negative number.
                if (!string.IsNullOrEmpty(text) && !(text.Length == 1 && text == "-"))
                {
                    if (Hexadecimal)
                    {
                        Value = Constrain(Convert.ToDecimal(Convert.ToInt32(Text, 16)));
                    }
                    else
                    {
                        Value = Constrain(Decimal.Parse(text, CultureInfo.CurrentCulture));
                    }
                }
            }
        }
        catch
        {
            // Leave value as it is
        }
        finally
        {
            UserEdit = false;
        }
    }

    /// </summary>
    /// Returns the provided value constrained to be within the min and max.
    /// This is exactly the same as the one in base class (which is private so we can't directly use it).
    /// </summary>
    private decimal Constrain(decimal value)
    {
        if (value < Minimum)
        {
            value = Minimum;
        }

        if (value > Maximum)
        {
            value = Maximum;
        }

        return value;
    }

    #endregion
}

2
这是我用来显示至少2位16进制数字的NumericUpDown控件的代码,前缀为0x。它将文本放入控件中,并通过使用.Net提供的字段ChangingText来避免使用“去抖动”。
    class HexNumericUpDown2Digits : NumericUpDown
    {
        protected override void UpdateEditText()
        {
            if (Hexadecimal)
            {
                ChangingText = true;
                Text = $"0x{(int)Value:X2}";
            }
            else
            {
                base.UpdateEditText();
            }
        }
    }

感谢您重新激活一个九年前的问题! - Dumbo

1
我最近遇到了这个问题,并发现了Cody Gray的精彩答案。我利用了它,但最近认同他的回答中的一条评论,谈论如果后缀仍然存在,文本将无法通过验证。我为此创建了一个可能不太专业的快速解决方案。
基本上,this.Text 字段被读取以获取数字。
一旦找到数字,它们就被放入 this.Text 中,但需要防抖或者你想叫它什么都可以,以确保我们不会创建一个堆栈溢出。
一旦只有数字的新文本准备好了,就会调用正常的 ParseEditText();UpdateEditText(); 完成整个过程。
这不是最资源友好或高效的解决方案,但今天大多数现代计算机应该完全可以胜任。
此外,你会注意到我创建了一个属性来更改后缀,以便在编辑器中更轻松地使用。
public class NumericUpDownUnit : System.Windows.Forms.NumericUpDown
    {

        public string Suffix{ get; set; }

        private bool Debounce = false;

        public NumericUpDownUnit()
        {

        }

        protected override void ValidateEditText()
        {
            if (!Debounce) //I had to use a debouncer because any time you update the 'this.Text' field it calls this method.
            {
                Debounce = true; //Make sure we don't create a stack overflow.

                string tempText = this.Text; //Get the text that was put into the box.
                string numbers = ""; //For holding the numbers we find.

                foreach (char item in tempText) //Implement whatever check wizardry you like here using 'tempText' string.
                {
                    if (Char.IsDigit(item))
                    {
                        numbers += item;
                    }
                    else
                    {
                        break;
                    }
                }

                decimal actualNum = Decimal.Parse(numbers, System.Globalization.NumberStyles.AllowLeadingSign);
                if (actualNum > this.Maximum) //Make sure our number is within min/max
                    this.Value = this.Maximum;
                else if (actualNum < this.Minimum)
                    this.Value = this.Minimum;
                else
                    this.Value = actualNum; 

                ParseEditText(); //Carry on with the normal checks.
                UpdateEditText();

                Debounce = false;
            }

        }

        protected override void UpdateEditText()
        {
            // Append the units to the end of the numeric value
            this.Text = this.Value + Suffix;
        }
    }

请随意改进我的答案或者在有错误的情况下纠正我,我是一个自学的程序员,仍在学习中。

0

当您的对象发生更改后,您的操作将开始,您需要在属性部分双击“ValueChanged”。 此操作完成后,您可以在代码中看到以下代码:

private void numericUpDownMounth_ValueChanged(object sender, EventArgs e)
{
   // Body
}

在//Body侧,您可以编写您的代码和控制器。就像其他人说的那样。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接