첨부 실행 코드는 나눔고딕코딩 폰트를 사용합니다.
유용한 소스 코드가 있으면 icodebroker@naver.com으로 보내주시면 감사합니다.
블로그 자료는 자유롭게 사용하세요.

■ 이미지 트리 사용하기

------------------------------------------------------------------------------------------------------------------------


TestProject.zip


ITreeNode.cs

 

 

using System.Drawing;

 

namespace TestProject

{

    /// <summary>

    /// 트리 노드 인터페이스

    /// </summary>

    public interface ITreeNode

    {

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

 

        #region 크기 구하기 - GetSize(graphics, font)

 

        /// <summary>

        /// 크기 구하기

        /// </summary>

        /// <param name="graphics">그래픽스</param>

        /// <param name="font">폰트</param>

        /// <returns>크기</returns>

        SizeF GetSize(Graphics graphics, Font font);

 

        #endregion

        #region 포인트 위치 여부 구하기 - IsAtPoint(graphics, font, centerPoint, targetPoint)

 

        /// <summary>

        /// 포인트 위치 여부 구하기

        /// </summary>

        /// <param name="graphics">그래픽스</param>

        /// <param name="font">폰트</param>

        /// <param name="centerPoint">중심 포인트</param>

        /// <param name="targetPoint">타겟 포인트</param>

        /// <returns>포인트 위치 여부</returns>

        bool IsAtPoint(Graphics graphics, Font font, PointF centerPoint, PointF targetPoint);

 

        #endregion

        #region 그리기 - Draw(x, y, graphics, pen, backgroundBrush, textBrush, font)

 

        /// <summary>

        /// 그리기

        /// </summary>

        /// <param name="x">X 좌표</param>

        /// <param name="y">Y 좌표</param>

        /// <param name="graphics">그래픽스</param>

        /// <param name="pen"></param>

        /// <param name="backgroundBrush">배경 브러시</param>

        /// <param name="textBrush">텍스트 브러시</param>

        /// <param name="font">폰트</param>

        void Draw(float x, float y, Graphics graphics, Pen pen, Brush backgroundBrush, Brush textBrush, Font font);

 

        #endregion

    }

}

 

 

TreeNode.cs

 

 

using System.Collections.Generic;

using System.Drawing;

 

namespace TestProject

{

    /// <summary>

    /// 트리 노드

    /// </summary>

    /// <typeparam name="TData">데이터 타입</typeparam>

    public class TreeNode<TData> where TData : ITreeNode

    {

        //////////////////////////////////////////////////////////////////////////////////////////////////// Field

        ////////////////////////////////////////////////////////////////////////////////////////// Public

 

        #region Field

 

        /// <summary>

        /// 데이터

        /// </summary>

        public TData Data;

 

        /// <summary>

        /// 자식 노드 리스트

        /// </summary>

        public List<TreeNode<TData>> ChildNodeList = new List<TreeNode<TData>>();

 

        /// <summary>

        /// 폰트

        /// </summary>

        public Font Font = null;

 

        /// <summary>

        ///

        /// </summary>

        public Pen Pen = Pens.Black;

 

        /// <summary>

        /// 폰트 브러시

        /// </summary>

        public Brush FontBrush = Brushes.Black;

 

        /// <summary>

        /// 배경 브러시

        /// </summary>

        public Brush BackgroundBrush = Brushes.White;

 

        /// <summary>

        /// 중심 포인트

        /// </summary>

        public PointF CenterPoint;

 

        #endregion

 

        //////////////////////////////////////////////////////////////////////////////////////////////////// Field

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

 

        #region Field

 

        /// <summary>

        /// 수평 오프셋

        /// </summary>

        private const float HORIZONTAL_OFFSET = 30;

 

        /// <summary>

        /// 수직 오프셋

        /// </summary>

        private const float VERTICAL_OFFSET = 20;

 

        #endregion

 

        //////////////////////////////////////////////////////////////////////////////////////////////////// Constructor

        ////////////////////////////////////////////////////////////////////////////////////////// Public

 

        #region 생성자 - TreeNode(data, font)

 

        /// <summary>

        /// 생성자

