//Copyright (c) 2007-2009, Adolfo Marinucci
//All rights reserved.
//Redistribution and use in source and binary forms, with or without modification,
//are permitted provided that the following conditions are met:
//
//* Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//* Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//* Neither the name of Adolfo Marinucci nor the names of its contributors may
// be used to endorse or promote products derived from this software without
// specific prior written permission.
//
//THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
//AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
//WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
//IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
//INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
//PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
//HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
//OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
//EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
using System;
using System.Collections.Generic;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using System.Windows.Markup;
using System.Diagnostics;
using System.Linq;
namespace AvalonDock
{
public class ResizingPanel : Panel, IDockableControl
{
/// <summary>
/// Gets or sets the orientation of the panel
/// </summary>
/// <remarks>If horizontal oriented children are positioned from left to right and width of each child is computed according to <see cref="ResizingWidth"/> attached property value. When vertical oriented children are arranged from top to bottom, according to <see cref="ResizingHeight"/> of each child.</remarks>
public Orientation Orientation
{
get { return (Orientation)GetValue(OrientationProperty); }
set { SetValue(OrientationProperty, value); }
}
/// <summary>
/// Give access to Orientation attached property
/// </summary>
/// <remarks>If horizontal oriented children are positioned from left to right and width of each child is computed according to <see cref="ResizingWidth"/> attached property value. When vertical oriented children are arranged from top to bottom, according to <see cref="ResizingHeight"/> of each child.</remarks>
public static readonly DependencyProperty OrientationProperty =
DependencyProperty.Register("Orientation", typeof(Orientation), typeof(ResizingPanel), new FrameworkPropertyMetadata(Orientation.Horizontal, FrameworkPropertyMetadataOptions.AffectsMeasure, OnOrientationChanged));
static void OnOrientationChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
((ResizingPanel)d).splitterListIsDirty = true;
}
public static GridLength GetResizeWidth(DependencyObject obj)
{
return (GridLength)obj.GetValue(ResizeWidthProperty);
}
public static void SetResizeWidth(DependencyObject obj, GridLength value)
{
obj.SetValue(ResizeWidthProperty, value);
}
public static readonly DependencyProperty ResizeWidthProperty =
DependencyProperty.RegisterAttached("ResizeWidth",
typeof(GridLength),
typeof(ResizingPanel),
new FrameworkPropertyMetadata(new GridLength(1.0, GridUnitType.Star),
OnSplitSizeChanged,
OnCoerceSplitSize),
new ValidateValueCallback(IsSplitSizeValid));
public static GridLength GetResizeHeight(DependencyObject obj)
{
return (GridLength)obj.GetValue(ResizeHeightProperty);
}
public static void SetResizeHeight(DependencyObject obj, GridLength value)
{
obj.SetValue(ResizeHeightProperty, value);
}
public static readonly DependencyProperty ResizeHeightProperty =
DependencyProperty.RegisterAttached("ResizeHeight",
typeof(GridLength),
typeof(ResizingPanel),
new FrameworkPropertyMetadata(new GridLength(1.0, GridUnitType.Star),
OnSplitSizeChanged,
OnCoerceSplitSize),
new ValidateValueCallback(IsSplitSizeValid));
static void OnSplitSizeChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
ResizingPanel parentPanel = LogicalTreeHelper.GetParent(sender) as ResizingPanel;
if (parentPanel != null)
parentPanel.InvalidateMeasure();
}
static object OnCoerceSplitSize(DependencyObject sender, object value)
{
GridLength gd = (GridLength)value;
if (gd.Value < 0.0)
gd = new GridLength(0.0, gd.GridUnitType);
return gd;
}
static bool IsSplitSizeValid(object value)
{
GridLength v = (GridLength)value;
return v.IsStar || v.IsAbsolute;//at the moment auto is not supported
}
public static Size GetEffectiveSize(DependencyObject obj)
{
return (Size)obj.GetValue(EffectiveSizeProperty);
}
public static void SetEffectiveSize(DependencyObject obj, Size value)
{
obj.SetValue(EffectiveSizeProperty, value);
}
public static readonly DependencyProperty EffectiveSizeProperty =
DependencyProperty.RegisterAttached("EffectiveSize", typeof(Size), typeof(ResizingPanel), new FrameworkPropertyMetadata(new Size()));
List<ResizingPanelSplitter> _splitterList = new List<ResizingPanelSplitter>();
/// <summary>
/// Correct sizes of children if all of them are set to absolutes
/// </summary>
void CorrectSizes()
{
IEnumerable<FrameworkElement> children = Children.OfType<FrameworkElement>().Where<FrameworkElement>(c => !(c is ResizingPanelSplitter));
if (children.All<FrameworkElement>(c => c.IsAbsolute()))
{
double totSum = children.Sum<FrameworkElement>(f => f.GetAbsoluteValue());
foreach (var c in children)
{
if (Orientation == Orientation.Horizontal)
SetResizeWidth(c, new GridLength(c.GetAbsoluteValue() / totSum, GridUnitType.Star));
else
SetResizeHeight(c, new GridLength(c.GetAbsoluteValue() / totSum, GridUnitType.Star));
}
}
}
/// <summary>
/// Helper funcs which correct elements size of a resizing panel
/// </summary>
internal void AdjustPanelSizes()
{
IEnumerable<FrameworkElement> children = Children.OfType<FrameworkElement>().Where<FrameworkElement>(c => !(c is ResizingPanelSplitter));
if (!this.IsLogicalChildContained<DocumentPane>())
{
//if no document pane is contained in this panel
//adjust elements so that any child will get a proportional star size
if (Orientation == Orientation.Horizontal)
{
double totSum = children.Sum<FrameworkElement>(f => f.IsAbsolute() ? f.GetAbsoluteValue() : GetEffectiveSize(f).Width);
foreach (var c in children)
SetResizeWidth(c, new GridLength((c.IsAbsolute() ? c.GetAbsoluteValue() : GetEffectiveSize(c).Width) / totSum, GridUnitType.Star));
}
else
{
double totSum = children.Sum<FrameworkElement>(f => f.IsAbsolute() ? f.GetAbsoluteValue() : GetEffectiveSize(f).Height);
foreach (var c in children)
SetResizeHeight(c, new GridLength((c.IsAbsolute() ? c.GetAbsoluteValue() : GetEffectiveSize(c).Height) / totSum, GridUnitType.Star));
}
}
}
/// <summary>
/// Compute the desidered size of the panel
/// </summary>
/// <param name="availableSize"></param>
/// <returns></returns>
protected override Size MeasureOverride(Size availableSize)
{
SetupSplitters();
CorrectSizes();
//Compute the list of visible children
List<FrameworkElement> visibleChildren = new List<FrameworkElement>();
for (int i = 0; i < VisualChildrenCount; i++)
{
FrameworkElement child = GetVisualChild(i) as FrameworkElement;
IDockableControl dockableControl = child as IDockableControl;
if (dockableControl != null &&
!dockableControl.IsDocked)
{
child.Measure(Size.Empty);
if (i == VisualChildrenCount - 1 &&
i > 0)
{
child = GetVisualChild(i - 1) as FrameworkElement;
Debug.Assert(child is ResizingPanelSplitter);
child.Measure(Size.Empty);
if (visibleChildren.Count > 0)
{
Debug.Assert(visibleChildren[visibleChildren.Count - 1] is ResizingPanelSplitter);
visibleChildren[visibleChildren.Count - 1].Measure(Size.Empty);
visibleChildren.RemoveAt(visibleChildren.Count - 1);
}
}
else if (i < VisualChildrenCount - 1)
{
i++;
child = GetVisualChild(i) as FrameworkElement;
Debug.Assert(child is ResizingPanelSplitter);
child.Measure(Size.Empty);
}
continue;
}
visibleChildren.Add(child);
}
//with no children no space needed
if (visibleChildren.Count == 0)
return new Size();
Debug.Assert(!(visibleChildren.Last<FrameworkElement>() is ResizingPanelSplitter));
if (availableSize.Width == double.PositiveInfinity &&
Orientation == Orientation.Horizontal)
{
Size newAvailSize = new Size();
foreach (FrameworkElement child in visibleChildren)
{
child.Measure(availableSize);
newAvailSize.Width += child.DesiredSize.Width;
newAvailSize.Height = Math.Max(child.DesiredSize.Height, newAvailSize.Height);
}
availableSize = newAvailSize;
}
//Thx to TMx
else if (availableSize.Height == double.PositiveInfinity &&
Orientation == Orientation.Vertical)
{
Size newAvailSize = new Size();
foreach (FrameworkElement child in visibleChildren)
{
child.Measure(newAvailSize);
newAvailSize.Width = Math.Max(child.DesiredSize.Width, newAvailSize.Width);
newAvailSize.Height += child.DesiredSize.Height;
}
availableSize = newAvailSize;
}
var splitters = from FrameworkElement child in visibleChildren
where child is ResizingPanelSplitter
select child;
var childStars = from FrameworkElement child in visibleChildren
where (!(child is ResizingPanelSplitter)) && child.IsStar()
select child;
var childAbsolutes = from FrameworkElement child in visibleChildren
where (!(child is ResizingPanelSplitter)) && child.IsAbsolute()
select child;
var childAutoSizes = from FrameworkElement child in visibleChildren
where (!(child is ResizingPanelSplitter)) && child.IsAuto()
select child;
//calculate the size of the splitters
Size splitterSize = new Size();
foreach (ResizingPanelSplitter splitter in splitters)
{
splitterSize.Width += splitter.MinWidth;
splitterSize.Height += splitter.MinHeight;
}
Size minimumSize = new Size(splitterSize.Width, splitterSize.Height);
foreach (FrameworkElement child in childStars)
{
minimumSize.Width += child.MinWidth;
minimumSize.Height += child.MinHeight;
}
foreach (FrameworkElement child in childAbsolutes)
{
minimumSize.Width += child.MinWidth;
minimumSize.Height += child.MinHeight;
}
foreach (FrameworkElement child in childAutoSizes)
{
minimumSize.Width += child.MinWidth;
minimumSize.Height += child.MinHeight;
}
Size minimumPrefferedSize = new Size(minimumSize.Width, minimumSize.Height);
foreach (FrameworkElement child in childAbsolutes)
{
minimumPrefferedSize.Width += child.GetAbsoluteValue() - child.MinWidth;
minimumPrefferedSize.Height += child.GetAbsoluteValue() - child.MinHeight;
}
foreach (FrameworkElement child in childAutoSizes)
{
minimumPrefferedSize.Width += child.DesiredSize.Width - child.MinWidth;
minimumPrefferedSize.Height += child.DesiredSize.Height - child.MinHeight;
}
if (Orientation == Orientation.Horizontal)
{
#region Horizontal Orientation
//if finalSize is not sufficient...
double offset = 0.0;
double maxHeight = 0.0;
if (minimumSize.Width >= availableSize.Width)
{
foreach (FrameworkElement child in visibleChildren)
{
child.Measure(new Size(child.MinWidth, availableSize.Height));
maxHeight = Math.Max(child.DesiredSize.Height, maxHeight);
offset += child.MinWidth;
}
}
else if (minimumPrefferedSize.Width >= availableSize.Width)
{
double delta = (minimumPrefferedSize.Width - availableSize.Width) / childAbsolutes.Count<FrameworkElement>();
foreach (FrameworkElement child in visibleChildren)
{
if (child is ResizingPanelSplitter)
{
child.Measure(new Size(child.MinWidth, availableSize.Height));
maxHeight = Math.Max(child.DesiredSize.Height, maxHeight);
offset += child.MinWidth;
}
else if (child.IsAbsolute())
{
child.Measure(new Size(Math.Max(child.GetAbsoluteValue() - delta, child.MinWidth), availableSize.Height));
maxHeight = Math.Max(child.DesiredSize.Height, maxHeight);
offset += child.GetAbsoluteValue() - delta;
}
else
{
child.Measure(new Size(child.MinWidth, availableSize.Height));
maxHeight = Math.Max(child.DesiredSize.Height, maxHeight);
offset += child.MinWidth;
}
}
}
else
{
double starsSum = childStars.Sum<FrameworkElement>(v => v.GetStarValue());
double starsFinalWidth =
availableSize.Width -
splitters.Sum<FrameworkElement>(s => s.MinWidth) -
childAbsolutes.Sum<FrameworkElement>(a => a.GetAbsoluteValue()) -
childAutoSizes.Sum<FrameworkElement>(a => a.DesiredSize.Width);
foreach (FrameworkElement child in visibleChildren)
{
if (child is ResizingPanelSplitter)
{
child.Measure(new Size(child.MinWidth, availableSize.Height));
maxHeight = Math.Max(child.DesiredSize.Height, maxHeight);
offset += child.MinWidth;
}
else if (child.IsAbsolute())
{
child.Measure(new Size(child.GetAbsoluteValue(), availableSize.Height));
maxHeight = Math.Max(child.DesiredSize.Height, maxHeight);
offset += child.GetAbsoluteValue();
}
else if (child.IsStar())
{
double w = child.GetStarValue() / starsSum * starsFinalWidth;
child.Measure(new Size(w, availableSize.Height));
maxHeight = Math.Max(child.DesiredSize.Height, maxHeight);
offset += w;
}
else
{
child.Measure(new Size(child.DesiredSize.Width, availableSize.Height));
maxHeight = Math.Max(child.DesiredSize.Height, maxHeight);
offset += child.DesiredSize.Width;
}
}
}
return new Size(offset, maxHeight);
#endregion
}
else
{
#region Vertical Orientation
//if finalSize is not sufficient...
double offset = 0.0;
double maxWidth = 0.0;
if (minimumSize.Height >= availableSize.Height)
{
foreach (FrameworkElement child in visibleChildren)
{
child.Measure(new Size(availableSize.Width, child.MinHeight));
maxWidth = Math.Max(child.DesiredSize.Width, maxWidth);
offset += child.MinHeight;
}
}
else if (minimumPrefferedSize.Height >= availableSize.Height)
{
double delta = (minimumPrefferedSize.Height - availableSize.Height) / childAbsolutes.Count<FrameworkElement>();
foreach (FrameworkElement child in visibleChildren)
{
if (child is ResizingPanelSplitter)
{
child.Measure(new Size(availableSize.Width, child.MinHeight));
maxWidth = Math.Max(child.DesiredSize.Width, maxWidth);
offset += child.MinHeight;
}
else if (child.IsAbsolute())
{
child.Measure(new Size(availableSize.Width, Math.Max(child.GetAbsoluteValue() - delta, child.MinHeight)));
maxWidth = Math.Max(child.DesiredSize.Width, maxWidth);
offset += child.GetAbsoluteValue() - delta;
}
else
{
child.Measure(new Size(availableSize.Width, child.MinHeight));
maxWidth = Math.Max(child.DesiredSize.Width, maxWidth);
offset += child.MinWidth;
}
}
}
else
{
double starsSum = childStars.Sum<FrameworkElement>(v => v.GetStarValue());
double starsFinalHeight =
availableSize.Height -
splitters.Sum<FrameworkElement>(s => s.MinHeight) -
childAbsolutes.Sum<FrameworkElement>(a => a.GetAbsoluteValue()) -
childAutoSizes.Sum<FrameworkElement>(a => a.DesiredSize.Height);
foreach (FrameworkElement child in visibleChildren)
{
if (child is ResizingPanelSplitter)
{
child.Measure(new Size(availableSize.Width, child.MinHeight));
maxWidth = Math.Max(child.DesiredSize.Width, maxWidth);
offset += child.MinWidth;
}
else if (child.IsAbsolute())
{
child.Measure(new Size(availableSize.Width, child.GetAbsoluteValue()));
maxWidth = Math.Max(child.DesiredSize.Width, maxWidth);
offset += child.GetAbsoluteValue();
}
else if (child.IsStar())
{
double w = child.GetStarValue() / starsSum * starsFinalHeight;
child.Measure(new Size(availableSize.Width, w));
maxWidth = Math.Max(child.DesiredSize.Width, maxWidth);
offset += w;
}
else
{
child.Measure(new Size(availableSize.Width, child.DesiredSize.Height));
maxWidth = Math.Max(child.DesiredSize.Width, maxWidth);
offset += child.DesiredSize.Width;
}
}
}
return new Size(maxWidth, offset);
#endregion
}
}
Size[] _childrenFinalSizes = null;
/// <summary>
/// Arranges children giving them a proportional space according to their <see cref="SplitSize"/> attached property value
/// </summary>
/// <param name="finalSize"></param>
/// <returns></returns>
protected override Size ArrangeOverride(Size finalSize)
{
//Compute the list of visible children
List<FrameworkElement> visibleChildren = new List<FrameworkElement>();
for (int i = 0; i < VisualChildrenCount; i++)
{
FrameworkElement child = GetVisualChild(i) as FrameworkElement;
IDockableControl dockableControl = child as IDockableControl;
if (dockableControl != null &&
!dockableControl.IsDocked)
{
child.Arrange(new Rect());
if (i == VisualChildrenCount - 1 &&
i > 0)
{
child = GetVisualChild(i - 1) as FrameworkElement;
Debug.Assert(child is ResizingPanelSplitter);
child.Arrange(new Rect());
if (visibleChildren.Count > 0)
{
Debug.Assert(visibleChildren[visibleChildren.Count - 1] is ResizingPanelSplitter);
visibleChildren[visibleChildren.Count - 1].Arrange(new Rect());
visibleChildren.RemoveAt(visibleChildren.Count - 1);
}
}
else if (i < VisualChildrenCount - 1)
{
i++;
child = GetVisualChild(i) as FrameworkElement;
child.Arrange(new Rect());
Debug.Assert(child is ResizingPanelSplitter);
}
continue;
}
visibleChildren.Add(child);
}
//with no children fill the space
if (visibleChildren.Count == 0)
{
_childrenFinalSizes = new Size[] { };
return new Size();
}
Debug.Assert(!(visibleChildren.Last<FrameworkElement>() is ResizingPanelSplitter));
_childrenFinalSizes = new Size[visibleChildren.Count];
var splitters = from FrameworkElement child in visibleChildren
where child is ResizingPanelSplitter
select child;
var childStars = from FrameworkElement child in visibleChildren
where (!(child is ResizingPanelSplitter)) && child.IsStar()
select child;
var childAbsolutes = from FrameworkElement child in visibleChildren
where (!(child is ResizingPanelSplitter)) && child.IsAbsolute()
select child;
var childAutoSizes = from FrameworkElement child in visibleChildren
where (!(child is ResizingPanelSplitter)) && child.IsAuto()
select child;
//calculate the size of the splitters
Size splitterSize = new Size();
foreach (ResizingPanelSplitter splitter in splitters)
{
splitterSize.Width += splitter.MinWidth;
splitterSize.Height += splitter.MinHeight;
}
Size minimumSize = new Size(splitterSize.Width, splitterSize.Height);
foreach (FrameworkElement child in childStars)
{
minimumSize.Width += child.MinWidth;
minimumSize.Height += child.MinHeight;
}
foreach (FrameworkElement child in childAbsolutes)
{
minimumSize.Width += child.MinWidth;
minimumSize.Height += child.MinHeight;
}
foreach (FrameworkElement child in childAutoSizes)
{
minimumSize.Width += child.MinWidth;
minimumSize.Height += child.MinHeight;
}
Size minimumPrefferedSize = new Size(minimumSize.Width, minimumSize.Height);
foreach (FrameworkElement child in childAbsolutes)
{
minimumPrefferedSize.Width += child.GetAbsoluteValue() - child.MinWidth;
minimumPrefferedSize.Height += child.GetAbsoluteValue() - child.MinHeight;
}
foreach (FrameworkElement child in childAutoSizes)
{
minimumPrefferedSize.Width += child.DesiredSize.Width - child.MinWidth;
minimumPrefferedSize.Height += child.DesiredSize.Height - child.MinHeight;
}
int iChild = 0;
if (Orientation == Orientation.Horizontal)
{
#region Horizontal Orientation
//if finalSize is not sufficient...
if (minimumSize.Width >= finalSize.Width)
{
foreach (FrameworkElement child in visibleChildren)
{
_childrenFinalSizes[iChild++] = new Size(child.MinWidth, finalSize.Height);
}
}
else if (minimumPrefferedSize.Width >= finalSize.Width)
{
double delta = (minimumPrefferedSize.Width - finalSize.Width) / childAbsolutes.Count<FrameworkElement>();
foreach (FrameworkElement child in visibleChildren)
{
if (child is ResizingPanelSplitter)
_childrenFinalSizes[iChild++] = new Size(child.MinWidth, finalSize.Height);
else if (child.IsAbsolute())
_childrenFinalSizes[iChild++] = new Size(Math.Max(child.GetAbsoluteValue() - delta, 0.0), finalSize.Height);
else
_childrenFinalSizes[iChild++] = new Size(child.MinWidth, finalSize.Height);
}
}
else
{
double starsSum = childStars.Sum<FrameworkElement>(v => v.GetStarValue());
double starsFinalWidth =
finalSize.Width -
splitters.Sum<FrameworkElement>(s => s.MinWidth) -
childAbsolutes.Sum<FrameworkElement>(a => a.GetAbsoluteValue()) -
childAutoSizes.Sum<FrameworkElement>(a => a.DesiredSize.Width);
foreach (FrameworkElement child in visibleChildren)
{
if (child is ResizingPanelSplitter)
_childrenFinalSizes[iChild++] = new Size(child.MinWidth, finalSize.Height);
else if (child.IsAbsolute())
_childrenFinalSizes[iChild++] = new Size(child.GetAbsoluteValue(), finalSize.Height);
else if (child.IsStar())
_childrenFinalSizes[iChild++] = new Size(child.GetStarValue() / starsSum * starsFinalWidth, finalSize.Height);
else
_childrenFinalSizes[iChild++] = new Size(child.DesiredSize.Width, finalSize.Height);
}
}
double offset = 0.0;
for (int i = 0; i < visibleChildren.Count; i++)
{
FrameworkElement child = visibleChildren[i] as FrameworkElement;
child.Arrange(new Rect(offset, 0.0, _childrenFinalSizes[i].Width, finalSize.Height));
offset += _childrenFinalSizes[i].Width;
SetEffectiveSize(child, new Size(_childrenFinalSizes[i].Width, finalSize.Height));
}
return new Size(offset, finalSize.Height);
#endregion
}
else
{
#region Vertical Orientation
//if finalSize is not sufficient...
if (minimumSize.Height >= finalSize.Height)
{
foreach (FrameworkElement child in visibleChildren)
{
_childrenFinalSizes[iChild++] = new Size(finalSize.Width, child.MinHeight);
}
}
else if (minimumPrefferedSize.Height >= finalSize.Height)
{
double delta = (minimumPrefferedSize.Height - finalSize.Height) / childAbsolutes.Count<FrameworkElement>();
foreach (FrameworkElement child in visibleChildren)
{
if (child is ResizingPanelSplitter)
_childrenFinalSizes[iChild++] = new Size(finalSize.Width, child.MinHeight);
else if (child.IsAbsolute())
_childrenFinalSizes[iChild++] = new Size(finalSize.Width, Math.Max(child.GetAbsoluteValue() - delta, 0.0));
else
_childrenFinalSizes[iChild++] = new Size(finalSize.Width, child.MinHeight);
}
}
else
{
double starsSum = childStars.Sum<FrameworkElement>(v => v.GetStarValue());
double starsFinalHeight =
finalSize.Height -
splitters.Sum<FrameworkElement>(s => s.MinHeight) -
childAbsolutes.Sum<FrameworkElement>(a => a.GetAbsoluteValue()) -
childAutoSizes.Sum<FrameworkElement>(a => a.DesiredSize.Height);
foreach (FrameworkElement child in visibleChildren)
{
if (child is ResizingPanelSplitter)
_childrenFinalSizes[iChild++] = new Size(finalSize.Width, child.MinHeight);
else if (child.IsAbsolute())
_childrenFinalSizes[iChild++] = new Size(finalSize.Width, child.GetAbsoluteValue());
else if (child.IsStar())
_childrenFinalSizes[iChild++] = new Size(finalSize.Width, child.GetStarValue() / starsSum * starsFinalHeight);
else
_childrenFinalSizes[iChild++] = new Size(finalSize.Width, child.DesiredSize.Height);
}
}
double offset = 0.0;
for (int i = 0; i < visibleChildren.Count; i++)
{
FrameworkElement child = visibleChildren[i] as FrameworkElement;
child.Arrange(new Rect(0.0, offset, finalSize.Width, _childrenFinalSizes[i].Height));
offset += _childrenFinalSizes[i].Height;
SetEffectiveSize(child, new Size(finalSize.Width, _childrenFinalSizes[i].Height));
}
return new Size(finalSize.Width, offset);
#endregion
}
}
bool setupSplitters = false;
bool splitterListIsDirty = false;
void SetupSplitters()
{
if (!splitterListIsDirty)
return;
if (setupSplitters)
return;
setupSplitters = true;
while (_splitterList.Count > 0)
{
ResizingPanelSplitter splitter = _splitterList[0];
splitter.DragStarted -= new DragStartedEventHandler(splitter_DragStarted);
splitter.DragDelta -= new DragDeltaEventHandler(splitter_DragDelta);
splitter.DragCompleted -= new DragCompletedEventHandler(splitter_DragCompleted);
_splitterList.Remove(splitter);
Children.Remove(splitter);
}
int i = 0;//child index
int j = 0;//splitter index
while (i < Children.Count - 1)
{
if (j == _splitterList.Count)
{
ResizingPanelSplitter splitter = new ResizingPanelSplitter();
_splitterList.Add(splitter);
splitter.DragStarted += new DragStartedEventHandler(splitter_DragStarted);
splitter.DragDelta += new DragDeltaEventHandler(splitter_DragDelta);
splitter.DragCompleted += new DragCompletedEventHandler(splitter_DragCompleted);
Children.Insert(i + 1, splitter);
}
i += 2;
j++;
}
for (j = 0; j < _splitterList.Count; j++)
{
_splitterList[j].Width = (Orientation == Orientation.Horizontal) ? 4 : double.NaN;
_splitterList[j].Height = (Orientation == Orientation.Vertical) ? 4 : double.NaN;
}
#if DEBUG
Debug.Assert(_splitterList.Count == Children.Count / 2);
i = 0;
while (Children.Count > 0)
{
Debug.Assert(Children[i] != null);
Debug.Assert(!(Children[i] is ResizingPanelSplitter));
i++;
if (i >= Children.Count)
break;
Debug.Assert((Children[i] is ResizingPanelSplitter));
i++;
}
#endif
splitterListIsDirty = false;
setupSplitters = false;
}
protected override void OnVisualChildrenChanged(DependencyObject visualAdded, DependencyObject visualRemoved)
{
base.OnVisualChildrenChanged(visualAdded, visualRemoved);
splitterListIsDirty = true;
}
void splitter_DragCompleted(object sender, DragCompletedEventArgs e)
{
Cursor = Cursors.Arrow;
}
/// <summary>
/// This method is called by a splitter when it is dragged
/// </summary>
/// <param name="splitter">Dragged splitter</param>
/// <param name="delta"></param>
void splitter_DragDelta(object sender, DragDeltaEventArgs e)
{
ResizingPanelSplitter splitter = e.Source as ResizingPanelSplitter;
int i = 0;
//Compute the list of visible children
List<FrameworkElement> visibleChildren = new List<FrameworkElement>();
for (i = 0; i < VisualChildrenCount; i++)
{
FrameworkElement child = GetVisualChild(i) as FrameworkElement;
IDockableControl dockableControl = child as IDockableControl;
if (dockableControl != null &&
!dockableControl.IsDocked)
{
if (i == VisualChildrenCount - 1 &&
i > 0)
{
//remove the last splitter added
if (visibleChildren.Count > 0 &&
visibleChildren.Last<FrameworkElement>() is ResizingPanelSplitter)
visibleChildren.RemoveAt(visibleChildren.Count - 1);
}
else if (i < VisualChildrenCount - 1)
{
//discard the next splitter
i++;
}
continue;
}
visibleChildren.Add(child);
}
if (visibleChildren.Count == 0)
return;
if (visibleChildren.Last<FrameworkElement>() is ResizingPanelSplitter)
visibleChildren.RemoveAt(visibleChildren.Count - 1);
Size[] currentSizes = new Size[visibleChildren.Count];
double delta = Orientation == Orientation.Horizontal ? e.HorizontalChange : e.VerticalChange;
if (_childrenFinalSizes == null)
return;
_childrenFinalSizes.CopyTo(currentSizes, 0);
int iSplitter = visibleChildren.IndexOf(splitter);
Debug.Assert(iSplitter > -1);
List<FrameworkElement> prevChildren = new List<FrameworkElement>();
for (i = iSplitter - 1; i >= 0; i--)
{
FrameworkElement child = visibleChildren[i] as FrameworkElement;
if (child is ResizingPanelSplitter)
continue;
if (child.IsAbsolute() || child.IsAuto())
{
if (prevChildren.Count == 0)
{
prevChildren.Add(child);
}
break;
}
if (child.IsStar())
{
prevChildren.Add(child);
}
}
List<FrameworkElement> nextChildren = new List<FrameworkElement>();
for (i = iSplitter + 1; i < visibleChildren.Count; i++)
{
FrameworkElement child = visibleChildren[i] as FrameworkElement;
if (child is ResizingPanelSplitter)
continue;
if (child.IsAbsolute() || child.IsAuto())
{
if (nextChildren.Count == 0)
nextChildren.Add(child);
break;
}
if (child.IsStar())
{
nextChildren.Add(child);
}
}
double prevMinSize = prevChildren.Sum<FrameworkElement>(c => Orientation == Orientation.Horizontal ? c.MinWidth : c.MinHeight);
double nextMinSize = nextChildren.Sum<FrameworkElement>(c => Orientation == Orientation.Horizontal ? c.MinWidth : c.MinHeight);
double prevMaxSize = prevChildren.Sum<FrameworkElement>(c => Orientation == Orientation.Horizontal ? c.MaxWidth : c.MaxHeight);
double nextMaxSize = nextChildren.Sum<FrameworkElement>(c => Orientation == Orientation.Horizontal ? c.MaxWidth : c.MaxHeight);
double prevSize = prevChildren.Sum<FrameworkElement>(c => Orientation == Orientation.Horizontal ? currentSizes[visibleChildren.IndexOf(c)].Width : currentSizes[visibleChildren.IndexOf(c)].Height);
double nextSize = nextChildren.Sum<FrameworkElement>(c => Orientation == Orientation.Horizontal ? currentSizes[visibleChildren.IndexOf(c)].Width : currentSizes[visibleChildren.IndexOf(c)].Height);
if (prevSize + delta < prevMinSize)
delta = prevMinSize - prevSize;
if (nextSize - delta < nextMinSize)
delta = -(nextMinSize - nextSize);
double remDelta = delta * 2;
while (!HelperFunc.AreClose(delta, 0.0))
{
int prevChildrenCountWithNoMinLen =
prevChildren.Count<FrameworkElement>(c => delta > 0 ? true : (Orientation == Orientation.Horizontal ? currentSizes[visibleChildren.IndexOf(c)].Width > c.MinWidth : currentSizes[visibleChildren.IndexOf(c)].Height > c.MinHeight));
int nextChildrenCountWithNoMinLen =
nextChildren.Count<FrameworkElement>(c => delta < 0 ? true : (Orientation == Orientation.Horizontal ? currentSizes[visibleChildren.IndexOf(c)].Width > c.MinWidth : currentSizes[visibleChildren.IndexOf(c)].Height > c.MinHeight));
delta = remDelta / 2.0;
for (i = 0; i < currentSizes.Length; i++)
{
FrameworkElement child = visibleChildren[i] as FrameworkElement;
if (child is ResizingPanelSplitter)
continue;
if (Orientation == Orientation.Horizontal)
{
if (prevChildren.Contains(child) && prevChildrenCountWithNoMinLen > 0)
{
double s = delta / prevChildrenCountWithNoMinLen;
if (currentSizes[i].Width + s < child.MinWidth)
s = child.MinWidth - currentSizes[i].Width;
currentSizes[i].Width += s;
remDelta -= s;
}
if (nextChildren.Contains(child) && nextChildrenCountWithNoMinLen > 0)
{
double s = delta / nextChildrenCountWithNoMinLen;
if (currentSizes[i].Width - s < child.MinWidth)
s = currentSizes[i].Width - child.MinWidth;
currentSizes[i].Width -= s;
remDelta -= s;
}
}
else
{
if (prevChildren.Contains(child) && prevChildrenCountWithNoMinLen > 0)
{
double s = delta / prevChildrenCountWithNoMinLen;
if (currentSizes[i].Height + s < child.MinHeight)
s = child.MinHeight - currentSizes[i].Height;
currentSizes[i].Height += s;
remDelta -= s;
}
if (nextChildren.Contains(child) && nextChildrenCountWithNoMinLen > 0)
{
double s = delta / nextChildrenCountWithNoMinLen;
if (currentSizes[i].Height - s < child.MinHeight)
s = currentSizes[i].Height - child.MinHeight;
currentSizes[i].Height -= s;
remDelta -= s;
}
}
}
}
Debug.Assert(HelperFunc.AreClose(delta, 0.0));
double totalStartsSum = 0.0;
double totalSizeForStarts = 0.0;
for (i = 0; i < visibleChildren.Count; i++)
{
FrameworkElement child = visibleChildren[i] as FrameworkElement;
if (child is ResizingPanelSplitter)
continue;
if (child.IsStar())
{
totalStartsSum += child.GetStarValue();
totalSizeForStarts += Orientation == Orientation.Horizontal ? currentSizes[i].Width : currentSizes[i].Height;
}
}
double starsScaleFactor = totalStartsSum / totalSizeForStarts;
for (i = 0; i < currentSizes.Length; i++)
{
FrameworkElement child = visibleChildren[i] as FrameworkElement;
if (child is ResizingPanelSplitter)
continue;
if (child.IsStar())
{
if (Orientation == Orientation.Horizontal)
{
SetResizeWidth(child,
new GridLength(HelperFunc.MultiplyCheckNaN(currentSizes[i].Width, starsScaleFactor), GridUnitType.Star));
}
else
{
SetResizeHeight(child,
new GridLength(HelperFunc.MultiplyCheckNaN(currentSizes[i].Height, starsScaleFactor), GridUnitType.Star));
}
}
else if (child.IsAbsolute())
{
if (Orientation == Orientation.Horizontal)
{
SetResizeWidth(child,
new GridLength(currentSizes[i].Width, GridUnitType.Pixel));
}
else
{
SetResizeHeight(child,
new GridLength(currentSizes[i].Height, GridUnitType.Pixel));
}
}
}
InvalidateMeasure();
//ResizingPanelSplitter splitter = e.Source as ResizingPanelSplitter;
//int iSplitter = Children.IndexOf(splitter);
//UIElement childPrev = null;
//UIElement childNext = null;
////int posInc = ResizingDirection == ResizingDirection.Direct ? 2 : -2;
//int posInc = 2;// FlowDirection == FlowDirection.LeftToRight ? 2 : -2;
//int negInc = -posInc;
//int i = iSplitter;
//while (i >= 0 ||
// i < Children.Count - 1)
//{
// if (NextChildIsVisible(i))
// {
// //childNext = Children[ResizingDirection == ResizingDirection.Direct ? i + 1 : i - 1];
// childNext = Children[i + 1];//FlowDirection == FlowDirection.LeftToRight ? i + 1 : i - 1];
// break;
// }
// i += posInc;
//}
//i = iSplitter;
//while (i >= 0 ||
// i < Children.Count - 1)
//{
// if (PrevChildIsVisible(i))
// {
// //childPrev = Children[ResizingDirection == ResizingDirection.Direct ? i - 1 : i + 1];
// childPrev = Children[i - 1];//FlowDirection == FlowDirection.LeftToRight ? i - 1 : i + 1];
// break;
// }
// i -= posInc;
//}
//Size resExtPrev = new Size((double)childPrev.GetValue(ResizeWidthProperty), (double)childPrev.GetValue(ResizeHeightProperty));
//Size resExtNext = new Size((double)childNext.GetValue(ResizeWidthProperty), (double)childNext.GetValue(ResizeHeightProperty));
//#region Orientation == Horizontal
//if (Orientation == Orientation.Horizontal)
//{
// double delta = e.HorizontalChange;
// if (!double.IsPositiveInfinity(resExtPrev.Width) &&
// (resExtPrev.Width + delta < 0))
// delta = -resExtPrev.Width;
// if (!double.IsPositiveInfinity(resExtNext.Width) &&
// resExtNext.Width - delta < 0)
// delta = resExtNext.Width;
// if (!double.IsPositiveInfinity(resExtPrev.Width))
// childPrev.SetValue(ResizeWidthProperty, resExtPrev.Width + delta);
// if (!double.IsPositiveInfinity(resExtNext.Width))
// childNext.SetValue(ResizeWidthProperty, resExtNext.Width - delta);
//}
//#endregion
//#region Orientation == Vertical
//else //if (Orientation == Orientation.Vertical)
//{
// double delta = e.VerticalChange;
// if (!double.IsPositiveInfinity(resExtPrev.Height) &&
// (resExtPrev.Height + delta < 0))
// delta = -resExtPrev.Height;
// if (!double.IsPositiveInfinity(resExtNext.Height) &&
// resExtNext.Height - delta < 0)
// delta = resExtNext.Height;
// if (!double.IsPositiveInfinity(resExtPrev.Height))
// childPrev.SetValue(ResizeHeightProperty, resExtPrev.Height + delta);
// if (!double.IsPositiveInfinity(resExtNext.Height))
// childNext.SetValue(ResizeHeightProperty, resExtNext.Height - delta);
//}
//#endregion
}
void splitter_DragStarted(object sender, DragStartedEventArgs e)
{
Cursor = Orientation == Orientation.Horizontal ? Cursors.SizeWE : Cursors.SizeNS;
}
#region IDockableControl Membri di
public bool IsDocked
{
get
{
foreach (UIElement child in this.Children)
{
if (child is IDockableControl)
if (((IDockableControl)child).IsDocked)
return true;
}
return false;
}
}
#endregion
/// <summary>
/// Remove a child from children collection
/// </summary>
/// <param name="childToRemove"></param>
internal void RemoveChild(FrameworkElement childToRemove)
{
int indexOfChildToRemove = Children.IndexOf(childToRemove);
Debug.Assert(indexOfChildToRemove != -1);
Children.RemoveAt(indexOfChildToRemove);
if (Children.Count > 0)
{
SetupSplitters();
if (Children.Count == 1)
{
UIElement singleChild = this.Children[0];
if (Parent is ResizingPanel)
{
ResizingPanel parentPanel = Parent as ResizingPanel;
if (parentPanel != null)
{
int indexOfThisPanel = parentPanel.Children.IndexOf(this);
parentPanel.Children.RemoveAt(indexOfThisPanel);
this.Children.Remove(singleChild);
parentPanel.Children.Insert(indexOfThisPanel, singleChild);
if (parentPanel.Orientation == Orientation.Horizontal)
{
SetResizeWidth(singleChild, GetResizeWidth(this));
}
else
{
SetResizeHeight(singleChild, GetResizeHeight(this));
}
}
}
else if (Parent is DockingManager)
{
DockingManager manager = Parent as DockingManager;
if (manager != null)
{
this.Children.Remove(singleChild);
manager.Content = singleChild;
}
}
}
}
else
{
ResizingPanel parentPanel = Parent as ResizingPanel;
if (parentPanel != null)
{
parentPanel.RemoveChild(this);
}
}
}
/// <summary>
/// Insert a new child element into the children collection.
/// </summary>
/// <param name="childToInsert">New child element to insert.</param>
/// <param name="relativeChild">Child after or before which <see cref="childToInsert"/> element must be insert.</param>
/// <param name="next">True if new child must be insert after the <see cref="relativeChild"/> element. False otherwise.</param>
internal void InsertChildRelativeTo(FrameworkElement childToInsert, FrameworkElement relativeChild, bool next)
{
int childRelativeIndex = Children.IndexOf(relativeChild);
Debug.Assert(childRelativeIndex != -1);
Children.Insert(
next ? childRelativeIndex + 1 : childRelativeIndex, childToInsert);
SetupSplitters();
}
}
internal static class ResizingPanelExFuncs
{
public static double GetAbsoluteValue(this FrameworkElement child)
{
ResizingPanel parentPanel = LogicalTreeHelper.GetParent(child) as ResizingPanel;
GridLength len = parentPanel.Orientation == Orientation.Horizontal ? ResizingPanel.GetResizeWidth(child) : ResizingPanel.GetResizeHeight(child);
if (!len.IsAbsolute)
throw new InvalidOperationException();
return len.Value;
}
public static double GetStarValue(this FrameworkElement child)
{
ResizingPanel parentPanel = LogicalTreeHelper.GetParent(child) as ResizingPanel;
GridLength len = parentPanel.Orientation == Orientation.Horizontal ? ResizingPanel.GetResizeWidth(child) : ResizingPanel.GetResizeHeight(child);
if (!len.IsStar)
throw new InvalidOperationException();
return len.Value;
}
public static bool IsStar(this FrameworkElement child)
{
ResizingPanel parentPanel = LogicalTreeHelper.GetParent(child) as ResizingPanel;
return parentPanel.Orientation == Orientation.Horizontal ?
ResizingPanel.GetResizeWidth(child).IsStar :
ResizingPanel.GetResizeHeight(child).IsStar;
}
public static bool IsAbsolute(this FrameworkElement child)
{
ResizingPanel parentPanel = LogicalTreeHelper.GetParent(child) as ResizingPanel;
return parentPanel.Orientation == Orientation.Horizontal ?
ResizingPanel.GetResizeWidth(child).IsAbsolute :
ResizingPanel.GetResizeHeight(child).IsAbsolute;
}
public static bool IsAuto(this FrameworkElement child)
{
ResizingPanel parentPanel = LogicalTreeHelper.GetParent(child) as ResizingPanel;
return parentPanel.Orientation == Orientation.Horizontal ?
ResizingPanel.GetResizeWidth(child).IsAuto :
ResizingPanel.GetResizeHeight(child).IsAuto;
}
}
}