Blog | Tag | Local | Guest | Login | Write |  RSS
Advanced WPF에 해당되는 글 11건
2008.12.23 :: Slide Image Menu 4622
2008.12.16 :: Flip Image Banner
2008.12.09 :: Dot Font Generator 1
2008.11.11 :: WPF UIElement Clone
2008.11.04 :: 복권 긁는 효과 1
2008.10.21 :: Mouse Over Detail View 1
Slide Image Menu

오늘은 Slide형태의 메뉴를 구현 해보도록 하겠습니다. 예전에 옥션이였나 G마켓이였나에서 본적이 있는 메뉴인데 생각이 나서 만들어 보았습니다. 아래는 시연동영상입니다.



1~6번까지 메뉴가 있으며 해당 번호를 클릭하면 슬라이드 되면세 해당 위치로 이동하게됩니다. 메뉴하나하나에 광고를 넣어서 광고용으로 사용해도 될것같습니다.  XAML을 사용해서 기본적인 내용을 구성했으며 C#에서는 이벤트에 따른 Animation호출 부분을 구현했습니다. 먼저 아래는 XAML 소스코드입니다.

  1. <WINDOW title="Image Slide View - WPF KOREA(Http://whatisthat.co.kr)" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" Height="275" Width="605" x:Class="ImageView.Window1" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" ResizeMode="NoResize">  
  2.     <WINDOW.RESOURCES>  
  3.         <DOUBLEANIMATION x:Key="MoveAnimation" Storyboard.TargetName="Panels" Storyboard.TargetProperty="(Canvas.Left)" SpeedRatio="2" />  
  4.     </WINDOW.RESOURCES>  
  5.     <GRID>  
  6.         <CANVAS Height="250" Width="600" ClipToBounds="True">  
  7.             <STACKPANEL Height="250" x:Name="Panels" Orientation="Horizontal" Canvas.Left="0">  
  8.                 <IMG height=250 width=600 Source="1.jpg">  
  9.                 <IMG height=250 width=600 Source="2.jpg">  
  10.                 <IMG height=250 width=600 Source="3.jpg">  
  11.                 <IMG height=250 width=600 Source="4.jpg">  
  12.                 <IMG height=250 width=600 Source="5.jpg">  
  13.                 <IMG height=250 width=600 Source="6.jpg">  
  14.             </STACKPANEL>  
  15.         </CANVAS>  
  16.            
  17.         <STACKPANEL Height="30" VerticalAlignment="Bottom" HorizontalAlignment="Right" Margin="5" Orientation="Horizontal">  
  18.             <STACKPANEL.RESOURCES>  
  19.                 <STYLE TargetType="TextBlock">  
  20.                     <Setter Property="FontSize" Value="30"/>  
  21.                     <Setter Property="Width" Value="25"/>  
  22.                     <Setter Property="Foreground" Value="White"/>  
  23.                     <Setter Property="FontFamily" Value="Verdata"/>  
  24.                     <Setter Property="FontStyle" Value="Italic"/>  
  25.                     <Setter Property="FontWeight" Value="Bold"/>  
  26.                     <Setter Property="Cursor" Value="Hand"/>  
  27.                     <Setter Property="Opacity" Value="0.5"/>  
  28.                     <Style.Triggers>  
  29.                         <Trigger Property="IsMouseOver" Value="True">  
  30.                             <Setter Property="Opacity" Value="1"/>  
  31.                         </Trigger>  
  32.                     </Style.Triggers>  
  33.                 </STYLE>  
  34.             </STACKPANEL.RESOURCES>  
  35.             <TEXTBLOCK Text="1" MouseDown="Button_Click" />  
  36.             <TEXTBLOCK Text="2" MouseDown="Button_Click" />  
  37.             <TEXTBLOCK Text="3" MouseDown="Button_Click" />  
  38.             <TEXTBLOCK Text="4" MouseDown="Button_Click" />  
  39.             <TEXTBLOCK Text="5" MouseDown="Button_Click" />  
  40.             <TEXTBLOCK Text="6" MouseDown="Button_Click" />  
  41.         </STACKPANEL>  
  42.   
  43.     </GRID>  
  44. </WINDOW>  



