Expanding Canvas 만들기 2




목차


지난 포스팅(http://crystalcube.co.kr/154) 에서 Expanding Canvas 를 만들어 보았습니다. 이 Canvas 는 Child 가 우측 또는 하단으로 이동할 경우에만 확장이 됩니다. 상단이나 좌측으로 이동할 때는 확장이 되지 않지요. 이 단점을 보완한, 무한 확장 캔버스를 만들어 보려고 합니다.




개요


앞서 방식대로 MeasureOverride 를 이용합니다. 그리고 추가적으로 ArrangeOverride 로 사용해야 합니다. 이 메소드에서 child 들의 위치를 새로 잡아줄 필요가 있기 때문입니다. 


child 를 가장 좌상단으로 이동한다고 가정해 보죠. 0,0 의 위치에서 더 좌측으로 이동하면 화면상으로 어떻게 될까요? 캔버스는 확장 된다고 하더라도, 화면상에서의 child 좌표는 여전히 0,0 일 것입니다.

만약 A 랑 B 가 있다고 해 봅시다. A 는 앞서 설명한대로 0,0 에 위치합니다. 그리고 B가 10,10 에 위치합니다. 이 경우 A 를 좌상단으로 5,5 만큼 더 이동하면 어떻게 될까요? A 는 여전히 화면상에서 0,0 위치에 있겠지요. 그러나 B 15,15 가 될 것입니다.


이런 부분들을 ArrangeOverride 에서 처리하려고 합니다.


코드를 보면, child 의 갯수가 1개인 경우 별도로 처리해 주는 코드들이 보입니다. 이 부분은 child 가 1개만 있는 경우 동작이 조금 다르기 때문입니다. 이 부분을 없애고 테스트 해 보시면 무슨 이야기인지 알게 되실겁니다. :)


다소 코드가 좀 지저분하네요 =_=;;






보시다시피, 약간 어색한 부분이 있습니다. 이것은 스크롤바의 스크롤이 마우스를 따라가지 않기 때문인데요.

이 부분이 어색하지 않게 하려면, 스크롤바를 수정해 주어야 합니다. 다음에 시간이 날때, 해당 부분 관련해서 포스팅 하도록 하겠습니다.


Behavior 관련 코드는 지난 포스팅을 참고하시기 바랍니다.



    [ExpandingCanvas.cs]



using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using MS.Internal;
using SW.Common.Extensions;


namespace SW.Common.Controls
{
    public class ExpandingCanvas : Canvas
    {
        static ExpandingCanvas()
        {
            if(DesignerProperties.GetIsInDesignMode(new DependencyObject()) == false)
            {
                Canvas.LeftProperty.OverrideMetadata(typeof(UIElement), new FrameworkPropertyMetadata(0.0, FrameworkPropertyMetadataOptions.AffectsParentMeasure));
                Canvas.TopProperty.OverrideMetadata(typeof(UIElement), new FrameworkPropertyMetadata(0.0, FrameworkPropertyMetadataOptions.AffectsParentMeasure));
                Canvas.RightProperty.OverrideMetadata(typeof(UIElement), new FrameworkPropertyMetadata(0.0, FrameworkPropertyMetadataOptions.AffectsParentMeasure));
                Canvas.BottomProperty.OverrideMetadata(typeof(UIElement), new FrameworkPropertyMetadata(0.0, FrameworkPropertyMetadataOptions.AffectsParentMeasure));
            }
            DefaultStyleKeyProperty.OverrideMetadata(typeof(ExpandingCanvas), new FrameworkPropertyMetadata(typeof(ExpandingCanvas)));
        }



        protected override Size MeasureOverride(Size constraint)
        {
            List<FrameworkElement> children = this.FindVisualDescendants<FrameworkElement>().ToList();
            if (children.Count <= 0) { return base.MeasureOverride(constraint); }

            double right = 0.0;
            double bottom = 0.0;

            if (children.Count == 1 && children[0] != null)
            {
                Point point = new Point(Canvas.GetLeft(children[0]), Canvas.GetTop(children[0]));
                point.X = double.IsNaN(point.X) ? 0.0 : point.X;
                point.Y = double.IsNaN(point.Y) ? 0.0 : point.Y;

                right = Math.Abs(point.X) + children[0].ActualWidth;
                bottom = Math.Abs(point.Y) + children[0].ActualHeight;
            }
            else
            {
                foreach (FrameworkElement child in children)
                {
                    Point p = child.TranslatePoint(new Point(child.ActualWidth, child.ActualHeight), this);
                    right = Math.Max(right, p.X);
                    bottom = Math.Max(bottom, p.Y);
                }
            }

            if (right <= 0.0 || bottom <= 0.0) { return base.MeasureOverride(constraint); }
            return new Size(right, bottom);
        }


        protected override Size ArrangeOverride(Size arrangeSize)
        {
            double minX = double.MaxValue;
            double minY = double.MaxValue;
            if (this.InternalChildren.Count <= 1) {
                minX = minY = 0.0;
            } else {
                foreach (UIElement element in InternalChildren) {
                    minX = Math.Min(Canvas.GetLeft(element), minX);
                    minY = Math.Min(Canvas.GetTop(element), minY);
                }
                minX = double.IsNaN(minX) ? 0.0 : minX;
                minY = double.IsNaN(minY) ? 0.0 : minY;
            }

            if (this.InternalChildren.Count == 1 && this.InternalChildren[0] != null)
            {
                UIElement element = this.InternalChildren[0];
                if (element != null)
                {
                    double x = 0.0;
                    double y = 0.0;
                    double left = Canvas.GetLeft(element);
                    if (!double.IsNaN(left))
                    {
                        x = left;
                    }
                    else
                    {
                        double right = Canvas.GetRight(element);
                        if (!double.IsNaN(right))
                            x = arrangeSize.Width - element.DesiredSize.Width - right;
                    }
                    double top = Canvas.GetTop(element);
                    if (!double.IsNaN(top))
                    {
                        y = top;
                    }
                    else
                    {
                        double bottom = Canvas.GetBottom(element);
                        if (!double.IsNaN(bottom))
                            y = arrangeSize.Height - element.DesiredSize.Height - bottom;
                    }
                    element.Arrange(new Rect(new Point((x < 0.0 ? 0.0 : x), (y < 0.0 ? 0.0 : y)), element.DesiredSize));
                }
            }
            else
            {
                foreach (UIElement element in this.InternalChildren)
                {
                    if (element != null)
                    {
                        double x = 0.0;
                        double y = 0.0;
                        double left = Canvas.GetLeft(element);
                        if (!double.IsNaN(left))
                        {
                            x = left;
                        }
                        else
                        {
                            double right = Canvas.GetRight(element);
                            if (!double.IsNaN(right))
                                x = arrangeSize.Width - element.DesiredSize.Width - right;
                        }
                        double top = Canvas.GetTop(element);
                        if (!double.IsNaN(top))
                        {
                            y = top;
                        }
                        else
                        {
                            double bottom = Canvas.GetBottom(element);
                            if (!double.IsNaN(bottom))
                                y = arrangeSize.Height - element.DesiredSize.Height - bottom;
                        }
                        element.Arrange(new Rect(new Point(x - minX, y - minY), element.DesiredSize));
                    }
                }
            }
            
            return arrangeSize;
        }

    }
}




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

Draggable Canvas ver 0.9  (2) 2014.11.17
편집용 Canvas 만들기  (0) 2014.11.16
격자 배경 그리기  (0) 2014.11.14
Expanding Canvas 만들기  (2) 2014.11.14
Drag Canvas 만들기  (0) 2014.11.13