Докинг без излишеств
От: Badenweiler Россия http://helloimyourmind.com/
Дата: 20.05.06 15:03
Оценка:
Простой такой докинг, без AutoHide и некоторых других вкусностей.
Докеры не являются mdichild'ами или просто child'ами, олько owned.


Исходники с примером:
http://www.rsdn.ru/File/44439/docking.zip [33kb]
namespace MarselKhaidarov.UI.SimpleDocking
{

    /// <summary>
    /// Контейнер, или родитель, для докеров. 
    /// От него нужно унаследоваться главной форме.
    /// </summary>
    internal abstract class DockersContainer : Form
    {
        protected List<DockerForm> dockers = new List<DockerForm>();

        public void ApplyDockingLayoutSettings(DockingLayoutSettings[] sets)
        {
            ResetAvailableBounds();

            foreach(DockingLayoutSettings ls in sets)
            {
                foreach(DockerForm d in dockers)
                {
                    if(d.Name == ls.Name)
                    {
                        d.DockByOwner = DockStyle.None;
                        d.Size = ls.Size;
                        d.DockByOwner = ls.DockByOwner;
                        d.Visible = ls.Visible;

                        UpdateAvailableBounds(d);

                        break;
                    }
                }
            }
        }

        public DockingLayoutSettings[] GetDockingLayoutSettings()
        {
            List<DockingLayoutSettings> sets = new List<DockingLayoutSettings>(dockers.Count);
            foreach(DockerForm d in dockers)
            {
                DockingLayoutSettings ls = new DockingLayoutSettings();

                ls.Name = d.Name;
                ls.Size = d.Size;
                ls.DockByOwner = d.DockByOwner;
                ls.Visible = d.Visible;

                sets.Add(ls);
            }

            return sets.ToArray();
        }

        public void UpdateLayoutByOwner()
        {
            ResetAvailableBounds();

            for(int i = 0; i < dockers.Count; i++)
            {
                if(dockers[i] != null)
                {
                    dockers[i].PerformLayoutByOwner();
                    UpdateAvailableBounds(dockers[i]);
                }
            }
        }

        protected Rectangle availableBounds;
        public Rectangle AvailableBounds
        {
            get
            {
                if(availableBounds == Rectangle.Empty)
                    UpdateAvailableBounds();

                return availableBounds;
            }
        }

        public event EventHandler AvailableBoundsChanged;
        protected virtual void OnAvailableBoundsChanged(EventArgs e)
        {
            if(AvailableBoundsChanged != null)
                AvailableBoundsChanged(this, e);
        }

        public void UpdateAvailableBounds()
        {
            ResetAvailableBounds();

            for(int i = 0; i < dockers.Count; i++)
                if(dockers[i] != null)
                    UpdateAvailableBounds(dockers[i]);
        }

        public void ResetAvailableBounds()
        {
            int menuHeight = MainMenuStrip == null ? 0 : MainMenuStrip.Height;

            availableBounds = new Rectangle(Location.X + SystemInformation.Border3DSize.Width * 3,
                Location.Y + SystemInformation.CaptionHeight + menuHeight + SystemInformation.Border3DSize.Height * 3,
                ClientSize.Width - SystemInformation.Border3DSize.Width * 2,
                ClientSize.Height - menuHeight - SystemInformation.Border3DSize.Height * 2);
        }

        public void UpdateAvailableBounds(DockerForm docker)
        {
            if(docker != null && docker.DockByOwner != DockStyle.None)
                availableBounds = GetAvailableBounds(availableBounds, docker);
        }

        private Rectangle GetAvailableBounds(Rectangle bounds, DockerForm docker)
        {
            Rectangle bs = bounds;

            if(docker.Visible)
            {
                switch(docker.DockByOwner)
                {
                    case DockStyle.Bottom:
                        bs.Height -= docker.Height;
                        break;
                    case DockStyle.Left:
                        bs.X += docker.Width;
                        bs.Width -= docker.Width;
                        break;
                    case DockStyle.Right:
                        bs.Width -= docker.Width;
                        break;
                    case DockStyle.Top:
                        bs.Y += docker.Height;
                        bs.Height -= docker.Height;
                        break;
                    default:
                        break;
                }
            }

            return bs;
        }

        protected override void OnLocationChanged(EventArgs e)
        {
            base.OnLocationChanged(e);

            UpdateLayoutByOwner();
        }

        protected override void OnSizeChanged(EventArgs e)
        {
            base.OnSizeChanged(e);

            UpdateLayoutByOwner();
        }

