첨부 소스 코드는 나눔고딕코딩 폰트를 사용합니다.
728x90
반응형
728x170

TestProject.zip
다운로드

▶ LogicalUnitCanvas.cs

using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;

namespace TestProject
{
    /// <summary>
    /// 논리적 단위 캔버스
    /// </summary>
    public class LogicalUnitCanvas : Canvas
    {
        //////////////////////////////////////////////////////////////////////////////////////////////////// Field
        ////////////////////////////////////////////////////////////////////////////////////////// Static
        //////////////////////////////////////////////////////////////////////////////// Public

        #region 원본 너비 속성 - OriginWidthProperty

        /// <summary>
        /// 원본 너비 속성
        /// </summary>
        public static readonly DependencyProperty OriginWidthProperty = DependencyProperty.RegisterAttached
        (
            "OriginWidth",
            typeof(double),
            typeof(LogicalUnitCanvas),
            new PropertyMetadata
            (
                0.0d,
                new PropertyChangedCallback(OriginSizePropertyChangedCallback)
            )
        );

        #endregion
        #region 원본 높이 속성 - OriginHeightProperty

        /// <summary>
        /// 원본 높이 속성
        /// </summary>
        public static readonly DependencyProperty OriginHeightProperty = DependencyProperty.RegisterAttached
        (
            "OriginHeight",
            typeof(double),
            typeof(LogicalUnitCanvas),
            new PropertyMetadata
            (
                0.0d,
                new PropertyChangedCallback(OriginSizePropertyChangedCallback)
            )
        );

        #endregion
        #region 기본 너비 속성 - BaseWidthProperty

        /// <summary>
        /// 기본 너비 속성
        /// </summary>
        public static readonly DependencyProperty BaseWidthProperty = DependencyProperty.Register
        (
            "BaseWidth",
            typeof(double),
            typeof(LogicalUnitCanvas),
            new PropertyMetadata(new PropertyChangedCallback(BaseWidthPropertyChangedCallback))
        );

        #endregion
        #region 기본 높이 속성 - BaseHeightProperty

        /// <summary>
        /// 기본 높이 속성
        /// </summary>
        public static readonly DependencyProperty BaseHeightProperty = DependencyProperty.Register
        (
            "BaseHeight",
            typeof(double),
            typeof(LogicalUnitCanvas),
            new PropertyMetadata(new PropertyChangedCallback(BaseHeightPropertyChangedCallback))
        );

        #endregion
        #region 현재 논리적 비율 속성 - CurruntLogicalRatioProperty

        /// <summary>
        /// 현재 논리적 비율 속성
        /// </summary>
        public static readonly DependencyProperty CurruntLogicalRatioProperty = DependencyProperty.Register
        (
            "CurruntLogicalRatio",
            typeof(Point),
            typeof(LogicalUnitCanvas)
        );

        #endregion

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

        #region 기본 너비 - BaseWidth

        /// <summary>
        /// 기본 너비
        /// </summary>
        public double BaseWidth
        {
            get
            {
                return (double)GetValue(BaseWidthProperty);
            }
            set
            {
                SetValue(BaseWidthProperty, value);
            }
        }

        #endregion
        #region 기본 높이 - BaseHeight

        /// <summary>
        /// 기본 높이
        /// </summary>
        public double BaseHeight
        {
            get
            {
                return (double)GetValue(BaseHeightProperty);
            }
            set
            {
                SetValue(BaseHeightProperty, value);
            }
        }

        #endregion
        #region 현재 논리적 비율 - CurruntLogicalRatio

        /// <summary>
        /// 현재 논리적 비율
        /// </summary>
        public Point CurruntLogicalRatio
        {
            get
            {
                return (Point)GetValue(CurruntLogicalRatioProperty);
            }
            set
            {
                SetValue(CurruntLogicalRatioProperty, value);
            }
        }

        #endregion

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

        #region 생성자 - LogicalUnitCanvas()

        /// <summary>
        /// 생성자
        /// </summary>
        public LogicalUnitCanvas()
        {
            SizeChanged += Canvas_SizeChanged;
        }

        #endregion

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

        #region 원본 너비 설정하기 - SetOriginWidth(element, width)

