用Silverlight打造位运算器(2)--制作数字文本框控件
上一篇文章中,我们介绍了如何制作一个简易工具条,要完成位运算器,还需要一个很重要的控件,就是放数字的编辑框。在我的构想中这个编辑框所拥有以下功能:
1、 不允许输入非法字符,如十进制时只能输入数字0-9和负号。
2、 可以很方便地在各个进制间进行转换。
关于第1点,最完美的解决方案就是在KeyDown事件中过滤字符,然后使用e.Handle=true方法来通知编辑框不再处理这次键盘按下事件,它最大的优点在于光标位置不会改变,但这个方法无法解决字符串粘贴时所带的非法字符。本来我想在TextChanged事件中做同样的处理,结果发现TextChanged事件中没有e.Handle参数,一查帮助,发现这个事件是异步事件,无法终止。上网找,中文资料,英文资料都找了,没找到合适的解决方案。最后没办法,只好自己在TextChanged事件里解决了。判断非法字符使用了正则表达式,以前不知道正则表达式,这里写得很痛苦,现在安逸多了。但总体来说,解决得并不是很完美,发生异常时光标无法定位在原来的位置,应该可以在SelectionChanged事件里解决这个问题,不过并未尝试,期待哪位大侠能给出完美的解决方案。
写这个控件时遇到的第二个问题是如果在控件的TextChanged事件里抛出异常,我居然不知道在哪里可以捕捉这些异常,有点诡异,还是期待有人能给出答案。没办法,如果不处理这些异常,用户在狂按键盘都无法输入字符时可能会认为我的程序挂掉了。只好另辟蹊径,在编辑框控件上方安装一个简易的弹出式消息框,这样做不是很规范,但先把问题解决了再说。这里还有一个问题未解决,就是如何计算一个字符串所占用的像素长度,学习有时就是这么痛苦!
这个控件完成后,我又发现了一个非常严重的问题:工具条在载入浏览器后无法使其中一个按钮处于按下状态,我的猜测是SpeedButton使用模板显示其外观,在载入窗体进行绘制时,必须按照默认状态进行绘制,对此我束手无策,还是希望有高手能给出答案。现在我能想出的解决办法就是不使用模板来显示外观,重写SpeedButton,这个留到下篇文章一起完成吧。
好,先来看看效果,这里需要注意,必须安装Silverlight 3.0版本才能正常运行示例。
请注意,工具条的BCD按钮本应该是处于按下状态的。
制作步骤:
1、在【BitLibrary】项目上单击鼠标右键,选择【添加】【新建项】,添加一个Silverlight用户控件,并命名为“BitTextBox.xaml”。更改BitTextBox.xaml文件如下:
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Canvas x:Name="LayoutRoot" Height="47" Width="335">
<TextBox x:Name="BitTxt" Text="0" FontSize="16" TextAlignment="Right"
Canvas.Left="0" Canvas.Top="17" Height="30" Width="335" AcceptsReturn="False"/>
<Border x:Name="TipBorder" Canvas.Top="0" Canvas.Left="60" Height="18" Width="275" BorderBrush="Red"
BorderThickness="2" Background="#00ffffff" Visibility="Collapsed">
<Border.Resources>
<Storyboard x:Name="Story" Completed="Story_Completed">
<ColorAnimationUsingKeyFrames Storyboard.TargetName="TipBorder" Storyboard.TargetProperty="(Border.BorderBrush).(SolidColorBrush.Color)">
<DiscreteColorKeyFrame Value="#FF718597" KeyTime="00:00:0.5" />
<DiscreteColorKeyFrame Value="Red" KeyTime="00:00:1" />
<DiscreteColorKeyFrame Value="#FF718597" KeyTime="00:00:1.5" />
<DiscreteColorKeyFrame Value="Red" KeyTime="00:00:2" />
</ColorAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="TipBorder"
Storyboard.TargetProperty="Opacity">
<DiscreteDoubleKeyFrame Value="1" KeyTime="0:0:3"/>
<LinearDoubleKeyFrame Value="0" KeyTime="0:0:4" />
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</Border.Resources>
<TextBlock x:Name="TxtMsg" TextAlignment="Right"/>
</Border>
</Canvas>
</UserControl>
这里在Canvas上方是一个Border内含TextBlock,TextBlock用于显示异常信息,Border显示动画,也就是闪两下,然后消失。Canvas下方是编辑框控件,用于显示二到十六进制的数字。
2、打开BitTextBox.xaml.cs文件,更改代码如下:
2 {
3 //二进制到十六进制的正则表达式
4 const string BIN_EXPRESSION = "^[0-1]*$";
5 const string OCT_EXPRESSION = "^[0-7]*$";
6 const string BCD_EXPRESSION = "^[0-9-][0-9]*$";
7 const string HEX_EXPRESSION = "^[a-f0-9A-F]*$";
8 public BitTextBox()
9 {
10 InitializeComponent();
11 BitTxt.TextChanged += new TextChangedEventHandler(BitTxt_TextChanged);
12 }
13 //声明依赖属性Text
14 public static readonly DependencyProperty TextProperty = DependencyProperty.Register(
15 "Text", typeof(string), typeof(BitTextBox), null);
16 public string Text
17 {
18 get { return BitTxt.Text; }
19 set { Value = Convert.ToInt32(value, (int)state); }
20 }
21
22 string CurrentRegular = BCD_EXPRESSION;//记录当前所使用的正则表达式
23 int value;
24 public int Value//编辑框内所表示的整数值
25 {
26 get { return this.value; }
27 set
28 {
29 this.value = value;
30 SetTextFromValue();
31 }
32 }
33 bool fillZero = true;
34 public bool FillZero //指示,当显示为二进制时,高位不足处是否填充0
35 {
36 get { return fillZero; }
37 set
38 {
39 fillZero = value;
40 if (state == SystemState.Bin)
41 {
42 SetTextFromValue();
43 }
44 }
45 }
46 SystemState state = SystemState.Bcd;//编辑框的当前进制状态
47 public SystemState State
48 {
49 get { return state; }
50 set
51 {
52 if (state == value) return;
53 switch (value)
54 {
55 case SystemState.Bin:
56 CurrentRegular = BIN_EXPRESSION;
57 break;
58 case SystemState.Oct:
59 CurrentRegular = OCT_EXPRESSION;
60 break;
61 case SystemState.Bcd:
62 CurrentRegular = BCD_EXPRESSION;
63 break;
64 case SystemState.Hex:
65 CurrentRegular = HEX_EXPRESSION;
66 break;
67 }
68 //转换进制
69 state = value;
70 SetTextFromValue();
71 }
72 }
73 //显示错误提示
74 public void ShowTip(string msg)
75 {
76 TxtMsg.Text = msg;
77 TipBorder.Visibility = Visibility.Visible;
78 TipBorder.Opacity = 1;
79 Story.Begin();
80 }
81 private void Story_Completed(object sender, EventArgs e)
82 {
83 TipBorder.Visibility = Visibility.Collapsed;
84 }
85 //清空编辑框内的数字
86 public void Clear()
87 {
88 Value = 0;
89 BitTxt.Focus();
90 BitTxt.SelectionStart = 0;
91 BitTxt.SelectionLength = 1;
92 }
93 void BitTxt_TextChanged(object sender, TextChangedEventArgs e)
94 {
95 string str = BitTxt.Text;
96 if (str == "-" && state==SystemState.Bcd)
97 {
98 return;
99 }
100 //正则表达式验证
101 if (!Regex.IsMatch(str, CurrentRegular))
102 {
103 SetTextFromValue();
104 switch (state) //显示异常信息
105 {
106 case SystemState.Bin:
107 ShowTip("只能输入数字“0”和“1”");
108 break;
109 case SystemState.Oct:
110 ShowTip("只能输入0~7之间的数字");
111 break;
112 case SystemState.Bcd:
113 ShowTip("只能输入0~9之间的数字");
114 break;
115 case SystemState.Hex:
116 ShowTip("只能输入数字0~9和字母a~f");
117 break;
118 }
119 return;
120 }
121 //出现空白时
122 if (str == "")
123 {
124 value = 0;
125 BitTxt.Text = "0";
126 return;
127 }
128 //防止数字超出int32所能容纳的范围
129 try
130 {
131 int i = Convert.ToInt32(str, (int)state);
132 }
133 catch
134 {
135 SetTextFromValue();
136 ShowTip("您所输入的数字超出了32位有符号整数所能承受的范围");
137 return;
138 }
139 value = Convert.ToInt32(str, (int)state);
140 BitTxt.Text = str.ToUpper();
141 }
142 //将值变成相应进制的字符串并赋给文本框
143 void SetTextFromValue()
144 {
145 string temp = Convert.ToString(this.value, (int)state).ToUpper();
146 if (state == SystemState.Bin && fillZero)
147 {
148 temp = temp.PadLeft(32, '0');
149 }
150 BitTxt.Text = temp;
151 }
152 }
这里声明了一个依赖属性Text,因为需要在Xaml文件中直接给编辑框赋文本值。如果你想在Xaml文件中调用其他文件,也可以声明它的依赖属性。
3、 更改MainPage.xaml代码如下:
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:src="clr-namespace:BitLibrary;assembly=BitLibrary"
mc:Ignorable="d" d:DesignWidth="470" d:DesignHeight="70">
<Canvas x:Name="LayoutRoot" Background="Goldenrod">
<src:ToolBar x:Name="toolBar" Grid.Row="1" SelectionChange="toolBar_SelectionChange"
Canvas.Left="10" Canvas.Top="17"/>
<src:BitTextBox x:Name="txtBox" Text="20091122" Canvas.Left="132" Canvas.Top="0"/>
<CheckBox x:Name="cbFillZero" Content="二进制数字不足32位补0" IsChecked="true"
Canvas.Left="132" Canvas.Top="52" Click="cbFillZero_Click"/>
</Canvas>
</UserControl>
4、 打开MainPage.xaml.cs,更改代码如下:
2 {
3 public MainPage()
4 {
5 InitializeComponent();
6 }
7
8 private void toolBar_SelectionChange(object sender, RoutedEventArgs e)
9 {
10 txtBox.State = toolBar.ToolState;
11 }
12
13 private void cbFillZero_Click(object sender, RoutedEventArgs e)
14 {
15 if (txtBox != null)
16 {
17 txtBox.FillZero = !txtBox.FillZero;
18 }
19 }
20 }
现在一个位运算器的雏形已经完成,可以在这个程序里进行进制转换。虽然现在还带着很多的疑惑,但学习的乐趣就在于此,发现问题,解决问题,获取知识和经验。