Background
During recent works, I was using the DialogHost
control of MaterialDesignTheme
to show a dialog instead of opening a window by myself.
The DialogHost
use Popup
to show the content of it.
My app is set to fullscreen and I wish the dialog will be just a little bit smaller than the screen. Due to the DialogHost
has DialogMargin
property to limit the dialog content, I just binding the Width
and the Height
of the dialog content like this
<md:DialogHost.DialogContent>
<ControlXXX
Width="{Binding ActualWidth, RelativeSource={RelativeSource FindAncestor, AncestorType=Border}}"
Height="{Binding ActualHeight, RelativeSource={RelativeSource FindAncestor, AncestorType=Border}}" />
</md:DialogHost.DialogContent>
The ancestor Border
has the same size of the screen, everything looks well.
But when run the app and open the dialog, the height of it was smaller than expected, my screen size is 2560X1600 and the height of the dialog was always <=1200 no matter what value I set. It was annoying that I spent a lot of time debugging wpf source code......
Why
Let's see part of the source code of Popup.cs
in WPF repository
...
private const double RestrictPercentage = 0.75; // This is how much the max dimensions will be reduced by
...
// Limit size to 75% of maxDimension's area and restrict to be smaller than limitDimension
internal Size RestrictSize(Size desiredSize)
{
// Make sure screen bounds and limit dimensions are up to date
Rect targetBounds, screenBounds;
Size limitSize;
GetPopupRootLimits(out targetBounds, out screenBounds, out limitSize);
// Convert from popup's space to screen space
desiredSize = (Size)_secHelper.GetTransformToDevice().Transform((Point)desiredSize);
desiredSize.Width = Math.Min(desiredSize.Width, screenBounds.Width);
desiredSize.Width = Math.Min(desiredSize.Width, limitSize.Width);
double maxHeight = RestrictPercentage * screenBounds.Width * screenBounds.Height / desiredSize.Width;
desiredSize.Height = Math.Min(desiredSize.Height, screenBounds.Height);
desiredSize.Height = Math.Min(desiredSize.Height, maxHeight);
desiredSize.Height = Math.Min(desiredSize.Height, limitSize.Height);
// Convert back from screen space to popup's space
desiredSize = (Size)_secHelper.GetTransformFromDevice().Transform((Point)desiredSize);
return desiredSize;
}
We can see that, no matter how much available area there are, the size of popup will be limit to 75% of available area, and the limit is set on height.
And we can not change the RestrictPercentage......
Solution
The restriction happens on Measure
and was set to PopupRoot
which is the child of Popup
and really host the popup content.
What I did was set the Height
of PopupRoot
to expected value and then set the Height
and Width
of the ControlXXX
as same as the ContentPresenter of it after loaded.
private async void ControlXXX_OnLoaded(object sender, RoutedEventArgs e)
{
var popupRoot = this.FindVisualAncestor(o =>
o.GetType().ToString() == "System.Windows.Controls.Primitives.PopupRoot");
if (popupRoot != null)
{
//_inDialog = true;
(popupRoot as FrameworkElement).Height = Application.Current.MainWindow.Height;
await Task.Yield();
var presenter = this.FindVisualAncestorByType<ContentPresenter>();
this.Height = presenter.ActualHeight;
this.Width = presenter.ActualWidth;
}
}
Comments