        /// <summary>
        /// 원본 너비 설정하기
        /// </summary>
        /// <param name="element">UI 엘리먼트</param>
        /// <param name="width">너비</param>
        public static void SetOriginWidth(UIElement element, double width)
        {
            element.SetValue(OriginWidthProperty, width);
        }

        #endregion
        #region 원본 너비 구하기 - GetOriginWidth(element)

        /// <summary>
        /// 원본 너비 구하기
        /// </summary>
        /// <param name="element">UI 엘리먼트</param>
        /// <returns>원본 너비</returns>
        public static double GetOriginWidth(UIElement element)
        {
            return (double)element.GetValue(OriginWidthProperty);
        }

        #endregion
        #region 원본 높이 설정하기 - SetOriginHeight(element, height)

        /// <summary>
        /// 원본 높이 설정하기
        /// </summary>
        /// <param name="element">UI 엘리먼트</param>
        /// <param name="height">높이</param>
        public static void SetOriginHeight(UIElement element, double height)
        {
            element.SetValue(OriginHeightProperty, height);
        }

        #endregion
        #region 원본 높이 구하기 - GetOriginHeight(element)

        /// <summary>
        /// 원본 높이 구하기
        /// </summary>
        /// <param name="element">UI 엘리먼트</param>
        /// <returns>원본 높이</returns>
        public static double GetOriginHeight(UIElement element)
        {
            return (double)element.GetValue(OriginHeightProperty);
        }

        #endregion

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

        #region 원본 크기 속성 변경시 콜백 처리하기 - OriginSizePropertyChangedCallback(d, e)

        /// <summary>
        /// 원본 크기 속성 변경시 콜백 처리하기
        /// </summary>
        /// <param name="d">의존 객체</param>
        /// <param name="e">이벤트 인자</param>
        private static void OriginSizePropertyChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            UIElement element = d as UIElement;

            if(element != null)
            {
                LogicalUnitCanvas canvas = FindAncestorByType<LogicalUnitCanvas>(element);

                if(canvas != null)
                {
                    canvas.LogicalUpdate();
                }
            }
        }

        #endregion
        #region 기본 너비 속성 변경시 콜백 처리하기 - BaseWidthPropertyChangedCallback(d, e)

        /// <summary>
        /// 기본 너비 속성 변경시 콜백 처리하기
        /// </summary>
        /// <param name="d">의존 객체</param>
        /// <param name="e">이벤트 인자</param>
        private static void BaseWidthPropertyChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            LogicalUnitCanvas canvas = d as LogicalUnitCanvas;

