Window/WPF2011. 3. 4. 21:19

ItemsPanel 을 변경하는 두 가지 방법에 대해 알아보겠습니다.

다음 코드들은
Grid 패널의 정렬 속성을 설정하고 ItemsPanel 로 설정하는 동일한 코드입니다.

1. Xaml 코드 사용

 StringBuilder xaml = new StringBuilder();
 
xaml.Append("<ItemsPanelTemplate xmlns=\"");
 xaml.Append("http://schemas.microsoft.com/winfx/2006/xaml/presentation\">");
 xaml.Append("<Grid HorizontalAlignment=\"Left\" VerticalAlignment=\"Top\" />");
 xaml.Append("</ItemsPanelTemplate>");
 StringReader stringReader = new StringReader(xaml.ToString());
 XmlReader xamlReader = XmlReader.Create(stringReader);
 
ItemsPanel = (ItemsPanelTemplate)XamlReader.Load(xamlReader);


2.
FrameworkElementFactory 클래스 사용

 FrameworkElementFactory factory = new FrameworkElementFactory(typeof(Grid));
 
factory.SetValue(Grid.HorizontalAlignmentProperty, HorizontalAlignment.Left);
 
factory.SetValue(Grid.VerticalAlignmentProperty, VerticalAlignment.Top);
 
ItemsPanel = new ItemsPanelTemplate(factory);


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

Brush ColorAnimation  (0) 2011.06.22
Storyboard.SetTargetProperty에 RenderTransformProperty 설정하기  (0) 2011.03.04
Image Rotate  (4) 2010.11.04
[ WPF ] Custom Slider  (4) 2010.08.13
[ WPF ] ClickOnce로 배포하기  (9) 2010.08.03
Posted by 열ㅇl
Window/WPF2010. 11. 4. 16:11

이미지를 마우스로 회전 하는 예제입니다.

 

 

소스는 다음과 같습니다.

 

public partial class Window1 : Window

    {

        private RotateTransform rotateTransform;

        private double angle;

        private double rotate;

        private Vector position;

 

        public Window1()

        {

            InitializeComponent();

            Loaded += new RoutedEventHandler(Window1_Loaded);

        }

 

        private void Window1_Loaded(object sender, RoutedEventArgs e)

        {

            vinyl.MouseLeftButtonDown += new MouseButtonEventHandler(vinyl_MouseLeftButtonDown);

            vinyl.MouseMove += new MouseEventHandler(vinyl_MouseMove);

            vinyl.MouseLeftButtonUp += new MouseButtonEventHandler(vinyl_MouseLeftButtonUp);

 

            rotateTransform = new RotateTransform();

            vinyl.RenderTransform = rotateTransform;

            vinyl.RenderTransformOrigin = new Point(0.5, 0.5);

 

            position = VisualTreeHelper.GetOffset(vinyl);

            position.X += vinyl.Width / 2;

            position.Y += vinyl.Height / 2;   

        }

 

        private void vinyl_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)

        {

            Mouse.Capture(vinyl, CaptureMode.None);

        }

 

        private void vinyl_MouseMove(object sender, MouseEventArgs e)

        {

            if (e.LeftButton == MouseButtonState.Pressed)

            {

                Point currentPoint = e.GetPosition(this);

 

                double _angle = Math.Atan2(position.Y - currentPoint.Y, position.X - currentPoint.X) *

(180 / Math.PI);

                rotateTransform.Angle = rotate + _angle - angle;

            }

        }

 

        private void vinyl_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)

        {

            Mouse.Capture(vinyl, CaptureMode.Element);

            Point point = e.GetPosition(this);

           

            angle = Math.Atan2(position.Y - point.Y, position.X - point.X) * (180 / Math.PI);

            rotate = rotateTransform.Angle;

        }

    }


Posted by 열ㅇl
Window/WPF2010. 8. 13. 23:22

 

이번 포스팅은 Slider에 대한 내용입니다.

우리가 사용하고 있는 미디어 플레이어 같은 프로그램들은 아래와 같이 슬라이더 바의 어느 영역을

클릭하고 움직이더라도 해당 지점으로 슬라이더의 위치가 움직입니다.

 

 

하지만 Slider컨트롤을 사용해보면 Thumb 영역이 아닌 다음과 같은 빨간 영역을 클릭 하게 되면 해당 위치로

Thumb 이 움직이나 이 후에 마우스 왼쪽 버튼을 클릭한 상태에서 움직여 보아도 Thumb 은 따라 오질 않습니다.

 

 

이를 개선하기 위한 방법으로

