在前一阵子,我在网上找到了这个相册DEMO,其不仅支持图相册图片的顺(逆)序浏览,还支持简单的图

片处理(放大缩小Zoom,旋转Rotate,透明Transparency),在图片上打水印以及使用Ink在图片上涂鸭。

 

   在线演示:

     http://silverlight.services.live.com/invoke/72193/ImageSnipperV2/iframe.html

 

 

  下面就是它的一些演示截图。
  
     首先是缩放,旋转和透明处理:


夜莺系统架构_相册

  


     然后是文字水印处理:


夜莺系统架构_button_02

  

  然后是使用Ink的涂鸭:

夜莺系统架构_.net_03

  


     相信做为一个相册(图片浏览)的基本功能已经没什么问题了。


     下面来看一下这个DEMO的类图,如下:

夜莺系统架构_button_04

  


     上图中的左半部用红框标识的区域是其控件设计类,因为本DEMO中所使用的控件如:按钮,滑动条,

复选框等均未使用Silverlight中所提供的控件,而是自己绘制并定义事件。因此这是我对该DEMO感兴趣的

另一个原因。而右侧则是一些工具类或图片处理类,如处理图片移动的MovableImage和TextBlock移动的

MovableTextBlock等。

 

  下面先简要介绍一下其中的Button按钮控件的设计思路。因为其继承自ButtonBase,所以有必要先看
一下ButtonBase的代码声明,下面是xaml中的内容:

 


<  ControlTemplate   xmlns 
 ="http://schemas.microsoft.com/client/2007" 
 
                  xmlns:x  ="http://schemas.microsoft.com/winfx/2006/xaml"    
 > 
 
     <  Grid  
 x:Name 
 ="Part_Root" 
  MouseEnter 
 ="btnClearMouseEnter" 
  MouseLeave 
 ="btnClearMouseLeave" 
  
          MouseLeftButtonDown  ="btnClearMouseDown"   MouseLeftButtonUp 
 ="btnClearMouseUp" 
 > 
 
       <  Grid.Resources 
 > 
 
         <  Storyboard  
 x:Name 
 ="Part_MouseEnter" 
 /> 
 
         <  Storyboard  
 x:Name 
 ="Part_MouseDown" 
 /> 
 
         <  Storyboard  
 x:Name 
 ="Part_MouseUp" 
 /> 
 
         <  Storyboard  
 x:Name 
 ="Part_MouseLeave" 
 /> 
 
       </  Grid.Resources 
 > 
 
       <  Rectangle  
 x:Name 
 ="Part_BackgroundRect" 
 /> 
 
       <  TextBlock  
 x:Name 
 ="Part_Caption" 
 /> 
 
       <  Rectangle  
 x:Name 
 ="Part_ForegroundRect" 
 /> 
 
       <  Rectangle  
 x:Name 
 ="Part_HighlightRect" 
 /> 
 
     </  Grid 
 > 
 
  </  ControlTemplate 
 >


 

      从上面代码可以看出其采用控件模版的方式进行定义。但其鼠标在按钮上移入移出等状态的Storyboard
(故事板)并未进行定义。而肯体的实现被放在了相应的子类(Button.xaml和RepeatButton.xaml)进行实现。
下面就是其中的Button.xaml内容:

 