            if(canvas != null && e.NewValue is double)
            {
                canvas.LogicalUpdate();
            }
        }

        #endregion
        #region 기본 높이 속성 변경시 콜백 처리하기 - BaseHeightPropertyChangedCallback(d, e)

        /// <summary>
        /// 기본 높이 속성 변경시 콜백 처리하기
        /// </summary>
        /// <param name="d">의존 객체</param>
        /// <param name="e">이벤트 인자</param>
        private static void BaseHeightPropertyChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            LogicalUnitCanvas canvas = d as LogicalUnitCanvas;

            if(canvas != null && e.NewValue is double)
            {
                canvas.LogicalUpdate();
            }
        }

        #endregion

        ////////////////////////////////////////////////////////////////////////////////////////// Instance
        //////////////////////////////////////////////////////////////////////////////// Public
        ////////////////////////////////////////////////////////////////////// Function

        #region 논리적 위치 업데이트하기 - LogicalUpdate()

        /// <summary>
        /// 논리적 위치 업데이트하기
        /// </summary>
        public void LogicalUpdate()
        {
            InvalidateMeasure();
            InvalidateArrange();
        }

        #endregion

        //////////////////////////////////////////////////////////////////////////////// Protected
        ////////////////////////////////////////////////////////////////////// Function

        #region 측정하기 (오버라이드) - MeasureOverride(availableSize)

        /// <summary>
        /// 측정하기 (오버라이드)
        /// </summary>
        /// <param name="availableSize">이용 가능한 크기</param>
        /// <returns>측정 크기</returns>
        protected override Size MeasureOverride(Size availableSize)
        {
            double availableWidth  = availableSize.Width;
            double availableHeight = availableSize.Height;

            double widthRatio  = availableWidth  / BaseWidth;
            double heightRatio = availableHeight / BaseHeight;

            foreach(FrameworkElement element in Children)
            {
                double width  = Convert.ToDouble(element.GetValue(LogicalUnitCanvas.OriginWidthProperty ));
                double height = Convert.ToDouble(element.GetValue(LogicalUnitCanvas.OriginHeightProperty));

                if
                (
                    double.IsInfinity(widthRatio ) == false &&
                    double.IsInfinity(heightRatio) == false &&
                    double.IsInfinity(width      ) == false &&
                    double.IsInfinity(height     ) == false
                )
                {
                    element.Width  = width  * widthRatio;
                    element.Height = height * heightRatio;
                }
            }

            if(double.IsInfinity(availableSize.Width) == true || double.IsInfinity(availableSize.Height) == true)
            {
                return base.MeasureOverride(availableSize);
            }
            else
            {
                return availableSize;
            }
        }

        #endregion
        #region 배열하기 (오버라이드) - ArrangeOverride(arrangeSize)

        /// <summary>
        /// 배열하기 (오버라이드)
        /// </summary>
        /// <param name="finalSize">최종 크기</param>
        /// <returns>배열 크기</returns>
        protected override Size ArrangeOverride(Size finalSize)
        {
            double finalWidth  = finalSize.Width;
            double finalHeight = finalSize.Height;

            double widthRatio  = finalWidth  / BaseWidth;
            double heightRatio = finalHeight / BaseHeight;

            CurruntLogicalRatio = new Point(BaseWidth / finalWidth, BaseHeight / finalHeight);

            foreach(FrameworkElement element in Children)
            {
                double x = Canvas.GetLeft(element);
                double y = Canvas.GetTop (element);

                if
                (
                    double.IsNaN(x             ) == false &&
                    double.IsNaN(y             ) == false &&
                    double.IsNaN(element.Width ) == false &&
                    double.IsNaN(element.Height) == false
                )
                {
                    Rect rectangle = new Rect
                    (
                        x * widthRatio,
                        y * heightRatio,
                        element.Width,
                        element.Height
                    );

                    element.Arrange(rectangle);
                }
            }

            return finalSize;
        }

        #endregion

        //////////////////////////////////////////////////////////////////////////////// Private
        ////////////////////////////////////////////////////////////////////// Event

        #region 캔버스 크기 변경시 처리하기 - Canvas_SizeChanged(sender, e)

        /// <summary>
        /// 캔버스 크기 변경시 처리하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        private void Canvas_SizeChanged(object sender, SizeChangedEventArgs e)
        {
            LogicalUpdate();

            CurruntLogicalRatio = new Point(BaseWidth / ActualWidth, BaseHeight / ActualHeight);
        }

        #endregion

        ////////////////////////////////////////////////////////////////////// Function

        #region 타입으로 조상 찾기 - FindAncestorByType<T>(d)

        /// <summary>
        /// 타입으로 조상 찾기
        /// </summary>
        /// <typeparam name="TAncestor">조상 타입</typeparam>
        /// <param name="d">의존 객체</param>
        /// <returns>조상 객체</returns>
        public static TAncestor FindAncestorByType<TAncestor>(DependencyObject d) where TAncestor : DependencyObject
        {
            if(d == null)
            {
                return default(TAncestor);
            }

            if(d is TAncestor)
            {
                return (TAncestor)d;
            }

            TAncestor parent = default(TAncestor);

            parent = FindAncestorByType<TAncestor>(VisualTreeHelper.GetParent(d));

            return parent;
        }

        #endregion
    }
}

 

728x90

 

▶ MainWindow.xaml