        protected override void OnShown(EventArgs e)
        {
            base.OnShown(e);

            for(int i = 0; i < dockers.Count; i++)
                dockers[i].Show(this);

            foreach(DockerForm d in dockers)
            {
                d.DockByOwnerChanged += new EventHandler(docker_DockByOwnerChanged);
                d.VisibleChanged += new EventHandler(docker_VisibleChanged);
            }
        }

        void docker_VisibleChanged(object sender, EventArgs e)
        {
            UpdateLayoutByOwner();
        }

        void docker_DockByOwnerChanged(object sender, EventArgs e)
        {
            DockerForm d = (DockerForm)sender;

            dockers.Remove(d);
            dockers.Add(d);

            UpdateLayoutByOwner();
        }

    }

    /// <summary>
    /// Форма-докер
    /// </summary>
    internal partial class DockerForm : Form
    {
        public DockerForm()
        {
            InitializeComponent();

            System.Windows.Forms.Application.Idle += new EventHandler(Application_Idle);
        }

        protected override void OnClosing(CancelEventArgs e)
        {
            base.OnClosing(e);

            e.Cancel = true;
            Hide();
        }

        #region layout

        private DockStyle dockByOwner = DockStyle.None;
        public DockStyle DockByOwner
        {
            get { return dockByOwner; }
            set
            {
                lastDockStyle = dockByOwner;
                dockByOwner = value;

                if(dockByOwner == DockStyle.None)
                    Size = nodockingSize;
                else
                    LayoutByOwner = true;

                OnDockByOwnerChanged(EventArgs.Empty);
            }
        }

        public event EventHandler DockByOwnerChanged;
        protected virtual void OnDockByOwnerChanged(EventArgs e)
        {
            if(DockByOwnerChanged != null)
                DockByOwnerChanged(this, e);
        }

        private bool layoutByOwner = false;
        public bool LayoutByOwner
        {
            get { return layoutByOwner; }
            set
            {
                layoutByOwner = value;
                PerformLayoutByOwner();

                OnLayoutByOwnerChanged(EventArgs.Empty);
            }
        }

        public event EventHandler LayoutByOwnerChanged;
        protected virtual void OnLayoutByOwnerChanged(EventArgs e)
        {
            if(LayoutByOwnerChanged != null)
                LayoutByOwnerChanged(this, e);
        }

        protected override void OnLoad(EventArgs e)
        {
            base.OnLoad(e);

            nodockingSize = Size;
        }

        private Size nodockingSize = Size.Empty;
        private DockStyle lastDockStyle = DockStyle.None;

        public void PerformLayoutByOwner()
        {
            if(layoutByOwner)
            {
                DockStyle ds = dockByOwner;
                dockByOwner = DockStyle.None;

                Rectangle bounds = ((DockersContainer)Owner).AvailableBounds;

                safeLocation = true;

                switch(ds)
                {
                    case DockStyle.Top:
                        Location = new Point(bounds.Left, bounds.Top);

                        Width = bounds.Width;
                        Height = nodockingSize.Height;
                        break;
                    case DockStyle.Bottom:
                        Location = new Point(bounds.Left, bounds.Bottom - Height);

                        Width = bounds.Width;
                        Height = nodockingSize.Height;
                        break;

                    case DockStyle.Left:
                        Location = new Point(bounds.Left, bounds.Top);

                        Height = bounds.Height;
                        Width = nodockingSize.Width;
                        break;
                    case DockStyle.Right:
                        Location = new Point(bounds.Right - Width, bounds.Top);

                        Height = bounds.Height;
                        Width = nodockingSize.Width;
                        break;

                    default:
                        break;
                }

                safeLocation = false;

                dockByOwner = ds;

                docked = true;
            }
        }

        protected override void OnResize(EventArgs e)
        {
            base.OnResize(e);

            if(!safeLocation)
            {
                if(dockByOwner == DockStyle.Top || dockByOwner == DockStyle.Bottom)
                {
                    nodockingSize.Height = Height;
                    OnDockByOwnerChanged(EventArgs.Empty);
                }

                if(dockByOwner == DockStyle.Left || dockByOwner == DockStyle.Right)
                {
                    nodockingSize.Width = Width;
                    OnDockByOwnerChanged(EventArgs.Empty);
                }

                //if(dockByOwner == DockStyle.None)
                //    nodockingSize = Size;
            }

            lastSize = Size;
        }

        private bool safeLocation = false;
        private Size lastSize;

        private DockStyle newDock;
        private bool docked = true;