SliderPreviewMouseMove 이벤트를 사용하여 Slider내의 마우스 좌표 값을 SliderValue 값으로 변경 하였습니다.

 

코드는 다음과 같습니다.

Slider를 상속받은 CustomSlider 클래스를 생성하였고 AutoMoveProperty 라는 프로퍼티를 만들어

프로퍼티 값 변경에 따라 작성한 PreviewMouseMove 이벤트를 연결 / 제거 합니다.

     class CustomSlider : Slider

    {

        // 이전 IsMoveToPointEnabled 값으로 되돌리기 위한 플래그

        private bool defaultIsMoveToPointEnabled;

 

        public static readonly DependencyProperty AutoMoveProperty =

            DependencyProperty.Register(

           "AutoMove",

           typeof(bool),

           typeof(CustomSlider),

           new FrameworkPropertyMetadata(false, ChangeAutoMoveProperty));

 

        public bool AutoMove

        {

            get { return (bool)GetValue(AutoMoveProperty); }

            set { SetValue(AutoMoveProperty, value); }

        }

 

        private static void ChangeAutoMoveProperty(DependencyObject d, DependencyPropertyChangedEventArgs e)

        {

            CustomSlider slider = d as CustomSlider;

 

            if (slider != null)

            {

                if ((bool)e.NewValue)

                {

                    slider.defaultIsMoveToPointEnabled = slider.IsMoveToPointEnabled;

                    slider.IsMoveToPointEnabled = true;

                    slider.PreviewMouseMove += CustomSlider_PreviewMouseMove;

                }

                else

                {

                    slider.IsMoveToPointEnabled = slider.defaultIsMoveToPointEnabled;

                    slider.PreviewMouseMove -= CustomSlider_PreviewMouseMove;

                }

            }

        }

 

        private static void CustomSlider_PreviewMouseMove(object sender, MouseEventArgs e)

        {

            if (e.LeftButton == MouseButtonState.Pressed)

            {

                CustomSlider slider = sender as CustomSlider;

 

                Point point = e.GetPosition(slider);

 

                // 현재 Slider 내 마우스 좌표 값을 Value 값으로 계산.

                slider.Value = point.X / (slider.ActualWidth / slider.Maximum);

            }

        }

    }

 

이를 Sliverlight에서 적용해보니 역시나 프로퍼티 오류등을 보이며 제대로 되지 않더군요. -_-

그래서 Sliverlight 환경에 맞게 다시 수정해 보았습니다.

 

크게 바뀐 점은 없고 Sliverlight 에서 없는 IsMoveToPointEnabled 프로퍼티를 삭제하고

WPF 와 달리 마우스 이벤트로 넘어온 인자를 통해 MouseButton의 상태를 알 수 없기 때문에 이를 확인하기 위해

마우스의 다운과 업 이벤트에서 플래그 값을 변경하였습니다.

또한 Slider의 마우스 다운과 업 이벤트를 발생시키 위해서 새로 핸들러를 추가하였습니다.

  public partial class CustomSlider : Slider

    {

        private bool IsMouseLeftDown;

 

        public static readonly DependencyProperty AutoMoveProperty =

            DependencyProperty.Register(

           "AutoMove",

           typeof(bool),

           typeof(CustomSlider),

           new PropertyMetadata(false, ChangeAutoMoveProperty));

 

        public bool AutoMove

        {

            get { return (bool)GetValue(AutoMoveProperty); }

            set { SetValue(AutoMoveProperty, value); }

        }

 

        public CustomSlider()

        {

            AddHandler(FrameworkElement.MouseLeftButtonDownEvent,

                new MouseButtonEventHandler(MouseLeftDown), true);

            AddHandler(FrameworkElement.MouseLeftButtonUpEvent,

                new MouseButtonEventHandler(MouseLeftUp), true);

        }

 

        private void MouseLeftDown(object sender, MouseButtonEventArgs e)

        {

            Point point = e.GetPosition(this);

            Value = point.X / (ActualWidth / Maximum);

            IsMouseLeftDown = true;

        }

 

        private void MouseLeftUp(object sender, MouseButtonEventArgs e)

        {

            IsMouseLeftDown = false;

        }

 

        private static void ChangeAutoMoveProperty(DependencyObject d, DependencyPropertyChangedEventArgs e)

        {

            CustomSlider slider = d as CustomSlider;

 

            if (slider != null)

            {

                if ((bool)e.NewValue)

                {

                    slider.MouseMove += CustomSlider_PreviewMouseMove;

                }

                else

                {

                    slider.MouseMove -= CustomSlider_PreviewMouseMove;

                }

            }

        }

 

        private static void CustomSlider_PreviewMouseMove(object sender, MouseEventArgs e)

        {

            CustomSlider slider = sender as CustomSlider;

           

            if (slider.IsMouseLeftDown)

            {

                Point point = e.GetPosition(slider);

 

                slider.Value = point.X / (slider.ActualWidth / slider.Maximum);

            }

        }

    }

 

