첨부 실행 코드는 나눔고딕코딩 폰트를 사용합니다.
본 블로그는 광고를 포함하고 있습니다.
광고 클릭에서 발생하는 수익금은 모두 블로그 콘텐츠 향상을 위해 쓰여집니다.

728x90
반응형
728x170

TestProject.zip
다운로드

[TestLibrary 프로젝트]

▶ IFormAdapter.cs

using System;
using System.Drawing;

namespace TestLibrary
{
    /// <summary>
    /// 폼 어댑터 인터페이스
    /// </summary>
    public interface IFormAdapter
    {
        //////////////////////////////////////////////////////////////////////////////////////////////////// Property

        #region 핸들 - Handle

        /// <summary>
        /// 핸들
        /// </summary>
        IntPtr Handle { get; }

        #endregion
        #region 테두리 사각형 - BoundRectangle

        /// <summary>
        /// 테두리 사각형
        /// </summary>
        Rectangle BoundRectangle { get; set; }

        #endregion
        #region 최대 크기 - MaximumSize

        /// <summary>
        /// 최대 크기
        /// </summary>
        Size MaximumSize { get; set; }

        #endregion
        #region 최소 크기 - MinimumSize

        /// <summary>
        /// 최소 크기
        /// </summary>
        Size MinimumSize { get; set; }

        #endregion
        #region 캡처 여부 - Capture

        /// <summary>
        /// 캡처 여부
        /// </summary>
        bool Capture { get; set; }

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Method

        #region 활성화 하기 - Activate()

        /// <summary>
        /// 활성화 하기
        /// </summary>
        void Activate();

        #endregion
        #region 화면 좌표계 구하기 - PointToScreen(point)

        /// <summary>
        /// 화면 좌표계 구하기
        /// </summary>
        /// <param name="point">포인트</param>
        /// <returns>화면 좌표</returns>
        Point PointToScreen(Point point);

        #endregion
    }
}

 

728x90

 

▶ WPFFormAdapter.cs

using System;
using System.Drawing;
using System.Windows;
using System.Windows.Input;
using System.Windows.Interop;
using System.Windows.Media;

namespace TestLibrary
{
    /// <summary>
    /// WPF 폼 어댑터
    /// </summary>
    public class WPFFormAdapter : IFormAdapter
    {
        //////////////////////////////////////////////////////////////////////////////////////////////////// Field
        ////////////////////////////////////////////////////////////////////////////////////////// Private

        #region Field

        /// <summary>
        /// 윈도우
        /// </summary>
        private readonly Window window;

        /// <summary>
        /// 원점
        /// </summary>
        private System.Drawing.Point? originPoint;

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Property
        ////////////////////////////////////////////////////////////////////////////////////////// Public

        #region 핸들 - Handle

        /// <summary>
        /// 핸들
        /// </summary>
        public IntPtr Handle
        {
            get
            {
                return new WindowInteropHelper(this.window).Handle;
            }
        }

        #endregion
        #region 테두리 사각형 - BoundRectangle

        /// <summary>
        /// 테두리 사각형
        /// </summary>
        public Rectangle BoundRectangle
        {
            get
            {
                System.Windows.Point wpfDevicePoint = GetWPFDevicePoint(this.window.ActualWidth, this.window.ActualHeight, this.window);

                System.Drawing.Point winFormOriginScreenPoint = GetWindowOriginScreenPoint();

                System.Drawing.Point winFormStartPoint = this.PointToScreen(winFormOriginScreenPoint);

                System.Drawing.Point winFormEndPoint = this.PointToScreen
                (
                    winFormOriginScreenPoint + new System.Drawing.Size
                    (
                        Convert.ToInt32(wpfDevicePoint.X),
                        Convert.ToInt32(wpfDevicePoint.Y)
                    )
                );

                winFormEndPoint.Offset(-winFormStartPoint.X, -winFormStartPoint.Y);

                return new Rectangle(winFormStartPoint.X, winFormStartPoint.Y, winFormEndPoint.X, winFormEndPoint.Y);
            }
            set
            {
                System.Windows.Point wpfRelativePoint = GetWPFRelativePoint(value.Width, value.Height, this.window);

                System.Drawing.Point winFormOriginScreenPoint = GetWindowOriginScreenPoint();

                System.Drawing.Point winFormScreenPoint = new System.Drawing.Point(-winFormOriginScreenPoint.X + value.X, -winFormOriginScreenPoint.Y + value.Y);

                System.Drawing.Point winFormStartPoint = this.PointFromScreen(new System.Drawing.Point(winFormScreenPoint.X, winFormScreenPoint.Y));

                this.window.Left += winFormStartPoint.X;
                this.window.Top  += winFormStartPoint.Y;

                this.window.Width  = wpfRelativePoint.X;
                this.window.Height = wpfRelativePoint.Y;
            }
        }

        #endregion
        #region 최대 크기 - MaximumSize

        /// <summary>
        /// 최대 크기
        /// </summary>
        public System.Drawing.Size MaximumSize
        {
            get
            {
                return new System.Drawing.Size
                (
                    Convert.ToInt32(this.window.MaxWidth),
                    Convert.ToInt32(this.window.MaxHeight)
                );
            }
            set
            { 
                this.window.MaxWidth  = value.Width;
                this.window.MaxHeight = value.Height;
            }
        }

        #endregion
        #region 최소 크기 - MinimumSize

        /// <summary>
        /// 최소 크기
        /// </summary>
        public System.Drawing.Size MinimumSize
        {
            get
            {
                return new System.Drawing.Size
                (
                    Convert.ToInt32(this.window.MinWidth),
                    Convert.ToInt32(this.window.MinHeight)
                );
            }
            set
            {
                this.window.MinWidth  = value.Width;
                this.window.MinHeight = value.Height;
            }
        }

        #endregion
        #region 캡처 여부 - Capture

        /// <summary>
        /// 캡처 여부
        /// </summary>
        public bool Capture
        {
            get
            {
                return this.window.IsMouseCaptured;
            }
            set
            {
                IInputElement inputElement = value ? this.window : null;

                Mouse.Capture(inputElement);
            }
        }

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Constructor
        ////////////////////////////////////////////////////////////////////////////////////////// Public

