Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Разработка Windows Forms > Дёргание TreeView


Автор: маерсон 12.2.2009, 05:51
Есть Node1(содержит в себе Child1, Child2, Child3) и Node2(содержит в себе Child4, Child5, Child6). Отрисовываются вручную в OwnerDrawAll. Программа по задумке периодически из Node2 удаляет один из Child'ов и сразу добавляет в Node1, таким образом перемещение чаилдов. Но когда происходит удаление и добавление(оба родительских нода развернуты всегда), TreeView дергается-мерцает. 
Пробовал перед удалением делать 
Код

BeginUpdate
 
и 
Код

EndUpdate 

после удаления нода - все равно мигает. Пробовал также и так:
Код

child.Remove();                      
TreeView.BeginUpdate();
TreeView.Nodes[0].Nodes.Add("Child #");
TreeView.EndUpdate();

Все равно дергается. Как бороться?

Автор: Partizan 12.2.2009, 10:57
маерсон, что значит дёргается? родительский узел сворачивается/разворачивается?

Автор: маерсон 12.2.2009, 11:16
не совсем так: мерцание дерева и при сворачивании\разворачивании наблюдается. Также мерцает при ресайзе.

Автор: unicuum 12.2.2009, 20:44
Цитата(маерсон @  12.2.2009,  05:51 Найти цитируемый пост)
Все равно дергается. Как бороться?

Если child это нод, и принадлежит TreeView, попробуй так.

Код
TreeView.BeginUpdate();
child.Remove();
TreeView.Nodes[0].Nodes.Add("Child #");
TreeView.EndUpdate();

Автор: маерсон 13.2.2009, 05:40
unicuum, тоже пробовал - моргает все равно при отрисовке.

Читал про свойство DoubleBuffer, возможно сумеет убрать моргание...но до него добраться можно, если наследовать от TreeView...правда не смог нормально реализовать и отобразить данные в дереве(вернее данные вообще перестали отображаться), хотя просто делал так:
Код

public class XtreeView : TreeView
{
     public XtreeView()
     {
          this.SetStyle(ControlStyles.DoubleBuffer | ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint, true);
     }
}

Автор: TIGERоX 13.2.2009, 13:27
 this.SetStyle(ControlStyles.DoubleBuffer, true);
хватит этого, остальные флаги говорят о том что пользователь на себя берет отрисовку контрола

Автор: маерсон 13.2.2009, 15:45
TIGERоX, уже испробывал - все равно моргает!

Автор: маерсон 13.2.2009, 20:07
Раз уж никто не сталкивался с такой аномалией, может посоветуете достойную аналогию TreeView?

Автор: Любитель 13.2.2009, 20:16
Если отрисовываешь вручную - и рисуешь дерево целиком, переопредели OnPaintBackground и ничего в нём не делай (не вызывай базовый обработчик).

Автор: TIGERоX 14.2.2009, 02:30
покажите пожалуйста весь код

Автор: маерсон 14.2.2009, 04:23
Код

    public class XTreeView : TreeView
    {
        public XTreeView()
        {
            SetStyle(ControlStyles.DoubleBuffer | ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint, true);
        }

        protected virtual void OnPaintBackground(PaintEventArgs pevent)
        {
            //
            // huh?
            //
        }

        protected virtual void OnPaint(PaintEventArgs e)
        {
            //
            base.OnPaint(e);
            //
        }
    }


А как тогда background image отрисовать?

Автор: unicuum 14.2.2009, 13:52
Цитата(маерсон @  13.2.2009,  05:40 Найти цитируемый пост)
unicuum, тоже пробовал - моргает все равно при отрисовке.

Значит ты не правильно перерисовываешь экран.

Цитата(маерсон @  13.2.2009,  20:07 Найти цитируемый пост)
Раз уж никто не сталкивался с такой аномалией, может посоветуете достойную аналогию TreeView? 

Лучше прочитай по этой теме какие-нибудь книжки, навроде "Коннелл Джон - Разработка элементов управления Microsoft .NET на Microsoft Visual Basic .NET" и прочее. Как правильно перерисовывать естественно без моргания ещё написано в MSDN.


Автор: маерсон 15.2.2009, 06:12
unicuum, спасибо, в мсдн почитал. Теперь отрисовывается вручную все в собтии OnPaint. Каждый Node рисую если он видим, но как быть с фоном? 
Предполагаю, что вот так:
Код

private void myTreeView1_Paint(object sender, PaintEventArgs e)
        {
            Graphics g = e.Graphics;
            Image img = new Bitmap("123.bmp");
            g.DrawImageUnscaled(img, ClientRectangle);
            //...
            //...
            //...
            // Рисуются ноды
            //...
            //...
            //...
            //...
            myTreeView1.Invalidate(); // кушает здесь много от процессора
        }

Верно ли писать в OnPaint myTreeView1.Invalidate() ?
И что лучше использовать g.DrawImageUnscaled(img, g.VisibleClipBounds) или g.DrawImageUnscaled(img, ClientRectangle) ?

Как можно отрисовать тогда фон, если он стирается и видно как он перерисовывается?