- XAML 에서의 사용법은 다음과 같이 AutoMove 멤버의 값을 True로 설정함으로써 사용할 수 있습니다.

 

<local:CustomSlider VerticalAlignment="Center" AutoMove="True"/>


- 블랜드에서는 다음과 같이 AutoMove 를 체크하여 사용하 실 수 있습니다.

 

소스를 받아 테스트 해보시면 잘 따라 움직이는 것을 보실 수 있습니다.

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

ListBox ItemsPanel 변경하기  (0) 2011.03.04
Image Rotate  (4) 2010.11.04
[ WPF ] ClickOnce로 배포하기  (9) 2010.08.03
[ WPF ] RegisterWindowMessage 를 이용한 프로그램 간 메시지 보내기  (178) 2010.07.21
[ WPF ] BackgroundWorker  (2) 2010.07.11
Posted by 열ㅇl
Window/WPF2010. 8. 3. 18:58

많은 배포 프로그램 중에서 가장 손쉽게 배포할 수 있는 것이 닷넷 2.0에서 발표된 ClickOnce가 아닌가 싶습니다.

게시한 URL만 알고 있으면 손쉽게 한번의 클릭으로 모든 걸 쉽게 할 수 있으니까요.^^

개발자의 측면에서는 더할 나위없이 편한 방법이 아닐 수 없습니다. ( 자동 업데이트 기능 등.. )

 

이번 포스팅에서는 WPF 프로그램을 ClickOnce로 배포하는 방법에 대해 알아보겠습니다.

3가지의 형태로 배포를 할 수 있는데요.

배포 방식 : http://msdn.microsoft.com/ko-kr/library/71baz9ah(VS.90).aspx

그 중 웹 배포에 대해 진행하겠습니다.

 

우선 컴퓨터에 IIS를 설치해야 할텐데요.

윈도우 7을 기준으로 알아보겠습니다. ( IIS 7.0 )

 

먼저,

[제어판] -> [프로그램 및 기능] -> [ Windows 기능 사용 / 사용 안함 ] 을 클릭합니다.

 

여러가지 Windows 기능 중에서 [ 인터넷 정보서비스 ] 탭을 체크 하면 기본 설정 값들에 체크가 되어집니다.

이 후 이 탭을 확장하여 배포에 필요한 다음 요소들을 체크 후 확인을 누릅니다.

 

- IIS 6 메타베이스 및 IIS 6 구성 호환성

- ASP.NET

- Windows 인증

 

설정이 완료 된후 웹 페이지 주소창에 http://localhost/ 를 입력하여 다음 그림이 나타나면 설치 완료!!

 

자, 그럼 이제 본격적으로 ClickOnce 배포를 해보겠습니다.

로컬 IIS 웹 사이트에 엑세스 하려면 관리자 계정의 컨텍스트에서 Visual Studio 를 실행해야 하기 때문에

Visual Studio를 마우스 오른쪽으로 클릭하여 관리자 권한으로 실행하여야 합니다.

 

 

간단하게 다음과 같은 프로그램을 만들었습니다.

 

이 후 프로젝트의 속성을 클릭합니다.

 

여러가지 속성 중에 서명, 보안, 게시 탭을 설정해 주어야 하는데요.

 

서명 탭을 클릭하여 간단하게 서명을 해보겠습니다.

테스트 프로그램이기 때문에 테스트 인증서 만들기를 클릭하여 암호를 입력하세요.

자세한 내용은 다음 주소를 참고하세요.

http://msdn.microsoft.com/ko-kr/library/che5h906(v=VS.80).aspx

 

보안 탭을 보시면 다음과 같이 완전 신뢰와 부분 신뢰를 선택할 수 있는데요.

다음 주소를 참고하여 프로그램 특성에 맞게 설정해주시면 되겠습니다.

http://msdn.microsoft.com/ko-kr/library/z17ceyya(VS.90).aspx

 

마지막으로 게시 탭입니다.

폴더 위치 게시는 현재 배포되는 위치 이며 설치 폴더 URL은 ClickOnce Application 이 실행되는 곳입니다.

배포 위치와 같다면 설정하지 않으셔도 됩니다.

 

설치 모드 및 설정

온라인으로만 응용 프로그램 사용 가능일 경우 배포된 웹 사이트에서만 실행이 가능하며,

