Window/WPF2010. 4. 22. 21:04

WPF에서 웹캠을 사용하기 위해 DirectShow를 이용한 방법들이 많이 있습니다.

 

캡쳐보드에 연결된 카메라를 인식하기 위해서

여러 자료를 검색한 결과,

다음의 컨트롤만이 가능하였습니다.

 

제작자의 블로그 : http://jmorrill.hjtcentral.com/Home/tabid/428/EntryId/15/WPF-Hackery-Part-I.aspx

소스코드 : http://videorendererelement.codeplex.com/SourceControl/list/changesets

 

최근 소스코드를 다운 받으신 후,

cmd 창을 열어 압축을 해제한 폴더의

BinRelease 경로로 들어가 regsvr32  DShowMediaBridge.ax 명령을 실행시켜 등록시킵니다.

 

상용화 프로그램에서 보여주는 화면을 별도로 제작중인 프로그램에서

보여주기 위해서 다음의 코드 수정을 하였습니다.

 

CaptureDevice 클래스의

SetupGraph 함수의 capGraph.RenderStream(PinCategory.Capture , MediaType.Video, capFilter, null, baseGrabFlt); 부분과

SetConfigParms 함수의 capGraph.FindInterface(PinCategory.Capture , MediaType.Video, capFilter, typeof(IAMStreamConfig).GUID, out o); 부분의 PinCategory.Capture 부분을 PinCategory.Preview로 바꾸면 됩니다.

 

< 실행화면 >

 

기타 참고 사이트 :

http://directshownet.sourceforge.net/

http://www.aforgenet.com/

 

 

'Window > WPF' 카테고리의 다른 글

[ WPF ] 클래스 구조  (0) 2010.04.27
[ WPF ] Flash in WPF  (3) 2010.04.23
[ WPF ] Using WPF in Winform  (0) 2010.03.12
[ WPF ] 옆으로 흐르는 TextBlock  (0) 2010.03.11
[ WPF ] Page간 데이터 전달  (0) 2010.03.06
Posted by 열ㅇl
Window/WPF2010. 3. 12. 16:53

WPF 컨트롤을 Winform 내부에 포함시키기 위해서는

ElementHost 클래스를 사용할 수 있습니다.

 

ElementHost 클래스는 System.Windows.Forms.Control을 상속받은 윈폼 컨트롤로

WPF 엘리먼트를 포함시킬 수 있도록 특화된 클래스입니다.

 

자, 이제 하나 하나 알아보겠습니다.

 

우선 윈폼 프로젝트를 생성 후 다음과 같이 참조 추가를 선택 하여,

 

 

WindowsFormIntegration.dll 을 추가합니다.

 

그럼 이제 포함 방법들에 대해 알아보겠습니다.

 

1) C# 코드

 

코드로만 작성 시엔 PresentationCore.dll, PresentationFramework.dll을 추가 후

 

다음과 같이 네임스페이스를 추가합니다.

 

using System.Windows.Controls;

using System.Windows.Forms.Integration;

 

간단하게 WPF 컨트롤 중 버튼을 추가해보겠습니다.

 

 ElementHost host = newElementHost();

 

 System.Windows.Controls.Button btn = new System.Windows.Controls.Button();

 btn.Content = "WPF Button!!";

 

 host.Child = btn;

 host.Location = newPoint(45, 10);

 

 this.Controls.Add(host); 

 

System.Windows.Controls.Button과 System.Windows.Forms.Button 사이의 모호성 때문에

위와 같이 정확한 타입을 명시해야 합니다.

 

2) 도구 상자

 

윈폼 창을 선택 후 도구상자를 열어 ElmentHost 선택하여 윈폼 안에 위치시킵니다.

자동으로 PresentationCore.dll, PresentationFramework.dll이 추가됩니다.

    

 

간단한 TextBlock을 추가해 보겠습니다.

 

 TextBlock tb = newTextBlock();

 tb.Text = "WPF TextBlock!!";

 

 elementHost1.Child = tb;

 

3) WPF UserControl

 

우선 UserControl을 생성하겠습니다.

 

Xaml 의 내용은 간단하게 버튼을 위치시킨 후 Click 이벤트를 주었습니다.

 <Grid>
     <Button Click="Button_Click">
         WPFControl!!
     </Button>
 </Grid