        /// </summary>

        /// <param name="data">데이터</param>

        /// <param name="font">폰트</param>

        public TreeNode(TData data, Font font)

        {

            Data = data;

            Font = font;

        }

 

        #endregion

        #region 생성자 - TreeNode(data)

 

        /// <summary>

        /// 생성자

        /// </summary>

        /// <param name="data">데이터</param>

        public TreeNode(TData data) : this(data, new Font("나눔고딕코딩", 12))

        {

            Data = data;

        }

 

        #endregion

 

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

        ////////////////////////////////////////////////////////////////////////////////////////// Public

 

        #region 자식 추가하기 - AddChild(childNode)

 

        /// <summary>

        /// 자식 추가하기

        /// </summary>

        /// <param name="childNode">자식 노드</param>

        public void AddChild(TreeNode<TData> childNode)

        {

            ChildNodeList.Add(childNode);

        }

 

        #endregion

        #region 배열하기 - Arrange(graphics, minimumX, minimumY)

 

        /// <summary>

        /// 배열하기

        /// </summary>

        /// <param name="graphics">그래픽스</param>

        /// <param name="minimumX">X 최소값</param>

        /// <param name="minimumY">Y 최소값</param>

        public void Arrange(Graphics graphics, ref float minimumX, ref float minimumY)

        {

            SizeF dataSize = Data.GetSize(graphics, Font);

 

            float x                 = minimumX;

            float biggestMinimumY   = minimumY + dataSize.Height;

            float childTreeMinimumY = minimumY + dataSize.Height + VERTICAL_OFFSET;

 

            foreach(TreeNode<TData> childNode in ChildNodeList)

            {

                float childMinimumY = childTreeMinimumY;

 

                childNode.Arrange(graphics, ref x, ref childMinimumY);

 

                if(biggestMinimumY < childMinimumY)

                {

                    biggestMinimumY = childMinimumY;

                }

 

                x += HORIZONTAL_OFFSET;

            }

 

            if(ChildNodeList.Count > 0)

            {

                x -= HORIZONTAL_OFFSET;

            }

 

            float childTreeWidth = x - minimumX;

 

            if(dataSize.Width > childTreeWidth)

            {

                x = minimumX + (dataSize.Width - childTreeWidth) / 2;

 

                foreach(TreeNode<TData> childNode in ChildNodeList)

                {

                    childNode.Arrange(graphics, ref x, ref childTreeMinimumY);

 

                    x += HORIZONTAL_OFFSET;

                }

 

                childTreeWidth = dataSize.Width;

            }

 

            CenterPoint = new PointF

            (

                minimumX + childTreeWidth  / 2,

                minimumY + dataSize.Height / 2

            );

 

            minimumX += childTreeWidth;

 

            minimumY = biggestMinimumY;

        }

 

        #endregion

        #region 트리 그리기 - DrawTree(graphics)

 

        /// <summary>

        /// 트리 그리기

        /// </summary>

        /// <param name="graphics">그래픽스</param>

        public void DrawTree(Graphics graphics)

        {

            DrawChildNodeLink(graphics);

 

            DrawChildNode(graphics);

        }

 

        #endregion

        #region 트리 그리기 - DrawTree(graphics, x, y)

 

        /// <summary>

        /// 트리 그리기

        /// </summary>

        /// <param name="graphics">그래픽스</param>

        /// <param name="x">X 좌표</param>

        /// <param name="y">Y 좌표</param>

        public void DrawTree(Graphics graphics, ref float x, float y)

        {

            Arrange(graphics, ref x, ref y);

 

            DrawTree(graphics);

        }

 

        #endregion

        #region 노드 구하기 - GetNode(graphics, targetPoint)

 

        /// <summary>

        /// 노드 구하기

        /// </summary>

        /// <param name="graphics">그래픽스</param>

        /// <param name="targetPoint">타겟 포인트</param>

        /// <returns>노드</returns>

        public TreeNode<TData> GetNode(Graphics graphics, PointF targetPoint)