오프라인으로도 응용 프로그램 사용 가능일 경우 로컬의 시작메뉴에서 실행이 가능합니다.

 

응용프로그램 파일은 실제 배포되는 파일들을 선택/설정할 수 있습니다.

필수구성요소는 프로그램 실행에 필요한 필수 구성요소에 대해 선택하고 설정 할 수 있습니다.

업데이트는 프로그램에 대한 업데이트 여부와 업데이트 시기등을 설정할 수 있습니다.

옵션은 설명( 게시 언어, 게시자 이름 등 ), 배포, 매니페스트, 파일연결에 대한 설정을 할 수 있습니다.

메니페스트의 바탕화면 바로 가기 만들기를 클릭하여 바로가기를 만들 수 있습니다.

 

게시 버전은 배포할 버전을 설정합니다.

게시 마법사는 위의 기능들 중 몇 가지만을 설정하여 게시할 수 있습니다.

 

배포 위치를 현재 로컬 주소의 ClickOnceTest 라는 가상 디렉토리로 설정하고

업데이트 시기를 응용프로그램 시작 전으로 설정합니다.

 

자, 그럼 지금 게시를 눌러 배포를 실행하겠습니다.

성공적으로 게시가 되었다면 해당 폴더와 파일들을 C:\inetpub\wwwroot 경로에서 확인 할 수 있습니다.

 

그럼 이제 설정한 해당 주소로 접속하여 파일을 실행하겠습니다.

http://localhost/ClickOnceTest/setup.exe

 

다음과 같이 파일 다운로드 창이 뜬 후 실행을 누르면

 

아래와 같은 창이 뜬 후 실행이 되어집니다.

  

  

 

시작 메뉴에 프로그램이 등록되어 지며 프로그램 및 기능에서 삭제할 수 있습니다.

 

프로그램을 수정하여 다시 게시한 후 프로그램을 실행하게 되면 업데이트 설치 여부를 물어보며

OK를 누르면 새로 수정된 프로그램이 실행되어 지는 것을 확인 할 수 있습니다.

       

  

 

ClickOnce의 Update 는 수정된 어셈블리만 다운받아 수정하므로 굉장히 효율적입니다.

ClickOnce에 대한 자세한 내용은 다음 URL을 참고하세요.

http://msdn.microsoft.com/ko-kr/library/t71a733d(VS.80).aspx

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

Image Rotate  (4) 2010.11.04
[ WPF ] Custom Slider  (4) 2010.08.13
[ WPF ] RegisterWindowMessage 를 이용한 프로그램 간 메시지 보내기  (178) 2010.07.21
[ WPF ] BackgroundWorker  (2) 2010.07.11
[ WPF ] 윈도우 이벤트  (4) 2010.07.11
Posted by 열ㅇl
Window/WPF2010. 7. 21. 21:22

서로 다른 응용 프로그램 사이에 메시지를 전달하는 방법에 대해 알아보겠습니다.

API 함수인 RegisterWindowMessage PostMessage 를 활용하면 되는데요.

 

RegisterWindowMessage 메시지를 등록하여 사용하기 때문에 서로 다른 프로그램에서 메시지를 약속하여야 합니다.

 

다음과 같은 원형을 가지고 있고 송수신 하는 프로그램에서 모두 실행하여야 합니다.

 uint RegisterWindowMessage (string lpString);

 

이 후에 PostMessage 함수를 사용하여 메시지를 보내주면 됩니다.

 bool PostMessage (IntPtr hWnd, uint Msg, uint wParam, uint lParam);

 

PostMessage 의 첫 번째 인자로 HWND_BROADCAST(0xffff) 값을 넣어주어야

다른 프로그램으로 메시지를 전송할 수 있습니다.

 

두 번째 인자로는 RegisterWindowMessage 함수를 실행한 반환 값을 넣어 주면 됩니다.

 

세 번째와 네 번째 인자를 사용하여 추가적으로 정보를 보낼 수 있습니다. (wParam : 2Byte, lParam : 4Byte)

이 방법은 4Byte 이상의 정보를 보낼 수 없다는 단점이 있습니다.

 

이 두 함수를 통해서 다른 프로그램으로 메시지를 보낼 수 있게 되었습니다.

 

그럼, 이제 메시지를 수신하는 방법을 알아보겠습니다.

윈도우 메시지를 수신해야 하는데요.