<  ControlTemplate   xmlns 
 ="http://schemas.microsoft.com/client/2007" 
 
                  xmlns:x  ="http://schemas.microsoft.com/winfx/2006/xaml"    
 > 
 
     <  Grid  
 x:Name 
 ="Part_Root" 
  MouseEnter 
 ="btnClearMouseEnter" 
  MouseLeave 
 ="btnClearMouseLeave" 
  
           MouseLeftButtonDown  ="btnClearMouseDown"   MouseLeftButtonUp 
 ="btnClearMouseUp" 
 > 
 
       <  Grid.Resources 
 > 
 
         <  Storyboard  
 x:Name 
 ="Part_MouseEnter" 
 > 
 
           <  ColorAnimation  
 Duration 
 ="00:00:00.25" 
  To 
 ="#3DFFFFFF" 
  Storyboard.TargetName 
 ="Part_HighlightRect" 
  
                    Storyboard.TargetProperty  ="(Shape.Fill).(SolidColorBrush.Color)"    
 /> 
 
         </  Storyboard 
 > 
 
         <  Storyboard  
 x:Name 
 ="Part_MouseDown" 
 > 
 
           <  ColorAnimation  
 Duration 
 ="00:00:00.2" 
  To 
 ="#22000000" 
  Storyboard.TargetName 
 ="Part_HighlightRect" 
  
                    Storyboard.TargetProperty  ="(Shape.Fill).(SolidColorBrush.Color)"    
 /> 
 
         </  Storyboard 
 > 
 
         <  Storyboard  
 x:Name 
 ="Part_MouseUp" 
 > 
 
           <  ColorAnimation  
 Duration 
 ="00:00:00.2" 
  To 
 ="#3DFFFFFF" 
  Storyboard.TargetName 
 ="Part_HighlightRect" 
  
                    Storyboard.TargetProperty  ="(Shape.Fill).(SolidColorBrush.Color)"    
 /> 
 
         </  Storyboard 
 > 
 
         <  Storyboard  
 x:Name 
 ="Part_MouseLeave" 
 > 
 
           <  ColorAnimation  
 Duration 
 ="00:00:00.25" 
  To 
 ="#00FFFFFF" 
  Storyboard.TargetName 
 ="Part_HighlightRect" 
  
                    Storyboard.TargetProperty  ="(Shape.Fill).(SolidColorBrush.Color)"    
 /> 
 
         </  Storyboard 
 > 
 
       </  Grid.Resources 
 > 
 
       <  Rectangle  
 x:Name 
 ="Part_BackgroundRect" 
  StrokeThickness 
 ="4" 
  RadiusX 
 ="16" 
  RadiusY 
 ="36" 
  Stroke 
 ="#46000000" 
 > 
 
         <  Rectangle.Fill 
 > 
 
           <  LinearGradientBrush  
 EndPoint 
 ="0.5,-0.4" 
  StartPoint 
 ="0.5,1.4" 
 > 
 
             <  GradientStop  
 Color 
 ="Gray" 
  Offset 
 ="0.242" 
 /> 
 
             <  GradientStop  
 Color 
 ="DarkBlue" 
  Offset 
 ="0.333" 
 /> 
 
           </  LinearGradientBrush 
 > 
 
         </  Rectangle.Fill 
 > 
 
       </  Rectangle 
 > 
 
       <  TextBlock  
 x:Name 
 ="Part_Caption" 
  VerticalAlignment 
 ="Center" 
  HorizontalAlignment 
 ="Center" 
  
           Foreground  ="Gold"   Text 
 ="Button" 
 > 
 
         <  TextBlock.RenderTransform 
 > 
 
           <  TranslateTransform  
 X 
 ="0" 
  Y 
 ="-2" 
 /> 
 
         </  TextBlock.RenderTransform 
 > 
 
       </  TextBlock 
 > 
 
       <  Rectangle  
 x:Name 
 ="Part_ForegroundRect" 
  VerticalAlignment 
 ="Top" 
  StrokeThickness 
 ="4" 
  RadiusX 
 ="16" 
  
           RadiusY  ="36"   Width 
 ="124" 
  Height 
 ="32" 
 > 
 
         <  Rectangle.Fill 
 > 
 
           <  LinearGradientBrush  
 EndPoint 
 ="0.5,-0.409" 
  StartPoint 
 ="0.5,1.409" 
 > 
 
             <  GradientStop  
 Color 
 ="#00FFFFFF" 
  Offset 
 ="0.13" 
 /> 
 
             <  GradientStop  
 Color 
 ="#FFFFFFFF" 
  Offset 
 ="1" 
 /> 
 
           </  LinearGradientBrush 
 > 
 
         </  Rectangle.Fill 
 > 
 
       </  Rectangle 
 > 
 
       <  Rectangle  
 VerticalAlignment 
 ="Top" 
  RadiusX 
 ="16" 
  RadiusY 
 ="36" 
  Fill 
 ="#00FFFFFF" 
  x:Name 
 ="Part_HighlightRect" 
 /> 
 
     </  Grid 
 > 
 
  </  ControlTemplate 
 >


 

   注:这样设计方式本人感觉很有意思,很有“面向对象”的味道,呵呵。
  
   下面简要浏览一下ButtonBase.xaml.cs的代码:

 