        {

            if(Data.IsAtPoint(graphics, Font, CenterPoint, targetPoint))

            {

                return this;

            }

 

            foreach(TreeNode<TData> childNode in ChildNodeList)

            {

                TreeNode<TData> hitNode = childNode.GetNode(graphics, targetPoint);

 

                if(hitNode != null)

                {

                    return hitNode;

                }

            }

 

            return null;

        }

 

        #endregion

        #region 노드 삭제하기 - DeleteNode(targetNode)

 

        /// <summary>

        /// 노드 삭제하기

        /// </summary>

        /// <param name="targetNode">타겟 노드</param>

        /// <returns>삭제 결과</returns>

        public bool DeleteNode(TreeNode<TData> targetNode)

        {

            foreach(TreeNode<TData> childNode in ChildNodeList)

            {

                if(childNode == targetNode)

                {

                    ChildNodeList.Remove(childNode);

 

                    return true;

                }

 

                if(childNode.DeleteNode(targetNode))

                {

                    return true;

                }

            }

 

            return false;

        }

 

        #endregion

 

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

 

        #region 자식 노드 링크 그리기 - DrawChildNodeLink(graphics)

 

        /// <summary>

        /// 자식 노드 링크 그리기

        /// </summary>

        /// <param name="graphics">그래픽스</param>

        private void DrawChildNodeLink(Graphics graphics)

        {

            foreach(TreeNode<TData> childNode in ChildNodeList)

            {

                graphics.DrawLine(Pen, CenterPoint, childNode.CenterPoint);

 

                childNode.DrawChildNodeLink(graphics);

            }

        }

 

        #endregion

        #region 자식 노드 그리기 - DrawChildNode(graphics)

 

        /// <summary>

        /// 자식 노드 그리기

        /// </summary>

        /// <param name="graphics">그래픽스</param>

        private void DrawChildNode(Graphics graphics)

        {

            Data.Draw(CenterPoint.X, CenterPoint.Y, graphics, Pen, BackgroundBrush, FontBrush, Font);

 

            foreach(TreeNode<TData> childNode in ChildNodeList)

            {

                childNode.DrawChildNode(graphics);

            }

        }

 

        #endregion

    }

}

 

 

ImageNode.cs

 

 

using System.Drawing;

using System.Windows.Forms;

 

namespace TestProject

{

    /// <summary>

    /// 이미지 노드

    /// </summary>

    public class ImageNode : ITreeNode

    {

        //////////////////////////////////////////////////////////////////////////////////////////////////// Field

        ////////////////////////////////////////////////////////////////////////////////////////// Public

 

        #region Field

 

        /// <summary>

        /// 노드 크기

        /// </summary>

        public SizeF NodeSize = new SizeF(100, 100);

 

        /// <summary>

        /// 설명

        /// </summary>

        public string Description;

 

        /// <summary>

        /// 이미지

        /// </summary>

        public Image Image = null;

 

        /// <summary>

        /// 선택 여부

        /// </summary>

        public bool Selected = false;

 

        #endregion

 

        //////////////////////////////////////////////////////////////////////////////////////////////////// Field

        ////////////////////////////////////////////////////////////////////////////////////////// Public

 

        #region 생성자 - ImageNode(description, image)

 

        /// <summary>

        /// 생성자

        /// </summary>

        /// <param name="description">설명</param>

        /// <param name="image">이미지</param>

        public ImageNode(string description, Image image)

        {

            Description = description;

            Image       = image;

        }

 

        #endregion

 

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

        ////////////////////////////////////////////////////////////////////////////////////////// Public

 

        #region 크기 구하기 - GetSize(graphics, font)

 

        /// <summary>

        /// 크기 구하기

        /// </summary>

        /// <param name="graphics">그래픽스</param>

        /// <param name="font">폰트</param>

        /// <returns>크기</returns>

        public SizeF GetSize(Graphics graphics, Font font)

        {

            return NodeSize;

        }

 

        #endregion

        #region 포인트 위치 여부 구하기 - IsAtPoint(graphics, font, centerPoint, targetPoint)

 

        /// <summary>

        /// 포인트 위치 여부 구하기

        /// </summary>

        /// <param name="graphics">그래픽스</param>

        /// <param name="font">폰트</param>

        /// <param name="centerPoint">중심 포인트</param>