지난 번 포스팅 방법을 사용하겠습니다. ( http://shine10ee.blog.me/10089908443 )

 

다음과 같이 조건문을 거친 후 사용하시면 되겠습니다.

 void ComponentDispatcher_ThreadFilterMessage(ref MSG msg, ref bool handled)

{

    if (msg.message == 메시지 반환 값 && msg.wParam != 윈도우 핸들)

    {

       // To do ( msg.lParam 값으로 정보 확인 )

    }

}

 

이로써 WPF 에서 약속한 메시지를 수신할 수 있게 되었습니다.

 

간단한 예제로써 MFC 와 WPF 사이에 메시지를 송 수신하는 프로그램을 만들어 보았습니다.

MFC 프로그램의 코드에서도 위와 마찬가지 과정을 포함하고 있습니다.

 

소스는 다음과 같습니다.

 

[ WPF ]

 public partial class Window1 : Window

{

    private uint message;

    private IntPtr handle;

 

    public const uint HWND_BROADCAST = 0xffff;

 

    [DllImport("user32.dll", CharSet = CharSet.Unicode)]

    public static extern uint RegisterWindowMessage(string lpString);

 

    [DllImport("user32.dll", CharSet = CharSet.Unicode)]

    public static extern bool PostMessage(IntPtr hWnd, uint Msg, uint wParam, uint lParam);

 

    public Window1()

    {

        InitializeComponent();

    }

 

    protected override void OnSourceInitialized(EventArgs e)

    {

        base.OnSourceInitialized(e);

 

        handle = new WindowInteropHelper(this).Handle;

        message = RegisterWindowMessage("User Message");

        ComponentDispatcher.ThreadFilterMessage += new ThreadMessageEventHandler(ComponentDispatcher_ThreadFilterMessage);

    }

 

    void ComponentDispatcher_ThreadFilterMessage(ref MSG msg, ref bool handled)

    {

        if (msg.message == message && msg.wParam != handle)

        {

            MessageBox.Show("MFC Message : " + msg.lParam.ToString());

        }

    }

 

    private void Button_Click(object sender, RoutedEventArgs e)

    {

        PostMessage((IntPtr)HWND_BROADCAST, message, (uint)handle, 100);

    }

}


 

[ MFC ]

BOOL CRegisterWindowMessage_MFCDlg::OnInitDialog()

{

    CDialog::OnInitDialog();

 

    // 이 대화 상자의 아이콘을 설정합니다. 응용 프로그램의 주 창이 대화 상자가 아닐 경우에는

    //  프레임워크가 이 작업을 자동으로 수행합니다.

    SetIcon(m_hIcon, TRUE);            // 큰 아이콘을 설정합니다.

    SetIcon(m_hIcon, FALSE);        // 작은 아이콘을 설정합니다.

 

    // 메시지 등록

    message = RegisterWindowMessage(_T("User Message"));

 

    return TRUE;  // 포커스를 컨트롤에 설정하지 않으면 TRUE를 반환합니다.

}

 

void CRegisterWindowMessage_MFCDlg::OnBnClickedButton1()

{

    // 메시지 보내기

    ::PostMessage(HWND_BROADCAST, message, (WPARAM)this->GetSafeHwnd(), 200);

}

 

LRESULT CRegisterWindowMessage_MFCDlg::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)

{

    // 메시지 수신

    if(message == this->message && (HWND)wParam !=this->GetSafeHwnd())

    {

        char buf[256];

 

        sprintf_s(buf, "WPF Message : %d", lParam);

        MessageBox(buf);

    }

 

    return CDialog::WindowProc(message, wParam, lParam);

}

 

[ 실행 화면 ]

               [ WPF ]                                                       [ MFC ]

 

 

                                              [ MFC -> WPF ]

 

 

                                             [ WPF -> MFC ]

 

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

[ WPF ] Custom Slider  (4) 2010.08.13
[ WPF ] ClickOnce로 배포하기  (9) 2010.08.03
[ WPF ] BackgroundWorker  (2) 2010.07.11
[ WPF ] 윈도우 이벤트  (4) 2010.07.11
[ WPF ] OpenCV로 캠 화면 띄우기  (3) 2010.06.20
Posted by 열ㅇl
Window/WPF2010. 7. 11. 15:46

어떤 작업이 오래 걸리는 경우 사용자에게 현재 진행률을 보여줘야 하는 경우가 있습니다.

이 경우에 사용할 수 있는 쓰레드가 BackgroundWorker 입니다.

백그라운드에서 비동기적으로 실행이 되는 동안 호출 스레드는 계속 정상적으로 실행이 됩니다.
 
현재 진행률을 설정할 수 있으며 작업이 완료된 시점의 이벤트를 발생할 수 있습니다.
중간에 작업을 취소할 수도 있습니다.