>
 

 privatevoid Button_Click(object sender, RoutedEventArgs e)

 {

     MessageBox.Show("WPF Button!!");

 }

 

윈폼에 ElementHost 를 올려 놓은 뒤 ◀를 눌러 WPFControl 목록을 선택합니다. 


4) 윈폼 컨트롤을 포함한 WPF 컨트롤 포함하기

 

이번엔 윈폼 컨트롤인 MonthCalendar를 포함하는 WPF Button 컨트롤을 포함해 보겠습니다.

WindowsFormsHost 의 Child로 MonthCalendar를 포함 후

Button의 Content로 추가하였습니다.

 

 WindowsFormsHost wHost = newWindowsFormsHost();

 wHost.Child = newMonthCalendar();

 

 System.Windows.Controls.Button btn1 = new System.Windows.Controls.Button();

 

 btn1.Content = wHost;

 elementHost3.Child = btn1;

 

자 지금까지의 결과를 확인해 보겠습니다.

   

 

Using WPF in Winform.zip

'Window > WPF' 카테고리의 다른 글

[ WPF ] Flash in WPF  (3) 2010.04.23
[ WPF ] 캡쳐보드 카메라 사용하기  (0) 2010.04.22
[ WPF ] 옆으로 흐르는 TextBlock  (0) 2010.03.11
[ WPF ] Page간 데이터 전달  (0) 2010.03.06
[ WPF ] 간단한 Image Animation  (2) 2010.02.12
Posted by 열ㅇl
Window/WPF2010. 3. 11. 18:09

이번 포스팅에서는

TV를 보면 자막이 옆으로 흐르는 것 같은 애니메이션을

TextBlock을 이용해 비하인드 코드로 구현하는 방법에 대해 알아보겠습니다.

 

예제는 다음과 같이 Grid와 Canvas에 대해 작성해 보았습니다.

 

 

주의 할 점은 Grid 패널은 자식 요소의 크기를

자신의 크기에 맞게 변경해 버리기 때문에 Text 의 길이가 Grid 패널의

사이즈를 넘어 갈 경우 짤리는 현상이 발생합니다.

 

이를 방지 하기 위해 오버되는 Text의 길이 만큼 Margin 의 Right 값을 변경합니다.

 

소스는 다음과 같습니다.

간단히 TranslateTransform 의 x 값을 변경하는 내용입니다.

  private void TranslateAnimation(TextBlock text, double time)

 {

    double right = 0;

    FrameworkElement parent = text.Parent as FrameworkElement;

 

    // 텍스트의 길이가 부모 패널을 넘어갈 때

    if (text.ActualWidth > parent.ActualWidth)

    {

        right = text.ActualWidth - parent.ActualWidth;

    }

 

    // 부모 패널이 Grid라면 Margin 값 변경

    // 텍스트가 잘리는 현상 방지

    if (parent is Grid)

    {

        text.Margin = new Thickness(0, 0, -right, 0);

    }

 

    // TranslateTransform을 생성해야 애니메이션 적용

    text.RenderTransform = new TranslateTransform();

 

    Storyboard story = new Storyboard();

 

    DoubleAnimation animation = new DoubleAnimation();

 

    animation.From = parent.ActualWidth;

    animation.To = -(parent.ActualWidth + right);

    animation.Duration = TimeSpan.FromSeconds(time);

    animation.RepeatBehavior = RepeatBehavior.Forever;

 

    // TranslateTransform.XProperty 값 설정

    DependencyProperty[] Dproperty = new DependencyProperty[]

    {

        TextBlock.RenderTransformProperty,

        TranslateTransform.XProperty

    };

 

    string path = "(0).(1)";

 

    Storyboard.SetTargetProperty(animation, new PropertyPath(path, Dproperty));

    story.Children.Add(animation);

    story.Begin(text);

 }

 

'Window > WPF' 카테고리의 다른 글

[ WPF ] 캡쳐보드 카메라 사용하기  (0) 2010.04.22
[ WPF ] Using WPF in Winform  (0) 2010.03.12
[ WPF ] Page간 데이터 전달  (0) 2010.03.06
[ WPF ] 간단한 Image Animation  (2) 2010.02.12
[ WPF ] Canvas Image Background  (0) 2010.02.11
Posted by 열ㅇl
Window/WPF2010. 3. 6. 02:34

