CHOU

[MSRDS] CCR에서 WinForm 이용해 보기 - 3 본문

Tech/Microsoft Products

[MSRDS] CCR에서 WinForm 이용해 보기 - 3

chobabo 2009. 4. 4. 16:53


 CCR 에서 WinForm들 간의 이벤트를 제어하는 방법을 알아보겠습니다. 우선 아래 그림과 같이 WinForm을 생성 한 후, 각각의 WinForm에서 발생하는 이벤트들을 중간의 CCR Program을 통해서 상호 중계해주는 예제를 알아보겠습니다.


그림1. 예제 다이어그램(출처: 네이버 MSRDS 카페)
 
 메인 폼에서는 각각의 WinForm으로 부터 메세지를 받는 포트들을 정의해 놓고, 이러한 포트들에 메시지가 등록될 경우 실행되는 핸들러를 정의 합니다. 이 핸들러에서는 다시 각각 WinForm 들의 메소드를 호출하는 명령어를 기술해 놓아서 수신받은 다른 폼에 전달되도록 합니다. 즉, 별도의 WinForm으로 부터 데이터를 받을 때에는 포트를 통해서 받고, 이 포트에 연결된 핸들러안에서 FormInvoke 메소드를 이용하여 다른 폼의 메소드를 호출함으로서 다른 폼에 데이터를 전달하게 됩니다.

 이번 예제 역시 네이버 MSRDS 공식카페에 김영준 수석님께서 올려주신 자료를 실행해보고 이해해 보겠습니다. 간단히 FormA와 FormB 두개의 WinForm을 생성한 후, 아무 폼이나 한 군데에서 마우스로 그림을 그리면 다른 폼에도 동일하게 그림이 그려지고, 메신저처럼 해당 문자열을 보내면 다른폼에도 보이게 하는 기능을 연습해 보겠습니다.

1. 우선 Winform 예제를 만들기 위해 C# 프로젝트를 생성하고, 아래 화면과 같이 레퍼런스 추가를 선택해서 .NET 부분에  Microsoft.Ccr.Core.dll, Microsoft.Ccr.Adapters.Winforms.dll을 선택해서 추가해 주고, 코드 상단에 using 문장을 추가해 줍니다.


그림2. 레퍼런스 추가화면


그림3. 코드 상단 Using 추가화면

2. 폼에 버튼 한개를 추가해 주고 이벤트 코드 안에 아래와 같은 코드를 입력해 줍니다.

        private void button1_Click(object sender, EventArgs e)
        {
            if (dispatcher == null)
                dispatcher = new Dispatcher(0, System.Threading.ThreadPriority.Lowest, true, "12번째 예제");

            if (dq == null)
                dq = new DispatcherQueue("DispatcherQueue", dispatcher);

            if (_communicatePortSet_A == null)
                _communicatePortSet_A = new CommunicatePortSet_A();

            if (_communicatePortSet_B == null)
                _communicatePortSet_B = new CommunicatePortSet_B();

            //Define ReceiverGroup and Handler for each Port           
            ITask myTask = Arbiter.Interleave(
                new TeardownReceiverGroup(
                    ),
                new ExclusiveReceiverGroup(
                    Arbiter.ReceiveWithIterator<OnLoad_A>(true, _communicatePortSet_A, OnLoadHandler_A),
                    Arbiter.ReceiveWithIterator<OnLoad_B>(true, _communicatePortSet_B, OnLoadHandler_B),
                    Arbiter.ReceiveWithIterator<OnClose_A>(true, _communicatePortSet_A, OnCloseHandler_A),
                    Arbiter.ReceiveWithIterator<OnClose_B>(true, _communicatePortSet_B, OnCloseHandler_B)
                    ),
                new ConcurrentReceiverGroup(
                    Arbiter.ReceiveWithIterator<OnDraw_A>(true, _communicatePortSet_A, OnDrawHandler_A),
                    Arbiter.ReceiveWithIterator<OnDraw_B>(true, _communicatePortSet_B, OnDrawHandler_B),
                    Arbiter.ReceiveWithIterator<OnReceiveText_A>(true, _communicatePortSet_A, OnReceiveTextHandler_A),
                    Arbiter.ReceiveWithIterator<OnReceiveText_B>(true, _communicatePortSet_B, OnReceiveTextHandler_B)
                    )
                );

            Arbiter.Activate(dq, myTask);


            //Define WinFormsServicePort
            if (myWinFormPort == null)
                myWinFormPort = WinFormsAdapter.Create(dq);

            //Define FormA Constructor
            RunForm runFormA = new RunForm(
                            delegate()
                            {
                                return new FormA(_communicatePortSet_A);
                            }
                        );

            //Construct form through Post method
            myWinFormPort.Post(runFormA);

            //Define FormB Constructor
            RunForm runFormB = new RunForm(
                            delegate()
                            {
                                return new FormB(_communicatePortSet_B);
                            }
                        );

            //Construct form through Post method
            myWinFormPort.Post(runFormB);           
        }