예제로 간단하게 버튼을 누르면 0에서 100까지의 진행률을 보여주며 중간에 Cancle 버튼을 누르면 작업이
취소되도록 하였습니다. UI 작업을 보여주기 위해 작업 동안 Ellipse를 랜덤하게 뿌려주게 하였습니다.
 
소스는 다음과 같습니다.

 public partial class Window1 : Window

{

    private BackgroundWorker thread = new BackgroundWorker();

    private readonly int max = 100;

    private BrushConverter brushConverter = new BrushConverter();

    private Random random = new Random();

 

    public Window1()

    {

        InitializeComponent();

    }

 

    protected override void OnInitialized(System.EventArgs e)

    {

        base.OnInitialized(e);

 

        progress.Maximum = max;

 

        // 진행률 전송 여부

        thread.WorkerReportsProgress = true;

 

        // 작업 취소 여부

        thread.WorkerSupportsCancellation = true;

 

        // 작업 쓰레드

        thread.DoWork += new DoWorkEventHandler(thread_DoWork);

 

        // 진행률 변경

        thread.ProgressChanged += new ProgressChangedEventHandler(thread_ProgressChanged);

 

        // 작업 완료

        thread.RunWorkerCompleted += new RunWorkerCompletedEventHandler(thread_RunWorkerCompleted);

    }

 

    void thread_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)

    {

        btnStart.Content = "Start";

        string result = "작업이 완료되었습니다.";

 

        // 작업이 취소된 경우

        if (e.Cancelled)

        {

            result = "작업이 취소되었습니다.";

        }

 

        MessageBox.Show(result);

    }

 

    void thread_ProgressChanged(object sender, ProgressChangedEventArgs e)

    {

        int value = e.ProgressPercentage;

 

        // 변경 값으로 갱신

        progress.Value =value;

        progressValue.Text = value.ToString() + "%";

    }

 

    void thread_DoWork(object sender, DoWorkEventArgs e)

    {

        BackgroundWorker worker = sender as BackgroundWorker;

 

        for (int i = 0; i <= max; i++)

        {

            // CancelAsync() 메서드가 호출되었다면 정지

            if (worker.CancellationPending == true)

            {

                e.Cancel = true;

                break;

            }

            else

            {

                System.Threading.Thread.Sleep(50);

 

                // UI 쓰레드에 접근

                this.Dispatcher.BeginInvoke(DispatcherPriority.Normal, (ThreadStart)delegate()

                {

                    ellipseGrid.Children.Add(RandomEllipse());

                });

 

 

                // 진행률 변경 값 전송

                worker.ReportProgress(i);

            }

        }

    }

 

[ 작업 진행 화면 ]

 

[ 작업 완료 화면 ]

 

[ 작업 취소 화면 ]

Posted by 열ㅇl
Window/WPF2010. 7. 11. 15:26

윈도우 이벤트를 가로채기 위해서 HwndSource.AddHook 방법을 많이 사용하지만 이 방법은

조금 느린 반응과 HwndSource.FromHwnd 로 얻어낸 HWND 윈도우에서 발생하는 메시지만 수신된다는 단점이 있는데요.
 
이 단점이 없는 방법으로 ComponentDispatcher.ThreadMessage 가 있습니다.

사용법은 다음과 같습니다.
 

  private void Window_Loaded(object sender, RoutedEventArgs e)

 {

    ComponentDispatcher.ThreadFilterMessage +=

       new ThreadMessageEventHandler(ComponentDispatcher_ThreadFilterMessage);

 }



void ComponentDispatcher_ThreadFilterMessage(ref MSG msg, ref bool handled)

{

   switch (msg.message)

   {

        // To do

   }

}

 
Win32의 메시지와 동일한 값으로 필요한 메시지들을 다음과 같이 정의하여 비교하시면 됩니다.
 

uint WM_CREATE = 0x0001;

uint WM_DESTROY = 0x0002;

uint WM_MOVE = 0x0003;

uint WM_SIZE = 0x0005;

 

참고로 윈도우의 핸들은 다음과 같이 얻어 올 수 있습니다.

IntPtr hwnd = new WindowInteropHelper(this).Handle;

Posted by 열ㅇl
Window/WPF2010. 6. 20. 20:50

OpenCv를 사용하여 캠 화면을 띄우는 방법에 대한 포스팅입니다.

 

들로네 님의 큰 도움이 있었습니다. ^^

들로네님 블로그 : http://blog.naver.com/tramper2

들로네님 운영 카페 : http://cafe.naver.com/opencvsharp.cafe

 