Page간 데이터를 전달하는 방법에 대해 알아보겠습니다.

 

예제는

첫 페이지에서 색상을 골라 다음 페이지로 값을 전달하는 3가지 방법과

현재 페이지의 값을 이전 페이지로 반환시키는 방법에 대한 내용입니다.

 

          

 

         

 

① 객체 매개변수를 받아들이는 Navigate

Navigate메소드는 Page의 인스턴스와 Uri뿐 아니라 어떤 것이든 전달 대상 페이지에 전송할 수 있습니다.

 

다음 소스와 같이 이동할 페이지의 인스턴스와 전달하려는 값을 넘길 수가 있습니다.

 // 전송할 객체를 매개변수로 넘겨줍니다.

private void ObjectNavigate(object sender, RoutedEventArgs e)   

{

    ColorPage colorPage = new ColorPage();

    // NavigationService의 LoadCompleted 이벤트 연결

    colorPage.SetLoadCompleted(NavigationService);

 

    this.NavigationService.Navigate(colorPage, color);

}

 

SetLoadCompleted의 함수 내용은 단순히 넘어온 NavigationService의 LoadCompleted 이벤트를

연결시켜줍니다.

public void SetLoadCompleted(NavigationService navigation)

{

    navigation.LoadCompleted += new LoadCompletedEventHandler(NavigationService_LoadCompleted);

}

 

이와 같은 이유는 아래 그림과 같이 페이지 이동 시 일어나는 이벤트의 발생 순서에 따라

Page의 Loaded 이벤트가 발생하기 전에 LoadCompleted이벤트가 발생하기 때문입니다.

 

LoadCompleted 이벤트에서는 ExtraData값으로 넘겨받은 매개변수를 사용할 수 있습니다.

 // 매개변수로 넘어온 값을 사용합니다.

void NavigationService_LoadCompleted(object sender, NavigationEventArgs e)

{

    if (e.ExtraData != null)

    {

        this.color.Fill = (Brush)converter.ConvertFromString((string)e.ExtraData);

    }

 

    this.NavigationService.LoadCompleted -= new LoadCompletedEventHandler(NavigationService_LoadCompleted);

}

 

② Instance만 사용하는 Navigate

오버로드 생성자를 사용하는 단순한 방법입니다.

매개변수의 데이터 타입을 정확히 할 수 있고, 프레임웍 차원에서 타입을 보증하기 때문에

데이터를 체크할 필요가 없는 장점이 있습니다.

 // 오버로드된 생성자의 인자로 넘겨 받습니다.

public ColorPage(string color)

{

    InitializeComponent();

 

    this.color.Fill = (Brush)converter.ConvertFromString(color);

}

 

 // 생성자의 매개변수로 넘겨줍니다.

private void InstanceNavigate(object sender, RoutedEventArgs e)

{

    this.NavigationService.Navigate(new ColorPage(color));

}

 

③ Application.Properties를 이용한 전역 데이터

여러 페이지에서 공유하기 위한 방법으로 사용할 수 있지만 안정성이 부족한 단점이 있습니다.

 // 전역 데이터로 공유합니다.

private void ApplicationProperties(object sender, RoutedEventArgs e)

{

    Application.Current.Properties["color"] = color;

}

 

 // 전역데이터를 사용합니다.

private void Page_Loaded(object sender, RoutedEventArgs e)

{

    if (Application.Current.Properties["color"] != null)

    {

        this.color.Fill = (Brush)converter.ConvertFromString((string)Application.Current.Properties["color"]);

        Application.Current.Properties.Remove("color");

    }

}

 

④ PageFunction

다음 그림과 같이 현재 페이지에서 작업을 처리하고

이전페이지로 돌아갈 때 처리 결과를 반환할 경우가 생깁니다.

 

 

위의 방법들을 사용할 수도 있겠지만 항상 새로운 Instance를 생성하기 때문에 문제가 발생할 수 있습니다.

WPF는 PageFunction 클래스를 사용하여 안정적이고 뒤로가기 버튼을 누른 것처럼 자동으로 이동할 수 있습니다.

 <PageFunction
   xmlns="
http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="
http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:sys="clr-namespace:System;assembly=mscorlib"
    x:Class="WpfApplication1.PageFunction1"
    x:TypeArguments="sys:String"
    Title="PageFunction1">
  <Grid>

  </Grid>