        #region 생성자 - WPFFormAdapter(window)

        /// <summary>
        /// 생성자
        /// </summary>
        /// <param name="window">윈도우</param>
        public WPFFormAdapter(Window window)
        {
            this.window = window;
        }

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Method
        ////////////////////////////////////////////////////////////////////////////////////////// Public

        #region 활성화 하기 - Activate()

        /// <summary>
        /// 활성화 하기
        /// </summary>
        public void Activate()
        {
            this.window.Activate();
        }

        #endregion

        #region 화면 좌표 구하기 - PointToScreen(winFormPoint)

        /// <summary>
        /// 화면 좌표 구하기
        /// </summary>
        /// <param name="winFormPoint">WinForm 포인트</param>
        /// <returns>화면 좌표</returns>
        public System.Drawing.Point PointToScreen(System.Drawing.Point winFormPoint)
        {
            System.Windows.Point wpfOriginPoint = new System.Windows.Point();

            System.Drawing.Point winFormOriginScreenPoint = GetWinFormPoint(window.PointToScreen(wpfOriginPoint));

            System.Drawing.Point winFormScreenPoint = winFormOriginScreenPoint + new System.Drawing.Size(winFormPoint);

            return winFormScreenPoint;
        }

        #endregion
        #region 상대 좌표 구하기 - PointFromScreen(winFormScreenPoint)

        /// <summary>
        /// 상대 좌표 구하기
        /// </summary>
        /// <param name="winFormScreenPoint">WinForm 화면 포인트</param>
        /// <returns>상대 좌표</returns>
        public System.Drawing.Point PointFromScreen(System.Drawing.Point winFormScreenPoint)
        {
            return GetWinFormPoint(window.PointFromScreen(GetWPFPoint(winFormScreenPoint)));
        }

        #endregion

        ////////////////////////////////////////////////////////////////////////////////////////// Private

        #region WinForm 포인트 구하기 - GetWinFormPoint(wpfPoint)

        /// <summary>
        /// WinForm 포인트 구하기
        /// </summary>
        /// <param name="wpfPoint">WPF 포인트</param>
        /// <returns>WinForm 포인트</returns>
        private static System.Drawing.Point GetWinFormPoint(System.Windows.Point wpfPoint)
        {
            return new System.Drawing.Point(Convert.ToInt32(wpfPoint.X), Convert.ToInt32(wpfPoint.Y));
        }

        #endregion
        #region WPF 포인트 구하기 - GetWPFPoint(winFormPoint)

        /// <summary>
        /// WPF 포인트 구하기
        /// </summary>
        /// <param name="winFormPoint">WinForm 포인트</param>
        /// <returns>WPF 포인트</returns>
        private static System.Windows.Point GetWPFPoint(System.Drawing.Point winFormPoint)
        {
            return new System.Windows.Point(winFormPoint.X, winFormPoint.Y);
        }

        #endregion

        #region WPF 디바이스 포인트 구하기 - GetWPFDevicePoint(relativeX, relativeY, sourceVisual)

        /// <summary>
        /// WPF 디바이스 포인트 구하기
        /// </summary>
        /// <param name="relativeX">상대 X 좌표</param>
        /// <param name="relativeY">상대 Y 좌표</param>
        /// <param name="sourceVisual">소스 비주얼</param>
        /// <returns>WPF 디바이스 포인트</returns>
        private static System.Windows.Point GetWPFDevicePoint(double relativeX, double relativeY, Visual sourceVisual)
        {
            System.Drawing.Point winFormPoint = new System.Drawing.Point(Convert.ToInt32(relativeX), Convert.ToInt32(relativeY));

            PresentationSource source = PresentationSource.FromVisual(sourceVisual);

            return source.CompositionTarget.TransformToDevice.Transform(GetWPFPoint(winFormPoint));
        }

        #endregion
        #region WPF 상대 포인트 구하기 - GetWPFRelativePoint(deviceX, deviceY, sourceVisual)

        /// <summary>
        /// WPF 상대 포인트 구하기
        /// </summary>
        /// <param name="deviceX">장치 X 좌표</param>
        /// <param name="deviceY">장치 Y 좌표</param>
        /// <param name="sourceVisual">소스 비주얼</param>
        /// <returns>WPF 상대 포인트</returns>
        private static System.Windows.Point GetWPFRelativePoint(double deviceX, double deviceY, Visual sourceVisual)
        {
            System.Drawing.Point winFormPoint = new System.Drawing.Point(Convert.ToInt32(deviceX), Convert.ToInt32(deviceY));

            PresentationSource source = PresentationSource.FromVisual(sourceVisual);

            return source.CompositionTarget.TransformFromDevice.Transform(GetWPFPoint(winFormPoint));
        }

        #endregion

        #region 윈도우 원점 화면 포인트 구하기 - GetWindowOriginScreenPoint()

        /// <summary>
        /// 윈도우 원점 화면 포인트 구하기
        /// </summary>
        /// <returns>윈도우 원점 화면 포인트</returns>
        private System.Drawing.Point GetWindowOriginScreenPoint()
        {
            if(!this.originPoint.HasValue)
            {
                System.Windows.Point wpfPoint = GetWPFDevicePoint(-window.Left, -window.Top, window);

                System.Drawing.Point screenPoint = PointToScreen(GetWinFormPoint(wpfPoint));

                this.originPoint = new System.Drawing.Point(-screenPoint.X, -screenPoint.Y);
            }

            return this.originPoint.Value;
        }

        #endregion
    }
}

 

300x250

 

▶ WinFormAdapter.cs

using System;
using System.Drawing;
using System.Windows.Forms;

namespace TestLibrary
{
    /// <summary>
    /// 윈폼 어댑터
    /// </summary>
    public class WinFormAdapter : IFormAdapter
    {
        //////////////////////////////////////////////////////////////////////////////////////////////////// Field
        ////////////////////////////////////////////////////////////////////////////////////////// Private

