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

728x90
반응형
728x170

TestProject.zip
다운로드

▶ Employees.xml

<Employees xmlns="">
    <Employee Name="Betty">
        <BirthDate>1970/08/31</BirthDate>
        <Face>IMAGE/Betty.png</Face>
        <LeftHanded>False</LeftHanded>
    </Employee>
    <Employee Name="Edgar">
        <BirthDate>1965/02/02</BirthDate>
        <Face>IMAGE/Edgar.png</Face>
        <LeftHanded>True</LeftHanded>
    </Employee>
    <Employee Name="Sally">
        <BirthDate>1980/07/12</BirthDate>
        <Face>IMAGE/Sally.png</Face>
        <LeftHanded>True</LeftHanded>
    </Employee>
    <Employee Name="Jim">
        <BirthDate>1975/06/15</BirthDate>
        <Face>IMAGE/Jim.png</Face>
        <LeftHanded>False</LeftHanded>
    </Employee>
    <Employee Name="Anne">
        <BirthDate>1975/04/07</BirthDate>
        <Face>IMAGE/Anne.png</Face>
        <LeftHanded>True</LeftHanded>
    </Employee>
    <Employee Name="John">
        <BirthDate>1955/12/02</BirthDate>
        <Face>IMAGE/John.png</Face>
        <LeftHanded>False</LeftHanded>
    </Employee>
</Employees>

 

728x90

 

▶ RadialPanelOrientation.cs

namespace TestProject
{
    /// <summary>
    /// 방사형 패널 방향
    /// </summary>
    public enum RadialPanelOrientation
    {
        /// <summary>
        /// 너비
        /// </summary>
        ByWidth,

        /// <summary>
        /// 높이
        /// </summary>
        ByHeight
    }
}

 

300x250

 

▶ RadialPanel.cs

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

namespace TestProject
{
    /// <summary>
    /// 방사형 패널
    /// </summary>
    public class RadialPanel : Panel
    {
        //////////////////////////////////////////////////////////////////////////////////////////////////// Dependency Property
        ////////////////////////////////////////////////////////////////////////////////////////// Static
        //////////////////////////////////////////////////////////////////////////////// Public

        #region 방향 속성 - OrientationProperty

        /// <summary>
        /// 방향 속성
        /// </summary>
        public static readonly DependencyProperty OrientationProperty;

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Field
        ////////////////////////////////////////////////////////////////////////////////////////// Private

        #region Field

        /// <summary>
        /// 파이 선 보여주기 여부
        /// </summary>
        private bool showPieLines;

        /// <summary>
        /// 가장 큰 자식 크기
        /// </summary>
        private Size largestChildSize;

        /// <summary>
        /// 개별 각도
        /// </summary>
        private double eachAngle;

        /// <summary>
        /// 반지름
        /// </summary>
        private double radius;

        /// <summary>
        /// 중심점에서 외부 가장자리
        /// </summary>
        private double outerEdgeFromCenter;

        /// <summary>
        /// 중심점에서 내부 가장자리
        /// </summary>
        private double innerEdgeFromCenter;

        #endregion

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

        #region 방향 - Orientation

        /// <summary>
        /// 방향
        /// </summary>
        public RadialPanelOrientation Orientation
        {
            set
            {
                SetValue(OrientationProperty, value);
            }
            get
            {
                return (RadialPanelOrientation)GetValue(OrientationProperty);
            }
        }

        #endregion
        #region 파이 선 보여주기 여부 - ShowPieLines

        /// <summary>
        /// 파이 선 보여주기 여부
        /// </summary>
        public bool ShowPieLines
        {
            set
            {
                if(value != this.showPieLines)
                {
                    InvalidateVisual();
                }

                this.showPieLines = value;
            }
            get
            {
                return this.showPieLines;
            }
        }

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Constructor
        ////////////////////////////////////////////////////////////////////////////////////////// Static

        #region 생성자 - RadialPanel()

