Silverlight开发中的疑难杂症-控件设计篇-如何实现一个NumericBox(下)

在上一章中已经完成了TextBoxFilterBehavior的实现,在这一章中主要是介绍如何在NumericBox中进行格式化处理,没有看过上一章的朋友请点击这里访问。

为了能够使用TextBoxFilterBehavior来进行非法字符的过滤,我们在构造函数中通过附加属性的形式添加了TextBoxFilterBehavior行为,代码如下:

 

ExpandedBlockStart.gif 构造函数
1  public  NumericBox()
2  {
3      TextBoxFilterBehavior behavior  =   new  TextBoxFilterBehavior();
4      behavior.TextBoxFilterOptions  =  TextBoxFilterOptions.Numeric  |  TextBoxFilterOptions.Dot;
5  Interaction.GetBehaviors( this ).Add(behavior); 
6  }
7 

效果等同于如下的xaml代码:

ExpandedBlockStart.gif 代码
< TextBox  HorizontalAlignment ="Left"  Margin ="170,198,0,218"  Width ="123"  TextWrapping ="Wrap"  Style ="{StaticResource TextBoxStyle1}" >
    
< i:Interaction.Behaviors >
        
< YQL_Core_Behaviors:TextBoxFilterBehavior  TextBoxFilterOptions ="Numeric" />
    
</ i:Interaction.Behaviors >
</ TextBox >

由于我们的NumericBox控件没有自定义的默认样式,所以只能通过代码的方式进行添加。

完成了非法字符的过滤,那么接下来我们主要要处理的就是以下几种情况:

能够控制小数点后的最大位数,超出位数则无法继续输入;

能够选择当小数点数位数不足时是否补0

去除开头部分多余的0(为方便处理,当在开头部分输入0时,自动在其后添加一个小数点);

针对前两种情况,我们通过添加两个依赖属性来提供给控件的使用者设置。其中一个用来控制最大的小数点位数,一个用来控制当小数点位数不足时,是否补零,属性定义如下:

ExpandedBlockStart.gif 代码
 1  #region  Dependency Properties
 2 
 3  ///   <summary>
 4  ///  最大小数点位数
 5  ///   </summary>
 6  public   int  MaxFractionDigits
 7  {
 8       get  {  return  ( int )GetValue(MaxFractionDigitsProperty); }
 9       set  { SetValue(MaxFractionDigitsProperty, value); }
10  }
11 
12  //  Using a DependencyProperty as the backing store for MaxFractionDigits.  This enables animation, styling, binding, etc...
13  public   static   readonly  DependencyProperty MaxFractionDigitsProperty  =
14      DependencyProperty.Register( " MaxFractionDigits " typeof ( int ),  typeof (NumericBox),  new  PropertyMetadata( 2 ));
15 
16  ///   <summary>
17  ///  不足位数是否补零
18  ///   </summary>
19  public   bool  IsPadding
20  {
21       get  {  return  ( bool )GetValue(IsPaddingProperty); }
22       set  { SetValue(IsPaddingProperty, value); }
23  }
24 
25  //  Using a DependencyProperty as the backing store for IsPadding.  This enables animation, styling, binding, etc...
26  public   static   readonly  DependencyProperty IsPaddingProperty  =
27      DependencyProperty.Register( " IsPadding " typeof ( bool ),  typeof (NumericBox),  new  PropertyMetadata( true ));
28 
29  #endregion
30 

然后在TextChanged事件中,我们首先取出小数点所在的位置,然后计算出小数点后面的数字的位数,如果超过了设置的最大小数点位数,则舍去后面多出的部分;如果不足且设置了需要补零,则在后面填充对应个数的‘0’。这里需要注意的一点是,当重新设置控件的Text属性时,光标的位置会被重新设定到文本的起始处。所以这里就需要我们首先保存光标的位置,然后在设置后进行恢复,从而保证用户输入的流畅性。

针对第三种情况,我考虑了很多种方案,但是在用户体验上都有不同程度的缺陷。设想如下的情况,如果当前的文本为“1234”,然后用户想要将其变成‘0.1234’,这个时候,按照正常的逻辑数字的最前方是不能有‘0’的,这样就导致了用户无法直接在前面输入‘0’,必须要将原有的“1234”都删除才可以,大大降低了用户体验。最后,通过与同事的讨论(该同事那时正处于半睡半醒状态),决定分两种情况来考虑:如果当前的文本中已经包含了小数点,那么则删除开头的所有的‘0’(如果开头除‘0’外的第一个字符是小数点,则保留一个‘0’);如果当前文本中没有包含小数点,那么保留一个‘0’,并在其后添加一个小数点,并且将光标移动到‘0’与小数点之间。在这种情况下最大程度的保证了用户输入的连贯性,同时又保证了输入数据的有效性。

考虑如下情形:

当前的文本为“1234”,然后用户想要将其变成‘0.1234’,这时他只要在开头输入‘0’,那么自然就变成了‘0.1234’;