        #region Field

        /// <summary>
        /// 폼
        /// </summary>
        private readonly Form form;

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Property
        ////////////////////////////////////////////////////////////////////////////////////////// Public

        #region (IFormAdapter) 핸들 - Handle

        /// <summary>
        /// 핸들
        /// </summary>
        public IntPtr Handle
        {
            get
            {
                return this.form.Handle;
            }
        }

        #endregion
        #region (IFormAdapter) 테두리 사각형 - BoundRectangle

        /// <summary>
        /// 테두리 사각형
        /// </summary>
        public Rectangle BoundRectangle
        {
            get
            {
                return this.form.Bounds;
            }
            set
            {
                this.form.Bounds = value;
            }
        }

        #endregion
        #region (IFormAdapter) 최대 크기 - MaximumSize

        /// <summary>
        /// 최대 크기
        /// </summary>
        public Size MaximumSize
        {
            get
            {
                return this.form.MaximumSize;
            }
            set
            {
                this.form.MaximumSize = value;
            }
        }

        #endregion
        #region (IFormAdapter) 최소 크기 - MinimumSize

        /// <summary>
        /// 최소 크기
        /// </summary>
        public Size MinimumSize
        {
            get
            {
                return this.form.MinimumSize;
            }
            set
            {
                this.form.MinimumSize = value;
            }
        }

        #endregion
        #region (IFormAdapter) 캡처 여부 - Capture

        /// <summary>
        /// 캡처 여부
        /// </summary>
        public bool Capture
        {
            get
            {
                return this.form.Capture;
            }
            set
            {
                this.form.Capture = value;
            }
        }

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Constructor
        ////////////////////////////////////////////////////////////////////////////////////////// Public

        #region 생성자 - WinFormAdapter(form)

        /// <summary>
        /// 생성자
        /// </summary>
        /// <param name="form">폼</param>
        public WinFormAdapter(Form form)
        {
            this.form = form;
        }

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Method
        ////////////////////////////////////////////////////////////////////////////////////////// Public

        #region (IFormAdapter) 활성화 하기 - Activate()

        /// <summary>
        /// 활성화 하기
        /// </summary>
        public void Activate()
        {
            this.form.Activate();
        }

        #endregion
        #region (IFormAdapter) 화면 좌표 구하기 - PointToScreen(point)

        /// <summary>
        /// 화면 좌표 구하기
        /// </summary>
        /// <param name="point">포인트</param>
        /// <returns>화면 좌표</returns>
        public Point PointToScreen(Point point)
        {
            return form.PointToScreen(point);
        }

        #endregion
    }
}

 

▶ StickyWindow.cs

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;

namespace TestLibrary
{
    /// <summary>
    /// 접착 윈도우
    /// </summary>
    public class StickyWindow : NativeWindow
    {
        //////////////////////////////////////////////////////////////////////////////////////////////////// Delegate
        ////////////////////////////////////////////////////////////////////////////////////////// Private

        #region 메시지 처리하기 대리자 - ProcessMessageDelegate(message)

        /// <summary>
        /// 메시지 처리하기 대리자
        /// </summary>
        /// <param name="message">메시지</param>
        /// <returns>처리 결과</returns>
        private delegate bool ProcessMessageDelegate(ref Message message);

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Enumeration
        ////////////////////////////////////////////////////////////////////////////////////////// Private

        #region 크기 변경 방향 - ResizeDirection

        /// <summary>
        /// 크기 변경 방향
        /// </summary>
        private enum ResizeDirection
        {
            /// <summary>
            /// 위쪽
            /// </summary>
            Top = 2,

            /// <summary>
            /// 아래쪽
            /// </summary>
            Bottom = 4,

            /// <summary>
            /// 왼쪽
            /// </summary>
            Left = 8,

            /// <summary>
            /// 오른쪽
            /// </summary>
            Right = 16
        };

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Field
        ////////////////////////////////////////////////////////////////////////////////////////// Static
        //////////////////////////////////////////////////////////////////////////////// Private

        #region Field

        /// <summary>
        /// 폼 어댑터 리스트
        /// </summary>
        private static List<IFormAdapter> _formAdapterList = new List<IFormAdapter>();

        /// <summary>
        /// 접착 갭
        /// </summary>
        private static int _stickGap = 20;

        #endregion

        ////////////////////////////////////////////////////////////////////////////////////////// Instance
        //////////////////////////////////////////////////////////////////////////////// Private

        #region Field

        /// <summary>
        /// 메시지 처리하기 대리자
        /// </summary>
        private ProcessMessageDelegate processMessageDelegate;

        /// <summary>
        /// 디폴트 메시지 처리하기 대리자
        /// </summary>
        private ProcessMessageDelegate defaultProcessMessageDelegate;

        /// <summary>
        /// 이동 메시지 처리하기 대리자
        /// </summary>
        private ProcessMessageDelegate moveProcessMessageDelegate;

        /// <summary>
        /// 크기 변경 메시지 처리하기 대리자
        /// </summary>
        private ProcessMessageDelegate resizeProcessMessageDelegate;


        /// <summary>
        /// 폼 이동 여부
        /// </summary>
        private bool movingForm;

        /// <summary>
        /// 폼 오프셋 포인트
        /// </summary>
        private Point formOffsetPoint;

        /// <summary>
        /// 오프셋 포인트
        /// </summary>
        private Point offsetPoint;


        /// <summary>
        /// 폼 크기 변경 여부
        /// </summary>
        private bool resizingForm;

        /// <summary>
        /// 크기 변경 방향
        /// </summary>
        private ResizeDirection resizeDirection;

        /// <summary>
        /// 폼 오프셋 사각형
        /// </summary>
        private Rectangle formOffsetRectangle;

        /// <summary>
        /// 마우스 포인트
        /// </summary>
        private Point mousePoint;


        /// <summary>
        /// 원본 폼
        /// </summary>
        private IFormAdapter originalForm;

        /// <summary>
        /// 폼 사각형
        /// </summary>
        private Rectangle formRectangle;