</PageFunction>

 

x:TypeArguments="sys:String"의 구문이 사용되었습니다.

PageFunction은 PageFunction<T> 타입의 제너릭 클래스이기 때문에 반환되는 값의 타입을 명시해야 합니다.
이는 타입 안전성을 확보한다는 점이 있습니다.

 

다음과 같이 Ruturn 이벤트를 통해 반환값을 받을 수 있습니다.

 // PageFunction 테스트

private void Hyperlink_Click(object sender, RoutedEventArgs e)

{

    PageFunction1 page = new PageFunction1();

    page.Return += new ReturnEventHandler<string>(page_Return);

    this.NavigationService.Navigate(page);

}

 

void page_Return(object sender, ReturnEventArgs<string> e)

{

    ((PageFunction1)sender).Return -= page_Return;

 

    result.Text = "Message : " + e.Result;

}

 

PageFunction 클래스는 OnReturn 메소드를 호출해서 반환되는 데이터를 다음과 같이 ReturnEventArgs타입으로

래핑하여 전달할 수 있습니다.

 private void Button_Click(object sender, RoutedEventArgs e)

{

    OnReturn(new ReturnEventArgs<string>(input.Text));

}

 

'Window > WPF' 카테고리의 다른 글

[ WPF ] Using WPF in Winform  (0) 2010.03.12
[ WPF ] 옆으로 흐르는 TextBlock  (0) 2010.03.11
[ WPF ] 간단한 Image Animation  (2) 2010.02.12
[ WPF ] Canvas Image Background  (0) 2010.02.11
[ WPF ] TextBox Select All Text  (0) 2010.02.11
Posted by 열ㅇl
Window/WPF2010. 2. 12. 20:34
 

이번 포스팅에서는

이미지 경로가 들어 있는 배열을 읽어 들어와

MouseEnter시 이미지 크기가 커지고 MouseLeave시 크기가 작아지는

간단한 애니메이션에 대해 알아 보겠습니다.

 

 

비하인드 코드에서 작성을 해보았습니다.

 

소스는 보시는 바와 같이

애니메이션을 만들어 이미지 객체의  EventTrigger에 연결 시키는 단순한 소스입니다.

   
    // 스토리 보드 생성

    Storyboard MenuReset = new Storyboard();

 

    DoubleAnimation withAnimation = new DoubleAnimation(100, new Duration(TimeSpan.Parse("0:0:0.15")));

    DoubleAnimation heightAnimation = new DoubleAnimation(150, new Duration(TimeSpan.Parse("0:0:0.15")));

 

    Storyboard.SetTargetProperty(withAnimation, new PropertyPath(WidthProperty));

    Storyboard.SetTargetProperty(heightAnimation, new PropertyPath(HeightProperty));

 

    MenuReset.Children.Add(withAnimation);

    MenuReset.Children.Add(heightAnimation);

 

    Storyboard ClickMenu = new Storyboard();

 

    DoubleAnimation _withAnimation = new DoubleAnimation(200, new Duration(TimeSpan.Parse("0:0:0.15")));

    DoubleAnimation _heightAnimation = new DoubleAnimation(300, new Duration(TimeSpan.Parse("0:0:0.15")));

 

    Storyboard.SetTargetProperty(_withAnimation, new PropertyPath(WidthProperty));

    Storyboard.SetTargetProperty(_heightAnimation, new PropertyPath(HeightProperty));

 

    ClickMenu.Children.Add(_withAnimation);

    ClickMenu.Children.Add(_heightAnimation);

 

    BeginStoryboard eventStoryboard1 = new BeginStoryboard();

    BeginStoryboard eventStoryboard2 = new BeginStoryboard();

 

    eventStoryboard1.Storyboard = ClickMenu;

    eventStoryboard2.Storyboard = MenuReset;

 

    // 이벤트 트리거

    EventTrigger eventTrigger = new EventTrigger(MouseEnterEvent);

    eventTrigger.Actions.Add(eventStoryboard1);

 

    EventTrigger eventTrigger2 = new EventTrigger(MouseLeaveEvent);

    eventTrigger2.Actions.Add(eventStoryboard2);

 

    foreach (string s in path)

    {

        Image image = new Image

        {

            Source = new BitmapImage(new Uri(s, UriKind.RelativeOrAbsolute)),

            Style = (Style)this.Resources["imageStyle"], // 스타일 적용

            Stretch = Stretch.Fill

        };

 

        // 이벤트 트리거 설정

        image.Triggers.Add(eventTrigger);

        image.Triggers.Add(eventTrigger2);

 

        stack.Children.Add(image);

    }

}

 