        /// <param name="targetPoint">타겟 포인트</param>

        /// <returns>포인트 위치 여부</returns>

        public bool IsAtPoint(Graphics graphics, Font font, PointF centerPoint, PointF targetPoint)

        {

            RectangleF rectangle = GetNodeRectangle(centerPoint);

 

            return rectangle.Contains(targetPoint);

        }

 

        #endregion

        #region 그리기 - Draw(x, y, graphics, pen, backgroundBrush, textBrush, font)

 

        /// <summary>

        /// 그리기

        /// </summary>

        /// <param name="x">X 좌표</param>

        /// <param name="y">Y 좌표</param>

        /// <param name="graphics">그래픽스</param>

        /// <param name="pen"></param>

        /// <param name="backgroundBrush">배경 브러시</param>

        /// <param name="textBrush">텍스트 브러시</param>

        /// <param name="font">폰트</param>

        public void Draw(float x, float y, Graphics graphics, Pen pen, Brush backgroundBrush, Brush textBrush, Font font)

        {

            RectangleF nodeRectangle       = GetNodeRectangle(new PointF(x, y));

            Rectangle  actualNodeRectangle = Rectangle.Round(nodeRectangle);

 

            if(Selected)

            {

                graphics.FillRectangle(Brushes.White, actualNodeRectangle);

 

                ControlPaint.DrawBorder3D

                (

                    graphics,

                    actualNodeRectangle,

                    Border3DStyle.Sunken

                );

            }

            else

            {

                graphics.FillRectangle(Brushes.LightGray, actualNodeRectangle);

 

                ControlPaint.DrawBorder3D

                (

                    graphics,

                    actualNodeRectangle,

                    Border3DStyle.Raised

                );

            }

 

            nodeRectangle.Inflate(-5, -5);

 

            nodeRectangle = GetImageRectangle(Image, nodeRectangle);

 

            graphics.DrawImage(Image, nodeRectangle);

        }

 

        #endregion

 

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

 

        #region 노드 사각형 구하기 - GetNodeRectangle(centerPoint)

 

        /// <summary>

        /// 노드 사각형 구하기

        /// </summary>

        /// <param name="centerPoint">중심 포인트</param>

        /// <returns>노드 사각형</returns>

        private RectangleF GetNodeRectangle(PointF centerPoint)

        {

            return new RectangleF

            (

                centerPoint.X - NodeSize.Width  / 2,

                centerPoint.Y - NodeSize.Height / 2,

                NodeSize.Width,

                NodeSize.Height

            );

        }

 

        #endregion

        #region 이미지 사각형 구하기 - GetImageRectangle(image, layoutRectangle)

 

        /// <summary>

        /// 이미지 사각형 구하기

        /// </summary>

        /// <param name="image">이미지</param>

        /// <param name="layoutRectangle">레이아웃 사각형</param>

        /// <returns>이미지 사각형</returns>

        private RectangleF GetImageRectangle(Image image, RectangleF layoutRectangle)

        {

            float imageWidth           = image.Width;

            float imageHeight          = image.Height;

            float imageAspectRatio     = imageWidth / imageHeight;

            float rectangleAspectRatio = layoutRectangle.Width / layoutRectangle.Height;

            float scale = 1;

 

            if(imageAspectRatio > rectangleAspectRatio)

            {

                scale = layoutRectangle.Width / imageWidth;

            }

            else

            {

                scale = layoutRectangle.Height / imageHeight;

            }

 

            imageWidth  *= scale;

            imageHeight *= scale;

 

            RectangleF imageRectangle = new RectangleF

            (

                layoutRectangle.X + (layoutRectangle.Width  - imageWidth ) / 2,

                layoutRectangle.Y + (layoutRectangle.Height - imageHeight) / 2,

                imageWidth,

                imageHeight

            );

 

            return imageRectangle;

        }

 

        #endregion

    }

}

 

 

MainForm.cs

 

 

using System;

using System.Drawing;

using System.Drawing.Drawing2D;

using System.Drawing.Text;

using System.Windows.Forms;

 

namespace TestProject

{

    /// <summary>

    /// 메인 폼