        /// <summary>
        /// 폼 원본 사각형
        /// </summary>
        private Rectangle formOriginalRectangle;


        /// <summary>
        /// 크기 변경시 접착 여부
        /// </summary>
        private bool stickOnResize;

        /// <summary>
        /// 이동시 접착 여부
        /// </summary>
        private bool stickOnMove;

        /// <summary>
        /// 화면 접착 여부
        /// </summary>
        private bool stickToScreen;

        /// <summary>
        /// 상대 접차 여부
        /// </summary>
        private bool stickToOther;

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Property
        ////////////////////////////////////////////////////////////////////////////////////////// Public

        #region 접착 갭 - SitckGap

        /// <summary>
        /// 접착 갭
        /// </summary>
        public int StickGap
        {
            get
            {
                return _stickGap;
            }
            set
            {
                _stickGap = value;
            }
        }

        #endregion
        #region 크기 변경시 접착 여부 - StickOnResize

        /// <summary>
        /// 크기 변경시 접착 여부
        /// </summary>
        public bool StickOnResize
        {
            get
            {
                return this.stickOnResize;
            }
            set
            {
                this.stickOnResize = value;
            }
        }

        #endregion
        #region 이동시 접착 여부 - StickOnMove

        /// <summary>
        /// 이동시 접착 여부
        /// </summary>
        public bool StickOnMove
        {
            get
            {
                return this.stickOnMove;
            }
            set
            {
                this.stickOnMove = value;
            }
        }

        #endregion
        #region 화면 접착 여부 - StickToScreen

        /// <summary>
        /// 화면 접착 여부
        /// </summary>
        public bool StickToScreen
        {
            get
            {
                return this.stickToScreen;
            }
            set
            {
                this.stickToScreen = value;
            }
        }

        #endregion
        #region 상대 접착 여부 - StickToOther

        /// <summary>
        /// 상대 접착 여부
        /// </summary>
        public bool StickToOther
        {
            get
            {
                return this.stickToOther;
            }
            set
            {
                this.stickToOther = value;
            }
        }

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Constructor
        ////////////////////////////////////////////////////////////////////////////////////////// Public

        #region 생성자 - StickyWindow(formAdapter)

        /// <summary>
        /// 생성자
        /// </summary>
        /// <param name="formAdapter">폼 어댑터</param>
        public StickyWindow(IFormAdapter formAdapter)
        {
            this.resizingForm = false;
            this.movingForm   = false;

            this.originalForm = formAdapter;

            this.formRectangle       = Rectangle.Empty;
            this.formOffsetRectangle = Rectangle.Empty;

            this.formOffsetPoint = Point.Empty;
            this.offsetPoint     = Point.Empty;
            this.mousePoint      = Point.Empty;

            this.stickOnMove   = true;
            this.stickOnResize = true;
            this.stickToScreen = true;
            this.stickToOther  = true;

            this.defaultProcessMessageDelegate = new ProcessMessageDelegate(ProcessDefaultMessage);
            this.moveProcessMessageDelegate    = new ProcessMessageDelegate(ProcessMoveMessage);
            this.resizeProcessMessageDelegate  = new ProcessMessageDelegate(ProcessResizeMessage);
            this.processMessageDelegate        = this.defaultProcessMessageDelegate;

            AssignHandle(this.originalForm.Handle);
        }

        #endregion
        #region 생성자 - StickyWindow(form)

        /// <summary>
        /// 생성자
        /// </summary>
        /// <param name="form">폼</param>
        public StickyWindow(Form form) : this(new WinFormAdapter(form))
        {   
        }

        #endregion
        #region 생성자 - StickyWindow(window)

        /// <summary>
        /// 생성자
        /// </summary>
        /// <param name="window">윈도우</param>
        public StickyWindow(System.Windows.Window window) : this(new WPFFormAdapter(window))
        {
        }

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Method
        ////////////////////////////////////////////////////////////////////////////////////////// Static
        //////////////////////////////////////////////////////////////////////////////// Public

        #region 폼 어댑터 등록하기 - RegisterFormAdapter(formAdapter)

        /// <summary>
        /// 폼 어댑터 등록하기
        /// </summary>
        /// <param name="formAdapter">폼 어댑터</param>
        public static void RegisterFormAdapter(IFormAdapter formAdapter)
        {
            _formAdapterList.Add(formAdapter);
        }

        #endregion
        #region 폼 어댑터 등록하기 - RegisterFormAdapter(form)

        /// <summary>
        /// 폼 어댑터 등록하기
        /// </summary>
        /// <param name="form">폼</param>
        public static void RegisterFormAdapter(Form form)
        {
            RegisterFormAdapter(new WinFormAdapter(form));
        }

        #endregion
        #region 폼 어댑터 등록하기 - RegisterFormAdapter(window)

        /// <summary>
        /// 폼 어댑터 등록하기
        /// </summary>
        /// <param name="window">윈도우</param>
        public static void RegisterFormAdapter(System.Windows.Window window)
        {
            RegisterFormAdapter(new WPFFormAdapter(window));
        }

        #endregion

        #region 폼 어댑터 등록 취소하기 - UnregisterFormAdapter(formAdapter)

        /// <summary>
        /// 폼 어댑터 등록 취소하기
        /// </summary>
        /// <param name="formAdapter">폼 어댑터</param>
        public static void UnregisterFormAdapter(IFormAdapter formAdapter)
        {
            _formAdapterList.Remove(formAdapter);
        }

        #endregion

        ////////////////////////////////////////////////////////////////////////////////////////// Instance
        //////////////////////////////////////////////////////////////////////////////// Protected

        #region 핸들 변경시 처리하기 - OnHandleChange()

        /// <summary>
        /// 핸들 변경시 처리하기
        /// </summary>
        [System.Security.Permissions.PermissionSet(System.Security.Permissions.SecurityAction.Demand, Name="FullTrust")]
        protected override void OnHandleChange()
        {
            if((int)Handle != 0 )
            {
                _formAdapterList.Add(this.originalForm);
            } 
            else
            {
                _formAdapterList.Remove(this.originalForm);
            }
        }