Posted by 열ㅇl
Window/WPF2010. 2. 11. 21:28

Canvas는 자식 객체들의 레이아웃에 영향을 주지 않으므로

자식 객체들은 항상 고정된 자신의 크기를 유지하게 됩니다.

 

그럼으로 Grid처럼 Image 객체의 Stretch속성 값을 Fill 로 설정을 해도

Canvas 크기 만큼 늘어나질 않죠.

 

이럴 경우 Image를 Canvas의 최하위 자식 요소, 즉 바탕화면으로 사용하려는

용도라면 Canvas의 Background로 지정하면 Canvas의 크기만큼 늘어나게 됩니다.

 

간단한 Xaml 코드는 다음과 같습니다.

 

<Canvas x:Name="canvas1"> 
   <Canvas.Background>
         <ImageBrush ImageSource="a.jpg"/>
   </Canvas.Background>

           .
           .
           .

 </Canvas>

'Window > WPF' 카테고리의 다른 글

[ WPF ] Page간 데이터 전달  (0) 2010.03.06
[ WPF ] 간단한 Image Animation  (2) 2010.02.12
[ WPF ] TextBox Select All Text  (0) 2010.02.11
[ WPF ] Window 사이즈 변경 시 컨트롤 크기 변경하기  (3) 2010.02.08
[WPF] Object Capture  (0) 2010.01.29
Posted by 열ㅇl
Window/WPF2010. 2. 11. 00:24

TextBox 사용 시 포커스가 왔을 때 다음과 같이

전체 내용을 선택해야 할 때가 있습니다.

 

 

 

 

 

 

 

 

 

 

 

 

여러 방법들이 있겠지만

간단하게 비하인드 코드에 작성된 이벤트 메서드를

여러 개의 TextBox의 GotFocus 이벤트와 연결하여 처리하는 방법에 대해 소개하겠습니다.

 

이벤트와 메서드를 연결합니다.

<TextBox GotFocus="TextBox_GotFocus" ... / >
<TextBox GotFocus="TextBox_GotFocus" ... / >

<TextBox GotFocus="TextBox_GotFocus" ... / >
<TextBox GotFocus="TextBox_GotFocus" ... / >

 

마우스로 클릭 후 포커스를 잃기 때문에 선택된 영역들이 다시 사라지게 됩니다.

이를 방지하기 위해 쓰레드를 사용하였습니다.

private void TextBox_GotFocus(object sender, RoutedEventArgs e)

{

    Dispatcher.CurrentDispatcher.BeginInvoke

    (

        DispatcherPriority.ContextIdle,

        new Action

        (

            delegate

            {

                (sender as TextBox).SelectAll();

            }

        )

    );                 

}

'Window > WPF' 카테고리의 다른 글

[ WPF ] 간단한 Image Animation  (2) 2010.02.12
[ WPF ] Canvas Image Background  (0) 2010.02.11
[ WPF ] Window 사이즈 변경 시 컨트롤 크기 변경하기  (3) 2010.02.08
[WPF] Object Capture  (0) 2010.01.29
[WPF] 시계 만들기  (0) 2010.01.26
Posted by 열ㅇl
Window/WPF2010. 2. 8. 15:55

Window 창의 크기를 변경 시

배치 된 컨트롤들의 크기도 함께 변경 할 경우가 있습니다.

Window의 SizeChanged 이벤트에서 다음과 같은 간단한 로직을 작성하면 됩니다.

 

Window 창의 처음 사이즈와 변경된 사이즈 만큼의 차이만큼

LayoutTransform의 ScaleTransform 값을 변경 시키면 됩니다.

 

예제 소스는 다음과 같습니다.

 

