Рисование дерева в Windows Forms

211
10 апреля 2019, 13:40

у меня проблема с AutoScroll и рисованием (суть вопроса после кода). Я вот по такому коду рисую AVL- дерево:

namespace AVL
{
    public partial class Form1 : Form
    {
        RandomFill rand;
        List<int> list;
        const int SIZE = 25;
        const int WIDTH = 30;
        const int HEIGHT = 30;
        Font dataFont = new Font(FontFamily.GenericSerif, HEIGHT / 2, FontStyle.Bold);
        AVL tr = new AVL();
        List<Point> pointList = new List<Point>(0);
        List<int> dataList = new List<int>(0);
        public Form1()
        {
            rand = new RandomFill(SIZE);
            list = rand.GetRandomFilledList();
            InitializeComponent();
            for (int i=0; i<SIZE; i++)
            {
                tr.Add(list[i]);
            }
        }
        public void DisplayTree(AVL.Node root, Graphics graph, Pen pen, List<Point> pointList, List<int> dataList)
        {
            DrawConnectingines(root, graph, pen);
            for (int i = 0; i < pointList.Count; i++)
            {
                graph.DrawEllipse(pen, pointList[i].X, pointList[i].Y, WIDTH, HEIGHT);
                graph.FillEllipse(Brushes.White, pointList[i].X, pointList[i].Y, WIDTH, HEIGHT);
                graph.DrawString(dataList[i].ToString(), dataFont, Brushes.Black, pointList[i].X, pointList[i].Y);
            }
        }
        private void DrawConnectingines(AVL.Node root, Graphics graph, Pen pen)
        {
            if (root.left != null)
            {
                graph.DrawLine(pen, root.point.X + WIDTH/2, root.point.Y + HEIGHT/2, root.left.point.X + WIDTH / 2, root.left.point.Y + HEIGHT / 2);
                DrawConnectingines(root.left, graph, pen);
            }
            if (root.right != null)
            {
                graph.DrawLine(pen, root.point.X + WIDTH / 2, root.point.Y + HEIGHT / 2, root.right.point.X + WIDTH / 2, root.right.point.Y + HEIGHT / 2);
                DrawConnectingines(root.right, graph, pen);
            }
        }
        private void PushNodesInList(AVL.Node root, List<Point> pointList, List<int> dataList)
        {
            if (root.left != null)
            {
                PushNodesInList(root.left, pointList, dataList);
            }
            root.point.X = (WIDTH + WIDTH) * pointList.Count;
            root.point.Y = (HEIGHT + HEIGHT) * root.level;
            pointList.Add(root.point);
            dataList.Add(root.data);
            if (root.right != null)
            {
                PushNodesInList(root.right, pointList, dataList);
            }
        }
        private void treePanel_Paint(object sender, PaintEventArgs e)
        {
            Graphics graph = e.Graphics;
            Pen pen = new Pen(Color.Black, 1);
            //рисование 
            tr.RecountDepth();
            DisplayTree(tr.GetRoot(), graph, pen, pointList, dataList);
        }
        private void button1_Click(object sender, EventArgs e)
        {
            pointList.Clear();
            dataList.Clear();
            PushNodesInList(tr.GetRoot(), pointList, dataList);
            treePanel.Refresh();
        }
    }
}

Проблема в том, что если дерево не влезает в treePanel, то нужен Scroll рисунка, но как его реализовать, я не знаю. Свойство AutoScroll у treePanel работает только если я вместо рисования кругов рисую Label-метки, но даже в этом случае рисунок как-то странно съезжает, т.е. проблему это не решает (наверное)

Помогите, пожалуйста кодом или советом!)

1-изображение - это нужно устранить 2-изображение - А вот так пока получается, но нужно , чтобы можно было скроллить После ответа ниже - понял, что делал неправильно, код будет примерно следующий (линии я пока не нарисовал, но скроллящееся дерево без соединительных линий - уже есть) Вот обновлённый код:

namespace AVL
{
    public partial class Form1 : Form
    {
        //GraphicsPath
        System.Drawing.Drawing2D.GraphicsPath myPath;
        //
        RandomFill rand;
        List<int> list;
        const int SIZE = 25;
        const int WIDTH = 30;
        const int HEIGHT = 30;
        Font dataFont = new Font(FontFamily.GenericSerif, HEIGHT / 2, FontStyle.Bold);
        AVL tree = new AVL();
        List<Point> pointList = new List<Point>(0);
        List<int> dataList = new List<int>(0);
        public Form1()
        {
            //на замену предыдущему способу - GraphicsPath!
            myPath = new System.Drawing.Drawing2D.GraphicsPath();
            //
            rand = new RandomFill(SIZE);
            list = rand.GetRandomFilledList();
            InitializeComponent();
            for (int i = 0; i < SIZE; i++)
            {
                tree.Add(list[i]);
            }
        }
        private void treePanel_Paint(object sender, PaintEventArgs e)
        {
            pointList.Clear();
            dataList.Clear();
            tree.RecountDepth();
            PushNodesInList(tree.GetRoot(), pointList, dataList);
        }
        public void DisplayTree(AVL.Node root, Graphics graph, Pen pen, List<Point> pointList, List<int> dataList)
        {
            for (int i = 0; i < pointList.Count; i++)
            {
                myPath.AddEllipse(pointList[i].X, pointList[i].Y, WIDTH, HEIGHT);
            }
        }
        private void PushNodesInList(AVL.Node root, List<Point> pointList, List<int> dataList)
        {
            if (root.left != null)
            {
                PushNodesInList(root.left, pointList, dataList);
            }
            root.point.X = (WIDTH + WIDTH) * pointList.Count + delta * WIDTH;
            root.point.Y = (HEIGHT + HEIGHT) * root.level;
            pointList.Add(root.point);
            dataList.Add(root.data);
            if (root.right != null)
            {
                PushNodesInList(root.right, pointList, dataList);
            }
        }
        private void CheckForScroll(AVL.Node root)
        { //Нужна для расширения panel1, которая есть наше дерево. Если не помещается дерево - panel1 расширится и на panel2 должен появиться скроллбар.
            int treePanelCurrentWidth = panel1.Width;
            if (root.right != null)
            {
                CheckForScroll(root.right);
            }
            if (root.right == null && root.point.X > treePanelCurrentWidth)
            {
                panel1.Width = root.point.X + WIDTH;
            }
        }
        private void button1_Click(object sender, EventArgs e)
        {
            CheckForScroll(tree.GetRoot()); 
            panel1.BackColor = Color.Yellow;
            panel1.Region = new Region(myPath);
        }
    }
}
Answer 1

Для начала проясним ошибочное предположение: нарисованное изображение не является контентом, с точки зрения ScrolableControl, от которого наследуются все контролы, которые поддерживают скроллинг. Таким образом, свойство AutoScroll не может вам помочь, так как учитывает только вложенные контролы. Даже принудительная ручная активация скролла вам в данном случае не поможет.

Какие есть варианты? Основных три. Остальные - производные.

  1. Так как скролл учитывает только контролы, то строить изображение с помощью кастомных контролов. Через свойство Control.Region можно придать контролу любую форму (вершина, ребро, да хоть весь граф).

  2. Использовать двухслойную модель контрола, т.е. за скролл отвечает один, а рисуем на другом, вложенном в первый. При этом во время рисования не забываем изменять размер контрола на котором рисуем так, чтобы изображение умещалось полностью, тогда, как только вложенный контрол перестанет умещаться в родительском, активируется автоскролл и появится возможность прокрутки.

  3. Берем скрол полностью в свои руки, исключив все автоматические рюшечки. Фактически, мы просто добавляем полосы скролла и с их помощью управляем положением изображения вручную. Для этого изображение целиком должно быть представлено объектом GraphicsPath, для которого можно задать позиционирование при отрисовке.

Если нужна только картинка, я бы предпочел последний вариант. Для интерактива - без вариантов первый. Второй - как переходный от вашего решения к любому более специализированному.

Пока как-то так.

READ ALSO
Не получается удочерить GameObject к Panel, что делать?

Не получается удочерить GameObject к Panel, что делать?

При нажатие на [GameObject] в моём случае им является [Button], нужно удалить это [GameObject] и заспавнить новый [GameObject] с чем мой скрипт успешно справляется,...

186
C# много POST запросов в цикле

C# много POST запросов в цикле

Необходимо выполнить очень много POST запросов на сайт, получить ответ (типо "1,2,3" добавить в массив и отправить запросы на другой URL

191
C# WPF Как добавить attached контрол в свой User Control?

C# WPF Как добавить attached контрол в свой User Control?

Есть свой User ControlКак вложить в него например TextBlock как ето сделано в ScrollViewer?

164
Скриншот всех nodes

Скриншот всех nodes

Есть ссылка на сайтНеобходимо получить selector или xpath для всех доступных элементов

184