1. Ana Sayfa
  2. C Sharp

C# Soy Ağacı Çizimi – Draw Family Tree with TreeNode

C# Soy Ağacı Çizimi – Draw Family Tree with TreeNode
C# Soy Ağacı Çizimi
+ - 0

Merhaba, C# Soy Ağacı Çizimi yazım ile C# eğitim setime devam ediyorum. C# ile neler yapılabileceği konusunda araştırmalar yaparken keşfettiğim bir konu oldu. Aslında aradığım şey tam olarak bu değildi. Bu kodları kullanarak çok farklı şeyler çıkarmıştım ama şimdilik sadece başlıkta yazan konuya ait kodları vereceğim.

C# Soy Ağacı Çizimi

Hiç C# ile soy ağacı çizilip çizilmeyeceğini düşündünüz mü? Bende düşünmemiştim. C# ile neler yapılabileceği konusunda araştırmalar yaparken keşfettiğim bir konu oldu. Elbette ben bu kodları çok farklı şekilde kullandım ama konumuz bu değil :)

C# Soy Ağacı Çizimi

Şimdi kodları vereceğim.

İlk olarak bir interface tanımlayacağız.

 

using System.Drawing;

namespace Soy_Agaci_Cizimi_Family_Tree
{
    // Bir TreeNode'un çizebileceği bir şeyi temsil eder.
    interface IDrawable
    {
        // Nesnenin gerekli boyutunu döndürün.
        SizeF GetSize(Graphics gr, Font font);

        // Düğüm bu noktanın üzerindeyse true değerini döndürün.
        bool IsAtPoint(Graphics gr, Font font, PointF center_pt, PointF target_pt);

        // (x, y) merkezli nesneyi çizin.
        void Draw(float x, float y, Graphics gr, Pen pen,
            Brush bg_brush, Brush text_brush, Font font);
    }
}

Hazırladığımız interface ‘den bir picturenode (resim düğümü) türetiyoruz.

 

using System.Drawing;
using System.Windows.Forms;

namespace Soy_Agaci_Cizimi_Family_Tree
{
    class PictureNode : IDrawable
    {
        // Constructor.
        public Image Picture = null;
        public string Description;
        public bool Selected = false;
        public PictureNode(string description, Image picture)
        {
            Description = description;
            Picture = picture;

            // Test için
            //NodeSize = new SizeF(Rand.Next(50, 150), Rand.Next(50, 150));
        }

        // Çizilen dikdörtgenlerin boyutu.
        public SizeF NodeSize = new SizeF(100, 100);

        // Test için
        //private static Random Rand = new Random();

        // Bu düğümün ihtiyaç duyduğu boyutu döndürün.
        public SizeF GetSize(Graphics gr, Font font)
        {
            return NodeSize;
        }

        //Düğümün konumunu veren bir RectangleF döndürün.
        private RectangleF Location(PointF center)
        {
            return new RectangleF(
                center.X - NodeSize.Width / 2,
                center.Y - NodeSize.Height / 2,
                NodeSize.Width, NodeSize.Height);
        }

        // Hedef bu düğümün altındaysa True döndürün.
        public bool IsAtPoint(Graphics gr, Font font, PointF center_pt, PointF target_pt)
        {
            RectangleF rect = Location(center_pt);
            return rect.Contains(target_pt);
        }

        //Kişiyi çizin.
        public void Draw(float x, float y, Graphics gr, Pen pen, Brush bg_brush, Brush text_brush, Font font)
        {
            // Bir sınır çizin.
            RectangleF rectf = Location(new PointF(x, y));
            Rectangle rect = Rectangle.Round(rectf);
            if (Selected)
            {
                gr.FillRectangle(Brushes.White, rect);
                ControlPaint.DrawBorder3D(gr, rect,
                    Border3DStyle.Sunken);
            }
            else
            {
                gr.FillRectangle(Brushes.LightGray, rect);
                ControlPaint.DrawBorder3D(gr, rect,
                    Border3DStyle.Raised);
            }

            //Resmi çiz.
            rectf.Inflate(-5, -5);
            rectf = PositionImage(Picture, rectf);
            gr.DrawImage(Picture, rectf);
        }

        // Dikdörtgende ortalanmış görüntüyü uzatmadan olabildiğince büyük çizmek için bir dikdörtgen bulun.
        private RectangleF PositionImage(Image picture, RectangleF rect)
        {
            //X ve Y ölçeklerini alın.
            float pic_wid = picture.Width;
            float pic_hgt = picture.Height;
            float pic_aspect = pic_wid / pic_hgt;
            float rect_aspect = rect.Width / rect.Height;
            float scale = 1;
            if (pic_aspect > rect_aspect)
            {
                scale = rect.Width / pic_wid;
            }
            else
            {
                scale = rect.Height / pic_hgt;
            }

            //  nereye çizmemiz gerektiğine bakın
            pic_wid *= scale;
            pic_hgt *= scale;
            RectangleF drawing_rect = new RectangleF(
                rect.X + (rect.Width - pic_wid) / 2,
                rect.Y + (rect.Height - pic_hgt) / 2,
                pic_wid, pic_hgt);
            return drawing_rect;
        }
    }
}