Автор: Любитель 15.2.2009, 17:44
Цитата(маерсон @  15.2.2009,  06:12 Найти цитируемый пост)
myTreeView1.Invalidate()

Нет, конечно - в OnPaint-е этого нельзя писать.

Для фона переопределяй метод OnPaintBackground, как я уже писал. Само собой лучше перерисовать только видимую часть.

Автор: unicuum 27.3.2009, 17:41
Попробовал тут реализовать этот функционал. В общем, тут надо разбираться в виндовых событиях, а я не разбираюсь.

Код
using System;
using System.Drawing;
using System.Windows.Forms;

namespace TreeViewImage
{
    /// <summary>
    /// Расширенный древовидный вид.
    /// </summary>
    public class ExtensionView : TreeView
    {
        /// <summary>
        /// Фоновое изображение.
        /// </summary>
        Image _backgroundImage;

        /// <summary>
        /// Прямоугольник (координаты и размер) фонового изображения.
        /// </summary>
        Rectangle _imageRectangle;

        /// <summary>
        /// Конструктор по умолчанию.
        /// </summary>
        public ExtensionView() : base()
        {
            PresetStyle();
        }

        /// <summary>
        /// Конструктор со значением.
        /// </summary>
        public ExtensionView(Image backImage) : this()
        {
            PresetStyle();
            BackImage = backImage;
        }

        /// <summary>
        /// Предустановка стилей.
        /// </summary>
        private void PresetStyle()
        {
            base.SetStyle(ControlStyles.DoubleBuffer, true);
            base.SetStyle(ControlStyles.SupportsTransparentBackColor, true);
        }

        /// <summary>
        /// Фоновое изображение.
        /// </summary>
        public Image BackImage
        {
            get
            {
                return _backgroundImage;
            }
            set
            {
                if (value != null)
                {
                    _backgroundImage = value;
                    _imageRectangle.Width = _backgroundImage.Width;
                    _imageRectangle.Height = _backgroundImage.Height;

                    this.Invalidate();
                }
                else
                    throw new NullReferenceException();
            }
        }

        /// <summary>
        /// Происходит при перерисовке заднего фона.
        /// </summary>
        /// <param name="e"></param>
        protected override void OnPaintBackground(System.Windows.Forms.PaintEventArgs e)
        {
            base.OnPaintBackground(e);
            e.Graphics.DrawImage(BackImage, _imageRectangle);
        }

        /// <summary>
        /// Происходит до сворачивания.
        /// </summary>
        /// <param name="e"></param>
        protected override void OnBeforeCollapse(TreeViewCancelEventArgs e)
        {
            base.OnBeforeCollapse(e);
            //this.Invalidate(_imageRectangle);
            this.Invalidate();
        }

        /// <summary>
        /// Происходит до разворачивания.
        /// </summary>
        /// <param name="e"></param>
        protected override void OnBeforeExpand(TreeViewCancelEventArgs e)
        {
            base.OnBeforeExpand(e);
            this.Invalidate(_imageRectangle);
        }

        /// <summary>
        /// Происходит при изменение размера.
        /// </summary>
        /// <param name="e"></param>
        protected override void OnResize(EventArgs e)
        {
            _imageRectangle.X = this.Width - BackImage.Width;
            _imageRectangle.Y = this.Height - BackImage.Size.Height;
            this.Invalidate();
        }

        /// <summary>
        /// Стереть задний фон.
        /// </summary>
        private const int WM_ERASEBKGND = 0x00000014;

        /// <summary>
        /// Перерисовывать узлы автоматически, а задний фон самим.
        /// </summary>
        /// <param name="m"></param>
        protected override void WndProc(ref System.Windows.Forms.Message m)
        {
            switch(m.Msg)
            {
                case WM_ERASEBKGND:
                    bool styleUserPaint = this.GetStyle(ControlStyles.UserPaint);
                    bool styleAllPainting =     this.GetStyle(ControlStyles.AllPaintingInWmPaint);
                    this.SetStyle(ControlStyles.UserPaint, true);
                    this.SetStyle(ControlStyles.AllPaintingInWmPaint, false);
                    base.WndProc(ref m);
                    this.SetStyle(ControlStyles.UserPaint, styleUserPaint);
                    this.SetStyle(ControlStyles.AllPaintingInWmPaint, styleAllPainting);
                    break;
                default:
                    base.WndProc(ref m);
                    break;
            }
        }

        /// <summary>
        /// Горизонтальная прокрутка.
        /// </summary>
        private const int WS_HSCROLL = 0x00100000;

        /// <summary>
        /// Убираем горизонтальную прокрутку.
        /// </summary>
        protected override CreateParams CreateParams 
        {
            get 
            {
                CreateParams parameters = base.CreateParams;
                parameters.Style |= WS_HSCROLL;
                return parameters;
            }
        }
    }
}


Надо что-то делать с событием сворачивания и разворачивания. Может заменить их на "после сворачивания и разворачивания". Тут дело даже не в том, что нельзя сделать нормально, просто надо тратить на это время, а майкрософт потом возьмёт и всё изменит и вообще сделает всё несовместимым.

user posted image
См. прикреплённый пример.

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)