        /// <summary>
        /// 생성자
        /// </summary>
        static RadialPanel()
        {
            OrientationProperty = DependencyProperty.Register
            (
                "Orientation",
                typeof(RadialPanelOrientation),
                typeof(RadialPanel),
                new FrameworkPropertyMetadata
                (
                    RadialPanelOrientation.ByWidth,
                    FrameworkPropertyMetadataOptions.AffectsMeasure
                )
            );
        }

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Method
        ////////////////////////////////////////////////////////////////////////////////////////// Protected

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

        /// <summary>
        /// 측정하기 (오버라이딩)
        /// </summary>
        /// <param name="availableSize">이용 가능한 크기</param>
        /// <returns>희망 크기</returns>
        protected override Size MeasureOverride(Size availableSize)
        {
            if(InternalChildren.Count == 0)
            {
                return new Size(0, 0);
            }

            this.eachAngle = 360d / InternalChildren.Count;

            this.largestChildSize = new Size(0, 0);

            foreach(UIElement child in InternalChildren)
            {
                child.Measure
                (
                    new Size
                    (
                        Double.PositiveInfinity,
                        Double.PositiveInfinity
                    )
                );

                this.largestChildSize.Width  = Math.Max(this.largestChildSize.Width , child.DesiredSize.Width );
                this.largestChildSize.Height = Math.Max(this.largestChildSize.Height, child.DesiredSize.Height);
            }

            if(Orientation == RadialPanelOrientation.ByWidth)
            {
                this.innerEdgeFromCenter = this.largestChildSize.Width / 2d / Math.Tan(Math.PI * this.eachAngle / 360d);
                this.outerEdgeFromCenter = this.innerEdgeFromCenter + this.largestChildSize.Height;
                this.radius              = Math.Sqrt
                (
                    Math.Pow(this.largestChildSize.Width / 2d, 2d) + Math.Pow(this.outerEdgeFromCenter, 2d)
                );
            }
            else
            {
                this.innerEdgeFromCenter = this.largestChildSize.Height / 2d / Math.Tan(Math.PI * this.eachAngle / 360d);
                this.outerEdgeFromCenter = this.innerEdgeFromCenter + this.largestChildSize.Width;
                this.radius              = Math.Sqrt
                (
                    Math.Pow(this.largestChildSize.Height / 2d, 2d) + Math.Pow(this.outerEdgeFromCenter, 2d)
                );
            }

            return new Size(2 * this.radius, 2 * this.radius);
        }

        #endregion
        #region 정렬하기 (오버라이딩) - ArrangeOverride(finalSize)

        /// <summary>
        /// 정렬하기 (오버라이딩)
        /// </summary>
        /// <param name="finalSize">최종 크기</param>
        /// <returns>최종 크기</returns>
        protected override Size ArrangeOverride(Size finalSize)
        {
            double childAngle  = 0;
            Point  centerPoint = new Point(finalSize.Width / 2d, finalSize.Height / 2d);
            double multiplier  = Math.Min
            (
                finalSize.Width  / (2d * this.radius),
                finalSize.Height / (2d * this.radius)
            );

            foreach(UIElement child in InternalChildren)
            {
                child.RenderTransform = Transform.Identity;

                if(Orientation == RadialPanelOrientation.ByWidth)
                {
                    child.Arrange
                    (
                        new Rect
                        (
                            centerPoint.X - multiplier * this.largestChildSize.Width / 2,
                            centerPoint.Y - multiplier * this.outerEdgeFromCenter,
                            multiplier * this.largestChildSize.Width,
                            multiplier * this.largestChildSize.Height
                        )
                    );
                }
                else
                {
                    child.Arrange
                    (
                        new Rect
                        (
                            centerPoint.X + multiplier * this.innerEdgeFromCenter,
                            centerPoint.Y - multiplier * this.largestChildSize.Height / 2,
                            multiplier * this.largestChildSize.Width,
                            multiplier * this.largestChildSize.Height
                        )
                    );
                }

                Point childRelativeCenterPoint = TranslatePoint(centerPoint, child);

                child.RenderTransform = new RotateTransform
                (
                    childAngle,
                    childRelativeCenterPoint.X,
                    childRelativeCenterPoint.Y
                );

                childAngle += this.eachAngle;
            }

            return finalSize;
        }