[TemplatePart(Name   =    
 " 
 Part_Root 
 " 
 , Type  
 = 
   
 typeof 
 (Panel))]
 [TemplatePart(Name   =    
 " 
 Part_Caption 
 " 
 , Type  
 = 
   
 typeof 
 (TextBlock))]
 [TemplatePart(Name   =    
 " 
 Part_ForegroundRect 
 " 
 , Type  
 = 
   
 typeof 
 (Rectangle))]
 [TemplatePart(Name   =    
 " 
 Part_BackgroundRect 
 " 
 , Type  
 = 
   
 typeof 
 (Rectangle))]
 [TemplatePart(Name   =    
 " 
 Part_HighlightRect 
 " 
 , Type  
 = 
   
 typeof 
 (Rectangle))]
 [TemplatePart(Name   =    
 " 
 Part_MouseEnter 
 " 
 , Type  
 = 
   
 typeof 
 (Storyboard))]
 [TemplatePart(Name   =    
 " 
 Part_MouseLeave 
 " 
 , Type  
 = 
   
 typeof 
 (Storyboard))]
 [TemplatePart(Name   =    
 " 
 Part_MouseDown 
 " 
 , Type  
 = 
   
 typeof 
 (Storyboard))]
 [TemplatePart(Name   =    
 " 
 Part_MouseUp 
 " 
 , Type  
 = 
   
 typeof 
 (Storyboard))]
  public    
 abstract 
   
 partial 
   
 class 
  ButtonBase : Control
 {
       ///    
 <summary> 
 
       ///   定义单击事件
       ///    
 </summary> 
 
        public 
   
 event 
  EventHandler Click;
       ///    
 <summary> 
 
       ///   执行单击事件的绑定方法
       ///    
 </summary> 
 
        protected 
   
 void 
  OnClick()
     {
           if   (Click  
 != 
   
 null 
 )
         {
             Click(  this  ,  
 new 
  EventArgs());
         }
     }

       ///    
 <summary> 
 
       ///   标题属性
       ///    
 </summary> 
 
        public 
   
 string 
  Caption
     {
           get   {  
 return 
   
 this 
 .Part_Caption.Text; }
           set   {  
 this 
 .Part_Caption.Text  
 = 
  value; }
     }
       ///    
 <summary> 
 
       ///   鼠标移入控件区域时启动Part_MouseEnter故事板,下面类似
       ///    
 </summary> 
 
       ///    
 <param name="sender"></param> 
 
       ///    
 <param name="e"></param> 
 
        protected 
   
 virtual 
   
 void 
  Part_Root_MouseEnter( 
 object 
  sender, MouseEventArgs e)
     {
         Part_MouseEnter.Begin();
     }

       protected    
 virtual 
   
 void 
  Part_Root_MouseLeave( 
 object 
  sender, MouseEventArgs e)
     {
         Part_MouseLeave.Begin();
     }

       protected    
 virtual 
   
 void 
  Part_Root_MouseLeftButtonDown( 
 object 
  sender, MouseButtonEventArgs e)
     {
         Part_MouseDown.Begin();
     }

       protected    
 virtual 
   
 void 
  Part_Root_MouseLeftButtonUp( 
 object 
  sender, MouseButtonEventArgs e)
     {
         Part_MouseUp.Begin();
           //  执行单击事件的绑定方法 
 
          OnClick();
     }

       protected   Storyboard Part_MouseEnter, Part_MouseDown, Part_MouseLeave, Part_MouseUp;
       protected   Rectangle Part_ForegroundRect, Part_BackgroundRect, Part_HighlightRect;
       protected   Panel Part_Root;
       protected   TextBlock Part_Caption;
 }


 

  其实上面的代码与我们平时写.net控件类似,也是属性事件的定义。当然不同的地方就是对故事板的使用,
而故事板会让我们的按钮在鼠标触发事件时在UI上看起来更酷。当然下面还要看一下相应的Button中的内容,因
为这才是实际运行时使用的控件,其代码如下:

    