    /// </summary>

    public partial class MainForm : Form

    {

        //////////////////////////////////////////////////////////////////////////////////////////////////// Field

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

 

        #region Field

 

        /// <summary>

        /// 루트 노드

        /// </summary>

        private TreeNode<ImageNode> rootNode = new TreeNode<ImageNode>

        (

            new ImageNode

            (

                "Queen Elizabeth II && Prince Philip Duke of Edinburgh",

                Properties.Resources.Elizabeth_Philip

            )

        );

 

        /// <summary>

        /// 선택 노드

        /// </summary>

        private TreeNode<ImageNode> selectedNode;

 

        #endregion

 

        //////////////////////////////////////////////////////////////////////////////////////////////////// Constructor

        ////////////////////////////////////////////////////////////////////////////////////////// Public

 

        #region 생성자 - MainForm()

 

        /// <summary>

        /// 생성자

        /// </summary>

        public MainForm()

        {

            InitializeComponent();

 

            #region 이벤트를 설정한다.

 

            Load                             += Form_Load;

            this.canvasPictureBox.Paint      += canvasPictureBox_Paint;

            this.canvasPictureBox.MouseClick += canvasPictureBox_MouseClick;

            this.canvasPictureBox.Resize     += canvasPictureBox_Resize;

 

            #endregion

        }

 

        #endregion

 

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

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

        //////////////////////////////////////////////////////////////////////////////// Event

 

        #region 폼 로드시 처리하기 - Form_Load(sender, e)

 

        /// <summary>

        /// 폼 로드시 처리하기

        /// </summary>

        /// <param name="sender">이벤트 발생자</param>

        /// <param name="e">이벤트 인자</param>

        private void Form_Load(object sender, EventArgs e)

        {

            TreeNode<ImageNode> charlesNode = new TreeNode<ImageNode>

            (

                new ImageNode

                (

                    "Prince Charles, Prince of Wales",

                    Properties.Resources.Charles

                )

            );

 

            TreeNode<ImageNode> anneNode = new TreeNode<ImageNode>

            (

                new ImageNode

                (

                    "Princess Anne",

                    Properties.Resources.Anne

                )

            );

 

            TreeNode<ImageNode> andrewNode = new TreeNode<ImageNode>

            (

                new ImageNode

                (

                    "Prince Andrew, Duke of York",

                    Properties.Resources.Andrew

                )

            );

 

            TreeNode<ImageNode> edwardNode = new TreeNode<ImageNode>

            (

                new ImageNode

                (

                    "Prince Edward, Earl of Wessex",

                    Properties.Resources.Edward

                )

            );

 

            TreeNode<ImageNode> williamNode = new TreeNode<ImageNode>

            (

                new ImageNode

                (

                    "Prince William",

                    Properties.Resources.William

                )

            );

 

            TreeNode<ImageNode> harryNode = new TreeNode<ImageNode>

            (

                new ImageNode

                (

                    "Prince Henry (Harry)",

                    Properties.Resources.Harry

                )

            );

 

            TreeNode<ImageNode> peterNode = new TreeNode<ImageNode>

            (

                new ImageNode

                (

                    "Peter Phillips",

                    Properties.Resources.Peter

                )

            );

 

            TreeNode<ImageNode> zaraNode = new TreeNode<ImageNode>

            (

                new ImageNode

                (

                    "Zara Phillips",

                    Properties.Resources.Zara

                )

            );

 

            TreeNode<ImageNode> beatriceNode = new TreeNode<ImageNode>

            (

                new ImageNode

                (

                    "Princess Beatrice",

                    Properties.Resources.Beatrice

                )

            );

 

            TreeNode<ImageNode> eugenieNode = new TreeNode<ImageNode>

            (

                new ImageNode

                (

                    "Princess Eugenie",

                    Properties.Resources.Eugenie

                )

            );

 

            TreeNode<ImageNode> louiseNode = new TreeNode<ImageNode>

            (

                new ImageNode

                (

                    "Lady Louise",

                    Properties.Resources.Louise

                )

            );

 

            TreeNode<ImageNode> severnNode = new TreeNode<ImageNode>

            (

                new ImageNode

                (

                    "Viscount Severn",

                    Properties.Resources.Severn

                )

            );

 

            this.rootNode.AddChild(charlesNode);

 

            charlesNode.AddChild(williamNode);

            charlesNode.AddChild(harryNode);

 

            this.rootNode.AddChild(anneNode);

 

            anneNode.AddChild(peterNode);

            anneNode.AddChild(zaraNode);

 

            this.rootNode.AddChild(andrewNode);

 

            andrewNode.AddChild(beatriceNode);

            andrewNode.AddChild(eugenieNode);

 

            this.rootNode.AddChild(edwardNode);

 

            edwardNode.AddChild(louiseNode);

            edwardNode.AddChild(severnNode);

 

            ArrangeTree();

        }

 