Yine interface’imizi kullanarak yeni bir class türetiyoruz.

using System.Collections.Generic;
using System.Drawing;

namespace Soy_Agaci_Cizimi_Family_Tree
{
    class TreeNode<T> where T : IDrawable
    {
        // Veri.
        public T Data;

        //Ağaçtaki alt düğümler.
        public List<TreeNode<T>> Children = new List<TreeNode<T>>();

        // Kardeşler arasında yatay olarak ve nesiller arasında dikey olarak atlamak için boşluk.
        private const float Hoffset = 5;
        private const float Voffset = 30;

        // Düzenlemeden sonra düğümün merkezi.
        private PointF Center;

        // Çizim özellikleri.
        public Font MyFont = null;
        public Pen MyPen = Pens.Black;
        public Brush FontBrush = Brushes.Black;
        public Brush BgBrush = Brushes.White;

        // Constructor.
        public TreeNode(T new_data)
            : this(new_data, new Font("Times New Roman", 12))
        {
            Data = new_data;
        }
        public TreeNode(T new_data, Font fg_font)
        {
            Data = new_data;
            MyFont = fg_font;
        }

        // Çocuklar listesine bir TreeNode ekleyin.
        public void AddChild(TreeNode<T> child)
        {
            Children.Add(child);
        }

        // Düğümü ve alt öğelerini izin verilen alana yerleştirin.
        // Alt ağacımızın sağ kenarını belirtmek için xmin'i ayarlayın.
        // Alt ağacımızın alt kenarını belirtmek için ymin'i ayarlayın.
        public void Arrange(Graphics gr, ref float xmin, ref float ymin)
        {
            // Bu düğümün ne kadar büyük olduğunu görün.
            SizeF my_size = Data.GetSize(gr, MyFont);

            // Çocuklarımızı yinelemeli olarak düzenleyin, bu düğüme yer bırakın.
            float x = xmin;
            float biggest_ymin = ymin + my_size.Height;
            float subtree_ymin = ymin + my_size.Height + Voffset;
            foreach (TreeNode<T> child in Children)
            {
                //Bu çocuğun alt ağacını düzenleyin.
                float child_ymin = subtree_ymin;
                child.Arrange(gr, ref x, ref child_ymin);

                // Bunun en büyük ymin değerini artırıp artırmadığına bakın.
                if (biggest_ymin < child_ymin) biggest_ymin = child_ymin;

                //Bir sonraki kardeşten önce yer açın.
                x += Hoffset;
            }

            // Son çocuktan sonraki boşluğu kaldırın.
            if (Children.Count > 0) x -= Hoffset;

            // Bu düğümün altındaki alt ağaçtan daha geniş olup olmadığına bakın.
            float subtree_width = x - xmin;
            if (my_size.Width > subtree_width)
            {
                // Alt ağacı bu düğümün altında ortalayın.
                // Çocukların alt ağaçlarını ortalayacak şekilde kendilerini yeniden düzenlemelerini sağlayın.
                x = xmin + (my_size.Width - subtree_width) / 2;
                foreach (TreeNode<T> child in Children)
                {
                    // Bu çocuğun alt ağacını düzenleyin.
                    child.Arrange(gr, ref x, ref subtree_ymin);

                    //Bir sonraki kardeşten önce yer açın.
                    x += Hoffset;
                }

                //Alt ağacın genişliği, bu düğümün genişliğidir.
                subtree_width = my_size.Width;
            }

            // Bu düğümün merkez konumunu ayarlayın.
            Center = new PointF(
                xmin + subtree_width / 2,
                ymin + my_size.Height / 2);

            // Geri dönmeden önce alt ağaç için yer açmak için xmin değerini artırın.
            xmin += subtree_width;

            // ymin için dönüş değerini ayarlayın.
            ymin = biggest_ymin;
        }

        //Verilen sol üst köşe ile bu düğümde köklenen alt ağacı çizin.
        public void DrawTree(Graphics gr, ref float x, float y)
        {
            // Ağacı düzenleyin.
            Arrange(gr, ref x, ref y);

            // Ağacı çizin.
            DrawTree(gr);
        }

        // Kökü bu düğümde olan alt ağacı çizin.
        public void DrawTree(Graphics gr)
        {
            // Bağlantıları çizin.
            DrawSubtreeLinks(gr);

            // Düğümleri çizin.
            DrawSubtreeNodes(gr);
        }