C#을 이용한 OpenCv와 DirectShow에 관한 유용한 정보가 많으니 들려보시면 많은 도움이 되실 겁니다.

 

캠의 목록 선택에 따라 화면을 보여주는 예제로

Timer를 이용하여 카메라의 각 프레임을 WriteableBitmap으로 변환하여 Image에 보여주는 방식입니다.

캠의 목록을 얻어오는 부분은 DirectShow를 사용하였습니다.

 

소스는 다음과 같습니다.

    public partial class Window1 : Window

    {

        private CvCapture capture;

        private DispatcherTimer timer;

        private WriteableBitmap writeBitmap;

        private IplImage src;

 

        public Window1()

        {

            InitializeComponent();           

        }

 

        protected override void OnInitialized(EventArgs e)

        {

            base.OnInitialized(e);

 

            // 캠 목록 얻어오기

            foreach (DirectShowLib.DsDevice ds in

                DirectShowLib.DsDevice.GetDevicesOfCat

                  (DirectShowLib.FilterCategory.VideoInputDevice))

            {

                camList.Items.Add(ds.Name);

            }

 

            SetTimer();

        }

 

        protected override void OnClosing(System.ComponentModel.CancelEventArgs e)

        {

            base.OnClosing(e);

 

            if (capture != null)

            {

                capture.Dispose();

            }

 

            if (timer != null)

            {

                timer.Stop();

            }

        }

 

        private void initCamera(int camIndex)

        {

            try

            {

                // 해당 카메라 가져오기

                capture = CvCapture.FromCamera(CaptureDevice.DShow, camIndex);

 

                // 이미지에 비트맵 연결

                writeBitmap = new WriteableBitmap(capture.FrameWidth,

                                          capture.FrameHeight, 96, 96, PixelFormats.Bgr24, null);

                camDisplay.Source = writeBitmap;

            }

            catch(Exception e)

            {

                if (timer != null)

                {

                    timer.Stop();

                }

 

                if (capture != null)

                {

                    capture.Dispose();

                    capture = null;

                }

                MessageBox.Show(e.ToString());

            }

        }

 

        private void SetTimer()

        {

            timer = new DispatcherTimer();

            timer.Interval = new TimeSpan(0, 0, 0, 0, 33);

            timer.Tick += new EventHandler(TimerClock_Tick);

        }

 

        void TimerClock_Tick(object sender, EventArgs e)

        {

            // 카메라의 프레임을 비트맵으로 변환

            using (src = capture.QueryFrame())

            {

                WriteableBitmapConverter.ToWriteableBitmap(src, writeBitmap);

            }

        }

 

        private void btnStart_Click(object sender, RoutedEventArgs e)

        {

            if (capture != null && !timer.IsEnabled)

            {

                timer.IsEnabled = true;

                timer.Start();

            }

        }

 

        private void btnStop_Click(object sender, RoutedEventArgs e)

        {

            if (capture != null)

            {

                timer.IsEnabled = false;

                timer.Stop();

            }

        }

 

        private void camList_SelectionChanged(object sender,   

              System.Windows.Controls.SelectionChangedEventArgs e)

        {

            initCamera(camList.SelectedIndex);

        }

    }

 

[ 결과 화면 ]

노트북이라 화질이 정말 안 좋군요. -_- ;;

 

필요한 사항들은 다음과 같습니다. (저는 모두 1.1 버전을 사용하였습니다. )

 

- OpenCV : http://sourceforge.net/projects/opencvlibrary/files/ 

- OpenCvShrap : http://cafe.naver.com/opencvsharp/481

 

캡쳐보드를 사용하는 경우 화면이 멈추는 현상이 발생할 수 있습니다.

    그럴 땐 캡쳐보드의 Deinterlace 속성을 변경시키거나 다른 옵션들을 보셔야 합니다.

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

[ WPF ] BackgroundWorker  (2) 2010.07.11
[ WPF ] 윈도우 이벤트  (4) 2010.07.11
[ WPF ] 이미지 그림판  (2) 2010.05.08
[ WPF ] UI 쓰레드 변경하기  (0) 2010.05.04
[ WPF ] WPF in Flash  (0) 2010.04.29
Posted by 열ㅇl
Window/WPF2010. 5. 8. 18:13

이미지를 불러와 간단한 꾸미기를 하고 저장하는 예제에 대해 포스팅하겠습니다.

예전 해당 영역을 이미지로 저장하는 포스팅의 내용을 약간 수정한 내용입니다.

 

간략적으로,

InkCanvas를 사용해 그림을 그리고 DrawingVisualDrawingContext를 통해

RenderTargetBitmap으로 변환 후 이를 BitmapEncoder를 사용해 해당 포맷에 맞게 저장합니다.

 