double orginalWidth, originalHeight;

 ScaleTransform scale = new ScaleTransform();

 

 public Window1()

 {

    InitializeComponent();

    this.Loaded += new RoutedEventHandler(Window1_Loaded);

 }

 

 void Window1_SizeChanged(object sender, SizeChangedEventArgs e)

 {

    ChangeSize(e.NewSize.Width, e.NewSize.Height);

 }

 

 void Window1_Loaded(object sender, RoutedEventArgs e)

 {

    orginalWidth = this.Width;

    originalHeight = this.Height;

 

    if (this.WindowState == WindowState.Maximized)

    {

        ChangeSize(this.ActualWidth, this.ActualHeight);

    }

 

    this.SizeChanged += new SizeChangedEventHandler(Window1_SizeChanged);

 }

 

 private void ChangeSize(double width, double height)

 {

    scale.ScaleX = width / orginalWidth;

    scale.ScaleY = height / originalHeight;

 

    FrameworkElement rootElement = this.Content as FrameworkElement;

 

    rootElement.LayoutTransform = scale;

 }

 

결과화면은 다음과 같습니다.

        

         

          

'Window > WPF' 카테고리의 다른 글

[ WPF ] Canvas Image Background  (0) 2010.02.11
[ WPF ] TextBox Select All Text  (0) 2010.02.11
[WPF] Object Capture  (0) 2010.01.29
[WPF] 시계 만들기  (0) 2010.01.26
[WPF] Image Pixel  (0) 2010.01.24
Posted by 열ㅇl
Window/WPF2010. 1. 29. 03:21
윈도우 창에 있는 모든 객체들은 그래픽적 요소를 가지고 있기 때문에

VisualBrush를 통해 브러쉬 요소로 사용할 수 가 있습니다.

 

이를 사용하여 윈도우 창에 있는 객체를 선택하거나 선택영역을 지정하여

해당 포맷에 맞는 이미지로 저장하는 간단한 예제를 만들어 보았습니다.

 

예제를 위해 눈사람 이라고 만들었는데 영 이상하네요. ㅎㅎ

 

객체 선택 : 마우스를 커서가 위치한 객체에 다음과 같이 빨간 테두리가 생깁니다.

                 Background 해제 시 객체만을 이미지로 저장합니다.

                 Background 체크 시 객체 영역을 이미지로 저장합니다.

                 저장 : 마우스 왼쪽 버튼

 

 

선택 영역 지정 : Cut 체크박스를 체크 한 후 마우스 왼쪽 버튼을 누른 채 이동하여 사각형을 만듭니다.

                         사각형을 드래그하여 이미지로 저장할 영역을 설정합니다.

                         저장 : 마우스 오른쪽 버튼

 

저장된 결과입니다.

 

예제는 부모 요소가 Canvas임을 고려하여 만들었습니다.

 

객체를 이미지로 변경하는 소스부분입니다.

DrawingVisual 객체로 객체 브러쉬로 사각형 그림을 그린 뒤 RenderTargetBitmap을 사용하여 Bitmap으로 변환합니다.

DrawRectangle의 Rect 첫번째 인자는 브러쉬로 칠할 시작 위치입니다.

 

   30 public static RenderTargetBitmap ConverterBitmapImage(FrameworkElement element)

   31 {

   32     DrawingVisual drawingVisual = new DrawingVisual();

   33     DrawinContext drawingContext = drawingVisual.RenderOpen();

   34 

   35     // 해당 객체의 그래픽요소로 사각형의 그림을 그립니다.

   36     drawingContext.DrawRectangle(new VisualBrush(element), null,

   37         new Rect(new Point(0, 0), new Point(element.ActualWidth, element.ActualHeight)));

   38     drawingContext.Close();

   39 

   40     // 비트맵으로 변환합니다.

   41     RenderTargetBitmap target =

   42         new RenderTargetBitmap((int)element.ActualWidth, (int)element.ActualHeight,

   43         96, 96, System.Windows.Media.PixelFormats.Pbgra32);

   44 

   45     target.Render(drawingVisual);

   46     return target;

   47 }

 

 

선택영역을 이미지로 변환하는 부분입니다.

CopyPixels 함수에서 부모 Canvas 이미지에서 해당 영역 만큼 픽셀을 복사해 와 비트맵 소스로 변환합니다.

   104 public static BitmapSource CutAreaToImage(int x, int y, int width, int height)

  105 {

             .

             .   // 예외 처리 부분

             .

  126 

  127     byte[] pixels = CopyPixels(x, y, width, height);

  128 

  129     int stride = (width * backround.Format.BitsPerPixel + 7) / 8;

  130 

  131    return BitmapSource.Create(width, height, 96, 96, PixelFormats.Pbgra32, null, pixels, stride);

  132 }

 

 

