65.9K
CodeProject 正在变化。 阅读更多。
Home

通用 Windows 应用 (UWP) 的数字文本框

starIconstarIconstarIcon
emptyStarIcon
starIcon
emptyStarIcon

3.77/5 (4投票s)

2016年11月20日

CPOL

2分钟阅读

viewsIcon

33994

downloadIcon

450

仅限数字的文本框,用于通用 Windows 应用,可以选择允许负数并设置自定义数字模式。

引言

增强 TextBox 的类,可选择仅允许数字、限制为正数,并使用“.”(点)作为十进制字符以跳转到其小数部分(如果有)。

背景

仅限数字的 TextBox 可以使用各种技术实现,最常见的是通过正确处理 Key Events,但是 Microsoft 建议不要在 UWP 中使用 Key Events 处理文本,因为它有各种输入法(键盘、触摸板、笔、游戏控制器、电视遥控器等),并且没有适当的方法知道特定键或键组合将生成哪个“文本”。

Microsoft 强烈建议通过验证过程(显示气球和警告错误)来完成,并让用户进行更正。

我主要编写金融应用程序,这些应用程序使用大量货币字段,它们特别需要同时处理数字数据,并根据用户文化显示正确格式化的数字,以便用户可以轻松地进行配对。

对我来说,输入数字(例如:41234456.4556)然后对其进行格式化,编写起来更加困难,因为它让我不断检查我正在输入的内容是否正确;但是如果我可以在编写时看到格式化的数字(例如:41,234,456.4556),那会使很多工作变得更容易。

还需要避免应用程序的某些字段中出现负数,虽然避免此类错误的最佳方法是通过验证,但可以完全通过避免此类错误的方式来处理。

使用代码

要使用该类,请使用 Tag 属性中的默认参数创建 ClsNumTextTagIn 的新实例,并创建 TextBox 控件的 3 个必要事件

  • SelectionChanged:在文本选择或指针位置更改时刷新类中的原始数据;
  • TextChanged:在 TextChanging 事件中处理后刷新类中的原始数据;
  • TextChanging: 处理文本。
<TextBox InputScope="Number" SelectionChanged="TextBox_SelectionChanged" TextChanged="TextBox_TextChanged" TextChanging="TextBox_TextChanging">
	<TextBox.Tag>
		<local:ClsNumTextTagIn />
	</TextBox.Tag>
</TextBox>
	private void TextBox_SelectionChanged(object sender, RoutedEventArgs e)
        {
            TextBox tb = (TextBox)sender;
            if (tb.Tag != null)
            {
                ((ClsNumTextTagIn)tb.Tag).Refresh(tb);
            }
        }

        private void TextBox_TextChanged(object sender, TextChangedEventArgs e)
        {
            TextBox tb = (TextBox)sender;
            if (tb.Tag != null)
            {
                ((ClsNumTextTagIn)tb.Tag).Refresh(tb);
            }
        }

        private void TextBox_TextChanging(TextBox sender, TextBoxTextChangingEventArgs args)
        {
            TextBox tb = (TextBox)sender;
            if (tb.Tag != null)
            {
                ((ClsNumTextTagIn)tb.Tag).NumericText(tb);
            }
        }
或者您可以通过设置属性进一步自定义设置
 
Tag: 使用参数在 Tag 中传递一些对象,作为此类正在使用的 TextBox.Tag 参数的替代方法。
MyPattern: 允许将数字模式指定为字符串;
例如:MyPattern="N4" 提示:仅整数:MyPattern = "N0"。
IsNumOnly: true => 文本应始终为数字,忽略非数字,false = 接受非数字文本;
例如:IsNumOnly="False"。
IsDotSepa: true = “.” 计为十进制分隔符(与文化无关),它将用于小数部分(如果有)。
例如:IsDotSepa ="True"。
CanNegNum: true => 文本框可以有负数,false => 不允许负数;
例如:CanNegNum="True"
LstRemStr: 您希望删除的任何其他字符串的列表(有助于接受粘贴的文本);
例如: <local:ClsNumTextTagIn.LstRemStr>
             <x:String>/</x:String>
             <x:String>,</x:String>
      </local:ClsNumTextTagIn.LstRemStr>