        #endregion

        #region 캔버스 픽처 박스 크기 조정시 처리하기 - canvasPictureBox_Resize(sender, e)

 

        /// <summary>

        /// 캔버스 픽처 박스 크기 조정시 처리하기

        /// </summary>

        /// <param name="sender">이벤트 발생자</param>

        /// <param name="e">이벤트 인자</param>

        private void canvasPictureBox_Resize(object sender, EventArgs e)

        {

            ArrangeTree();

        }

 

        #endregion

        #region 캔버스 픽처 박스 페인트시 처리하기 - canvasPictureBox_Paint(sender, e)

 

        /// <summary>

        /// 캔버스 픽처 박스 페인트시 처리하기

        /// </summary>

        /// <param name="sender">이벤트 발생자</param>

        /// <param name="e">이벤트 인자</param>

        private void canvasPictureBox_Paint(object sender, PaintEventArgs e)

        {

            e.Graphics.SmoothingMode     = SmoothingMode.AntiAlias;

            e.Graphics.TextRenderingHint = TextRenderingHint.AntiAliasGridFit;

 

            this.rootNode.DrawTree(e.Graphics);

        }

 

        #endregion

        #region 캔버스 픽처 박스 마우스 클릭시 처리하기 - canvasPictureBox_MouseClick(sender, e)

 

        /// <summary>

        /// 캔버스 픽처 박스 마우스 클릭시 처리하기

        /// </summary>

        /// <param name="sender">이벤트 발생자</param>

        /// <param name="e">이벤트 인자</param>

        private void canvasPictureBox_MouseClick(object sender, MouseEventArgs e)

        {

            FindNodeUnderMouse(e.Location);

        }

 

        #endregion

 

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

 

        #region 트리 배열하기 - ArrangeTree()

 

        /// <summary>

        /// 트리 배열하기

        /// </summary>

        private void ArrangeTree()

        {

            using(Graphics graphics = this.canvasPictureBox.CreateGraphics())

            {

                float minimumX = 0f;

                float minimumY = 0f;

 

                this.rootNode.Arrange(graphics, ref minimumX, ref minimumY);

 

                minimumX = (ClientSize.Width - minimumX) / 2;

                minimumY = 10;

 

                this.rootNode.Arrange(graphics, ref minimumX, ref minimumY);

            }

 

            this.canvasPictureBox.Refresh();

        }

 

        #endregion

        #region 마우스 아래 노드 찾기 - FindNodeUnderMouse(point)

 

        /// <summary>

        /// 마우스 아래 노드 찾기

        /// </summary>

        /// <param name="point">포인트</param>

        private void FindNodeUnderMouse(PointF point)

        {

            if(this.selectedNode != null)

            {

                this.selectedNode.Data.Selected = false;

 

                this.statusLabel.Text = string.Empty;

            }

 

            using(Graphics graphics = this.canvasPictureBox.CreateGraphics())

            {

                this.selectedNode = this.rootNode.GetNode(graphics, point);

            }

 

            if(this.selectedNode != null)

            {

                this.selectedNode.Data.Selected = true;

 

                this.statusLabel.Text = this.selectedNode.Data.Description;

            }

 

            this.canvasPictureBox.Refresh();

        }

 

        #endregion

    }

}

 

------------------------------------------------------------------------------------------------------------------------

Posted by 사용자 icodebroker

댓글을 달아 주세요