<Window x:Class="TestProject.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:TestProject"
    Width="800"
    Height="600"
    Title="Canvas 클래스 : 논리적 단위 캔버스 사용하기"
    FontFamily="나눔고딕코딩"
    FontSize="16">
    <Window.Resources>
        <Style x:Key="HeaderTextBlockKey" TargetType="{x:Type TextBlock}">
            <Setter Property="Foreground" Value="Blue" />
            <Setter Property="FontWeight" Value="Bold" />
        </Style>
        <Style x:Key="ValueTextBlockKey" TargetType="{x:Type TextBlock}">
            <Setter Property="Foreground" Value="Black" />
        </Style>
        <Style TargetType="{x:Type Ellipse}">
            <Setter Property="Opacity"         Value="0.75" />
            <Setter Property="Stroke"          Value="Gray" />
            <Setter Property="StrokeThickness" Value="2"    />
        </Style>
    </Window.Resources>
    <Grid Margin="10">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto" />
            <ColumnDefinition Width="*"    />
        </Grid.ColumnDefinitions>
        <StackPanel Grid.Column="0"
            Width="260">
            <Grid Margin="10">
                <Grid.RowDefinitions>
                    <RowDefinition Height="Auto" />
                    <RowDefinition Height="Auto" />
                    <RowDefinition Height="Auto" />
                    <RowDefinition Height="Auto" />
                    <RowDefinition Height="Auto" />
                    <RowDefinition Height="Auto" />
                </Grid.RowDefinitions>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="180" />
                    <ColumnDefinition Width="*"   />
                </Grid.ColumnDefinitions>
                <TextBlock Grid.Row="0" Grid.Column="0"
                    Style="{StaticResource HeaderTextBlockKey}"
                    Text="BaseWidth" />
                <TextBlock Grid.Row="0" Grid.Column="1"
                    Style="{StaticResource ValueTextBlockKey}"
                    Text="{Binding BaseWidth, ElementName=logicalUnitCanvas}" />
                <TextBlock Grid.Row="1" Grid.Column="0"
                    Style="{StaticResource HeaderTextBlockKey}"
                    Text="BaseHeight" />
                <TextBlock Grid.Row="1" Grid.Column="1"
                    Style="{StaticResource ValueTextBlockKey}"
                    Text="{Binding BaseHeight, ElementName=logicalUnitCanvas}" />
                <TextBlock Grid.Row="2" Grid.Column="0"
                    Style="{StaticResource HeaderTextBlockKey}"
                    Text="ActualWidth" />
                <TextBlock Grid.Row="2" Grid.Column="1"
                    Style="{StaticResource ValueTextBlockKey}"
                    Text="{Binding ActualWidth, ElementName=logicalUnitCanvas}" />
                <TextBlock Grid.Row="3" Grid.Column="0"
                    Style="{StaticResource HeaderTextBlockKey}"
                    Text="ActualHeight" />
                <TextBlock Grid.Row="3" Grid.Column="1"
                    Style="{StaticResource ValueTextBlockKey}"
                    Text="{Binding ActualHeight, ElementName=logicalUnitCanvas}" />
                <TextBlock Grid.Row="4" Grid.Column="0"
                    Style="{StaticResource HeaderTextBlockKey}"
                    Text="CurruntLogicalRatio.X" />
                <TextBlock Grid.Row="4" Grid.Column="1"
                    Style="{StaticResource ValueTextBlockKey}"
                    Text="{Binding CurruntLogicalRatio.X, ElementName=logicalUnitCanvas}" />
                <TextBlock Grid.Row="5" Grid.Column="0"
                    Style="{StaticResource HeaderTextBlockKey}"
                    Text="CurruntLogicalRatio.Y" />
                <TextBlock Grid.Row="5" Grid.Column="1"
                    Style="{StaticResource ValueTextBlockKey}"
                    Text="{Binding CurruntLogicalRatio.Y, ElementName=logicalUnitCanvas}" />
            </Grid>
        </StackPanel>
        <Border Grid.Column="1"
            BorderBrush="Black"
            BorderThickness="1">
            <local:LogicalUnitCanvas x:Name="logicalUnitCanvas"
                BaseWidth="4000"
                BaseHeight="3000">
                <Ellipse Canvas.Left="100" Canvas.Top="100"
                    local:LogicalUnitCanvas.OriginWidth="500"
                    local:LogicalUnitCanvas.OriginHeight="500"
                    Fill="Orange" />
            </local:LogicalUnitCanvas>
        </Border>
    </Grid>
</Window>

 

▶ MainWindow.xaml.cs

using System.Windows;

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

        #region 생성자 - MainWindow()

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

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

댓글을 달아 주세요