        #endregion
        #region 윈도우 프로시저 처리하기 - WndProc(message)

        /// <summary>
        /// 윈도우 프로시저 처리하기
        /// </summary>
        /// <param name="message">메시지</param>
        [System.Security.Permissions.PermissionSet(System.Security.Permissions.SecurityAction.Demand, Name="FullTrust")]
        protected override void WndProc(ref Message message)
        {
            if(!processMessageDelegate(ref message))
            {
                base.WndProc(ref message);
            }
        }

        #endregion

        //////////////////////////////////////////////////////////////////////////////// Private

        #region 취소하기 - Cancel()

        /// <summary>
        /// 취소하기
        /// </summary>
        private void Cancel()
        {
            this.originalForm.Capture   = false;
            this.movingForm             = false;
            this.resizingForm           = false;
            this.processMessageDelegate = this.defaultProcessMessageDelegate;
        }

        #endregion

        #region 크기 변경 시작하기 - StartResize(resizeDirection)

        /// <summary>
        /// 크기 변경 시작하기
        /// </summary>
        /// <param name="resizeDirection">크기 변경 방향</param>
        /// <returns>처리 결과</returns>
        private bool StartResize(ResizeDirection resizeDirection)
        {
            if(this.stickOnResize)
            {
                this.resizeDirection       = resizeDirection;
                this.formRectangle         = this.originalForm.BoundRectangle;
                this.formOriginalRectangle = this.originalForm.BoundRectangle;

                if(!this.originalForm.Capture)
                {
                    this.originalForm.Capture = true;
                }

                this.processMessageDelegate = this.resizeProcessMessageDelegate;

                return true;
            }
            else
            {
                return false;
            }
        }

        #endregion
        #region 비클라이언트 영역 마우스 다운시 처리하기 - OnNCLButtonDown(hitTest, point)

        /// <summary>
        /// 비클라이언트 영역 마우스 다운시 처리하기
        /// </summary>
        /// <param name="hitTest">히트 테스트</param>
        /// <param name="point">포인트</param>
        /// <returns>처리 결과</returns>
        private bool OnNCLButtonDown(int hitTest, Point point)
        {
            Rectangle parentRectangle = this.originalForm.BoundRectangle;

            this.offsetPoint = point;

            switch(hitTest)
            {
                case WIN32Helper.HitTest.HTCAPTION :
                {
                    if(this.stickOnMove)
                    {
                        this.offsetPoint.Offset(-parentRectangle.Left, -parentRectangle.Top);

                        StartMove();

                        return true;
                    }
                    else
                    {
                        return false;
                    }
                }
                case WIN32Helper.HitTest.HTTOPLEFT :

                    return StartResize(ResizeDirection.Top | ResizeDirection.Left);

                case WIN32Helper.HitTest.HTTOP :

                    return StartResize(ResizeDirection.Top);

                case WIN32Helper.HitTest.HTTOPRIGHT :

                    return StartResize(ResizeDirection.Top | ResizeDirection.Right);

                case WIN32Helper.HitTest.HTRIGHT :

                    return StartResize(ResizeDirection.Right);

                case WIN32Helper.HitTest.HTBOTTOMRIGHT :

                    return StartResize(ResizeDirection.Bottom | ResizeDirection.Right);

                case WIN32Helper.HitTest.HTBOTTOM :

                    return StartResize(ResizeDirection.Bottom);

                case WIN32Helper.HitTest.HTBOTTOMLEFT :

                    return StartResize(ResizeDirection.Bottom | ResizeDirection.Left);

                case WIN32Helper.HitTest.HTLEFT :

                    return StartResize(ResizeDirection.Left);
            }

            return false;
        }

        #endregion
        #region 디폴트 메시지 처리하기 - ProcessDefaultMessage(message)

        /// <summary>
        /// 디폴트 메시지 처리하기
        /// </summary>
        /// <param name="message">메시지</param>
        /// <returns>처리 결과</returns>
        private bool ProcessDefaultMessage(ref Message message)
        {
            switch(message.Msg)
            {
                case WIN32Helper.WindowMessage.WM_NCLBUTTONDOWN :
                {
                    this.originalForm.Activate();

                    this.mousePoint.X = (short)WIN32Helper.Bit.GetLowWord ((int)message.LParam);
                    this.mousePoint.Y = (short)WIN32Helper.Bit.GetHighWord((int)message.LParam);

                    if(OnNCLButtonDown((int)message.WParam, mousePoint))
                    {
                        message.Result = (IntPtr)((this.resizingForm || this.movingForm) ? 1 : 0);

                        return true;
                    }

                    break;
                }
            }

            return false;
        }

        #endregion

        #region 크기 변경하기 - Resize(toRectangle, insideStick)