public     partial 
   
 class 
  Button : ButtonBase
     {
           public   Button()
         {
               //  加载Button.xaml中的内容,为下面获取元素进行相应操作 
 
                string 
  xaml  
 = 
  ResourceHelper.GetTemplate( 
 this 
 .GetType());
             ControlTemplate template   =   (ControlTemplate)XamlReader.Load(xaml);
               this  .Template  
 = 
  template;
               this  .ApplyTemplate();
         }

           ///    
 <summary> 
 
           ///   对当前模板(xaml)中的元素进行(主要是鼠标)事件绑定
           ///    
 </summary> 
 
            public 
   
 override 
   
 void 
  OnApplyTemplate()
         {
             Part_Root   =   (Panel)GetTemplateChild( 
 " 
 Part_Root 
 " 
 );
             Part_Caption   =   (TextBlock)GetTemplateChild( 
 " 
 Part_Caption 
 " 
 );
             Part_ForegroundRect   =   (Rectangle)GetTemplateChild( 
 " 
 Part_ForegroundRect 
 " 
 );
             Part_BackgroundRect   =   (Rectangle)GetTemplateChild( 
 " 
 Part_BackgroundRect 
 " 
 );
             Part_HighlightRect   =   (Rectangle)GetTemplateChild( 
 " 
 Part_HighlightRect 
 " 
 );
             Part_MouseEnter   =   (Storyboard)GetTemplateChild( 
 " 
 Part_MouseEnter 
 " 
 );
             Part_MouseLeave   =   (Storyboard)GetTemplateChild( 
 " 
 Part_MouseLeave 
 " 
 );
             Part_MouseDown   =   (Storyboard)GetTemplateChild( 
 " 
 Part_MouseDown 
 " 
 );
             Part_MouseUp   =   (Storyboard)GetTemplateChild( 
 " 
 Part_MouseUp 
 " 
 );

             Part_Root.SizeChanged   +=    
 new 
  SizeChangedEventHandler(Part_Root_SizeChanged);
             Part_Root.MouseEnter   +=    
 new 
  MouseEventHandler(Part_Root_MouseEnter);
             Part_Root.MouseLeave   +=    
 new 
  MouseEventHandler(Part_Root_MouseLeave);
             Part_Root.MouseLeftButtonDown   +=    
 new 
  MouseButtonEventHandler(Part_Root_MouseLeftButtonDown);
             Part_Root.MouseLeftButtonUp   +=    
 new 
  MouseButtonEventHandler(Part_Root_MouseLeftButtonUp);
         }

           ///    
 <summary> 
 
           ///   按钮的实际高度或宽度发生变化时的处理事件
           ///    
 </summary> 
 
           ///    
 <param name="sender"></param> 
 
           ///    
 <param name="e"></param> 
 
            void 
  Part_Root_SizeChanged( 
 object 
  sender, SizeChangedEventArgs e)
         {
             Part_ForegroundRect.Width   =   Part_Root.ActualWidth  
 - 
  16d;
             Part_ForegroundRect.Height   =   Part_Root.ActualHeight  
 - 
  12d;
             Part_HighlightRect.Width   =   Part_Root.ActualWidth  
 - 
  10d;
             Part_HighlightRect.Height   =   Part_Root.ActualHeight  
 - 
  8d;
               if   (Part_Root.ActualWidth  
 > 
  Part_Root.ActualHeight)
             {
                 Part_BackgroundRect.RadiusX   =   Part_ForegroundRect.RadiusX  
 = 
  Part_HighlightRect.RadiusX  
 = 
  
                                   Part_Root.ActualHeight   /   2d;
                 Part_BackgroundRect.RadiusY   =   Part_ForegroundRect.RadiusY  
 = 
  Part_HighlightRect.RadiusY  
 = 
  
                                   Part_Root.ActualWidth   /   4d;
             }
               else  
             {
                 Part_BackgroundRect.RadiusX   =   Part_ForegroundRect.RadiusX  
 = 
  Part_HighlightRect.RadiusX  
 = 
  
                                   Part_Root.ActualHeight   /   4d;
                 Part_BackgroundRect.RadiusY   =   Part_ForegroundRect.RadiusY  
 = 
  Part_HighlightRect.RadiusY  
 = 
  
                                   Part_Root.ActualWidth   /   2d;
             }
         }
     }


    
    到这里还有另一个按钮控件RepeatButton没有介绍,其实它的内容也上面的Button代码相似,所以就不多介绍了。
当然RepeatButton最终的用处是被放在了ImageSelector控件中做为子控制被加载,这其中与我们开发“复合型”控件
相似。

  下面就是Button控制的运行效果,如下所示:


夜莺系统架构_相册_05

  



     当然这个DEMO在控件开发上还有一些有特色的地方,比如CheckBox控件等,我会在接下来的文章中加以说明,

呵呵。


 

  好了,今天的内容就先到这里了。

     tag:silverlight,button,imagesnipper

     作者:代震军,daizhj