由于光标停留在‘0’跟‘.’之间,所以如果他要将之前的‘0’改为‘1’,只需要直接输入‘1’,那么文本就自然变成了‘1.1234’(‘01.1234=>1.1234’);

而如果他想要继续输入后面的小数部分,那么只要输入小数点,光标会自动跳过小数点,到达小数的输入部分。

这是我目前能够想到的最符合需求也是最容易实现的方法,如果您有更好的实现方案请给我回复,完整的代码如下:

ExpandedBlockStart.gif 代码
///   <summary>
///  只能输入数字的TextBox
///   </summary>
public   class  NumericBox : TextBox
{
    
#region  Dependency Properties

    
///   <summary>
    
///  最大小数点位数
    
///   </summary>
     public   int  MaxFractionDigits
    {
        
get  {  return  ( int )GetValue(MaxFractionDigitsProperty); }
        
set  { SetValue(MaxFractionDigitsProperty, value); }
    }

    
//  Using a DependencyProperty as the backing store for MaxFractionDigits.  This enables animation, styling, binding, etc...
     public   static   readonly  DependencyProperty MaxFractionDigitsProperty  =
        DependencyProperty.Register(
" MaxFractionDigits " typeof ( int ),  typeof (NumericBox),  new  PropertyMetadata( 2 ));

    
///   <summary>
    
///  不足位数是否补零
    
///   </summary>
     public   bool  IsPadding
    {
        
get  {  return  ( bool )GetValue(IsPaddingProperty); }
        
set  { SetValue(IsPaddingProperty, value); }
    }

    
//  Using a DependencyProperty as the backing store for IsPadding.  This enables animation, styling, binding, etc...
     public   static   readonly  DependencyProperty IsPaddingProperty  =
        DependencyProperty.Register(
" IsPadding " typeof ( bool ),  typeof (NumericBox),  new  PropertyMetadata( true ));

    
#endregion

    
public  NumericBox()
    {
        TextBoxFilterBehavior behavior 
=   new  TextBoxFilterBehavior();
        behavior.TextBoxFilterOptions 
=  TextBoxFilterOptions.Numeric  |  TextBoxFilterOptions.Dot;
        Interaction.GetBehaviors(
this ).Add(behavior);

        
this .TextChanged  +=   new  TextChangedEventHandler(NumericBox_TextChanged);
    }

    
///   <summary>
    
///  设置Text文本以及光标位置
    
///   </summary>
    
///   <param name="text"></param>
     private   void  SetTextAndSelection( string  text)
    {
        
// 保存光标位置
         int  selectionIndex  =   this .SelectionStart;
        
this .Text  =  text;
        
// 恢复光标位置 系统会自动处理光标位置超出文本长度的情况
         this .SelectionStart  =  selectionIndex;
    }

    
///   <summary>
    
///  去掉开头部分多余的0
    
///   </summary>
     private   void  TrimZeroStart()
    {
        
string  resultText  =   this .Text;
        
// 计算开头部分0的个数
         int  zeroCount  =   0 ;
        
foreach  ( char  c  in   this .Text)
        {
            
if  (c  ==   ' 0 ' ) { zeroCount ++ ; }
            
else  {  break ; }
        }

        
// 当前文本中包含小数点
         if  ( this .Text.Contains( ' . ' ))
        {
            
// 0后面跟的不是小数点,则删除全部的0
             if  ( this .Text[zeroCount]  !=   ' . ' )
            {
                resultText 
=   this .Text.TrimStart( ' 0 ' );
            }
            
// 否则,保留一个0
             else   if  (zeroCount  >   1 )
            {
                resultText 
=   this .Text.Substring(zeroCount  -   1 );
            }
        }
        
// 当前文本中不包含小数点,则保留一个0,并在其后加一个小数点,并将光标设置到小数点前
         else   if  (zeroCount  >   0 )
        {
            resultText 
=   " 0. "   +   this .Text.TrimStart( ' 0 ' );
            
this .SelectionStart  =   1 ;
        }

        SetTextAndSelection(resultText);
    }

    
void  NumericBox_TextChanged( object  sender, TextChangedEventArgs e)
    {
        
int  decimalIndex  =   this .Text.IndexOf( ' . ' );
        
if  (decimalIndex  >=   0 )
        {
            
// 小数点后的位数
             int  lengthAfterDecimal  =   this .Text.Length  -  decimalIndex  -   1 ;
            
if  (lengthAfterDecimal  >  MaxFractionDigits)
            {
                SetTextAndSelection(
this .Text.Substring( 0 this .Text.Length  -  (lengthAfterDecimal  -  MaxFractionDigits)));
            }
            
else   if  (IsPadding)
            {
                SetTextAndSelection(
this .Text.PadRight( this .Text.Length  +  MaxFractionDigits  -  lengthAfterDecimal,  ' 0 ' ));
            }
        }
        TrimZeroStart();
    }
}

 

 

转载于:https://www.cnblogs.com/yingql/archive/2010/03/09/1681956.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值