Reputation: 1264
I've looked for quite a while now for a way to be able to tell a WPF control (or window) to keep a certain aspect ratio.
Window
I found this solution, that works quite well. But since it uses the Win32 API and window handles it's not working for any WPF Control
s (because as far as I know in WPF only the window itself has a handle)Control
one usually gets the advice to put the Control
in a ViewBox
, but I don't want to scale my controls, I want them to resize (and keep any border width or font size).Control
involve any form of binding the Width
to the ActualHeight
or the Height
to the ActualWidth
, or using the SizeChanged
event, but this results in heavy flickering while resizing and it's not very reliable.
In case of binding the Width
to the ActualHeight
you can't resize only the Width
(by dragging the right border) because the ActualHeight
doesn't change.
In case of the event it gets tricky when width and height change at the same time, then you'd have to change the size inside the SizeChanged
event... and did I mention the flickering?After a lot of reading and searching I came to the conclusion that the best way to force any control to keep a certain aspect ratio would be to do that inside the Measure
and Arrange
functions.
I found this solution that creates a Decorator
control with overridden Measure
and Measure
functions, but that would mean to put any control that's supposed to keep it's aspect ratio inside it's own Decorator
. I could live with that if I had to, but I wonder if there's a better way to do it.
So, here's my question. Is it possible to create an attached property Ratio
and an attached property KeepRatio
and somehow override the Measure
and Arrange
functions of the controls in question in the OnKeepRatioChanged
and RatioChanged
callbacks of the attached properties?
Upvotes: 1
Views: 1034
Reputation: 174
If you want to override Arrange/Measure methods then there is no need in attached properties. This wrapper should be fine:
public partial class RatioKeeper : UserControl
{
public static readonly DependencyProperty VerticalAspectProperty = DependencyProperty.Register(
"VerticalAspect", typeof(double), typeof(RatioKeeper), new PropertyMetadata(1d));
public static readonly DependencyProperty HorizontalAspectProperty = DependencyProperty.Register(
"HorizontalAspect", typeof(double), typeof(RatioKeeper), new PropertyMetadata(1d));
public double HorizontalAspect
{
get { return (double) GetValue(HorizontalAspectProperty); }
set { SetValue(HorizontalAspectProperty, value); }
}
public double VerticalAspect
{
get { return (double) GetValue(VerticalAspectProperty); }
set { SetValue(VerticalAspectProperty, value); }
}
public RatioKeeper()
{
InitializeComponent();
}
//arrangeBounds provides size of a host.
protected override Size ArrangeOverride(Size arrangeBounds)
{
//Calculation of a content size that wont exceed host's size and will be of the desired ratio at the same time
var horizontalPart = arrangeBounds.Width / HorizontalAspect;
var verticalPart = arrangeBounds.Height / VerticalAspect;
var minPart = Math.Min(horizontalPart, verticalPart);
var size = new Size(minPart * HorizontalAspect, minPart * VerticalAspect);
//apply size to wrapped content
base.ArrangeOverride(size);
//return size to host
return size;
}
}
Upvotes: 1