버튼 이벤트를 입력 하셨다면 아래와 같은 코드를 이용하여 핸들러에 대한 정의를 해주면 됩니다.

        FormA _formA;
        FormB _formB;

        CommunicatePortSet_A _communicatePortSet_A;
        CommunicatePortSet_B _communicatePortSet_B;

        Dispatcher dispatcher;
        DispatcherQueue dq;

        WinFormsServicePort myWinFormPort;

        IEnumerator<ITask> OnLoadHandler_A(OnLoad_A onLoad)
        {
            _formA = onLoad.FormA;

            yield break;
        }

        IEnumerator<ITask> OnLoadHandler_B(OnLoad_B onLoad)
        {
            _formB = onLoad.FormB;

            yield break;
        }

        IEnumerator<ITask> OnCloseHandler_A(OnClose_A onClose)
        {
            _formA = onClose.FormA;

            //Dispose All global objects
            _formA = null;
            _formB = null;
            _communicatePortSet_A = null;
            _communicatePortSet_B = null;
            myWinFormPort = null;
            dq = null;
            dispatcher = null;

            yield break;
        }

        IEnumerator<ITask> OnCloseHandler_B(OnClose_B onClose)
        {
            _formB = onClose.FormB;

            //Dispose All global objects
            _formA = null;
            _formB = null;
            _communicatePortSet_A = null;
            _communicatePortSet_B = null;
            myWinFormPort = null;
            dq = null;
            dispatcher = null;

            yield break;
        }


        IEnumerator<ITask> OnDrawHandler_A(OnDraw_A onDraw)
        {
            //Send Draw Message
            myWinFormPort.FormInvoke(
                    delegate()
                    {
                        _formB.DrawImage(onDraw.X, onDraw.Y);
                    }
                );

            yield break;
        }

        IEnumerator<ITask> OnDrawHandler_B(OnDraw_B onDraw)
        {
            //Send Draw Message
            myWinFormPort.FormInvoke(
                    delegate()
                    {
                        _formA.DrawImage(onDraw.X, onDraw.Y);
                    }
                );

            yield break;
        }

        IEnumerator<ITask> OnReceiveTextHandler_A(OnReceiveText_A onReceiveText)
        {
            //Send Message
            myWinFormPort.FormInvoke(
                    delegate()
                    {
                        _formB.AddMessage("From A: " + onReceiveText.Text);
                    }
                );

            yield break;
        }

        IEnumerator<ITask> OnReceiveTextHandler_B(OnReceiveText_B onReceiveText)
        {
            //Send Message
            myWinFormPort.FormInvoke(
                    delegate()
                    {
                        _formA.AddMessage("From B: " + onReceiveText.Text);
                    }
                );

            yield break;
        }

3. 이제는 폼2개를 생성하여 FormA, FormB 로 지정하고 아래와 같은 인터페이스 구성을 합니다.


그림3. FormA, FormB 인터페이스 구성화면

그리고 아래의 코드를 FormA, FormB에 추가해 줍니다.

FormA
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using Microsoft.Ccr.Core;

namespace CCRSample12
{
    partial class FormA : Form
    {
        CommunicatePortSet_A _communicatePortSet_A;

        //Flag for Mouse down, up
        bool _mouseDown = false;

        public FormA(CommunicatePortSet_A communicatePortSet_A)
        {
            _communicatePortSet_A = communicatePortSet_A;

            InitializeComponent();
        }

        public void AddMessage(String msg)
        {
            txtReceived.AppendText(msg+"\r\n");
        }

        //This method is called from MainForm
        public void DrawImage(int x, int y)
        {
            //Draw Line
            Graphics g = pictureBox1.CreateGraphics();
            g.FillEllipse(new SolidBrush(Color.Blue), new Rectangle(x, y, 3, 3));
        }

        //When the form is loaded, Post this form into the Port<OnLoad_A>
        private void FormA_Load(object sender, EventArgs e)
        {
            _communicatePortSet_A.Post(new OnLoad_A(this));
        }

        private void btnClose_Click(object sender, EventArgs e)
        {
            _communicatePortSet_A.Post(new OnClose_A(this));

            System.Threading.Thread.CurrentThread.Abort();
            this.Close();
        }

        private void btnSend_Click(object sender, EventArgs e)
        {
            _communicatePortSet_A.Post(new OnReceiveText_A(this, txtSending.Text));
        }

        private void pictureBox1_MouseDown(object sender, MouseEventArgs e)
        {
            _mouseDown = true;
        }

        private void pictureBox1_MouseUp(object sender, MouseEventArgs e)
        {
            _mouseDown = false;
        }

        private void pictureBox1_MouseMove(object sender, MouseEventArgs e)
        {
            if (_mouseDown)
            {               
                //Draw Line
                Graphics g = pictureBox1.CreateGraphics();
                g.FillEllipse(new SolidBrush(Color.Red), new Rectangle(e.X, e.Y, 3, 3));
               
                //Send Point to the Port
                _communicatePortSet_A.Post(new OnDraw_A(this, e.X, e.Y));
            }
        }
    }

    class CommunicatePortSet_A : PortSet<OnLoad_A, OnClose_A, OnDraw_A, OnReceiveText_A>
    {
    }

    class CommunicateBaseClass_A
    {
        private FormA _formA;