다음은 C# 코드입니다.

  1. private void Button_Click(object sender, RoutedEventArgs e)   
  2. {   
  3.   
  4.     DoubleAnimation MoveAnimation = Resources["MoveAnimation"as DoubleAnimation;   
  5.     TextBlock SelectedTextBlock = sender as TextBlock;   
  6.     MoveAnimation.To = -(int.Parse(SelectedTextBlock.Text) - 1) * 600;   
  7.   
  8.     Panels.BeginAnimation(Canvas.LeftProperty, MoveAnimation,HandoffBehavior.Compose);   
  9. }  



크기가 일정한 내용의 메뉴 페이지를 StackPanel을 사용해 순차적으로 쌓아놓고 버튼을 클릭하면 버튼에 많는 만큼 StackPanel을 이동하는 방법을 이용해 구현 했습니다. 간단하죠? 기타 질문은 리플이나 메일로 주시면 답변해드리겠습니다 ^^


Flip Image Banner

안녕하세요. 이번시간에는 현재 옥션에서 사용중인 Image Banner를 구현해보도록 하겠습니다. 여러개의 배너(혹은 광고물)이 책장이 넘어가듯이 넘어가는 효과로 이벤트 광고나 베너용으로 활용하면 좋을것 같습니다. 아래는 시연 동영상 입니다.



XAML을 사용해서 기본적인 내용을 구성했으며 C#에서는 이벤트에 따른 Animation호출 부분을 구현했습니다. 먼저 아래는 XAML 소스코드입니다.

  1. <WINDOW title="Flip Image Banner - WPF KOREA(http://whatisthat.co.kr)" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" Background="Red" Height="275" Width="605" x:Class="FlipImage.Window1" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">  
  2.        
  3.     <WINDOW.RESOURCES>  
  4.                    
  5.         <STORYBOARD FillBehavior="Stop" x:Key="TopAnimation" Storyboard.TargetName="TargetTop">  
  6.             <DOUBLEANIMATION Storyboard.TargetName="TopSkew" From="0" Duration="0:0:0.1" To="-10" Storyboard.TargetProperty="(SkewTransform.AngleX)" />  
  7.             <DOUBLEANIMATION Storyboard.TargetName="TopScale" From="1" Duration="0:0:0.25" To="0" Storyboard.TargetProperty="(ScaleTransform.ScaleY)" />  
  8.         </STORYBOARD>  
  9.            
  10.         <STORYBOARD FillBehavior="Stop" x:Key="BottomAnimation" Storyboard.TargetName="TargetTop">  
  11.             <DOUBLEANIMATION Storyboard.TargetName="BottomSkew" From="10" Duration="0:0:0.25" To="0" Storyboard.TargetProperty="(SkewTransform.AngleX)" BeginTime="0:0:0.25" />  
  12.             <DOUBLEANIMATION Storyboard.TargetName="BottomScale" From="0" Duration="0:0:0.25" To="1" Storyboard.TargetProperty="(ScaleTransform.ScaleY)" BeginTime="0:0:0.25" />  
  13.             <DOUBLEANIMATION Storyboard.TargetName="BottomSkew" From="0" Duration="0:0:0.1" To="5" Storyboard.TargetProperty="(SkewTransform.AngleX)" BeginTime="0:0:0.5" AutoReverse="True" />  
  14.             <DOUBLEANIMATION Storyboard.TargetName="BottomScale" From="1" Duration="0:0:0.1" To="0.9" Storyboard.TargetProperty="(ScaleTransform.ScaleY)" BeginTime="0:0:0.5" AutoReverse="True" />  
  15.         </STORYBOARD>  
  16.            
  17.         <IMAGEBRUSH x:Key="TopBrush" Viewbox="0,0,1,0.5" />  
  18.         <IMAGEBRUSH x:Key="BottomBrush" Viewbox="0,0.5,1,0.5" />  
  19.   
  20.     </WINDOW.RESOURCES>  
  21.        
  22.     <GRID Background="Black" Height="250" Width="600">  
  23.            
  24.         <RECTANGLE Height="125" Fill="{StaticResource TopBrush}" VerticalAlignment="Top" x:Name="OriginalTop" />  
  25.            
  26.         <RECTANGLE Height="125" Fill="{StaticResource BottomBrush}" VerticalAlignment="Bottom" x:Name="OriginalBottom" />  
  27.            
  28.         <RECTANGLE Height="125" Fill="{StaticResource TopBrush}" VerticalAlignment="Top" x:Name="TargetTop" RenderTransformOrigin="0.5,1">  
  29.             <RECTANGLE.RENDERTRANSFORM>  
  30.                 <TRANSFORMGROUP>  
  31.                     <SKEWTRANSFORM x:Name="TopSkew" AngleX="0" />  
  32.                     <SCALETRANSFORM x:Name="TopScale" ScaleY="1" />  
  33.                 </TRANSFORMGROUP>  
  34.             </RECTANGLE.RENDERTRANSFORM>  
  35.         </RECTANGLE>  
  36.            
  37.         <RECTANGLE Height="125" Fill="{StaticResource BottomBrush}" VerticalAlignment="Bottom" x:Name="TargetBottom" RenderTransformOrigin="0,0">  
  38.             <RECTANGLE.RENDERTRANSFORM>  
  39.                 <TRANSFORMGROUP>  
  40.                     <SKEWTRANSFORM x:Name="BottomSkew" AngleX="0" />  
  41.                     <SCALETRANSFORM x:Name="BottomScale" ScaleY="1" />  
  42.                 </TRANSFORMGROUP>  
  43.             </RECTANGLE.RENDERTRANSFORM>  
  44.         </RECTANGLE>  
  45.         <STACKPANEL Height="30" VerticalAlignment="Bottom" HorizontalAlignment="Right" Margin="20" Orientation="Horizontal">  
  46.             <STACKPANEL.RESOURCES>  
  47.                 <STYLE TargetType="TextBlock">  
  48.                     <Setter Property="FontSize" Value="30"/>  
  49.                     <Setter Property="Width" Value="25"/>  
  50.                     <Setter Property="Foreground" Value="White"/>  
  51.                     <Setter Property="FontFamily" Value="Verdata"/>  
  52.                     <Setter Property="FontStyle" Value="Italic"/>  
  53.                     <Setter Property="FontWeight" Value="Bold"/>  
  54.                     <Setter Property="Cursor" Value="Hand"/>  
  55.                     <Setter Property="Opacity" Value="0.5"/>  
  56.                     <Style.Triggers>  
  57.                         <Trigger Property="IsMouseOver" Value="True">  
  58.                             <Setter Property="Opacity" Value="1"/>  
  59.                         </Trigger>  
  60.                     </Style.Triggers>  
  61.                 </STYLE>  
  62.             </STACKPANEL.RESOURCES>  
  63.             <TEXTBLOCK Text="1" MouseDown="Button_Click" />  
  64.             <TEXTBLOCK Text="2" MouseDown="Button_Click" />  
  65.             <TEXTBLOCK Text="3" MouseDown="Button_Click" />  
  66.             <TEXTBLOCK Text="4" MouseDown="Button_Click" />  
  67.             <TEXTBLOCK Text="5" MouseDown="Button_Click" />  
  68.             <TEXTBLOCK Text="6" MouseDown="Button_Click" />  
  69.         </STACKPANEL>  
  70.   
  71.   
  72.     </GRID>  
  73.        
  74. </WINDOW>  



다음은 C# 코드입니다.

  1. public partial class Window1 : Window   
  2. {   
  3.   
  4.     public Window1()   
  5.     {   
  6.         InitializeComponent();   
  7.   
  8.         OriginalTop.Fill = (Resources["TopBrush"as ImageBrush).Clone();   
  9.         OriginalBottom.Fill = (Resources["BottomBrush"as ImageBrush).Clone();   
  10.         TargetTop.Fill = (Resources["TopBrush"as ImageBrush).Clone();   
  11.         TargetBottom.Fill = (Resources["BottomBrush"as ImageBrush).Clone();   
  12.            
  13.         (Resources["BottomAnimation"as Storyboard).Completed += new EventHandler(BottomAnimation_Completed);   
  14.         (Resources["TopAnimation"as Storyboard).Completed += new EventHandler(TopAnimation_Completed);   
  15.   
  16.     }   
  17.   
  18.     private void Button_Click(object sender, RoutedEventArgs e)   
  19.     {   
  20.         TextBlock SelectedTextBlock = sender as TextBlock;   
  21.         ChangeTarget("pack://Application:,,,/" + SelectedTextBlock.Text + ".jpg");   
  22.     }   
  23.   
  24.     private void ChangeTarget(String Source)   
  25.     {   
  26.         ImageSource OrigianlSource = (TargetTop.Fill as ImageBrush).ImageSource as ImageSource;   
  27.         ImageSource TargetSource = new BitmapImage(new Uri(Source));   
  28.   
  29.         BottomScale.ScaleY = 0;   
  30.   
  31.         (OriginalTop.Fill as ImageBrush).ImageSource = TargetSource;   
  32.         (TargetTop.Fill as ImageBrush).ImageSource = OrigianlSource;   
  33.         (TargetBottom.Fill as ImageBrush).ImageSource = TargetSource;   
  34.   
  35.         (Resources["TopAnimation"as Storyboard).Begin(this);   
  36.         (Resources["BottomAnimation"as Storyboard).Begin(this);   
  37.     }   
  38.     void TopAnimation_Completed(object sender, EventArgs e)   
  39.     {   
  40.         (TargetTop.Fill as ImageBrush).ImageSource = (TargetBottom.Fill as ImageBrush).ImageSource as ImageSource;   
  41.     }   
  42.   
  43.     void BottomAnimation_Completed(object sender, EventArgs e)   
  44.     {   
  45.         (OriginalBottom.Fill as ImageBrush).ImageSource = (TargetBottom.Fill as ImageBrush).ImageSource as ImageSource;   
  46.     }   
  47. }  


Skew Transform과 ScaleTransform을 사용해서 간단하게 책장을 넘어가는 효과를 구현했습니다. 그리고 현재 페이지(위쪽,아래쪽) 다음페이지(위쪽 아래쪽)을 표현하기 위해서 4개의 객체로 분리해서 사용했으며 ChangeTarget 함수를 이용합니다. 시간관계상 설명은 여기까지로 하고, 기타 질문은 리플이나 메일로 보내주시기 바랍니다.


Dot Font Generator

안녕하세요. 이번시간에는 TextBlock에 표시되는 글자를 Pixel단위로 쪼개어 Dot Font로 활용하는 방법에 대해서 소개해 드리겠습니다. 방법이야 여러가지가 있겠지만 저는 RenderTargetBitmp을 사용해 텍스트의 이미지를 생성하고 해당 이미지를 MemoryStream으로 복사 한 뒤 픽셀 단위로 문자열에 해당하는 부분을 검출하는 방법을 사용했습니다. 아래는 시연동영상입니다.



Generate Text에 변환할 문자를 입력하고 적당히 Spacing 을 조절한 뒤 Generate 버튼을 누르면 변환이 이루어집니다. Result 부분에는 변환된 점의 좌표가 출력되면 Preview에는 해당 점을 직접 그렸을 경우 미리보기가 가능합니다. 소스코드는 아래와 같습니다. 이번 예제에서의 XAML은 대부분 UI구성과 관련된 내용만 포함 하고 있으므로 설명은 하지 않고 C#코드에서 실제 Dot Font를 생성하는 부분만 설명을 하도록 하겠습니다. (전체 소스코드는 첨부하도록 하겠습니다.)

아래는 C#코드입니다.

  1. public partial class Window1 : Window   
  2. {   
  3.     public Window1()   
  4.     {   
  5.         InitializeComponent();   
  6.     }   
  7.   
  8.     private void BtnGenerate_Click(object sender, RoutedEventArgs e)   
  9.     {   
  10.   
  11.         ResultTextBlock.Text = TxtText.Text;   
  12.         List<POINT> Result = GenerateDotFont(ResultTextBlock, (int)SliderSpacing.Value);   
  13.         DrawDotFont(Result);   
  14.         LstResult.ItemsSource = Result;   
  15.               
  16.     }   
  17.   
  18.     public void DrawDotFont(List<POINT> DotFont)   
  19.     {   
  20.   
  21.         DrawingVisual DrawingVisual = new DrawingVisual();   
  22.         DrawingContext DrawingContext = DrawingVisual.RenderOpen();   
  23.   
  24.         foreach (Point Dot in DotFont)   
  25.         {   
  26.             DrawingContext.DrawRectangle(Brushes.White, null,   
  27.                 new Rect(Dot.X,Dot.Y,2,2));   
  28.         }   
  29.   
  30.         DrawingContext.Close();   
  31.   
  32.         RenderTargetBitmap bmp = new RenderTargetBitmap((int)ResultTextCanvas.Width, (int)ResultTextCanvas.Height, 96, 96, PixelFormats.Pbgra32);   
  33.         bmp.Render(DrawingVisual);   
  34.         ResultTextCanvas.Background = new ImageBrush(bmp);   
  35.   
  36.     }   
  37.   
  38.   
  39.   
  40.     public List<POINT> GenerateDotFont(TextBlock ReferenceTextBlock,int Space)   
  41.     {   
  42.   
  43.         int Width = (int)ReferenceTextBlock.Width;   
  44.         int Height = (int)ReferenceTextBlock.Height;   
  45.   
  46.         FormattedText text = new FormattedText(ReferenceTextBlock.Text,   
  47.                 CultureInfo.CurrentCulture,   
  48.                 FlowDirection.LeftToRight,   
  49.                 new Typeface(ReferenceTextBlock.FontFamily, ReferenceTextBlock.FontStyle, ReferenceTextBlock.FontWeight, ReferenceTextBlock.FontStretch),   
  50.                 ReferenceTextBlock.FontSize, ReferenceTextBlock.Foreground);   
  51.   
  52.         DrawingVisual DrawingVisual = new DrawingVisual();   
  53.         DrawingContext DrawingContext = DrawingVisual.RenderOpen();   
  54.         DrawingContext.DrawText(text, new Point(0, 0));   
  55.         DrawingContext.Close();   
  56.   
  57.         RenderTargetBitmap bmp = new RenderTargetBitmap(Width, Height, 96, 96, PixelFormats.Pbgra32);   
  58.         bmp.Render(DrawingVisual);   
  59.   
  60.         BmpBitmapEncoder Encoder = new BmpBitmapEncoder();   
  61.         Encoder.Frames.Add(BitmapFrame.Create(bmp));   
  62.   
  63.         MemoryStream MemoryStream = new MemoryStream();   
  64.         Encoder.Save(MemoryStream);   
  65.   
  66.         // Header Skip   
  67.         MemoryStream.Seek(54, SeekOrigin.Begin);   
  68.   
  69.         byte[] Buff = new byte[MemoryStream.Length - 54];   
  70.         MemoryStream.Read(Buff, 0, (int)(MemoryStream.Length - 54));   
  71.   
  72.         List<POINT> Result = new List<POINT>();   
  73.         int HeightOffSet = 0;   
  74.         for (int Y = 0; Y < Height; Y+=Space)   
  75.         {   
  76.             HeightOffSet = Width * Y;   
  77.   
  78.             for (int X = 0; X < Width; X+=Space)   
  79.             {   
  80.                 if (Buff[((HeightOffSet + X) << 2 )] == 255)   
  81.                     Result.Add(new Point(X, Height - Y));   
  82.   
  83.             }   
  84.         }   
  85.   
  86.         return Result;   
  87.   
  88.     }   
  89.   
  90. }  

코드를 보면 2가지 함수를 확인 하실 수 있을 것입니다. DrawDotFont는 List<Point> Type의 Dot Data가 포함된 DotList를 사용하여 Canvas에 DotFont를 그려주는 역할을 하며 GenerateDotFont는 ReferenceTextBlock의 정보에 맞춰 DotFont를 생성하는 역할을 합니다.

우리가 자세히 살펴봐야하는 함수는 GenerateDotFont 인데요, 아래와 같이 총 5개의 과정을 통해 시작됩니다.

1. DrawingVisual과 DrawingContext를 사용해서 TextVisual을 생성합니다.
2. RenderTargetBitmap을 사용하여 DrawingVisual객체를 렌더링 합니다.
3. 렌더링한 RenderTargetBitmap객체를 BmpBitmapEncoder를 사용하여 Bmp형식으로 변환합니다.
4. MemoryStream을 사용하여 Bmp로 변환된 객체의 픽셀정보를 가져옵니다.
5. Spacing만큼 픽셀을 건너 뛰면서 해당 픽셀이 글자 부분인지를 체크합니다.

다소 접하기 어려운 Class를 활용하는 예제라서 처음 보기에는 어려워 보일 수 있는 코드지만, 한줄한줄 따라가다보면 쉽게 이해하실 수 있을것입니다. 그럼 오늘 포스팅은 여기까지 하고 기타 질문은 리플이나 메일로 보내주시면 답변드리겠습니다.
(조만간 Dot Font를 활용한 예제를 올리도록 하겠습니다.)

전체 소스코드입니다.





이전시간에 올렸었던 DotFont를 사용한 샘플입니다. 기능은 사용자에게 키워드를 입력받고 해당하는 도서들을 검색합니다. 그리고 도서를 출력할 때 키워드를 DotFont로 변환하여 점 하나하나를 검색된 도서를 출력합니다. 아래는 시연 동영상입니다.



멤버십에서 작업을 하고있는데.. 멤버십 인터넷이 상당히 느려서 검색 결과가 약간 늦게 뜨네요.. ㅠㅠ
동영상을 끝까지 보다보면 한글도 잘표시가 되는 것을 확인 하실 수 있습니다. 이번 예제는 코드가 꾀 긴 관계로 중요한 코드만 여기서 설명하고 전체 소스코드는 첨부파일로 올리도록하도록 하겠습니다..

아래는 이번 예제에서 가장 핵심(?) 되는 부분이라고 할 수 있는 DotFont의 활용 부분입니다.

  1. foreach (Point Dot in DotString)   
  2. {   
  3.     Book BookItem = BookItems[Random.Next(0, BookItems.Count - 1)];   
  4.     BitmapSource Source = BookItem.Image;   
  5.     Rectangle DotItem = new Rectangle();   
  6.     DotItem.Width = DotItem.Height = Random.Next(1500,2000) / 100;   
  7.     DotItem.Fill = new ImageBrush(Source);   
  8.     DotItem.Tag = BookItem;   
  9.   
  10.     DotItem.RenderTransform = new RotateTransform(Random.Next(-360, 360));   
  11.     DotItem.MouseEnter += new MouseEventHandler(DotItem_MouseEnter);   
  12.     DotItem.MouseLeave += new MouseEventHandler(DotItem_MouseLeave);   
  13.     DotItem.MouseLeftButtonUp += new MouseButtonEventHandler(DotItem_MouseLeftButtonUp);   
  14.        
  15.     Canvas.SetLeft(DotItem, Dot.X - DotItem.Width / 2);   
  16.     Canvas.SetTop(DotItem, Dot.Y - DotItem.Height / 2);   
  17.   
  18.     ResultCanvas.Children.Add(DotItem);   
  19.   
  20. }  
DotString은 DotFont의 Raw Point가 저장된 List<Point>객체입니다. ForEach문을 사용하여 각각의 포인트들 검색한 도서를 매핑시킵니다. 여기서 BookItems는 검색된 도서가 저장되어 있는 List입니다.

기타 질문은 리플이나 메일 보내주시면 답변드리도록 하겠습니다.

PS. 코드를 보시면 중간에 OPENAPI Key를 입력하시는 부분이 있습니다. 해당 부분을 본인의 OpenAPI Key로 설정하셔야 동작됩니다.





안녕하세요. 예전에 제가 Treeview를 이용해서 Architecture View를 구현하는 방법에 대해 소개해 드린적이 있습니다. 이번시간에는 Treeview를 이용하여 Org Chart를 표현하는 방법에 대해 소개해 드리도록 하겠습니다. 아래는 결과 그림 입니다.


이번 예제에서 여기서 중요한 부분은 Treeview의 Template과 연결선을 그리는 방법으로 포스팅에서는 해당 부분만 다루고 나머지는 소스코드를 첨부하도록 하겠습니다.

먼저 Style부분입니다.
  1. <STYLE TargetType="TreeViewItem">  
  2.     <Setter Property="Template">  
  3.         <Setter.Value>  
  4.             <ControlTemplate TargetType="TreeViewItem">  
  5.                 <StackPanel>  
  6.                     <Border Width="60" Height="30" Background="Transparent" BorderBrush="Black" BorderThickness="1"    
  7.                             CornerRadius="5" Margin="2,0,2,30" Padding="5" Name="PART_Header">  
  8.                         <ContentPresenter ContentSource="Header"      
  9.                           HorizontalAlignment="Center" VerticalAlignment="Center"/>  
  10.                     </Border>  
  11.                     <ItemsPresenter/>  
  12.                 </StackPanel>  
  13.             </ControlTemplate>  
  14.         </Setter.Value>  
  15.     </Setter>  
  16.     <Setter Property="ItemsPanel">  
  17.         <Setter.Value>  
  18.             <ItemsPanelTemplate>  
  19.                 <StackPanel HorizontalAlignment="Center"       
  20.                      IsItemsHost="True" Margin="5"       
  21.                      Orientation="Horizontal"  />  
  22.             </ItemsPanelTemplate>  
  23.         </Setter.Value>  
  24.     </Setter>  
  25. </STYLE>  



이전에 소개해 드렸던 Architecture View와 비슷함으로 코드 설명은 ArchitectureView를 참고하시기 바랍니다.  다음은 C#코드입니다.

  1. public static Pen ConnectionPen = new Pen(Brushes.Black, 1);   
  2.   
  3. protected override void OnRender(DrawingContext DrawingContext)   
  4. {   
  5.        
  6.     if (this.Items.Count > 0)   
  7.         DrawConnections(DrawingContext, (TreeViewItem)this.Items[0]);   
  8.     base.OnRender(DrawingContext);   
  9. }   
  10.   
  11.   
  12. private void DrawConnections(DrawingContext DrawingContext, TreeViewItem ParentNode)   
  13. {   
  14.   
  15.     Point ParentNodePoint = GetHeaderCenterPoint(ParentNode, false);   
  16.   
  17.     double ParentToChildDistance = 999999;   
  18.     foreach (FrameworkElement ChildNode in ParentNode.Items)   
  19.     {   
  20.         Point ChildNodePoint = GetHeaderCenterPoint(ChildNode, true);   
  21.   
  22.         if (ParentToChildDistance > ChildNodePoint.Y - ParentNodePoint.Y)   
  23.             ParentToChildDistance = ChildNodePoint.Y - ParentNodePoint.Y;                   
  24.     }   
  25.   
  26.     if(ParentToChildDistance != 999999)   
  27.         DrawingContext.DrawLine(ConnectionPen, new Point(ParentNodePoint.X, ParentNodePoint.Y + ParentToChildDistance / 2), ParentNodePoint);   
  28.   
  29.     foreach (FrameworkElement ChildNode in ParentNode.Items)   
  30.     {   
  31.         Point ChildNodePoint = GetHeaderCenterPoint(ChildNode, true);   
  32.   
  33.         DrawingContext.DrawLine(ConnectionPen, new Point(ParentNodePoint.X, ParentNodePoint.Y + ParentToChildDistance / 2), ChildNodePoint);   
  34.   
  35.         if (ChildNode.GetType() == typeof(TreeViewItem))   
  36.             DrawConnections(DrawingContext, (TreeViewItem)ChildNode);   
  37.     }   
  38.   
  39. }   
  40.   
  41.   
  42. private Point GetTopCenterPoint(FrameworkElement Visual)   
  43. {   
  44.     return Visual.TranslatePoint(new Point(Visual.ActualWidth / 2, 0), this);   
  45. }   
  46.   
  47. private Point GetBottomCenterPoint(FrameworkElement Visual)   
  48. {   
  49.     return Visual.TranslatePoint(new Point(Visual.ActualWidth / 2, Visual.ActualHeight), this);   
  50. }   
  51.   
  52. private Point GetHeaderCenterPoint(FrameworkElement Control, bool IsTopCenter)   
  53. {   
  54.     if(IsTopCenter)   
  55.         return GetTopCenterPoint(GetHeader(Control));   
  56.     else  
  57.         return GetBottomCenterPoint(GetHeader(Control));   
  58. }   
  59.   
  60.   
  61. private FrameworkElement GetHeader(FrameworkElement Control)   
  62. {   
  63.     FrameworkElement ResultControl = (VisualTreeHelper.GetChild(Control, 0) as FrameworkElement).FindName("PART_Header"as FrameworkElement;   
  64.   
  65.     return ResultControl;   
  66. }  


TreeView가 랜더링 될때 각각의 노드들의 위치를 얻고 해당 부모와 자식 노드를 연결짖는 작업을 합니다.  노드의 Header부분의 위치를 구해야 함으로 Template에 헤더의 이름을 PART_Header라고 지정한뒤 VisualTreeHelper.GetChild를 사용하여 Template의 객체를 찾았습니다. 그리고 각노드들의 Treeview로부터의 상대 좌표를 얻기 위해서 Visual.TransaltePoint사용했습니다. 

기타 궁금하신 사항이나 질문은 리플이나 메일 주시면 답변해드리도록 하겠습니다.
아래는 소스코드입니다. (OrgChart를 이용한  SearchEngine을 구현하던중이라 프로젝트 이름이 SearchTree입니다 -_-이점은 그냥 넘어가주세요~)





WPF UIElement Clone

안녕하세요. 이번시간에는 기존의 WinForm에서 사용하던 Clone메서드를 WPF에서는 어떻게 사용하는지에 대해 알아보도록 하겠습니다. WPF에서는 특별하게 Clone 메서드를 제공하고 있지 않기때문에 Clone 메서드를 직접 구현 해야 합니다. 프로젝트를 하다가 생각나서 구현해놨던건데 알려드리면 유용할것 같아 포스팅합니다..
 
저같은 경우는 방법이야 여러가지가 있겠지만, 저같은 경우는 간단하게 UIElement객체를 XAML로 Export하고 다시 해당 객체를 Import하는 방식으로 사용합니다. 생각보다 부하가 심할 수 도 있는 부분이긴하나, 간단하게 한두번 정도 사용하기에는 큰 무리는 없을 것으로 보입니다.

아래는 제가 사용하는 CloneElement 메서드 소스코드입니다.

  1. public static UIElement CloneElement(UIElement Source)   
  2. {   
  3.   
  4.     if (Source == nullreturn null;   
  5.   
  6.     string XAML = System.Windows.Markup.XamlWriter.Save(Source);   
  7.   
  8.     System.IO.StringReader StringReader = new System.IO.StringReader(XAML);   
  9.     System.Xml.XmlReader xmlReader = System.Xml.XmlTextReader.Create(StringReader);   
  10.   
  11.     return (UIElement)System.Windows.Markup.XamlReader.Load(xmlReader);   
  12.   
  13. }  
사용은 아래와 같이 사용하시면 됩니다.
  1. Button OriginalButton = new Button();   
  2. Button CloneButton = CloneElement(OriginalButton) as Button;  


복권 긁는 효과

간단히 복권을 긁는 효과를 구현 해보도록 하겠습니다. 아래는 시연 동영상입니다.



아래는 소스코드입니다. XAML만으로 구현했기때문에 이해하시는데에 큰 문제는 없으실것 같습니다.

  1. <GRID Width="250" Height="250" Background="Red">  
  2.   
  3.     <IMG height=250 width=250 Source="PICTURE.JPG">  
  4.   
  5.     <WRAPPANEL>  
  6.         <WRAPPANEL.RESOURCES>  
  7.             <STYLE TargetType="Rectangle">  
  8.                 <Setter Property="Fill" Value="Black"/>  
  9.                 <Setter Property="Width" Value="25"/>  
  10.                 <Setter Property="Height" Value="25"/>  
  11.                 <Style.Triggers>  
  12.                     <EventTrigger RoutedEvent="Rectangle.MouseEnter">  
  13.                         <EventTrigger.Actions>  
  14.                             <BeginStoryboard>  
  15.                                 <Storyboard Storyboard.TargetProperty="(Rectangle.Opacity)">  
  16.                                     <DoubleAnimation To="0" Duration="0:0:0.5"/>  
  17.                                     <ColorAnimation To="White" Duration="0:0:0.5"  
  18.                                       Storyboard.TargetProperty="(Rectangle.Fill).(SolidColorBrush.Color)"/>  
  19.                                 </Storyboard>  
  20.                             </BeginStoryboard>  
  21.                         </EventTrigger.Actions>  
  22.                     </EventTrigger>  
  23.                 </Style.Triggers>  
  24.             </STYLE>  
  25.         </WRAPPANEL.RESOURCES>  
  26.   
  27.         <RECTANGLE />       <RECTANGLE />       <RECTANGLE />       <RECTANGLE />      <RECTANGLE />  
  28.         <RECTANGLE />       <RECTANGLE />       <RECTANGLE />       <RECTANGLE />      <RECTANGLE />  
  29.         <RECTANGLE />       <RECTANGLE />       <RECTANGLE />       <RECTANGLE />      <RECTANGLE />  
  30.         <RECTANGLE />       <RECTANGLE />       <RECTANGLE />       <RECTANGLE />      <RECTANGLE />  
  31.         <RECTANGLE />       <RECTANGLE />       <RECTANGLE />       <RECTANGLE />      <RECTANGLE />  
  32.         <RECTANGLE />       <RECTANGLE />       <RECTANGLE />       <RECTANGLE />      <RECTANGLE />  
  33.         <RECTANGLE />       <RECTANGLE />       <RECTANGLE />       <RECTANGLE />      <RECTANGLE />  
  34.         <RECTANGLE />       <RECTANGLE />       <RECTANGLE />       <RECTANGLE />      <RECTANGLE />  
  35.         <RECTANGLE />       <RECTANGLE />       <RECTANGLE />       <RECTANGLE />      <RECTANGLE />  
  36.         <RECTANGLE />       <RECTANGLE />       <RECTANGLE />       <RECTANGLE />      <RECTANGLE />  
  37.         <RECTANGLE />       <RECTANGLE />       <RECTANGLE />       <RECTANGLE />      <RECTANGLE />  
  38.         <RECTANGLE />       <RECTANGLE />       <RECTANGLE />       <RECTANGLE />      <RECTANGLE />  
  39.         <RECTANGLE />       <RECTANGLE />       <RECTANGLE />       <RECTANGLE />      <RECTANGLE />  
  40.         <RECTANGLE />       <RECTANGLE />       <RECTANGLE />       <RECTANGLE />      <RECTANGLE />  
  41.         <RECTANGLE />       <RECTANGLE />       <RECTANGLE />       <RECTANGLE />      <RECTANGLE />  
  42.         <RECTANGLE />       <RECTANGLE />       <RECTANGLE />       <RECTANGLE />      <RECTANGLE />  
  43.         <RECTANGLE />       <RECTANGLE />       <RECTANGLE />       <RECTANGLE />      <RECTANGLE />  
  44.         <RECTANGLE />       <RECTANGLE />       <RECTANGLE />       <RECTANGLE />      <RECTANGLE />  
  45.         <RECTANGLE />       <RECTANGLE />       <RECTANGLE />       <RECTANGLE />      <RECTANGLE />  
  46.         <RECTANGLE />       <RECTANGLE />       <RECTANGLE />       <RECTANGLE />      <RECTANGLE />  
  47.   
  48.     </WRAPPANEL>  
  49. </GRID>  


가장먼저 눈에 띄는 부분이 엄청난 분량의 Rectangle입니다. 활용 가능성을 보여드리기 위해 이렇게 많은 객체를 사용했지만 저렇게 사용하는 것이 최적화된 코드는 아니라는걸 알려드립니다! 위 코드에서 가장 중요한 부분은 Rectangle객체를 하나의 Style로 지정하여 마우스 오버가 될때 DoubleAnimation 과 ColorAnimation을 사용해서 뒷부분이 밝혀지는 효과를 주는데 있습니다.

기타 질문은 리플이나 메일로 보내주시면 답변해드리겠습니다.




예전에 인터넷에서 플래시로 한번 봤었던 내용인데 실습용으로 괜찮을거 같아서 한번 구현해 보았습니다. 하나의 그림을 총 4부분으로 나누어서 출력하고 마우스의 움직임에 따라서 마우스와의 거리에 따라 영역이 따라오는 속도를 조절하는 내용입니다. 마땅히 떠오르는 제목이 없어서.. 다이나믹 마우스 무브 애니메이션이라고 했지만.. 그냥 마우스 따라다니는 그림 정도라 할수도 있겠습니다..

아래는 시연 동영상 입니다.



컴퓨터 화면을 촬영한것이라서 많이 버벅대는 느낌이 있어서 조금 아쉽습니다..

아래는 XAML 소스코드입니다.
  1. <WINDOW title="Mouse Move Here!! - WPF KOREA!!" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" ResizeMode="NoResize" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" x:Class="MouseOver.Window1" Width="400" Height="450" Background="White">  
  2.     <CANVAS Width="400" Height="400">  
  3.   
  4.         <CANVAS.RESOURCES>  
  5.             <STYLE TargetType="Image">  
  6.                 <Setter Property="Width" Value="400"/>  
  7.                 <Setter Property="Height" Value="400"/>  
  8.                 <Setter Property="Source" Value="character.JPG"/>  
  9.             </STYLE>  
  10.             <STYLE TargetType="Canvas">  
  11.                 <Setter Property="Background" Value="White"/>  
  12.                 <Setter Property="Width" Value="400"/>  
  13.                 <Setter Property="Height" Value="400"/>  
  14.             </STYLE>  
  15.         </CANVAS.RESOURCES>  
  16.            
  17.         <CANVAS MouseMove="ImageTop_MouseMove">  
  18.             <IMG x:Name="ImageTop">  
  19.             <CANVAS.CLIP>  
  20.                 <RECTANGLEGEOMETRY Rect="0,0,400,100" />  
  21.             </CANVAS.CLIP>  
  22.         </CANVAS>  
  23.   
  24.         <CANVAS MouseMove="ImageTopBottom_MouseMove">  
  25.             <IMG x:Name="ImageTopBottom">  
  26.             <CANVAS.CLIP>  
  27.                 <RECTANGLEGEOMETRY Rect="0,100,400,100" />  
  28.             </CANVAS.CLIP>  
  29.         </CANVAS>  
  30.         <CANVAS MouseMove="ImageBottomTop_MouseMove">  
  31.             <IMG x:Name="ImageBottomTop">  
  32.             <CANVAS.CLIP>  
  33.                 <RECTANGLEGEOMETRY Rect="0,200,400,100" />  
  34.             </CANVAS.CLIP>  
  35.         </CANVAS>  
  36.         <CANVAS MouseMove="ImageBottom_MouseMove">  
  37.             <IMG x:Name="ImageBottom">  
  38.             <CANVAS.CLIP>  
  39.                 <RECTANGLEGEOMETRY Rect="0,300,400,100" />  
  40.             </CANVAS.CLIP>  
  41.         </CANVAS>  
  42.            
  43.     </CANVAS>  
  44.   
  45. </WINDOW>  


아래는 C# 소스코드입니다.

  1. public partial class Window1 : Window   
  2. {   
  3.     public Window1()   
  4.     {   
  5.         InitializeComponent();   
  6.   
  7.         Canvas.SetLeft(ImageTop, 0);   
  8.         Canvas.SetLeft(ImageTopBottom, 0);   
  9.         Canvas.SetLeft(ImageBottomTop, 0);   
  10.         Canvas.SetLeft(ImageBottom, 0);   
  11.   
  12.     }   
  13.   
  14.     DoubleAnimation TopMoveAnimation = new DoubleAnimation();   
  15.     DoubleAnimation TopBottomMoveAnimation = new DoubleAnimation();   
  16.     DoubleAnimation BottomTopMoveAnimation = new DoubleAnimation();   
  17.     DoubleAnimation BottomMoveAnimation = new DoubleAnimation();   
  18.        
  19.     private void ImageBottom_MouseMove(object sender, MouseEventArgs e)   
  20.     {   
  21.         SetAnimationPosition(e.GetPosition(this).X);   
  22.   
  23.         TopMoveAnimation.SpeedRatio = 0.5;   
  24.         TopBottomMoveAnimation.SpeedRatio = 1;   
  25.         BottomTopMoveAnimation.SpeedRatio = 2;   
  26.         BottomMoveAnimation.SpeedRatio = 4;   
  27.   
  28.         BeginAnimation();   
  29.     }   
  30.   
  31.     private void ImageBottomTop_MouseMove(object sender, MouseEventArgs e)   
  32.     {   
  33.         SetAnimationPosition(e.GetPosition(this).X);   
  34.   
  35.         TopMoveAnimation.SpeedRatio = 0.5;   
  36.         TopBottomMoveAnimation.SpeedRatio = 2;   
  37.         BottomTopMoveAnimation.SpeedRatio = 4;   
  38.         BottomMoveAnimation.SpeedRatio = 1;   
  39.   
  40.         BeginAnimation();   
  41.     }   
  42.   
  43.     private void ImageTopBottom_MouseMove(object sender, MouseEventArgs e)   
  44.     {   
  45.         SetAnimationPosition(e.GetPosition(this).X);   
  46.   
  47.         TopMoveAnimation.SpeedRatio = 2;   
  48.         TopBottomMoveAnimation.SpeedRatio = 4;   
  49.         BottomTopMoveAnimation.SpeedRatio = 1;   
  50.         BottomMoveAnimation.SpeedRatio = 0.5;   
  51.   
  52.         BeginAnimation();   
  53.     }   
  54.   
  55.     private void ImageTop_MouseMove(object sender, MouseEventArgs e)   
  56.     {   
  57.         SetAnimationPosition(e.GetPosition(this).X);   
  58.   
  59.         TopMoveAnimation.SpeedRatio = 4;   
  60.         TopBottomMoveAnimation.SpeedRatio = 2;   
  61.         BottomTopMoveAnimation.SpeedRatio = 1;   
  62.         BottomMoveAnimation.SpeedRatio = 0.5;   
  63.   
  64.         BeginAnimation();   
  65.     }   
  66.   
  67.     private void SetAnimationPosition(double LeftPosition)   
  68.     {   
  69.         LeftPosition -= 200;   
  70.         TopMoveAnimation.To = LeftPosition;   
  71.         TopBottomMoveAnimation.To = LeftPosition;   
  72.         BottomTopMoveAnimation.To = LeftPosition;   
  73.         BottomMoveAnimation.To = LeftPosition;   
  74.     }   
  75.   
  76.     private void BeginAnimation()   
  77.     {   
  78.         ImageTop.BeginAnimation(Canvas.LeftProperty, TopMoveAnimation, HandoffBehavior.Compose);   
  79.         ImageTopBottom.BeginAnimation(Canvas.LeftProperty, TopBottomMoveAnimation, HandoffBehavior.Compose);   
  80.         ImageBottomTop.BeginAnimation(Canvas.LeftProperty, BottomTopMoveAnimation, HandoffBehavior.Compose);   
  81.         ImageBottom.BeginAnimation(Canvas.LeftProperty, BottomMoveAnimation, HandoffBehavior.Compose);   
  82.     }   
  83. }  


간단하게 설명을 하자면, Canvas위에 Image를 영역별로 Clip해서 출력한 뒤 Canvas의 마우스 이벤트에 따라서 Animation을 적용한 것입니다. 수동으로 SpeedRatio를 수정해서 반응속도를 조절했습니다. 보다 자연스러운 Animation을 위해서 BeginAnimation을 호출할 때 HandoffBehavior를 주어서 약간의 관성을 주었습니다. 기타 궁금하신 사항은 리플이나 메일로 주시면 답변 해드리도록 하겠습니다.


Mouse Over Detail View

이번시간에는 XAML만을 사용하여 쇼핑몰 등에서 사용 가능한 Detail View를 구현해보도록 하겠습니다. (이번에도 역시 정확한 이름짖기가 애매해서 -_- Detail View라고 했습니다.) 아래는 시연 동영상 입니다.



제가 좋아하는 만화중에 하나인 드래곤볼입니다ㅎㅎ 기능에 대해서 설명하자면 화면 왼쪽에는 손오공이 오른쪽에는 베지터라는 메뉴가 있습니다. 해당 메뉴에 마우스를 오버 할 경우 해당 메뉴에서 더 보여주고 싶은 자세한 내용을 포함 할 수 있습니다. 소스코드를 본 뒤 더 자세히 이야기를 해보겠습니다.

  1. <WINDOW title="DragonBall - WPFKOREA(http://whatisthat.co.kr)" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" x:Class="DragonBall.Window1" Width="600" Height="400">  
  2.     <WINDOW.RESOURCES>  
  3.         <IMAGEBRUSH x:Key="Background" ImageSource="Background.jpg" />  
  4.            
  5.         <STYLE TargetType="Button" x:Key="ProfileBox">  
  6.             <Style.Resources>  
  7.                 <Style TargetType="TextBlock">  
  8.                     <Setter Property="Foreground" Value="White"/>  
  9.                     <Setter Property="TextAlignment" Value="Center"/>  
  10.                     <Setter Property="FontSize" Value="11"/>  
  11.                     <Setter Property="FontFamily" Value="Trebuchet MS"/>  
  12.                     <Setter Property="VerticalAlignment" Value="Center"/>  
  13.                     <Setter Property="HorizontalAlignment" Value="Center"/>  
  14.                     <Setter Property="Cursor" Value="Hand"/>  
  15.                 </STYLE>  
  16.             </STYLE.RESOURCES>  
  17.                
  18.             <SETTER Value="Black" Property="Background" />  
  19.             <SETTER Value="55" Property="Width" />  
  20.             <SETTER Value="17.5" Property="Height" />  
  21.   
  22.             <SETTER Property="Template">  
  23.                 <SETTER.VALUE>  
  24.                     <CONTROLTEMPLATE>  
  25.                         <GRID Width="{TemplateBinding Width}" Height="{TemplateBinding Height}" Background="{TemplateBinding Background}">  
  26.                             <TEXTBLOCK Text="{TemplateBinding Tag}" />  
  27.                             <CONTENTPRESENTER x:Name="ContentBase" Opacity="0" Content="{TemplateBinding Button.Content}" />  
  28.                         </GRID>  
  29.                         <CONTROLTEMPLATE.TRIGGERS>  
  30.                             <TRIGGER Value="True" Property="Button.IsMouseOver">  
  31.                                 <TRIGGER.ENTERACTIONS>  
  32.                                     <BEGINSTORYBOARD>  
  33.                                         <STORYBOARD Storyboard.TargetName="ContentBase">  
  34.                                             <DOUBLEANIMATION Storyboard.TargetProperty="(Grid.Height)" Duration="0:0:0.5" To="175" />  
  35.                                             <DOUBLEANIMATION Storyboard.TargetProperty="(Canvas.Top)" Duration="0:0:0.5" To="142.5" />  
  36.                                             <DOUBLEANIMATION Storyboard.TargetProperty="Opacity" Duration="0:0:0.5" To="1" />  
  37.                                         </STORYBOARD>  
  38.                                     </BEGINSTORYBOARD>  
  39.                                 </TRIGGER.ENTERACTIONS>  
  40.                                 <TRIGGER.EXITACTIONS>  
  41.                                     <BEGINSTORYBOARD>  
  42.                                         <STORYBOARD Storyboard.TargetName="ContentBase">  
  43.                                             <DOUBLEANIMATION Storyboard.TargetProperty="(Grid.Height)" Duration="0:0:0.5" To="17.5" />  
  44.                                             <DOUBLEANIMATION Storyboard.TargetProperty="(Canvas.Top)" Duration="0:0:0.5" To="300" />  
  45.                                             <DOUBLEANIMATION Storyboard.TargetProperty="Opacity" Duration="0:0:0.5" To="0" />  
  46.                                         </STORYBOARD>  
  47.                                     </BEGINSTORYBOARD>  
  48.                                 </TRIGGER.EXITACTIONS>  
  49.                             </TRIGGER>  
  50.                         </CONTROLTEMPLATE.TRIGGERS>  
  51.                     </CONTROLTEMPLATE>  
  52.                 </SETTER.VALUE>  
  53.             </SETTER>  
  54.   
  55.                
  56.         </STYLE>  
  57.   
  58.         <STYLE TargetType="Button" x:Key="LeftProfileBox" BasedOn="{StaticResource ProfileBox}">  
  59.                
  60.             <Style.Triggers>  
  61.                 <Trigger Property="IsMouseOver" Value="True">  
  62.                     <Trigger.EnterActions>  
  63.                         <BeginStoryboard>  
  64.                             <Storyboard>  
  65.                                 <DoubleAnimation To="200" Duration="0:0:0.5" BeginTime="0:0:0.5" Storyboard.TargetProperty="(Grid.Width)"/>  
  66.                                 <DoubleAnimation To="30" Duration="0:0:0.5" BeginTime="0:0:0.5" Storyboard.TargetProperty="(Canvas.Left)"/>  
  67.                             </Storyboard>  
  68.                         </BeginStoryboard>  
  69.                     </Trigger.EnterActions>  
  70.                     <Trigger.ExitActions>  
  71.                         <BeginStoryboard>  
  72.                             <Storyboard>  
  73.                                 <DoubleAnimation To="55" Duration="0:0:0.5" BeginTime="0:0:0.5" Storyboard.TargetProperty="(Grid.Width)"/>  
  74.                                 <DoubleAnimation To="175" Duration="0:0:0.5" BeginTime="0:0:0.5" Storyboard.TargetProperty="(Canvas.Left)"/>  
  75.                             </Storyboard>  
  76.                         </BeginStoryboard>  
  77.                     </Trigger.ExitActions>  
  78.                 </Trigger>                   
  79.             </Style.Triggers>  
  80.                
  81.         </STYLE>  
  82.            
  83.         <STYLE TargetType="Button" x:Key="RightProfileBox" BasedOn="{StaticResource ProfileBox}">  
  84.             <Style.Triggers>  
  85.                 <Trigger Property="IsMouseOver" Value="True">  
  86.                     <Trigger.EnterActions>  
  87.                         <BeginStoryboard>  
  88.                             <Storyboard>  
  89.                                 <DoubleAnimation To="200" Duration="0:0:0.5" BeginTime="0:0:0.5" Storyboard.TargetProperty="(Grid.Width)"/>  
  90.                             </Storyboard>  
  91.                         </BeginStoryboard>  
  92.                     </Trigger.EnterActions>  
  93.                     <Trigger.ExitActions>  
  94.                         <BeginStoryboard>  
  95.                             <Storyboard>  
  96.                                 <DoubleAnimation To="55" Duration="0:0:0.5" BeginTime="0:0:0.5" Storyboard.TargetProperty="(Grid.Width)"/>  
  97.                             </Storyboard>  
  98.                         </BeginStoryboard>  
  99.                     </Trigger.ExitActions>  
  100.                 </Trigger>  
  101.             </Style.Triggers>  
  102.         </STYLE>  
  103.            
  104.     </WINDOW.RESOURCES>  
  105.     <GRID>  
  106.         <CANVAS Width="511" Height="328" Background="{StaticResource Background}">  
  107.             <BUTTON style="StaticResource: " Tag="Son Goku" Canvas.Top="300" Canvas.Left="175">  
  108.                 <IMG height=175 width=200 Source="Goku.jpg">  
  109.             </BUTTON>  
  110.             <BUTTON style="StaticResource: " Tag="Vegeta" Canvas.Top="300" Canvas.Left="275">  
  111.                 <IMG height=175 width=200 Source="Vegeta.jpg">  
  112.             </BUTTON>  
  113.         </CANVAS>  
  114.     </GRID>  
  115. </WINDOW>  



코드가 급하게 포스팅한 내용이라 코드가 상당히 지저분 하지만 전반적인 작동원리에 대해서 이해하시는데에는 크게 문제는 없을 것 같습니다. 메뉴는 기본적으로 Button을 사용하기 때문에 Button의 Style을 적용합니다. MouseOver상태에 따라서 보여줄 객체를 정하기 위해서 Trigger에 IsMouseOver Property를 적용했습니다. 두 매뉴가 확장댈때의 위치가 다르기 때문에 Left와 Right를 기존의 Style을 상속받아 확장될때의 크기를 다시 적용했습니다.

  1. <GRID Width="{TemplateBinding Width}" Height="{TemplateBinding Height}" Background="{TemplateBinding Background}">  
  2.     <TEXTBLOCK Text="{TemplateBinding Tag}" />  
  3.     <CONTENTPRESENTER x:Name="ContentBase" Opacity="0" Content="{TemplateBinding Button.Content}" />  
  4. </GRID>  



Button Style부분의 코드입니다. 작은 상태에서의 보여줄 텍스트는 Tag를 Template Binding하여 표현하고 확장됬을경우 보여줄 컨텐츠를 보여주기 위해 ContentPresenter를 사용했습니다. ContentPresenter의 경우 기본적으로 Opacity가 0이여서 출력되지 않으며 MouseOver시에 Opacity가 변경됩니다.

  1. <BUTTON style="StaticResource: " Tag="Son Goku" Canvas.Top="300" Canvas.Left="175">  
  2.     <IMG height=175 width=200 Source="Goku.jpg">  
  3. </BUTTON>  
  4. <BUTTON style="StaticResource: " Tag="Vegeta" Canvas.Top="300" Canvas.Left="275">  
  5.     <IMG height=175 width=200 Source="Vegeta.jpg">  
  6. </BUTTON>  



사용할 때는 위와 같이 Tag Property에 버튼의 이름을 정의하고 Content Property에 Detail View에서의 컨텐츠를 추가 하면됩니다. 급하게 포스팅하느라 내용이 약간 어수선 한 부분이 있지만 시간이 되면 다시 정리하도록 하겠습니다. 자세한 내용이나 질문은 댓글이나 메일로 보내주시기 바랍니다.


안녕하세요. 오늘은 RotateTransform과 Bitmap Effect를 활용한 간단한 샘플을 구현 해보았습니다. 아래 동영상은 이번시간에 소개할 샘플의 작동 화면 입니다.

대략 보면 뭐하는 녀석이구나 집작은 모두 하시겠지만, 설명을 하자면.. 마우스를 올려놓았을 때 해당 부분의 일정 영역이 뿌옇게 되고 회전한뒤 원상복귀 되는 내용입니다. 실제 Application에 이러한 기능을 구현해서 사용할지는 의문이지만, Interactive Design과 같은 분야에서는 응용가능 한 예제라 생각됩니다.

아래는 전체 소스코드입니다.

<WINDOW title="WPF KOREA" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" x:Class="Rotation.Window1" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Height="300" Width="300">
   
    <GRID>
        <GRID.RESOURCES>
            <IMAGEBRUSH x:Key="WonderGirls" ImageSource="WonderGirls.jpg" />
            <STYLE TargetType="Rectangle">
                <Setter Property="Width" Value="200"/>
                <Setter Property="Height" Value="200"/>
                <Setter Property="RenderTransformOrigin" Value="0.5,0.5"/>
                <Setter Property="Fill" Value="{StaticResource WonderGirls}"/>
                <Setter Property="RenderTransform">
                    <Setter.Value>
                        <RotateTransform/>
                    </Setter.Value>
                </Setter>
                <Setter Property="BitmapEffect">
                    <Setter.Value>
                        <BlurBitmapEffect Radius="0"/>
                    </Setter.Value>
                </Setter>
                <Style.Triggers>
                    <EventTrigger RoutedEvent="Rectangle.MouseEnter">
                        <EventTrigger.Actions>
                            <BeginStoryboard>
                                <Storyboard  >
                                    <DoubleAnimation To="360" FillBehavior="Stop"
                                                     AccelerationRatio="0.2"
                                                     Storyboard.TargetProperty="(Rectangle.RenderTransform).(RotateTransform.Angle)"/>
                                    <DoubleAnimation To="5" FillBehavior="Stop"
                                                     AutoReverse="True" Duration="0:0:0.5"
                                                     Storyboard.TargetProperty="(Rectangle.BitmapEffect).(BlurBitmapEffect.Radius)"/>
                                </Storyboard>
                            </BeginStoryboard>
                        </EventTrigger.Actions>
                    </EventTrigger>
                </Style.Triggers>
            </STYLE>
        </GRID.RESOURCES>
       
        <RECTANGLE>
            <RECTANGLE.CLIP>
                <ELLIPSEGEOMETRY Center="100 100" RadiusX="100" RadiusY="100" />
            </RECTANGLE.CLIP>
        </RECTANGLE>
        <RECTANGLE>
            <RECTANGLE.CLIP>
                <ELLIPSEGEOMETRY Center="100 100" RadiusX="75" RadiusY="75" />
            </RECTANGLE.CLIP>
        </RECTANGLE>
        <RECTANGLE>
            <RECTANGLE.CLIP>
                <ELLIPSEGEOMETRY Center="100 100" RadiusX="50" RadiusY="50" />
            </RECTANGLE.CLIP>
        </RECTANGLE>
        <RECTANGLE>
            <RECTANGLE.CLIP>
                <ELLIPSEGEOMETRY Center="100 100" RadiusX="25" RadiusY="25" />
            </RECTANGLE.CLIP>
        </RECTANGLE>
    </GRID>
</WINDOW>

소스상에 특별히 어려운 부분이 없으므로 설명은 생략 하도록 하고, 질문 있으신분은 리플로 말씀해주시기 바랍니다. 추가로 이번 코드는 이전에 DockBar만들기(http://whatisthat.co.kr/71) 라는 주제로 다루어 진 내용이 많이 포함되어 함께 참고 하시면 좋을것 같습니다.

사용자 삽입 이미지