InkCanvas를 이미지로 변환 후 저장하기 때문에

꾸미기 용도에 맞게 자식 객체로 여러가지를 포함시킨 후 저장하셔도 되겠죠.^^

 

이제, 소스를 통해 하나씩 알아보겠습니다.

 

1. 이미지를 InkCanvas의 Background로 지정합니다.

// 이미지 불러오기

private void btn_Open(object sender, RoutedEventArgs e)

{

    OpenFileDialog openDialog = new OpenFileDialog();

 

    if (openDialog.ShowDialog() == true)

    {

        if (File.Exists(openDialog.FileName))

        {

            BitmapImage bitmapImage = new BitmapImage(new Uri(openDialog.FileName,

                UriKind.RelativeOrAbsolute));

 

            // InkCanvas의 배경으로 지정

            inkCanvas.Background = new ImageBrush(bitmapImage);

        }

    }

}

 

2. 해당 객체(InkCanvas)를 비트맵으로 변환합니다.

 // 해당 객체를 이미지로 변환

private static RenderTargetBitmap ConverterBitmapImage(FrameworkElement element)

{

    DrawingVisual drawingVisual = new DrawingVisual();

    DrawingContext drawingContext = drawingVisual.RenderOpen();

 

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

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

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

 

    drawingContext.Close();

 

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

    RenderTargetBitmap target =

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

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

 

    target.Render(drawingVisual);

    return target;

}

 

3. 변환된 이미지를 해당 포맷에 맞게 저장합니다.

 // 해당 이미지 저장

private static void ImageSave(BitmapSource source)

{

    SaveFileDialog saveDialog = new SaveFileDialog();

 

    // 이미지 포맷들

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

    saveDialog.AddExtension = true;

 

    if (saveDialog.ShowDialog() == true)

    {

        BitmapEncoder encoder =  null;

        // 파일 생성

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

 

        // 파일 포맷

        string upper = saveDialog.SafeFileName.ToUpper();

        char[] format = upper.ToCharArray(saveDialog.SafeFileName.Length - 3, 3);

        upper = new string(format);

 

        // 해당 포맷에 맞게 인코더 생성

        switch (upper.ToString())

        {

            case "PNG":

                encoder = new PngBitmapEncoder();

                break;

 

            case "JPG":

                encoder = new JpegBitmapEncoder();

                break;

 

            case "GIF":

                encoder = new GifBitmapEncoder();

                break;

 

            case "BMP":

                encoder = new BmpBitmapEncoder();

                break;

        }

        // 인코더 프레임에 이미지 추가

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

        // 파일에 저장

        encoder.Save(stream);

        stream.Close();

    }

}

 

 

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

 

1. 이미지를 불러옵니다.

        

 

2. 간단히 꾸미기를 하고 저장합니다.

      

 

3. 저장된 이미지

 

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

[ WPF ] 윈도우 이벤트  (4) 2010.07.11
[ WPF ] OpenCV로 캠 화면 띄우기  (3) 2010.06.20
[ WPF ] UI 쓰레드 변경하기  (0) 2010.05.04
[ WPF ] WPF in Flash  (0) 2010.04.29
[ WPF ] 클래스 구조  (0) 2010.04.27
Posted by 열ㅇl
Window/WPF2010. 5. 4. 11:00

WPF에는  UI쓰레드와 비 UI 쓰레드로 구분되어 집니다.

UI 쓰레드는 UI 객체를 수정 및 변경할 수 있는 유일한 메인 쓰레드 입니다.

 

이와 반대로 사용자가 생성하는 쓰레드는 비 UI 쓰레드로

이 쓰레드 안에서 UI 객체를 수정 및 변경하게 되면 InvalidOperationException 발생하게 됩니다.

 

프로그래밍 도중 사용자가 생성한 쓰레드 내에서 UI 객체의 상태를 변경해야 할 때가 있는데요.

이때 사용하는 클래스가 Dispatcher 입니다.

 

간단한 사용법은 다음과 같습니다.

 

 this.Dispatcher.BeginInvoke(DispatcherPriority.Normal, (ThreadStart)delegate()

 {

     // 변경할 내용

 }

 );

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

[ WPF ] OpenCV로 캠 화면 띄우기  (3) 2010.06.20
[ WPF ] 이미지 그림판  (2) 2010.05.08
[ WPF ] WPF in Flash  (0) 2010.04.29
[ WPF ] 클래스 구조  (0) 2010.04.27
[ WPF ] Flash in WPF  (3) 2010.04.23
Posted by 열ㅇl