        public FormA FormA
        {
            get { return _formA; }
            set { _formA = value; }
        }

        public CommunicateBaseClass_A(FormA formA)
        {
            _formA = formA;
        }
    }

    class OnLoad_A : CommunicateBaseClass_A
    {
        public OnLoad_A(FormA form) : base(form)
        {
        }
    }

    class OnClose_A : CommunicateBaseClass_A
    {
        public OnClose_A(FormA form)
            : base(form)
        {
        }
    }

    class OnDraw_A : CommunicateBaseClass_A
    {
        int _x;

        public int X
        {
            get { return _x; }
            set { _x = value; }
        }

        int _y;

        public int Y
        {
            get { return _y; }
            set { _y = value; }
        }

        public OnDraw_A(FormA form, int x, int y) : base(form)
        {
            _x = x;
            _y = y;
        }
    }

    class OnReceiveText_A : CommunicateBaseClass_A
    {
        string _text;

        public string Text
        {
            get { return _text; }
            set { _text = value; }
        }

        public OnReceiveText_A(FormA form, string text) : base(form)
        {
            _text = text;
        }
    }
}



FormB
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using Microsoft.Ccr.Core;

namespace CCRSample12
{
    partial class FormB : Form
    {
        CommunicatePortSet_B _communicatePortSet_B;

        //Flag for Mouse down, up
        bool _mouseDown = false;

        public FormB(CommunicatePortSet_B communicatePortSet_B)
        {
            _communicatePortSet_B = communicatePortSet_B;

            InitializeComponent();
        }

        public void AddMessage(String msg)
        {
            txtReceived.AppendText(msg + "\r\n");
        }

        //This method is called from MainForm
        public void DrawImage(int x, int y)
        {
            //Draw Line
            Graphics g = pictureBox1.CreateGraphics();
            g.FillEllipse(new SolidBrush(Color.Blue), new Rectangle(x, y, 3, 3));
        }

        private void FormB_Load(object sender, EventArgs e)
        {
            _communicatePortSet_B.Post(new OnLoad_B(this));
        }

        private void btnClose_Click(object sender, EventArgs e)
        {
            _communicatePortSet_B.Post(new OnClose_B(this));

            System.Threading.Thread.CurrentThread.Abort();
            this.Close();
        }

        private void btnSend_Click(object sender, EventArgs e)
        {
            _communicatePortSet_B.Post(new OnReceiveText_B(this, txtSending.Text));
        }

        private void pictureBox1_MouseDown(object sender, MouseEventArgs e)
        {
            _mouseDown = true;
        }

        private void pictureBox1_MouseUp(object sender, MouseEventArgs e)
        {
            _mouseDown = false;
        }

        private void pictureBox1_MouseMove(object sender, MouseEventArgs e)
        {
            if (_mouseDown)
            {
                //Draw Line
                Graphics g = pictureBox1.CreateGraphics();
                g.FillEllipse(new SolidBrush(Color.Red), new Rectangle(e.X, e.Y, 3, 3));

                //Send Point to the Port
                _communicatePortSet_B.Post(new OnDraw_B(this, e.X, e.Y));
            }
        }
    }

    class CommunicatePortSet_B : PortSet<OnLoad_B, OnClose_B, OnDraw_B, OnReceiveText_B>
    {
    }

    class CommunicateBaseClass_B
    {
        private FormB _formB;

        public FormB FormB
        {
            get { return _formB; }
            set { _formB = value; }
        }

        public CommunicateBaseClass_B(FormB formB)
        {
            _formB = formB;
        }
    }

    class OnLoad_B : CommunicateBaseClass_B
    {
        public OnLoad_B(FormB form)
            : base(form)
        {
        }
    }

    class OnClose_B : CommunicateBaseClass_B
    {
        public OnClose_B(FormB form)
            : base(form)
        {
        }
    }

    class OnDraw_B : CommunicateBaseClass_B
    {
        int _x;

        public int X
        {
            get { return _x; }
            set { _x = value; }
        }

        int _y;

        public int Y
        {
            get { return _y; }
            set { _y = value; }
        }

        public OnDraw_B(FormB form, int x, int y)
            : base(form)
        {
            _x = x;
            _y = y;
        }
    }

    class OnReceiveText_B : CommunicateBaseClass_B
    {
        string _text;

        public string Text
        {
            get { return _text; }
            set { _text = value; }
        }

        public OnReceiveText_B(FormB form, string text)
            : base(form)
        {
            _text = text;
        }
    }
}

올바르게 코드를 삽입하여 실행하면 아래와 같은 영상의 결과를 볼 수 있습니다. FormA, FormB는 간단한 채팅화면과 자신이 그린 이미지를 상대방이 볼 수 있는 기능을 가지고 있습니다.


영상1. 결과 화면영상

 김영준 수석님께서는 이번 예제까지 올바르게 습득을 하게되면 간단한 로봇 조종기 또는 로봇 시뮬레이션을 할 수 있다 했기 때문에 이번 예제를 수행하고 다음에는 간단한 로봇 시뮬레이션 UI를 만들어 보겠습니다.


참고자료

1. http://cafe.naver.com/msrskorea