public void Arrange()
{
if(Popup == null
|| PopupChild == null
#if SILVERLIGHT
|| OutsidePopupCanvas == null
#endif
|| Application.Current == null
#if SILVERLIGHT
|| Application.Current.Host == null
|| Application.Current.Host.Content == null
#endif
// ReSharper disable RedundantLogicalConditionalExpressionOperand
|| false)
// ReSharper restore RedundantLogicalConditionalExpressionOperand
{
return;
}
#if SILVERLIGHT
Content hostContent = Application.Current.Host.Content;
double rootWidth = hostContent.ActualWidth;
double rootHeight = hostContent.ActualHeight;
#else
UIElement u = Parent;
if(Application.Current.Windows.Count > 0)
{
// TODO: USE THE CURRENT WINDOW INSTEAD! WALK THE TREE!
u = Application.Current.Windows[0];
}
while(u as Window == null && u != null)
{
u = VisualTreeHelper.GetParent(u) as UIElement;
}
Window w = u as Window;
if(w == null)
{
return;
}
double rootWidth = w.ActualWidth;
double rootHeight = w.ActualHeight;
#endif
double popupContentWidth = PopupChild.ActualWidth;
double popupContentHeight = PopupChild.ActualHeight;
// ReSharper disable CompareOfFloatsByEqualityOperator
if(rootHeight == 0 || rootWidth == 0 || popupContentWidth == 0 || popupContentHeight == 0)
// ReSharper restore CompareOfFloatsByEqualityOperator
{
return;
}
const double rootOffsetX = 0;
const double rootOffsetY = 0;
double myControlHeight = Parent.ActualHeight;
double myControlWidth = Parent.ActualWidth;
// Use or come up with a maximum popup height.
double popupMaxHeight = MaxDropDownHeight;
if(double.IsInfinity(popupMaxHeight) || double.IsNaN(popupMaxHeight))
{
popupMaxHeight = (rootHeight - myControlHeight) * 3 / 5;
}
popupContentWidth = Math.Min(popupContentWidth, rootWidth);
popupContentHeight = Math.Min(popupContentHeight, popupMaxHeight);
popupContentWidth = Math.Max(myControlWidth, popupContentWidth);
// We prefer to align the popup box with the left edge of the
// control, if it will fit.
double popupX = rootOffsetX;
if(rootWidth < popupX + popupContentWidth)
{
// Since it doesn't fit when strictly left aligned, we shift it
// to the left until it does fit.
popupX = rootWidth - popupContentWidth;
popupX = Math.Max(0, popupX);
}
// We prefer to put the popup below the combobox if it will fit.
bool below = true;
double popupY = rootOffsetY + myControlHeight;
if(rootHeight < popupY + popupContentHeight)
{
below = false;
// It doesn't fit below the combobox, lets try putting it above
// the combobox.
popupY = rootOffsetY - popupContentHeight;
if(popupY < 0)
{
// doesn't really fit below either. Now we just pick top
// or bottom based on wich area is bigger.
if(rootOffsetY < (rootHeight - myControlHeight) / 2)
{
below = true;
popupY = rootOffsetY + myControlHeight;
}
else
{
popupY = rootOffsetY - popupContentHeight;
}
}
}
// Now that we have positioned the popup we may need to truncate
// its size.
popupMaxHeight = below ? Math.Min(rootHeight - popupY, popupMaxHeight) : Math.Min(rootOffsetY, popupMaxHeight);
Popup.HorizontalOffset = 0;
Popup.VerticalOffset = 0;
#if SILVERLIGHT
OutsidePopupCanvas.Width = rootWidth;
OutsidePopupCanvas.Height = rootHeight;
// Transform the transparent canvas to the plugin's coordinate
// space origin.
Matrix transformToRootMatrix = mt.Matrix;
Matrix newMatrix;
transformToRootMatrix.Invert(out newMatrix);
mt.Matrix = newMatrix;
OutsidePopupCanvas.RenderTransform = mt;
#endif
PopupChild.MinWidth = myControlWidth;
PopupChild.MaxWidth = rootWidth;
PopupChild.MinHeight = 0;
PopupChild.MaxHeight = Math.Max(0, popupMaxHeight);
PopupChild.Width = popupContentWidth;
PopupChild.HorizontalAlignment = HorizontalAlignment.Left;
PopupChild.VerticalAlignment = VerticalAlignment.Top;
// Set the top left corner for the actual drop down.
Canvas.SetLeft(PopupChild, popupX - rootOffsetX);
Canvas.SetTop(PopupChild, popupY - rootOffsetY);
}