<TextBox InputScope="Number" SelectionChanged="TextBox_SelectionChanged" TextChanged="TextBox_TextChanged" TextChanging="TextBox_TextChanging">
     <TextBox.Tag>
          <local:ClsNumTextTagIn MyPattern="N4" IsNumOnly="False" IsDotSepa="True" CanNegNum="False">
               <local:ClsNumTextTagIn.LstRemStr>
                    <x:String>/</x:String>
                    <x:String>,</x:String>
               </local:ClsNumTextTagIn.LstRemStr>
          </local:ClsNumTextTagIn>
     </TextBox.Tag>
</TextBox>
 
在您的项目中创建 ClsNumTextTagIn
public class ClsNumTextTagIn
    {
        private class cTxtBoxProperty
        {
            public int iBeg { get; set; }
            public int iSel { get; set; }
            public int iLen { get; set; }
            public string sLef { get; set; }
            public string sRig { get; set; }
            public string sSel { get; set; }
            public string sTxt { get; set; }
            public cTxtBoxProperty(Windows.UI.Xaml.Controls.TextBox sender)
            {
                iBeg = sender.SelectionStart;
                iSel = sender.SelectionLength;
                iLen = sender.Text.Length;
                sLef = sender.Text.Substring(0, sender.SelectionStart);
                sRig = sender.Text.Substring(sender.SelectionStart + sender.SelectionLength);
                sSel = sender.SelectedText;
                sTxt = sender.Text;
            }
        }

        private cTxtBoxProperty org { get; set; }
        public string MyPattern { get; set; }
        public bool IsNumOnly { get; set; }
        public bool IsDotSepa { get; set; }
        public bool CanNegNum { get; set; }
        public System.Collections.Generic.List<string> LstRemStr { get; set; }
        public object Tag { get; set; } //use this Tag

        public ClsNumTextTagIn()
        {
            //org = new cTxtBoxProperty(sender);
            MyPattern = "N2";
            IsNumOnly = true;
            IsDotSepa = true;
            CanNegNum = true;
            LstRemStr = new System.Collections.Generic.List<string>();
            //Tag = anyTag; //use this Tag
        }
        /// <summary>
        /// Routine to test numeric if current TextChanging is numeric and display accordinly to current culture.
        /// 
        ///<param name="sender" />Target TextBox
        ///<param name="pattern" />Currently only Numeric Pattern is supported eg: "N2", "N6"...etc. N stands for number.
        ///<param name="numbersOnly" />true = allow numbers only; false = leave text if its not numeric decimal.
        ///<param name="dotAsDecimalSeparator" />true = "." counts as decimal separator (regardless of culture) and it will go for decimal part of pattern (if any).
        ///<param name="allowNegativeNumbers" />true = can have negative numbers, false = prevents negative numbers.
        ///<param name="lstRemoveStrings" />List of any other strings you wish remove eg: "(", " / ", ")", " - ", " + "...etc, if none just pass "null" parameter.
        ///<param name="anyTag" />Use paramenter to pass some object in Tag, if none just pass "null" parameter.
        public ClsNumTextTagIn(Windows.UI.Xaml.Controls.TextBox sender, string pattern, bool numbersOnly, bool dotAsDecimalSeparator, bool allowNegativeNumbers, System.Collections.Generic.List<string> lstRemoveStrings, System.Object anyTag)
        {
            org = new cTxtBoxProperty(sender);
            MyPattern = pattern;
            IsNumOnly = numbersOnly;
            IsDotSepa = dotAsDecimalSeparator;
            CanNegNum = allowNegativeNumbers;
            LstRemStr = lstRemoveStrings;
            Tag = anyTag; //use this Tag
        }
        public void Refresh(Windows.UI.Xaml.Controls.TextBox sender)
        {
            org = new cTxtBoxProperty(sender);
        }
        public void NumericText(Windows.UI.Xaml.Controls.TextBox sender)
        { //logics
            if (org == null)
            {
                org = new cTxtBoxProperty(sender);
            }

            System.Globalization.CultureInfo ci = System.Globalization.CultureInfo.CurrentUICulture;
            cTxtBoxProperty edi = new cTxtBoxProperty(sender);
            string sPtn = MyPattern;
            string sAdd = string.Empty;
            string sSub = string.Empty;
            int iChg = edi.iLen - org.iBeg - org.sRig.Length;

            if (iChg >= 0)
            {
                sAdd = edi.sTxt.Substring(org.iBeg, iChg);
                sSub = org.sSel;
            }
            else
            { //backspace?
                if (org.iLen >= edi.iLen)
                {
                    if (org.iBeg - (org.iLen - edi.iLen) >= 0)
                    {
                        sSub = org.sTxt.Substring(org.iBeg - (org.iLen - edi.iLen), (org.iLen - edi.iLen));
                    }
                }
            }

            int iBgx = edi.iBeg;
            int iSlx = 0;
            string sFnl = org.sTxt;

            int iNeg = sFnl.IndexOf(ci.NumberFormat.NegativeSign);
            int iDot = sFnl.IndexOf(ci.NumberFormat.NumberDecimalSeparator);

            if (sAdd == ci.NumberFormat.NegativeSign)
            {
                if (iNeg == -1)
                { //add
                    if (CanNegNum)
                    {//accepts negative numbers
                        //if (ci.TextInfo.IsRightToLeft)
                        //{
                        //    sFnl = ci.NumberFormat.NegativeSign + sFnl; //sFnl = sFnl + ci.NumberFormat.NegativeSign;
                        //    iBgx = edi.iBeg - ci.NumberFormat.NegativeSign.Length;
                        //}
                        //else
                        //{
                            sFnl = ci.NumberFormat.NegativeSign + sFnl;
                            iBgx = edi.iBeg;
                        //}
                    }
                    else
                    {//does not accepts negative numbers


                        //if (ci.TextInfo.IsRightToLeft)
                        //{
                            iBgx = edi.iBeg - ci.NumberFormat.NegativeSign.Length;
                        //}
                        //else
                        //{
                        //    iBgx = edi.iBeg;
                        //}

                        
                    }
                }
                else
                {//remove
                    sFnl = sFnl.Remove(iNeg, ci.NumberFormat.NegativeSign.Length);
                    if (iNeg >= iBgx)
                    {
                        iBgx = edi.iBeg - ci.NumberFormat.NegativeSign.Length;
                    }
                    else
                    {
                        //if (ci.TextInfo.IsRightToLeft)
                        //{
                        //    iBgx = edi.iBeg - ci.NumberFormat.NegativeSign.Length;
                        //}
                        //else
                        //{
                            iBgx = edi.iBeg - ci.NumberFormat.NegativeSign.Length - ci.NumberFormat.NegativeSign.Length;
                        //}
                    }
                }
            } //end NegativeSign
            else if (sAdd == ci.NumberFormat.NumberDecimalSeparator)
            { //NumberDecimalSeparator
                if (iDot == -1)
                { //add
                    sFnl = edi.sTxt;
                }
                else
                { //go to point
                    sFnl = org.sTxt;
                    iBgx = iDot + ci.NumberFormat.NumberDecimalSeparator.Length;
                }
            }
            else if (sAdd == ".")
            { //dotAsDecimalSeparator
                if (IsDotSepa)
                {
                    if (iDot == -1)
                    { //add
                        sFnl = edi.sTxt;
                    }
                    else
                    { //go to point
                        sFnl = org.sTxt;
                        iBgx = iDot + ci.NumberFormat.NumberDecimalSeparator.Length;
                    }
                }
                else
                {
                    sFnl = edi.sTxt; //override
                }
            }
            else if (sSub == ci.NumberFormat.NumberGroupSeparator & org.iSel == 0)
            { //backspace and GroupSeparator
                if (edi.iBeg == 0)
                {
                    sFnl = edi.sTxt;
                }
                else
                {
                    sFnl = edi.sLef.Replace(ci.NumberFormat.NumberGroupSeparator, string.Empty);
                    int iRem = edi.sLef.Length - sFnl.Length;
                    sFnl = sFnl.Substring(0, edi.sLef.Length - iRem - 1) + edi.sRig;
                    iBgx = iBgx - iRem - 1;
                }
            }//end backspace and GroupSeparator
            else
            {
                sFnl = edi.sTxt;
            }

            if (iBgx > sFnl.Length)
            {
                iBgx = sFnl.Length;
            }
            string sLfx = sFnl.Substring(0, iBgx);
            string sRgx = sFnl.Substring(iBgx);

            sRgx = sRgx.Replace(ci.NumberFormat.NumberGroupSeparator, string.Empty);
            sFnl = sFnl.Replace(ci.NumberFormat.NumberGroupSeparator, string.Empty);

            if (LstRemStr != null)
            {
                foreach (string sRem in LstRemStr)
                {
                    sRgx = sRgx.Replace(sRem, string.Empty); //automatically trimmed
                    sFnl = sFnl.Replace(sRem, string.Empty); //automatically trimmed
                }
            }

            //s += "Rgx: " + sRgx.ToString() + Environment.NewLine;
            //s += "Fnl: " + sFnl.ToString() + Environment.NewLine;

            if (!CanNegNum)
            {//remove negative sign
                sRgx = sRgx.Replace(ci.NumberFormat.NegativeSign, string.Empty);
                sFnl = sFnl.Replace(ci.NumberFormat.NegativeSign, string.Empty);
            }

            decimal dFnl = decimal.Zero;
            decimal dRgx = decimal.Zero;
            if (decimal.TryParse(sFnl, out dFnl))
            {

                int iDtx = sFnl.IndexOf(ci.NumberFormat.NumberDecimalSeparator);
                int iDif = sFnl.Length - dFnl.ToString().Length;

                if (iDtx == -1)
                {//DecimalSeparator does not exist
                    sFnl = dFnl.ToString(sPtn);
                    int iTmp = sFnl.IndexOf(ci.NumberFormat.NumberDecimalSeparator);
                    if (iTmp == -1)
                    {//DecimalSeparator not found after apply pattern
                        if (sRgx == string.Empty)
                        {
                            iBgx = sFnl.Length;
                        }
                        else
                        {
                            if (sRgx.StartsWith(decimal.Zero.ToString()))
                            {
                                sRgx = decimal.One.ToString() + sRgx;
                                if (decimal.TryParse(sRgx, out dRgx))
                                {
                                    sRgx = dRgx.ToString(sPtn);
                                    if (sRgx.StartsWith(decimal.One.ToString() + ci.NumberFormat.NumberDecimalSeparator))
                                    {
                                        iBgx = sFnl.Length - sRgx.Length + (decimal.One.ToString() + ci.NumberFormat.NumberDecimalSeparator).Length;
                                    }
                                    else
                                    {
                                        iBgx = sFnl.Length - sRgx.Length + decimal.One.ToString().Length;
                                    }
                                }
                            }
                            else
                            {
                                if (decimal.TryParse(sRgx, out dRgx))
                                {
                                    sRgx = dRgx.ToString(sPtn);
                                    iBgx = sFnl.Length - sRgx.Length;
                                }
                            }
                        }
                    }
                    else
                    {//DecimalSeparator found after apply pattern
                        if (sRgx == string.Empty)
                        {
                            iBgx = iTmp;
                        }
                        else
                        {
                            if (sRgx.StartsWith(decimal.Zero.ToString()))
                            {
                                sRgx = decimal.One.ToString() + sRgx;
                                if (decimal.TryParse(sRgx, out dRgx))
                                {
                                    sRgx = dRgx.ToString(sPtn);
                                    if (sRgx.StartsWith(decimal.One.ToString() + ci.NumberFormat.NumberDecimalSeparator))
                                    {
                                        iBgx = sFnl.Length - sRgx.Length + (decimal.One.ToString() + ci.NumberFormat.NumberDecimalSeparator).Length;
                                    }
                                    else
                                    {
                                        iBgx = sFnl.Length - sRgx.Length + decimal.One.ToString().Length;
                                    }
                                }
                            }
                            else
                            {
                                if (decimal.TryParse(sRgx, out dRgx))
                                {
                                    sRgx = dRgx.ToString(sPtn);
                                    iBgx = sFnl.Length - sRgx.Length;
                                }
                            }
                        }
                    }
                    if (iBgx < 0)
                    {
                        iBgx = 0;
                    }
                }
                else
                {//DecimalSeparator does exist
                    if (sFnl.Length - sRgx.Length <= iDtx)
                    {//before DecimalSeparator
                        sFnl = dFnl.ToString(sPtn);
                        if (sRgx.StartsWith(decimal.Zero.ToString()))
                        {
                            sRgx = decimal.One.ToString() + sRgx;
                            if (decimal.TryParse(sRgx, out dRgx))
                            {
                                sRgx = dRgx.ToString(sPtn);
                                if (sRgx.StartsWith(decimal.One.ToString() + ci.NumberFormat.NumberDecimalSeparator))
                                {
                                    iBgx = sFnl.Length - sRgx.Length + (decimal.One.ToString() + ci.NumberFormat.NumberDecimalSeparator).Length;
                                }
                                else
                                {
                                    iBgx = sFnl.Length - sRgx.Length + decimal.One.ToString().Length;
                                }
                            }
                        }
                        else if (sRgx.StartsWith(ci.NumberFormat.NumberDecimalSeparator))
                        {
                            if (decimal.TryParse(sRgx, out dRgx))
                            {
                                sRgx = dRgx.ToString(sPtn);
                                iBgx = sFnl.Length - sRgx.Length + ci.NumberFormat.NumberDecimalSeparator.Length;
                            }
                        }
                        else
                        {
                            if (decimal.TryParse(sRgx, out dRgx))
                            {
                                sRgx = dRgx.ToString(sPtn);
                                iBgx = sFnl.Length - sRgx.Length;
                            }
                        }

                        if (iBgx < 0)
                        {
                            iBgx = 0;
                        }
                    }
                    else
                    {//after DecimalSeparator
                        sFnl = dFnl.ToString(sPtn);
                        int iTmp = sFnl.IndexOf(ci.NumberFormat.NumberDecimalSeparator); //override
                        if (iTmp == -1)
                        {// pattern does not accept DecimalSeparator
                            iBgx = sFnl.Length;
                        }
                        else
                        {
                            if (sSub != string.Empty & sAdd == string.Empty)
                            {// if backspace (after decimal separator)
                                if (iTmp + 1 < iBgx)
                                {
                                    iBgx = iBgx - 1;
                                }
                                else
                                {
                                    iBgx = iTmp;
                                }
                            }

                            if (sFnl.Length > iBgx)
                            {
                                if (iBgx > iTmp)
                                {
                                    iSlx = 1;
                                }
                            }
                        }
                    }
                }
                sender.Text = sFnl;
                sender.SelectionStart = iBgx;
                sender.SelectionLength = iSlx;
            }//end of if decimal
            else
            {//not decimal
                if (IsNumOnly)
                { //IsNumOnly =true
                    decimal dOrg = decimal.Zero;
                    if (sFnl != string.Empty && decimal.TryParse(org.sTxt, out dOrg))
                    {
                        sender.Text = org.sTxt;
                        sender.SelectionStart = org.iBeg;
                        sender.SelectionLength = org.iSel;
                    }
                    else
                    {
                        decimal dAdd = decimal.Zero;
                        decimal.TryParse(sAdd, out dAdd);
                        sFnl = dAdd.ToString(sPtn);
                        int iTmp = sFnl.IndexOf(ci.NumberFormat.NumberDecimalSeparator);

                        if (iTmp == -1)
                        {
                            iBgx = sFnl.Length;
                        }
                        else
                        {
                            iBgx = iTmp;
                        }
                        sender.Text = sFnl;
                        sender.SelectionStart = iBgx;
                        sender.SelectionLength = 0;
                    }
                }
                else
                { //numbersOnly false
                    sender.Text = edi.sTxt;
                    sender.SelectionStart = edi.iBeg;
                    sender.SelectionLength = edi.iSel;
                }
            }
        }
    }

 

关注点

可以改进 NumericText 例程以包括允许的最小和最大数字的参数。

小数点的当前文本处理方式可能会让一些用户感到烦恼。

尚未在具有 从右到左 模式的语言中进行测试!

请帮助识别任何错误或改进建议。

注意:如果您正在不同的平台上测试附加的示例应用程序,请相应地将调试更改为 x86 或 x64。

谢谢。

历史

版本 1.01 - 添加了示例应用程序和屏幕截图; 包含图像以显示不同的世界文化。

版本 1.00 - 原始。

© . All rights reserved.