        #endregion
        #region 렌더링시 처리하기 - OnRender(drawingContext)

        /// <summary>
        /// 렌더링시 처리하기
        /// </summary>
        /// <param name="drawingContext">드로잉 컨텍스트</param>
        protected override void OnRender(DrawingContext drawingContext)
        {
            base.OnRender(drawingContext);

            if(ShowPieLines)
            {
                Point centerPoint = new Point(RenderSize.Width / 2d, RenderSize.Height / 2d);

                double multiplier = Math.Min
                (
                    RenderSize.Width  / (2d * this.radius),
                    RenderSize.Height / (2d * this.radius)
                );

                Pen pen = new Pen(SystemColors.WindowTextBrush, 1d);

                pen.DashStyle = DashStyles.Dash;

                drawingContext.DrawEllipse
                (
                    null,
                    pen,
                    centerPoint,
                    multiplier * this.radius,
                    multiplier * this.radius
                );

                double childAngle = -this.eachAngle / 2;

                if(Orientation == RadialPanelOrientation.ByWidth)
                {
                    childAngle += 90;
                }

                foreach(UIElement child in InternalChildren)
                {
                    drawingContext.DrawLine
                    (
                        pen, centerPoint,
                        new Point
                        (
                            centerPoint.X + multiplier * this.radius * Math.Cos(2 * Math.PI * childAngle / 360),
                            centerPoint.Y + multiplier * this.radius * Math.Sin(2 * Math.PI * childAngle / 360)
                        )
                    );

                    childAngle += this.eachAngle;
                }
            }
        }

        #endregion
    }
}

 

▶ 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="ItemsPanelTemplate 엘리먼트 : ListBox 엘리먼트의 ItemPanel 속성에 커스텀 패널 설정하기"
    FontFamily="나눔고딕코딩"
    FontSize="16">
    <Window.Resources>
        <XmlDataProvider x:Key="EmployeeXmlDataProviderKey"
            Source="Employees.xml"
            XPath="Employees" />
    </Window.Resources>
    <Grid>
        <ListBox Name="listBox"
            HorizontalAlignment="Center"
            VerticalAlignment="Center"
            ItemsSource="{Binding Source={StaticResource EmployeeXmlDataProviderKey}, XPath=Employee}"
            SelectedValuePath="Face">
            <ListBox.ItemsPanel>
                <ItemsPanelTemplate>
                    <local:RadialPanel Orientation="ByHeight" />
                </ItemsPanelTemplate>
            </ListBox.ItemsPanel>
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <DockPanel Margin="3">
                        <Image DockPanel.Dock="Right"
                            Stretch="None"
                            Source="{Binding XPath=Face}" />
                        <UniformGrid Rows="3"
                            VerticalAlignment="Center" 
                            Margin="10">
                            <TextBlock
                                TextAlignment="Center" 
                                Text="{Binding XPath=@Name}" />
                            <TextBlock
                                TextAlignment="Center"
                                FontSize="12pt"
                                Text="{Binding XPath=BirthDate}" />
                            <TextBlock Name="leftHandedTextBlock"
                                TextAlignment="Center"
                                Text="Right-Handed" />
                        </UniformGrid>
                    </DockPanel>
                    <DataTemplate.Triggers>
                        <DataTrigger Binding="{Binding XPath=LeftHanded}" Value="True">
                            <Setter
                                TargetName="leftHandedTextBlock"
                                Property="Text"
                                Value="Left-Handed" />
                        </DataTrigger>
                    </DataTemplate.Triggers>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>
        <Image
            HorizontalAlignment="Center"
            VerticalAlignment="Center" 
            Stretch="None"
            Source="{Binding ElementName=listBox, Path=SelectedValue}" />
    </Grid>
</Window>
728x90
반응형
그리드형(광고전용)
Posted by icodebroker
TAG , ,

댓글을 달아 주세요