        // Kökü bu düğümde olan alt ağaç için bağlantıları çizin.
        private void DrawSubtreeLinks(Graphics gr)
        {
            // Bak bakalım 1 çocuğumuz var mı?
            if (Children.Count == 1)
            {
                //Sadece merkezleri bağlayın.
                gr.DrawLine(MyPen, Center, Children[0].Center);
            }
            else if (Children.Count > 1)
            {
                // Çocukların üzerine yatay bir çizgi çizin.
                float xmin = Children[0].Center.X;
                float xmax = Children[Children.Count - 1].Center.X;
                SizeF my_size = Data.GetSize(gr, MyFont);
                float y = Center.Y + my_size.Height / 2 + Voffset / 2f;
                gr.DrawLine(MyPen, xmin, y, xmax, y);

                // Dikey çizgiyi ebeveynden yatay çizgiye çizin.
                gr.DrawLine(MyPen, Center.X, Center.Y, Center.X, y);

                //Çocuklara yatay çizgiden çizgiler çizin.
                foreach (TreeNode<T> child in Children)
                {
                    gr.DrawLine(MyPen,
                        child.Center.X, y,
                        child.Center.X, child.Center.Y);
                }
            }

            // Çocukların alt ağaçlarını yinelemeli olarak çizmelerini sağlayın.
            foreach (TreeNode<T> child in Children)
            {
                child.DrawSubtreeLinks(gr);
            }
        }

        // Bu düğümde köklenen alt ağaç için düğümleri çizin.
        private void DrawSubtreeNodes(Graphics gr)
        {
            // Bu düğümü çizin.
            Data.Draw(Center.X, Center.Y, gr, MyPen, BgBrush, FontBrush, MyFont);

            //Çocuğun alt ağaç düğümlerini yinelemeli olarak çizmesini sağlayın.
            foreach (TreeNode<T> child in Children)
            {
                child.DrawSubtreeNodes(gr);
            }
        }

        // Bu noktada TreeNode'u döndürün (veya orada yoksa null).
        public TreeNode<T> NodeAtPoint(Graphics gr, PointF target_pt)
        {
            // Noktanın bu düğümün altında olup olmadığına bakın.
            if (Data.IsAtPoint(gr, MyFont, Center, target_pt)) return this;

            // Noktanın alt ağaçtaki bir düğümün altında olup olmadığına bakın.
            foreach (TreeNode<T> child in Children)
            {
                TreeNode<T> hit_node = child.NodeAtPoint(gr, target_pt);
                if (hit_node != null) return hit_node;
            }

            return null;
        }

        // Bu düğümün alt ağacından bir hedef düğümü silin.
        // Düğümü silersek true değerini döndürür.
        public bool DeleteNode(TreeNode<T> target)
        {
            // Hedefin alt ağacımızda olup olmadığına bakın.
            foreach (TreeNode<T> child in Children)
            {
                // Çocuk olup olmadığına bakın.
                if (child == target)
                {
                    // Bu çocuğu sil.
                    Children.Remove(child);
                    return true;
                }

                // Çocuğun alt ağacında olup olmadığına bakın.
                if (child.DeleteNode(target)) return true;
            }

            // Alt ağacımızda yok.
            return false;
        }
    }
}

Yukarıdaki kodlarımız görselde gördüğünüz gibi bize çizimi verecektir.

Arkadaşlar bu kodları kaynak siteden alarak kendi github profilime de ekledim. Ben bu kodları bir şirket için organizasyon şeması üretmek ve bir projem bir veri takım verilerin görselleştirilmesi amacı ile kullanmıştım. Sizin için ne gibi bir konuda faydası olur bilmiyorum. Belki yeni fikirler verebilir.


Bu yazımda bu kadardı arkadaşlar. Diğer yazılarımızda görüşmek üzere…

C Sharp Eğitim Seti eğitimi sayfasına gitmek için tıklayınız.  Derslerime özel olarak hazırladığım Github Projeme buradan ulaşabilirsiniz… Ayrıca bu projenin Github Sayfasına bağlantıya tıklayarak ulaşabilirsiniz.

Arkadaşlar ayrıca Telegram kanalıma abone olabilir ve yeni yazılardan anında haberdar olabilirisiniz. https://t.me/mbchsarpsqlyardim. Aynı zamanda sorular sorabileceğiniz chat grubumuz da mevcuttur.

Discord’a katılmayı unutmayın.

Sağlıcakla ve kodla kalın….

Kaynak: CSharpHelper

Bu yazıya tepkiniz ne oldu?

Yazar Hakkında

Lise Ağ Sistemleri ve Yönetimi bölümü, üniversite Bilgisayar Programcılığı bölümü Ön Lisans, Yönetim Bilişim Sistemleri Lisans öğrenimi aldım. Askerlik görevimi tamamladım. Uzmanlık alanım; C# ve SQL Programlama dilleri ile müşteri odaklı, kullanıcı dostu ERP ve CRM gibi sistemleri geliştirmektir. Ayrıca şuanda PHP ve MYSQL alanında projeler geliştirmekteyim. C++, Phyton, Xamarin, MVC gibi konuları öğrenmek ve kendimi geliştirme çabası içerisindeyim. Discord için: https://discord.gg/FBxZeHu9

Değerli yorumlarınızı bekliyorum. :)