CopyPixels은 단지 CopyPixels 함수로 픽셀을 복사합니다.

    82 public static byte[] CopyPixels(int x, int y, int width, int height)

   83 {

   84     byte[] pixels = new byte[width * height * 4];

   85     int stride = (width * backround.Format.BitsPerPixel + 7) / 8;

   86 

   87     // Canvas 이미지에서 객체 역역만큼 픽셀로 복사

   88     canvasImage.CopyPixels(new Int32Rect(x, y, width, height), pixels, stride, 0);

   89 

   90     return pixels;

   91 }

 

 

다음은 객체 영역을 계산하는 부분입니다.

객체 이미지 픽셀과 Canvas영역에서 객체 영역만큼 잘라온 픽셀을 통해 계산해 보았는데

수정이 필요한 부분이네요. -_-;; 아니면 다른 방식으로 객체 영역만을 가져와야 하겠습니다.

하나의 픽셀은 4가지의 값을 (B,G,R,A) 가지고 있기 때문에 객체 브러쉬를 통해 비트맵으로 변환하게 되면 객체가 아닌

부분은 알파값이 제로이기 때문에 이를 사용해 봤지만 에러가 좀 있네요. ㅎㅎ

  142 private static void PixelTransform(byte[] pixels, byte[] element_pixels, int length)

  143 {

  144    for(int i=3; i<length; i+=4)

  145    {

  146        if (element_pixels[i] == 0)

  147        {

  148            pixels[i] = 0;

  149        }

  150        else

  151        {

  152            if (pixels[i] > element_pixels[i])

  153            {

  154                pixels[i] = element_pixels[i];

  155                pixels[i - 1] = element_pixels[i - 1];

  156                pixels[i - 2] = element_pixels[i - 2];

  157                pixels[i - 3] = element_pixels[i - 3];

  158            }

  159        }

  160    }

  161 }

 

Save함수는 포맷에 맞게 인코딩 후 저장합니다.

   169 public static void Save(BitmapSource source, bool png)

  170 {

  171     SaveFileDialog dlg = new SaveFileDialog();

  172 

  173     dlg.Filter = "PNG|*.png|JPG|*.jpg|GIF|*.gif|BMP|*.bmp";

  174     dlg.AddExtension = true;

  175 

  176     if (dlg.ShowDialog() == true)

  177     {

  178         FileStream stream = new FileStream(dlg.FileName, FileMode.Create, FileAccess.Write);

  179         BitmapEncoder encoder = new PngBitmapEncoder();

  180 

  181         dlg.FileName.ToCharArray(dlg.FileName.Length - 3, 3);

  182 

  183         string upper = dlg.FileName.ToUpper();

  184         char[] format = upper.ToCharArray(dlg.FileName.Length - 3, 3);

  185         upper = new string(format);

  186 

  187         if (!png)

  188         {

  189             switch (upper.ToString())

  190             {

  191                 case "JPG":

  192                     encoder = new JpegBitmapEncoder();

  193                     break;

  194 

  195                 case "GIF":

  196                     encoder = new GifBitmapEncoder();

  197                     break;

  198 

  199                 case "BMP":

  200                     encoder = new BmpBitmapEncoder();

  201                     break;

  202             }

  203         }

  204 

  205         encoder.Frames.Add(BitmapFrame.Create(source));

  206 

  207         encoder.Save(stream);

  208         stream.Close();

  209     }

  210 }

 

'Window > WPF' 카테고리의 다른 글

[ WPF ] TextBox Select All Text  (0) 2010.02.11
[ WPF ] Window 사이즈 변경 시 컨트롤 크기 변경하기  (3) 2010.02.08
[WPF] 시계 만들기  (0) 2010.01.26
[WPF] Image Pixel  (0) 2010.01.24
[WPF] Drag, Scale, Rotate  (1) 2010.01.24
Posted by 열ㅇl
Window/WPF2010. 1. 26. 17:42

DispatcherTimer, DependencyProperty, IValueConverter 이용해

위젯 형식의 간단한 시계 컨트롤을 만드는 방법에 대해 포스팅 하겠습니다.

 

