zoukankan      html  css  js  c++  java
  • 用Silverlight打造位运算器(1)--制作简易工具条控件

     

    那日,一网友发了一份他做破解计算器源码给我,看到这个程序,我突然想起了我写的第一个程序――位运算器,那是一个半途而废的程序。当时已经写了一千多行的代码,由于初学,里面有大量的重复代码,估计写完得用3000行以上的代码,而且大部是重复代码,这让我很困惑,于是放弃了继续完成它,转而寻找解除困惑的途径。之后,随着学习的深入,虽然这种困惑早已不复存在,但位运算器也消失在我的记忆之中了。网友的程序让我有了完成这个位运算器的念头,或许因为它是我写的第一个程序,在我的内心深处,多少还存留有一些对它的眷顾吧。

    说做就做,接下来就要考虑使用什么语言来实现它,用C#WindowApplication来写当然没问题,但那样学不到什么新东西,亏本的买卖不能做。Silverlight之前虽然学过一些,也做过一些东西,但说实话,还属于初学者,正好可以借此机会进一步了解Silverlight

    位运算器在我的构思中需要有工具条按钮,就是按下去不会弹上来那种按钮。当年Delphi中有一个SpeedButton控件正好可以实现这样的功能。C#WindowsApplication中可以使用RadioButton来实现这样的工具条。很遗憾,Silverlight中没有相关控件。但可以更改ListBox的模板来模拟这样的功能,这个我试验过,但由于ListBox存在Focus状态,ListItem在处于Focus状态时会有一个边框,看着很不顺眼,需要重写模板把这个状态去掉。定制RadioButton外观应该也能实现这样的功能(这点未做实验,有待验证)。但想来想去,为了学些新东西,还是自己实现一个工具条控件吧。经过一番努力,终于实现了这个工具条。本文将对制作的步骤进行详细讲解。

    好,先来看看效果,这里需要注意,必须安装Silverlight 3.0版本才能正常运行示例。

     

    关于控件制作,请先观看一篇写得非常棒的文章:

    http://msdn.microsoft.com/zh-cn/magazine/cc721611.aspx

    由于我的控件放在另一个项目之中,操作步骤会有所不同,所以这里有些地方我还得重新详细地介绍。

    第一步,先做一个最简单的控件:

    1、 首先创建一个Silverlight应用程序,项目命名为“BitCalculator”。

    2、 在解决方案资源管理器的【解决方案“BitCalculator”】上单击鼠标右键,选中【添加】à【新建项目】添加一个【Silverlight类库】,命名为“BitLibrary”。

    3、 将【BitLibrary】项目下的“Class1.cs”类改名为“SpeedButton.cs”。

    4、 SpeedButton.cs类的代码更改如下:

     

    1 namespace BitLibrary
    2 {
    3         public class SpeedButton : ContentControl
    4         {
    5 
    6         }
    7 }
    8 

     

    这里,我们让SpeedButton类继续自ContentControl,道理很简单,Button类也是从ContentControl类继续的。很多的拥有Content属性的内置控件都继承自ContentControl类。我们记得,在C#中,Button使用的是Text属性,为什么在Silverlight中会改为Content属性呢?这是因为Content属性允许用户对按钮表面的内容进行自定义,如加一些图片或其他控件什么的,而所有这些都不会影响按钮被单击。

     

    5、  现在这个最简单的控件已经完成,可以加载到主页面测试一下了。打开BitCalculator】项目下的【MainPage.xaml】文件,首先添加对这个控件的引用:

    xmlns:src="clr-namespace:BitLibrary;assembly=BitLibrary"

    这里的“src”是一个前缀,也可以把它理解为引用名,你高兴的话可以换成其他的单词。之后所有需要使用到SpeedButton的地方都需要加上“src:”前缀。

    (这一段是我介绍的那篇文章未描述步骤)还需要做一件事才能在MainPage.xaml中看到SpeedButton:在【BitCalculator】项目上单击鼠标右键,选中【添加引用】打开“添加引用”窗口,在【项目】栏中选中“BitLibrary”,单击【确定】按钮添加对BitLibrary的引用。

    接下来,在MainPage.xaml中加入SpeedButton控件:

    <src:SpeedButton/>

    完成后,MainPage.xaml的所有代码应该象下面这个样子:

     

    <UserControl x:Class="BitCalculator.MainPage"
    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="640" d:DesignHeight="480">
        
    <Grid x:Name="LayoutRoot">
            
    <src:SpeedButton/>
        
    </Grid>
    </UserControl>

     

    6、运行程序,我们发现浏览器中无任何内容,这很正常,我们的SpeedButton里面什么都没有。

     

    第二步,修改控件模板:

    我们首先看看使用常规方法是否能让SpeedButton显示一些内容:在MainPage.xaml】中更改SpeedButton的声明如下:

     

    <src:SpeedButton Width="30" Height="30" Background="Black"
                             Foreground
    ="#FF000000" Content="HEX"
                             BorderBrush
    ="Black" BorderThickness="2"/>

     

    运行程序,我们发现除了Content里的“HEX“这几个字母外,其他的背景及边框根本没有按照我们的设想显示出来。这时,可以通过创建默认控件模板指定控件外观。

    1、             BitLibrary】项目上单击鼠标右键,选择【添加】à【新建文件夹】添加一个新的文件夹,并把文件夹命名为“themes”。

    2、             在【themes】文件夹上单击鼠标右键,选择【添加】à【新建项】,新建一个文本文件,并命名为:“generic.xaml”。这里需要注意,文件夹和文件的名称都不能搞错。

    3、             打开generic.xaml文件,并填入如下代码:

     

    <ResourceDictionary     xmlns="http://schemas.microsoft.com/client/2007"
        xmlns:x
    ="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:src
    ="clr-namespace:BitLibrary;assembly=BitLibrary"
        xmlns:vsm
    ="clr-namespace:System.Windows;assembly=System.Windows">
        
    <!-- SpeedButton Template -->
        
    <Style TargetType="src:SpeedButton">
            
    <Setter Property="Width" Value="30"/>
            
    <Setter Property="Height" Value="30"/>
            
    <Setter Property="Background" Value="#FF1F3B53"/>
            
    <Setter Property="Foreground" Value="#FF000000"/>
            
    <Setter Property="Padding" Value="3"/>
            
    <Setter Property="BorderThickness" Value="1"/>
            
    <Setter Property="BorderBrush">
                
    <Setter.Value>
                    
    <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
                        
    <GradientStop Color="#FFA3AEB9" Offset="0"/>
                        
    <GradientStop Color="#FF8399A9" Offset="0.375"/>
                        
    <GradientStop Color="#FF718597" Offset="0.375"/>
                        
    <GradientStop Color="#FF617584" Offset="1"/>
                    
    </LinearGradientBrush>
                
    </Setter.Value>
            
    </Setter>
            
    <Setter Property="Template">
                
    <Setter.Value>
                    
    <ControlTemplate TargetType="src:SpeedButton">
                        
    <Grid>
                            
    <vsm:VisualStateManager.VisualStateGroups>
                                
    <vsm:VisualStateGroup x:Name="CommonStates">
                                    
    <vsm:VisualState x:Name="Normal"/>
                                    
    <vsm:VisualState x:Name="MouseOver">
                                        
    <Storyboard>
                                            
    <DoubleAnimation Duration="0" Storyboard.TargetName="BackgroundAnimation" Storyboard.TargetProperty="Opacity" To="1"/>
                                            
    <ColorAnimation Duration="0" Storyboard.TargetName="BackgroundGradient" Storyboard.TargetProperty="(Rectangle.Fill).(GradientBrush.GradientStops)[1].(GradientStop.Color)" To="#F2FFFFFF"/>
                                            
    <ColorAnimation Duration="0" Storyboard.TargetName="BackgroundGradient" Storyboard.TargetProperty="(Rectangle.Fill).(GradientBrush.GradientStops)[2].(GradientStop.Color)" To="#CCFFFFFF"/>
                                            
    <ColorAnimation Duration="0" Storyboard.TargetName="BackgroundGradient" Storyboard.TargetProperty="(Rectangle.Fill).(GradientBrush.GradientStops)[3].(GradientStop.Color)" To="#7FFFFFFF"/>
                                        
    </Storyboard>
                                    
    </vsm:VisualState>
                                    
    <vsm:VisualState x:Name="Pressed">
                                        
    <Storyboard>
                                            
    <ColorAnimation Duration="0" Storyboard.TargetName="Background" Storyboard.TargetProperty="(Border.Background).(SolidColorBrush.Color)" To="#FF6DBDD1"/>
                                            
    <DoubleAnimation Duration="0" Storyboard.TargetName="BackgroundAnimation" Storyboard.TargetProperty="Opacity" To="1"/>
                                            
    <ColorAnimation Duration="0" Storyboard.TargetName="BackgroundGradient" Storyboard.TargetProperty="(Rectangle.Fill).(GradientBrush.GradientStops)[0].(GradientStop.Color)" To="#D8FFFFFF"/>
                                            
    <ColorAnimation Duration="0" Storyboard.TargetName="BackgroundGradient" Storyboard.TargetProperty="(Rectangle.Fill).(GradientBrush.GradientStops)[1].(GradientStop.Color)" To="#C6FFFFFF"/>
                                            
    <ColorAnimation Duration="0" Storyboard.TargetName="BackgroundGradient" Storyboard.TargetProperty="(Rectangle.Fill).(GradientBrush.GradientStops)[2].(GradientStop.Color)" To="#8CFFFFFF"/>
                                            
    <ColorAnimation Duration="0" Storyboard.TargetName="BackgroundGradient" Storyboard.TargetProperty="(Rectangle.Fill).(GradientBrush.GradientStops)[3].(GradientStop.Color)" To="#3FFFFFFF"/>
                                        
    </Storyboard>
                                    
    </vsm:VisualState>
                                
    </vsm:VisualStateGroup>
                            
    </vsm:VisualStateManager.VisualStateGroups>
                            
    <Border x:Name="Background" CornerRadius="3" Background="White" BorderThickness="{TemplateBinding BorderThickness}" BorderBrush="{TemplateBinding BorderBrush}">
                                
    <Grid Background="{TemplateBinding Background}"  Margin="1">
                                    
    <Border Opacity="0"  x:Name="BackgroundAnimation" Background="#FF448DCA" />
                                    
    <Rectangle x:Name="BackgroundGradient" >
                                        
    <Rectangle.Fill>
                                            
    <LinearGradientBrush StartPoint=".7,0" EndPoint=".7,1">
                                                
    <GradientStop Color="#FFFFFFFF" Offset="0" />
                                                
    <GradientStop Color="#F9FFFFFF" Offset="0.375" />
                                                
    <GradientStop Color="#E5FFFFFF" Offset="0.625" />
                                                
    <GradientStop Color="#C6FFFFFF" Offset="1" />
                                            
    </LinearGradientBrush>
                                        
    </Rectangle.Fill>
                                    
    </Rectangle>
                                
    </Grid>
                            
    </Border>
                            
    <ContentPresenter
                                  
    x:Name="contentPresenter"
                                  Content
    ="{TemplateBinding Content}"
                                  ContentTemplate
    ="{TemplateBinding ContentTemplate}"
                                  VerticalAlignment
    ="{TemplateBinding VerticalContentAlignment}"
                                  HorizontalAlignment
    ="{TemplateBinding HorizontalContentAlignment}"
                                  Margin
    ="{TemplateBinding Padding}"/>
                        
    </Grid>
                    
    </ControlTemplate>
                
    </Setter.Value>
            
    </Setter>
        
    </Style>
    </ResourceDictionary>

     

    这段代码其实是从Button的模板直接拷贝过来的,这里只是稍作修改,把用不上的东西去掉,如Focus状态,Button模板可以在Silverlight的帮助文档中获取。我懒得去重新设计外观了,将就用一下吧。

    4、             打开SpeedButton.cs文件,给SpeedButton类添加构造函数如下:

     

    1 public class SpeedButton : ContentControl
    2     {
    3         public SpeedButton()
    4         {
    5             DefaultStyleKey = typeof(SpeedButton);
    6         }
    7 }
    8 

     

    5、             修改MainPage.xaml文件,更改SpeedButton的声明如下:

     

    <src:SpeedButton Content="HEX" />

     

    6、运行程序,现在一个完整钮呈现在我们的面前,如下图所示。

     

    当我们把鼠标指针移到按钮上方或按下按钮时无任何反应,这时需要对鼠标的事件进行处理,指定这些事件的可视状态。

     

    第三步,给按钮添加鼠标响应事件:

    虽然我们给按钮设定了模板,并在模板中指定了鼠标经过及按钮按下时的状态,但我们并没有在SpeedButton中指定何时呈现这些状态。下面处理SpeedButton的鼠标事件,并指定相应的可视状态。

    打开SpeedButton.cs文件,并修改代码如下:

     

     1 public class SpeedButton : ContentControl
     2     {
     3         public SpeedButton()
     4         {
     5             DefaultStyleKey = typeof(SpeedButton);
     6             //指定鼠标事件方法
     7             MouseEnter += new MouseEventHandler(SpeedButton_MouseEnter);
     8             MouseLeave += new MouseEventHandler(SpeedButton_MouseLeave);
     9             MouseLeftButtonDown += new MouseButtonEventHandler(SpeedButton_MouseLeftButtonDown);
    10         }
    11         private Boolean isPress = false//指示按钮是否处于按下状态
    12         public Boolean IsPress
    13         {
    14             get
    15             {
    16                 return isPress;
    17             }
    18             set
    19             {
    20                 if (IsPress == value) return;
    21                 isPress = value;
    22                 if (isPress)
    23                 {   //将按钮显示为模板中的Pressed可视状态。
    24                     VisualStateManager.GoToState(this"Pressed"true);
    25                 }
    26                 else
    27                 {
    28                     VisualStateManager.GoToState(this"Normal"true);
    29                 }
    30             }
    31         }
    32         void SpeedButton_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
    33         {
    34             IsPress = !IsPress;
    35         }
    36 
    37         void SpeedButton_MouseLeave(object sender, MouseEventArgs e)
    38         {
    39             if (!isPress)
    40             {
    41                 VisualStateManager.GoToState(this"Normal"true);
    42             }
    43         }
    44 
    45         void SpeedButton_MouseEnter(object sender, MouseEventArgs e)
    46         {
    47             if (!isPress)
    48             {
    49                 VisualStateManager.GoToState(this"MouseOver"true);
    50             }
    51         }
    52 }
    53 

     

    运行程序,用鼠标单击按钮看看有何不同。至此,一个简单的SpeedButton制作完毕。

     

    第四步,制作ToolBar控件:

    工具按钮是有了,接下来开始制作工具条。工具条其实就是一个容器,里面存放着几个工具按钮,当单击其中一个按钮时,另一个处于按下状态的按钮会自动弹上来,并且可以随时知道工具条当前按下的是哪个按钮。工具条的技术难题在于,当点击按钮改变工具条的选中项时,工具条如何知道按钮被点击,并触发一个事件来通知用户呢?按钮虽然拥有MouseLeftButtonDown事件,但如何把这个事件传递给工具条呢?

    最初的想法是制作一个通用的工具条,但说实话,水平还不够,Silverlight中的很多机制还未了解透彻。而且通用控件需要考虑的东西太多,光代码可能就比位运算器多很多。这次还是制作一个位运算器专用的控件吧,好处是代码非常少,也容易理解。等未来水平达到了再考虑制作通用控件吧。

    1、在BitLibrary】项目上单击鼠标右键,选择【添加】à【新建项】,添加一个Silverlight用户控件,并命名为“ToolBar.xaml”。更改ToolBar.xaml文件如下:

     

    <UserControl x:Class="BitLibrary.ToolBar"
        xmlns
    ="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
        xmlns:x
    ="http://schemas.microsoft.com/winfx/2006/xaml" 
        xmlns:src
    ="clr-namespace:BitLibrary;assembly=BitLibrary">
        
    <StackPanel x:Name="LayoutRoot" Height="30" Width="120" Loaded="LayoutRoot_Loaded"
                    Orientation
    ="Horizontal"
                    VerticalAlignment
    ="Center"
                    HorizontalAlignment
    ="Center">
            
    <src:SpeedButton x:Name="btnBin" Content="BIN"/>
            
    <src:SpeedButton x:Name="btnOct" Content="OCT"/>
            
    <src:SpeedButton x:Name="btnBcd" Content="BCD"/>
            
    <src:SpeedButton x:Name="btnHex" Content="HEX"/>
        
    </StackPanel>
    </UserControl>

     

    ToolBar控件只是由一个StackPanel装载了四个SpeedButton控件,并且每个按钮都命了名,以方便在后台代码文件中调用。

    接下来在Loaded="LayoutRoot_Loaded"上单击鼠标右键,在弹出菜单中选择【导航到事件处理程序】,这时自动打开ToolBar.xaml.cs文件,并自动生成了LayoutRoot_Loaded事件方法。

    2、打开MainPage.xaml文件,并将<Grid>标签内的控件改为ToolBar:

    <src:ToolBar/>

    运行程序,这时带有四个SpeedButton的工具条呈现,单击每个按钮,可以发现当按下一个按钮时,其他按钮不会自动弹起。下面着手解决这个问题。

     

    第五步,让工具条内的按钮互斥:

    1、工具条内的四个按钮分别代表四种状态:

    BIN:二进制

    OCT:八进制

    BCD:十进制

    HEX:十六进制

    为了方便后面写程序,这里声明一个枚举来表示这四种状态。

    BitLibrary】项目上单击鼠标右键,选择【添加】à【新建项】,生成一个新类,命名为:“SystemState.cs”。在其中输入如下代码:

     

    1 public enum SystemState
    2 {
    3         None,
    4         Bin,
    5         Oct,
    6         Bcd,
    7         Hex
    8 }
    9 

     

    2、给按钮声明一个SystemState属性,用于保存这个按钮所属状态。打开SpeedButton.cs文件,并在SpeedButton添加一个State属性:

     

    1 public SystemState State
    2     {
    3         get;
    4         set;
    5 }

    3、打开ToolBar.xaml.cs文件,更改代码如下:

     

     1 public partial class ToolBar : UserControl
     2     {   //声明一个路由事件SelectionChange,用于在状态改变时通知用户
     3         public event RoutedEventHandler SelectionChange;
     4         public ToolBar()
     5         {
     6             InitializeComponent();
     7         }
     8 
     9         private void LayoutRoot_Loaded(object sender, RoutedEventArgs e)
    10         {   //设置每个按钮所属状态
    11             btnBin.State = SystemState.Bin;
    12             btnOct.State = SystemState.Oct;
    13             btnBcd.State = SystemState.Bcd;
    14             btnHex.State = SystemState.Hex;
    15             //将每个按钮的MouseLeftButtonDown事件连接至ToolBar的btn_MouseLeftButtonDown方法之中
    16             btnBin.MouseLeftButtonDown += new MouseButtonEventHandler(btn_MouseLeftButtonDown);
    17             btnOct.MouseLeftButtonDown += new MouseButtonEventHandler(btn_MouseLeftButtonDown);
    18             btnBcd.MouseLeftButtonDown += new MouseButtonEventHandler(btn_MouseLeftButtonDown);
    19             btnHex.MouseLeftButtonDown += new MouseButtonEventHandler(btn_MouseLeftButtonDown);
    20         }
    21         private SystemState toolState = SystemState.None;
    22         public SystemState ToolState //表示工具条当前所属状态
    23         {
    24             get { return toolState; }
    25             set
    26             {
    27                 if (toolState == value) return;
    28                 toolState = value;
    29                 //恢复每个按钮的正常状态
    30                 btnBin.IsPress = false;
    31                 btnOct.IsPress = false;
    32                 btnBcd.IsPress = false;
    33                 btnHex.IsPress = false;
    34                 //根据当前状态使相应按钮处于按下状态
    35                 switch (toolState)
    36                 {
    37                     case SystemState.Bin:
    38                         btnBin.IsPress = true;
    39                         break;
    40                     case SystemState.Oct:
    41                         btnOct.IsPress = true;
    42                         break;
    43                     case SystemState.Bcd:
    44                         btnBcd.IsPress = true;
    45                         break;
    46                     case SystemState.Hex:
    47                         btnHex.IsPress = true;
    48                         break;
    49                 }
    50                 //触发SelectionChange事件
    51                 if (SelectionChange != null
    52                 {
    53                     SelectionChange(thisnew RoutedEventArgs());
    54                 }
    55             }
    56         }
    57         //所有按钮单击时都会连接至这个方法
    58         void btn_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
    59         {   //通过设置ToolState属性以执行set访问器代码
    60             this.ToolState = ((SpeedButton)sender).State;
    61         }
    62 }
    63 

     

    这里我们把所有按钮的单击事件连接至ToolBar类里的btn_MouseLeftButtonDown()法之中,这样按钮的单击事件就可以传递至ToolBar工具条了。呵呵,这一招是看ToolKit源码时学到的,这方法够巧妙。

    现在可以运行程序,可以发现,当按下一个按钮时,之前被按下的按钮会自动弹起。

    4、为了测试我们按下按钮时是否可以马上知道工具条当前状态,更改MainPage.xaml代码如下:

     

    <UserControl x:Class="BitCalculator.MainPage"
        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="120" d:DesignHeight="70">
        
    <StackPanel x:Name="LayoutRoot">
            
    <src:ToolBar x:Name="toolBar" Grid.Row="1" SelectionChange="toolBar_SelectionChange"/>
            
    <TextBlock x:Name="txtMsg" Width="120" TextAlignment="Center" FontSize="13" Margin="10"/>
        
    </StackPanel>
    </UserControl>

     

    打开MainPage.xaml.cs文件,并更改代码如下:

     

     1 public partial class MainPage : UserControl
     2 {
     3         public MainPage()
     4         {
     5             InitializeComponent();
     6         }
     7 
     8         private void toolBar_SelectionChange(object sender, RoutedEventArgs e)
     9         {
    10             txtMsg.Text = "你按下了" + toolBar.ToolState.ToString() + "按钮";
    11         }
    12 }

     

    现在就完成了如文章开头所演示的效果。待续。。。。。。

  • 相关阅读:
    Redis在CentOS和Windows安装过程
    celery在Django中的集成使用
    Celery 框架学习笔记(生产者消费者模式)
    异步任务队列Celery在Django中的使用
    如何使用django+celery+RabbitMQ实现异步执行
    PowerMock+SpringMVC整合并测试Controller层方法
    Python获取指定文件夹下的文件名
    Python中super的应用
    Linux系统(Centos)下安装nodejs并配置环境
    面试题37:序列化二叉树
  • 原文地址:https://www.cnblogs.com/abatei/p/1605742.html
Copyright © 2011-2022 走看看