        /// <summary>
        /// 크기 변경하기
        /// </summary>
        /// <param name="toRectangle">TO 사각형</param>
        /// <param name="insideStick">내부 접착 여부</param>
        private void Resize(Rectangle toRectangle, bool insideStick)
        {
            if(this.formRectangle.Right >= (toRectangle.Left - _stickGap) && this.formRectangle.Left <= (toRectangle.Right + _stickGap))
            {
                if((this.resizeDirection & ResizeDirection.Top) == ResizeDirection.Top)
                {
                    if(Math.Abs(this.formRectangle.Top - toRectangle.Bottom) <= Math.Abs(this.formOffsetRectangle.Top) && insideStick)
                    {
                        this.formOffsetRectangle.Y = this.formRectangle.Top - toRectangle.Bottom;
                    }
                    else if(Math.Abs(this.formRectangle.Top - toRectangle.Top) <= Math.Abs(this.formOffsetRectangle.Top))
                    {
                        this.formOffsetRectangle.Y = this.formRectangle.Top - toRectangle.Top;
                    }
                }

                if((this.resizeDirection & ResizeDirection.Bottom) == ResizeDirection.Bottom)
                {
                    if(Math.Abs(this.formRectangle.Bottom - toRectangle.Top) <= Math.Abs(this.formOffsetRectangle.Bottom) && insideStick)
                    {
                        this.formOffsetRectangle.Height = toRectangle.Top - this.formRectangle.Bottom;
                    }
                    else if(Math.Abs(this.formRectangle.Bottom - toRectangle.Bottom) <= Math.Abs(this.formOffsetRectangle.Bottom))
                    {
                        this.formOffsetRectangle.Height = toRectangle.Bottom - this.formRectangle.Bottom;
                    }
                }
            }

            if(this.formRectangle.Bottom >= (toRectangle.Top - _stickGap) && this.formRectangle.Top <= (toRectangle.Bottom + _stickGap))
            {
                if((this.resizeDirection & ResizeDirection.Right) == ResizeDirection.Right)
                {
                    if(Math.Abs(this.formRectangle.Right - toRectangle.Left) <= Math.Abs(this.formOffsetRectangle.Right) && insideStick)
                    {
                        this.formOffsetRectangle.Width = toRectangle.Left - this.formRectangle.Right;
                    }
                    else if(Math.Abs(this.formRectangle.Right - toRectangle.Right) <= Math.Abs(this.formOffsetRectangle.Right))
                    {
                        this.formOffsetRectangle.Width = toRectangle.Right - this.formRectangle.Right;
                    }
                }

                if((this.resizeDirection & ResizeDirection.Left) == ResizeDirection.Left)
                {
                    if(Math.Abs(this.formRectangle.Left - toRectangle.Right) <= Math.Abs(this.formOffsetRectangle.Left) && insideStick)
                    {
                        this.formOffsetRectangle.X = this.formRectangle.Left - toRectangle.Right;
                    }
                    else if(Math.Abs(this.formRectangle.Left - toRectangle.Left) <= Math.Abs(this.formOffsetRectangle.Left))
                    {
                        this.formOffsetRectangle.X = this.formRectangle.Left - toRectangle.Left;
                    }
                }
            }
        }

        #endregion
        #region 크기 변경하기 - Resize(sourcePoint)

        /// <summary>
        /// 크기 변경하기
        /// </summary>
        /// <param name="sourcePoint">소스 포인트</param>
        private void Resize(Point sourcePoint)
        {
            sourcePoint = this.originalForm.PointToScreen(sourcePoint);

            Screen screen = Screen.FromPoint(sourcePoint);

            this.formRectangle = this.originalForm.BoundRectangle;

            int right  = this.formRectangle.Right;
            int bottom = this.formRectangle.Bottom;

            if((this.resizeDirection & ResizeDirection.Left) == ResizeDirection.Left)
            {
                this.formRectangle.Width = this.formRectangle.X - sourcePoint.X + this.formRectangle.Width;
                this.formRectangle.X     = right - this.formRectangle.Width;
            }

            if((this.resizeDirection & ResizeDirection.Right) == ResizeDirection.Right)
            {
                this.formRectangle.Width = sourcePoint.X - this.formRectangle.Left;
            }

            if((this.resizeDirection & ResizeDirection.Top) == ResizeDirection.Top)
            {
                this.formRectangle.Height = this.formRectangle.Height - sourcePoint.Y + this.formRectangle.Top;
                this.formRectangle.Y      = bottom - this.formRectangle.Height;
            }

            if((this.resizeDirection & ResizeDirection.Bottom) == ResizeDirection.Bottom)
            {
                this.formRectangle.Height = sourcePoint.Y - this.formRectangle.Top;
            }

            this.formOffsetRectangle.X      = _stickGap + 1;
            this.formOffsetRectangle.Y      = _stickGap + 1;
            this.formOffsetRectangle.Height = 0;
            this.formOffsetRectangle.Width  = 0;

            if(this.stickToScreen)
            {
                Resize(screen.WorkingArea, false);
            }

            if(this.stickToOther)
            {
                foreach(IFormAdapter formAdapter in _formAdapterList)
                {
                    if(formAdapter != this.originalForm)
                    {
                        Resize(formAdapter.BoundRectangle, true);
                    }
                }
            }

            if(this.formOffsetRectangle.X == _stickGap + 1)
            {
                this.formOffsetRectangle.X = 0;
            }

            if(this.formOffsetRectangle.Width == _stickGap + 1)
            {
                this.formOffsetRectangle.Width = 0;
            }

            if(this.formOffsetRectangle.Y == _stickGap + 1)
            {
                this.formOffsetRectangle.Y = 0;
            }

            if(this.formOffsetRectangle.Height == _stickGap + 1)
            {
                this.formOffsetRectangle.Height = 0;
            }

            if((this.resizeDirection & ResizeDirection.Left) == ResizeDirection.Left)
            {
                int newWidth = this.formRectangle.Width + this.formOffsetRectangle.Width + this.formOffsetRectangle.X;

                if(this.originalForm.MaximumSize.Width != 0)
                {
                    newWidth = Math.Min(newWidth, this.originalForm.MaximumSize.Width);
                }

                newWidth = Math.Min(newWidth, SystemInformation.MaxWindowTrackSize.Width);
                newWidth = Math.Max(newWidth, this.originalForm.MinimumSize.Width);
                newWidth = Math.Max(newWidth, SystemInformation.MinWindowTrackSize.Width);

                this.formRectangle.X     = right - newWidth;
                this.formRectangle.Width = newWidth;
            } 
            else
            {
                this.formRectangle.Width += this.formOffsetRectangle.Width + this.formOffsetRectangle.X;
            }

            if((this.resizeDirection & ResizeDirection.Top) == ResizeDirection.Top)
            {
                int newHeight = this.formRectangle.Height + this.formOffsetRectangle.Height + this.formOffsetRectangle.Y;

                if(this.originalForm.MaximumSize.Height != 0)
                {
                    newHeight = Math.Min(newHeight, this.originalForm.MaximumSize.Height);
                }

                newHeight = Math.Min(newHeight, SystemInformation.MaxWindowTrackSize.Height);
                newHeight = Math.Max(newHeight, this.originalForm.MinimumSize.Height);
                newHeight = Math.Max(newHeight, SystemInformation.MinWindowTrackSize.Height);
                
                this.formRectangle.Y      = bottom - newHeight;
                this.formRectangle.Height = newHeight;
            } 
            else
            {
                this.formRectangle.Height += this.formOffsetRectangle.Height + this.formOffsetRectangle.Y;
            }

            this.originalForm.BoundRectangle = this.formRectangle;
        }