        private void LayoutMoved()
        {
            if(lastSize == Size && layoutByOwner)
            {
                DockStyle tempds = dockByOwner;
                dockByOwner = DockStyle.None;

                Rectangle bounds = ((DockersContainer)Owner).AvailableBounds;
                dockByOwner = tempds;

                Point cur = new Point(Cursor.Position.X - bounds.X, Cursor.Position.Y - bounds.Y);
                if(bounds.Contains(Cursor.Position))
                {
                    DockStyle ds = DockStyle.None;

                    float y1 = ((float)bounds.Height / bounds.Width) * cur.X;
                    float y2 = ((float)bounds.Height / bounds.Width) * (bounds.Width - cur.X);

                    if(cur.Y > y1)
                    {
                        if(cur.Y > y2)
                            ds = DockStyle.Bottom;
                        else
                            ds = DockStyle.Left;
                    }
                    else
                    {
                        if(cur.Y > y2)
                            ds = DockStyle.Right;
                        else
                            ds = DockStyle.Top;
                    }

                    newDock = ds;
                    docked = false;
                }
                else
                {
                    if(newDock != DockStyle.None || dockByOwner != DockStyle.None)
                    {
                        DockByOwner = newDock = DockStyle.None;
                        docked = true;
                        Size = nodockingSize;
                    }
                }
            }
        }

        protected override void OnMove(EventArgs e)
        {
            base.OnMove(e);

            LayoutMoved();
        }

        /// <summary>
        /// Здесь отслеживается перемещение формы.
                /// И если оно было, форма дочится.
        /// </summary>
        void Application_Idle(object sender, EventArgs e)
        {
            if(!docked && newDock != dockByOwner)
            {
                DockByOwner = newDock;
                docked = true;
            }
        }

        #endregion layout

    }


    /// <summary>
    /// Настройки для отдельного докера.
    /// Нужны, чтобы, например, сохранить всю раскладку
    /// докеров в конфиг.
    /// </summary>
    [Serializable()]
    internal sealed class DockingLayoutSettings
    {
        public DockingLayoutSettings() {}

        private string name = String.Empty;
        public string Name
        {
            get { return name; }
            set { name = value; }
        }

        private DockStyle dockByOwner = DockStyle.None;
        public DockStyle DockByOwner
        {
            get { return dockByOwner; }
            set { dockByOwner = value; }
        }

        private Size size = Size.Empty;
        public Size Size
        {
            get { return size; }
            set { size = value; }
        }

        private bool visible = true;
        public bool Visible
        {
            get { return visible; }
            set { visible = value; }
        }
    }
}


Использование:
internal partial class Form1 : DockersContainer
    {
        public Form1()
        {
            InitializeComponent();

            //Not required
            IsMdiContainer = true;

            List<DockingLayoutSettings> sets = new List<DockingLayoutSettings>(2);
            {
                DockerForm df1 = new DockerForm();
                df1.Name = "df1";
                dockers.Add(df1);
                DockingLayoutSettings ls = new DockingLayoutSettings();
                ls.Name = "df1";
                ls.Size = df1.Size;
                ls.Visible = true;
                ls.DockByOwner = DockStyle.Top;
                sets.Add(ls);

                DockerForm df2 = new DockerForm();
                df2.Name = "df2";
                dockers.Add(df2);
                ls = new DockingLayoutSettings();
                ls.Name = "df2";
                ls.Size = df2.Size;
                ls.Visible = true;
                ls.DockByOwner = DockStyle.Right;
                sets.Add(ls);

                DockerForm df3 = new DockerForm();
                df3.Name = "df3";
                dockers.Add(df3);
                ls = new DockingLayoutSettings();
                ls.Name = "df3";
                ls.Size = df3.Size;
                ls.Visible = true;
                ls.DockByOwner = DockStyle.Bottom;
                sets.Add(ls);

                DockerForm df4 = new DockerForm();
                df4.Name = "df4";
                dockers.Add(df4);
                ls = new DockingLayoutSettings();
                ls.Name = "df4";
                ls.Size = df4.Size;
                ls.Visible = true;
                ls.DockByOwner = DockStyle.Left;
                sets.Add(ls);
            }
            defaultDockingLayout = sets.ToArray();
        }

        private DockingLayoutSettings[] defaultDockingLayout;

        protected override void OnShown(EventArgs e)
        {
            base.OnShown(e);

            ApplyDockingLayoutSettings(defaultDockingLayout);
        }
        }


Сорри, что без комментариев, но вроде-бы и так понятно.
Re: Докинг без излишеств
От: Рома Мик Россия http://romamik.com
Дата: 20.05.06 19:31
Оценка: :)
Здравствуйте, Badenweiler, Вы писали:

B>Простой такой докинг, без AutoHide и некоторых других вкусностей.

B>Докеры не являются mdichild'ами или просто child'ами, олько owned.
B>...
B>Сорри, что без комментариев, но вроде-бы и так понятно.
Сорри, но это на каком языке (на с++?) и для какой платформы, gui-библиотеки (VCL, .NET Forms, что-то еще?)?
Re[2]: Докинг без излишеств
От: Badenweiler Россия http://helloimyourmind.com/
Дата: 21.05.06 06:00
Оценка: +1 :)
Здравствуйте, Рома Мик, Вы писали:

РМ>Сорри, но это на каком языке (на с++?) и для какой платформы, gui-библиотеки (VCL, .NET Forms, что-то еще?)?


Опа, не подумал.
Но если ты знаешь с++, ты поймешь, что это не он Думаю не сложно догадаться, что это C#, причем 2.0.
А насчет VCL —
Re: Докинг без излишеств
От: Badenweiler Россия http://helloimyourmind.com/
Дата: 21.05.06 07:17
Оценка:
Работы при DragFullWindows добился, а без — испортил. Теперь более-менее работает, но хуже
Подправленный кусок:
//DockerForm
                    if(SystemInformation.DragFullWindows)
                    {
                        newDock = ds;
                        docked = false;
                    }
                    else
                        DockByOwner = ds;
                }
                else
                {
                    if(newDock != DockStyle.None || dockByOwner != DockStyle.None)
                    {
                        DockByOwner = newDock = DockStyle.None;
                        docked = true;
                        Size = nodockingSize;
                    }
                }
            }
        }

        protected override void OnMove(EventArgs e)
        {
            base.OnMove(e);

            LayoutMoved();
        }

        void Application_Idle(object sender, EventArgs e)
        {
            if(SystemInformation.DragFullWindows && !docked && newDock != dockByOwner)
            {
                DockByOwner = newDock;
                docked = true;
            }
        }




Кстати, как всем этим пользоваться:
Свободное пространство (AvailableBounds) здесь поделено на 4 части:
 ______
|\    /|
| \  / |
|  \/  |
|  /\  |
| /  \ |
|/____\|

Чтобы задочить докер, нужно лишь перетащить его к одному из этих треугольников — он задочится к соответствующейму краю.
Re[2]: Докинг без излишеств
От: HotDog Швейцария www.denebspace.com
Дата: 22.05.06 06:36
Оценка:
Здравствуйте, Badenweiler, Вы писали:

B>Работы при DragFullWindows добился, а без — испортил. Теперь более-менее работает, но хуже

B>Подправленный кусок:

Ну теперь осталось всего ничего...Доделать до "стандартного" поведения, с нужными кнопочками и поведением
Re[3]: Докинг без излишеств
От: Badenweiler Россия http://helloimyourmind.com/
Дата: 22.05.06 11:09
Оценка:
Здравствуйте, HotDog, Вы писали:

HD>Ну теперь осталось всего ничего...Доделать до "стандартного" поведения, с нужными кнопочками и поведением

Зачем? Этого не нужно. Многим (в т.ч. мне) хватит и этого
Re: Докинг без излишеств
От: _FRED_ Черногория
Дата: 23.05.06 13:33
Оценка: 4 (1) +1
Здравствуйте, Badenweiler, Вы писали:

B>Простой такой докинг, без AutoHide и некоторых других вкусностей.

B>Докеры не являются mdichild'ами или просто child'ами, олько owned.

Зачем наследование от Form, и, как результат, принуждение к использованию объявленных в этой библиотеке базовых классов? Во всех случаях можно обойтись без наследования (получая форму как свойство и подписываясь на события)? Усугубляется это ещё и тем, что объявленный класс абстрактен. и, =>, его наследник не будет открываться в дизайнере.

ИМХО, красиво это было бы сделать компонентом, который затаскивается на форму и делает её контейнером. То же возможно и с дочерними формами.
... << RSDN@Home 1.2.0 alpha rev. 650>>
Now playing: «Тихо в лесу…»
Help will always be given at Hogwarts to those who ask for it.
Re[2]: Докинг без излишеств
От: Badenweiler Россия http://helloimyourmind.com/
Дата: 24.05.06 02:46
Оценка:
Здравствуйте, _FRED_, Вы писали:

_FR>ИМХО, красиво это было бы сделать компонентом, который затаскивается на форму и делает её контейнером. То же возможно и с дочерними формами.

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