Простой такой докинг, без 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);
}
}
Сорри, что без комментариев, но вроде-бы и так понятно.
Здравствуйте, Badenweiler, Вы писали:
B>Простой такой докинг, без AutoHide и некоторых других вкусностей.
B>Докеры не являются mdichild'ами или просто child'ами, олько owned.
B>...
B>Сорри, что без комментариев, но вроде-бы и так понятно.
Сорри, но это на каком языке (на с++?) и для какой платформы, gui-библиотеки (VCL, .NET Forms, что-то еще?)?
Re[2]: Докинг без излишеств
Здравствуйте, Рома Мик, Вы писали:
РМ>Сорри, но это на каком языке (на с++?) и для какой платформы, gui-библиотеки (VCL, .NET Forms, что-то еще?)?
Опа, не подумал.
Но если ты знаешь с++, ты поймешь, что это не он
Думаю не сложно догадаться, что это C#, причем 2.0.
А насчет VCL —
Работы при 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]: Докинг без излишеств
Здравствуйте, Badenweiler, Вы писали:
B>Работы при DragFullWindows добился, а без — испортил. Теперь более-менее работает, но хуже
B>Подправленный кусок:
Ну теперь осталось всего ничего...Доделать до "стандартного" поведения, с нужными кнопочками и поведением
Re[3]: Докинг без излишеств
Здравствуйте, HotDog, Вы писали:
HD>Ну теперь осталось всего ничего...Доделать до "стандартного" поведения, с нужными кнопочками и поведением
Зачем? Этого не нужно. Многим (в т.ч. мне) хватит и этого
Здравствуйте, 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]: Докинг без излишеств
Здравствуйте, _FRED_, Вы писали:
_FR>ИМХО, красиво это было бы сделать компонентом, который затаскивается на форму и делает её контейнером. То же возможно и с дочерними формами.
Ну это в следующий раз. Сейчас мне лично хватит того, что есть.
Пока на собственное сообщение не было ответов, его можно удалить.
Удалить