        #endregion
        #region 크기 변경 종료하기 - EndResize()

        /// <summary>
        /// 크기 변경 종료하기
        /// </summary>
        private void EndResize()
        {
            Cancel();
        }

        #endregion
        #region 크기 변경 메시지 처리하기 - ProcessResizeMessage(message)

        /// <summary>
        /// 크기 변경 메시지 처리하기
        /// </summary>
        /// <param name="message">메시지</param>
        /// <returns>처리 결과</returns>
        private bool ProcessResizeMessage(ref Message message)
        {
            if(!this.originalForm.Capture)
            {
                Cancel();

                return false;
            }

            switch(message.Msg)
            {
                case WIN32Helper.WindowMessage.WM_LBUTTONUP :
                {
                    EndResize();

                    break;
                }
                case WIN32Helper.WindowMessage.WM_MOUSEMOVE :
                {
                    this.mousePoint.X = (short)WIN32Helper.Bit.GetLowWord ((int)message.LParam);
                    this.mousePoint.Y = (short)WIN32Helper.Bit.GetHighWord((int)message.LParam);

                    Resize(this.mousePoint);

                    break;
                }
                case WIN32Helper.WindowMessage.WM_KEYDOWN :
                {
                    if((int)message.WParam == WIN32Helper.VirtualKey.VK_ESCAPE)
                    {
                        this.originalForm.BoundRectangle = this.formOriginalRectangle;

                        Cancel();
                    }

                    break;
                }
            }

            return false;
        }

        #endregion

        #region 이동 시작하기 - StartMove()

        /// <summary>
        /// 이동 시작하기
        /// </summary>
        private void StartMove()
        {
            this.formRectangle         = this.originalForm.BoundRectangle;
            this.formOriginalRectangle = this.originalForm.BoundRectangle;

            if(!this.originalForm.Capture)
            {
                this.originalForm.Capture = true;
            }

            this.processMessageDelegate = this.moveProcessMessageDelegate;
        }

        #endregion
        #region 정규화 하기 - Normalize(sourceValue, minimumValue, maximumValue)

        /// <summary>
        /// 정규화 하기
        /// </summary>
        /// <param name="sourceValue">소스 값</param>
        /// <param name="minimumValue">최소 값</param>
        /// <param name="maximumValue">최대 값</param>
        /// <returns>정규화 값</returns>
        private int Normalize(int sourceValue, int minimumValue, int maximumValue)
        {
            if(sourceValue <= minimumValue )
            {
                return minimumValue;
            }
            else if(sourceValue >= maximumValue)
            {
                return maximumValue;
            }

            return sourceValue;
        }

        #endregion
        #region 이동하기 - Move(toRectangle, insideStick)

        /// <summary>
        /// 이동하기
        /// </summary>
        /// <param name="toRectangle">TO 사각형</param>
        /// <param name="insideStick">내부 접착 여부</param>
        private void Move(Rectangle toRectangle, bool insideStick)
        {
            if(this.formRectangle.Bottom >= (toRectangle.Top - _stickGap) && this.formRectangle.Top <= (toRectangle.Bottom + _stickGap))
            {
                if(insideStick)
                {
                    if((Math.Abs(this.formRectangle.Left - toRectangle.Right) <= Math.Abs(this.formOffsetPoint.X)))
                    {
                        this.formOffsetPoint.X = toRectangle.Right - this.formRectangle.Left;
                    }

                    if((Math.Abs(this.formRectangle.Left + this.formRectangle.Width - toRectangle.Left) <= Math.Abs(this.formOffsetPoint.X)))
                    {
                        this.formOffsetPoint.X = toRectangle.Left - this.formRectangle.Width - this.formRectangle.Left;
                    }
                }

                if(Math.Abs(this.formRectangle.Left - toRectangle.Left) <= Math.Abs(this.formOffsetPoint.X))
                {
                    this.formOffsetPoint.X = toRectangle.Left - this.formRectangle.Left;
                }

                if(Math.Abs(this.formRectangle.Left + this.formRectangle.Width - toRectangle.Left - toRectangle.Width ) <= Math.Abs(this.formOffsetPoint.X))
                {
                    this.formOffsetPoint.X = toRectangle.Left + toRectangle.Width - this.formRectangle.Width - this.formRectangle.Left;
                }
            }

            if(this.formRectangle.Right >= (toRectangle.Left - _stickGap) && this.formRectangle.Left <= (toRectangle.Right + _stickGap))
            {
                if(insideStick)
                {
                    if(Math.Abs(this.formRectangle.Top - toRectangle.Bottom) <= Math.Abs(this.formOffsetPoint.Y) && insideStick)
                    {
                        this.formOffsetPoint.Y = toRectangle.Bottom - this.formRectangle.Top;
                    }
 
                    if(Math.Abs(this.formRectangle.Top + this.formRectangle.Height - toRectangle.Top) <= Math.Abs(this.formOffsetPoint.Y) && insideStick)
                    {
                        this.formOffsetPoint.Y = toRectangle.Top - this.formRectangle.Height - this.formRectangle.Top;
                    }
                }

                if(Math.Abs(this.formRectangle.Top - toRectangle.Top) <= Math.Abs(this.formOffsetPoint.Y))
                {
                    this.formOffsetPoint.Y = toRectangle.Top - this.formRectangle.Top;
                }

                if(Math.Abs(this.formRectangle.Top + this.formRectangle.Height - toRectangle.Top - toRectangle.Height ) <= Math.Abs(this.formOffsetPoint.Y))
                {
                    this.formOffsetPoint.Y =  toRectangle.Top + toRectangle.Height - this.formRectangle.Height - this.formRectangle.Top;
                }
            }
        }