결과물은 다음과 같습니다.

하나의 클래스를 통해 2개의 시계를 만들어 마우스 오른쪽 버튼을 누를 때마다 변경되도록 하였습니다.

드래그 :  마우스 왼쪽 버튼

종료 : ESC 버튼

 

         

           < 아날로그 >                                  < 디지털 >

 

 

우선 시계 모양으로 쓰일 이미지가 필요하겠죠.

위의 이미지는 배드걸 님의 블로그에서 얻을 수 있었습니다. 감사합니다.^^

 

이제 소스를 살펴보겠습니다.

코드가 짧기 때문에 이해가 쉬우실 겁니다.

 

우선 DateTime형식의 DependencyProperty를 생성 후 ClockTimer가 초기화가 될 때 타이머를 발생시킵니다.

타이머가 발생할 때마다 현재 DateTime 값으로 프로퍼티 값을 변경 시켜줍니다.

 

   10 class ClockTimer : FrameworkElement

   11 {

   12     private DispatcherTimer timer;

   13 

   14     private static DependencyProperty DateTimeProperty =

   15         DependencyProperty.Register("DateTime", typeof(DateTime), typeof(ClockTimer),

   16         new PropertyMetadata(DateTime.Now));

   17 

   18     protected override void OnInitialized(EventArgs e)

   19     {

   20         base.OnInitialized(e);

   21 

   22         timer = new DispatcherTimer();

   23         timer.Interval = TimeSpan.FromMilliseconds(1000);

   24         timer.Tick += new EventHandler(Timer_Tick);

   25         timer.Start();

   26     }

   27 

   28     private void Timer_Tick(object sender, EventArgs e)

   29     {

   30         SetValue(DateTimeProperty, DateTime.Now);

   31     }

   32 }

 

이제 이를 바인딩 용도에 맞게 IValueConverter를 사용 해 클래스들을 작성합니다.

다음은 초침을 위한 Converter입니다. 간단한 산수를 활용해(-_-;) 현재 각도를 구합니다.

   34 [ValueConversion(typeof(DateTime), typeof(int))]

   35 public class SecondsConverter : IValueConverter

   36 {

   37     public object Convert(object value, Type targetType, object parameter, CultureInfo culture)

   38     {

   39         DateTime date = (DateTime)value;

   40         return ((DateTime)value).Second * 6;

   41     }

   42 

   43     public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)

   44     {

   45         return null;

   46     }

   47 }

 

이제 이를 사용할 객체의 Resorces로 등록합니다.

<Grid.Resources>
   <clock:SecondsConverter x:Key="SecondsConverter"/>
   <clock:MinutesConverter x:Key="MinutesConverter"/>
   <clock:HoursConverter x:Key="HoursConverter"/>
   <clock:TimeStringConverter x:Key="TimeStringConverter"/>
    <clock:DateConverter x:Key="DateConverter"/>
</Grid.Resources>

 

마지막으로 ClockTimer 클래스 추가 후 알맞게 바인딩 합니다.

xmlns:clock="clr-namespace:WPFClock"

 

<clock:ClockTimer x:Name="Clock"/>

 

<RotateTransform Angle="{Binding Path=DateTime, Converter={StaticResource SecondsConverter}, ElementName=Clock}"/>

 

Text="{Binding Path=DateTime, Converter={StaticResource DateConverter}, ElementName=Clock}"

 

시계의 바늘들의 회전 중심을 맞추기가 약간 까다로웠지만

Height 값 만큼 Margin 값을 변경 시킨 후 RenderTransformOrigin 의 값을 변경하여 맞출 수 있었습니다.

 

 Height="42" Margin="0,0,0,42" RenderTransformOrigin="0.5,1"

 

자신이 원하는 이미지로 간단한 시계를 만들어 사용하는 것도 괜찮겠네요.

 

'Window > WPF' 카테고리의 다른 글

[ WPF ] Window 사이즈 변경 시 컨트롤 크기 변경하기  (3) 2010.02.08
[WPF] Object Capture  (0) 2010.01.29
[WPF] Image Pixel  (0) 2010.01.24
[WPF] Drag, Scale, Rotate  (1) 2010.01.24
[ WPF ] Drag  (0) 2009.09.27
Posted by 열ㅇl