안녕하세요? 맨날맑음 입니다.

몇몇 프로그램을 보면, 윈도우끼리 도킹 기능으로 서로 일정거리 만큼 가까워지면 딱 달라붙게 만들어진 것을 볼 수 있습니다. 이런 기능의 명칭을 정확히 몰라서; 일단 제목은 '자석 윈도우'라고 정해 보았는데요;

밑의 예(이스트소프트의 알송)에서 볼 수 있듯이 각 윈도우끼리는 가까이 가면 달라 붙고, 제일 위의 윈도우를 이동하면 붙어있는 윈도우도 따라서 이동하게 됩니다.
이런 기능을 간단하게 Windows Form을 이용하여 만드는 방법을 알아보려 합니다. 이런 기능을 제공하는 Class Library가 있을까 찾아 보다가 결국 찾지는 못하고, 한 외국 사이트에 이와같은 소스를 발견하였습니다. 현재 출처를 정확하게 기억 나지 않습니다. 그래서 완성된 소스는 올리지 않습니다.(필요하신분은 요청하세요)
Form1.cs
 private void Form1_Move(object sender, EventArgs e)
        {
            if (isForm2Docked)
            {
                tempForm2.SetDesktopLocation(tempForm2.Location.X + (this.Location.X - prevLoc.X),
                    tempForm2.Location.Y + (this.Location.Y - prevLoc.Y));
            }
            prevLoc.X = this.Location.X;
            prevLoc.Y = this.Location.Y;
        }
 
메인이 되는 폼의 Move 이벤트에서는 현재 서브폼이 붙어있는지 확인하여, 붙어있으면 위치를 함께 옮겨줍니다.

Form2.cs
private void Form2_Move(object sender, EventArgs e)
        {
            bool amIDocked = false;
            Point newPoint = GetNewFormPosition(this, passedInForm, out amIDocked);
            this.SetDesktopLocation(newPoint.X, newPoint.Y);
            ((Form1)passedInForm).isForm2Docked = amIDocked;
        }

        public Point GetNewFormPosition(Form thisForm, Form parentForm, out bool isDocked)
        {
            int[] xRange = new int[2] { parentForm.Location.X, parentForm.Location.X + parentForm.Width };
            int[] yRange = new int[2] { parentForm.Location.Y, parentForm.Location.Y + parentForm.Height };
            int xGap = Math.Abs(thisForm.Location.X - parentForm.Location.X);
            int yGap = Math.Abs(thisForm.Location.Y - parentForm.Location.Y);
            int leftGap = Math.Abs((thisForm.Location.X + thisForm.Width) - parentForm.Location.X);
            int rightGap = Math.Abs(thisForm.Location.X - (parentForm.Location.X + parentForm.Width));
            int topGap = Math.Abs((thisForm.Location.Y + thisForm.Height) - parentForm.Location.Y);
            int bottomGap = Math.Abs(thisForm.Location.Y - (parentForm.Location.Y + parentForm.Height));
            int xNew = thisForm.Location.X;
            int yNew = thisForm.Location.Y;
            isDocked = false;

            if ((leftGap <= dockGap) && (((thisForm.Location.Y >= yRange[0]) && (thisForm.Location.Y <= yRange[1])) ||
                ((thisForm.Location.Y + parentForm.Height >= yRange[0]) && (thisForm.Location.Y + parentForm.Height <= yRange[1]))))
            {
                xNew = parentForm.Location.X - thisForm.Width;
                if (yGap <= dockGap) yNew = parentForm.Location.Y;
                isDocked = true;
            }
            if ((rightGap <= dockGap) && (((thisForm.Location.Y >= yRange[0]) && (thisForm.Location.Y <= yRange[1])) ||
                ((thisForm.Location.Y + parentForm.Height >= yRange[0]) && (thisForm.Location.Y + parentForm.Height <= yRange[1]))))
            {
                xNew = parentForm.Location.X + parentForm.Width;
                if (yGap <= dockGap) yNew = parentForm.Location.Y;
                isDocked = true;
            }
            if ((topGap <= dockGap) && (((thisForm.Location.X >= xRange[0]) && (thisForm.Location.X <= xRange[1])) ||
                ((thisForm.Location.X + parentForm.Width >= xRange[0]) && (thisForm.Location.X + parentForm.Width <= xRange[1]))))
            {
                yNew = parentForm.Location.Y - thisForm.Height;
                if (xGap <= dockGap) xNew = parentForm.Location.X;
                isDocked = true;
            }
            if ((bottomGap <= dockGap) && (((thisForm.Location.X >= xRange[0]) && (thisForm.Location.X <= xRange[1])) ||
                ((thisForm.Location.X + parentForm.Width >= xRange[0]) && (thisForm.Location.X + parentForm.Width <= xRange[1]))))
            {
                yNew = parentForm.Location.Y + parentForm.Height;
                if (xGap <= dockGap) xNew = parentForm.Location.X;
                isDocked = true;
            }
            return new Point(xNew, yNew);
        }

서브폼에서 좀 복잡해 보이긴 하지만, 메인폼의 영역을 알아와, 현재 서브폼의 위치가 달라 붙어야하는 위치라면 붙여 주고, 메인폼에 그 사실을 알리는 역할을 합니다.
이 소스를 응용하면, 상용프로그램처럼 윈도우간 도킹 기능을 만드는 것도 어렵지 않을 것 같습니다.

ps. 좌표를 일일이 비교해가며 구현하는 방법 외에 Class Library나 다른 방법을 아시는 분은 꼭 피드백을 부탁 드립니다!!
Posted by 맨날맑음