        #endregion
        #region 이동하기 - Move(sourcePoint)

        /// <summary>
        /// 이동하기
        /// </summary>
        /// <param name="sourcePoint">소스 포인트</param>
        private void Move(Point sourcePoint)
        {
            sourcePoint = this.originalForm.PointToScreen(sourcePoint);

            Screen screen = Screen.FromPoint(sourcePoint);

            if(!screen.WorkingArea.Contains(sourcePoint))
            {
                sourcePoint.X = Normalize(sourcePoint.X, screen.WorkingArea.Left, screen.WorkingArea.Right );
                sourcePoint.Y = Normalize(sourcePoint.Y, screen.WorkingArea.Top , screen.WorkingArea.Bottom);
            }

            sourcePoint.Offset(-offsetPoint.X, -offsetPoint.Y);

            this.formRectangle.Location = sourcePoint;

            this.formOffsetPoint.X = _stickGap + 1;
            this.formOffsetPoint.Y = _stickGap + 1;

            if(this.stickToScreen)
            {
                Move(screen.WorkingArea, false);
            }

            if(this.stickToOther)
            {
                foreach(IFormAdapter formAdapter in _formAdapterList)
                {
                    if(formAdapter != this.originalForm)
                    {
                        Move(formAdapter.BoundRectangle, true);
                    }
                }
            }

            if(this.formOffsetPoint.X == _stickGap + 1)
            {
                this.formOffsetPoint.X = 0;
            }

            if(this.formOffsetPoint.Y == _stickGap + 1)
            {
                this.formOffsetPoint.Y = 0;
            }

            this.formRectangle.Offset(this.formOffsetPoint);

            this.originalForm.BoundRectangle = this.formRectangle;
        }

        #endregion
        #region 이동 종료하기 - EndMove()

        /// <summary>
        /// 이동 종료하기
        /// </summary>
        private void EndMove()
        {
            Cancel();
        }

        #endregion
        #region 이동 메시지 처리하기 - ProcessMoveMessage(message)

        /// <summary>
        /// 이동 메시지 처리하기
        /// </summary>
        /// <param name="message">메시지</param>
        /// <returns>처리 결과</returns>
        private bool ProcessMoveMessage(ref Message message)
        {
            if(!this.originalForm.Capture)
            {
                Cancel();

                return false;
            }

            switch(message.Msg)
            {
                case WIN32Helper.WindowMessage.WM_LBUTTONUP :
                {
                    EndMove();

                    break;
                }
                case WIN32Helper.WindowMessage.WM_MOUSEMOVE :
                {
                    this.mousePoint.X = (short)WIN32Helper.Bit.GetLowWord ((int)message.LParam);
                    this.mousePoint.Y = (short)WIN32Helper.Bit.GetHighWord((int)message.LParam);

                    Move(this.mousePoint);

                    break;
                }
                case WIN32Helper.WindowMessage.WM_KEYDOWN :
                {
                    if((int)message.WParam == WIN32Helper.VirtualKey.VK_ESCAPE)
                    {
                        this.originalForm.BoundRectangle = this.formOriginalRectangle;

                        Cancel();
                    }

                    break;
                }
            }

            return false;
        }

        #endregion
    }
}

 

[TestWPFLibrary 프로젝트]

▶ MainWindow.xaml.cs

using System.Windows;

using TestLibrary;

namespace TestWPFProject
{
    /// <summary>
    /// 메인 윈도우
    /// </summary>
    public partial class MainWindow : Window
    {
        //////////////////////////////////////////////////////////////////////////////////////////////////// Constructor
        ////////////////////////////////////////////////////////////////////////////////////////// Public

        #region 생성자 - MainWindow()

        /// <summary>
        /// 생성자
        /// </summary>
        public MainWindow()
        {
            InitializeComponent();

            Loaded                     += Window_Loaded;
            this.newWindowButton.Click += newWindowButton_Click;
        }

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Method
        ////////////////////////////////////////////////////////////////////////////////////////// Private

        #region 윈도우 로드시 처리하기 - Window_Loaded(sender, e)

        /// <summary>
        /// 윈도우 로드시 처리하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            StickyWindow.RegisterFormAdapter(this);
        }

        #endregion
        #region 새 윈도우 버튼 클릭시 처리하기 - newWindowButton_Click(sender, e)

        /// <summary>
        /// 새 윈도우 버튼 클릭시 처리하기
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void newWindowButton_Click(object sender, RoutedEventArgs e)
        {
            TestWindow window = new TestWindow();

            window.Show();
        }

        #endregion
    }
}

 

▶ TestWindow.xaml.cs

using System.Windows;

using TestLibrary;

namespace TestWPFProject
{
    /// <summary>
    /// 테스트 윈도우
    /// </summary>
    public partial class TestWindow : Window
    {
        //////////////////////////////////////////////////////////////////////////////////////////////////// Field
        ////////////////////////////////////////////////////////////////////////////////////////// Private

        #region Field

        /// <summary>
        /// 접착 윈도우
        /// </summary>
        private StickyWindow stickyWindow;

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Constructor
        ////////////////////////////////////////////////////////////////////////////////////////// Public

        #region 생성자 - TestWindow()

        /// <summary>
        /// 생성자
        /// </summary>
        public TestWindow()
        {
            InitializeComponent();

            Loaded += Window_Loaded;
        }

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Method
        ////////////////////////////////////////////////////////////////////////////////////////// Private

        #region 윈도우 로드시 처리하기 - Window_Loaded(sender, e)

        /// <summary>
        /// 윈도우 로드시 처리하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            this.stickyWindow = new StickyWindow(this);

            this.stickyWindow.StickToScreen = true;
            this.stickyWindow.StickToOther  = true;
            this.stickyWindow.StickOnResize = true;
            this.stickyWindow.StickOnMove   = true;
        }

        #endregion
    }
}
728x90
반응형
그리드형(광고전용)
Posted by 사용자 icodebroker
TAG , ,

댓글을 달아 주세요