推薦並簡要分析一個silverlight的相冊DEMO--ImageSnipper(V2)

下面就是它的一些演示截圖。
  
     首先是縮放,旋轉和透明處理:

  

     然後是文字水印處理:

  
  然後是使用Ink的塗鴨:

  

     相信做爲一個相冊(圖片瀏覽)的基本功能已經沒什麼問題了。

     下面來看一下這個DEMO的類圖,如下:

  

     上圖中的左半部用紅框標識的區域是其控件設計類,因爲本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控制的運行效果,如下所示:

  

     當然這個DEMO在控件開發上還有一些有特色的地方,比如CheckBox控件等,我會在接下來的文章中加以說明,
呵呵。
 
  好了,今天的內容就先到這裏了。


本文轉自 daizhenjun 51CTO博客,原文鏈接:http://blog.51cto.com/daizhj/100970,如需轉載請自行聯繫原作者