Search code examples
wpfresizeaspect-ratio

WPF Resize UserControl with aspect ratio


I have a UserControl and that UserControl has to be resized with aspect ratio. That means: width:height = 2:1. Currently I am using this code:

    protected override Size ArrangeOverride(Size arrangeBounds)
    {
        if (ActualWidth == 0 || ActualHeight == 0) return arrangeBounds;
        base.ArrangeOverride(arrangeBounds);
        double ratio = 2;

        if (Parent != null)
        {
            var size = new Size(arrangeBounds.Height * ratio, arrangeBounds.Height);

            double containerWidth = ((FrameworkElement)Parent).ActualWidth;
            if (containerWidth < size.Width)
            {
                double newHeight = arrangeBounds.Height * (containerWidth / size.Width);
                canvas.Width = newHeight * ratio;
                canvas.Height = newHeight;
            }
            else
            {
                canvas.Width = size.Height * ratio;
                canvas.Height = size.Height;
            }
        }

        return arrangeBounds;
    }

But it is not really working. That means it works but not every time. If I max. the window it sometimes does not get resized, so its a bit "random" if the control gets resized. So if someone would have a better solution if would be very nice.


Solution

  • It's a bit late, but I recently came across the same problem and since I did not find a good solution I decided to write my own layout control/decorator and wrote a blog post about it here:

    http://coding4life.wordpress.com/2012/10/15/wpf-resize-maintain-aspect-ratio/

    Basically my solution was to overwrite both MeasureOverride and ArrangeOverride. So far it works very nicely in all of the common containers and I didn't encounter any problems like you described.

    I recommend reading the post, where you find a working decorator control, but the most important methods are these:

       protected override Size MeasureOverride(Size constraint)
       {
          if (Child != null)
          {
             constraint = SizeToRatio(constraint, false);
             Child.Measure(constraint);
    
             if(double.IsInfinity(constraint.Width)
                || double.IsInfinity(constraint.Height))
             {
                return SizeToRatio(Child.DesiredSize, true);
             }
    
             return constraint;
          }
    
          // we don't have a child, so we don't need any space
          return new Size(0, 0);
       }
    
       protected override Size ArrangeOverride(Size arrangeSize)
       {
          if (Child != null)
          {
             var newSize = SizeToRatio(arrangeSize, false);
    
             double widthDelta = arrangeSize.Width - newSize.Width;
             double heightDelta = arrangeSize.Height - newSize.Height;
    
             double top = 0;
             double left = 0;
    
             if (!double.IsNaN(widthDelta)
                && !double.IsInfinity(widthDelta))
             {
                left = widthDelta/2;
             }
    
             if (!double.IsNaN(heightDelta)
                && !double.IsInfinity(heightDelta))
             {
                top = heightDelta/2;
             }
    
             var finalRect = new Rect(new Point(left, top), newSize);
             Child.Arrange(finalRect);
          }
    
          return arrangeSize;
       }
    
       public Size SizeToRatio(Size size, bool expand)
       {
          double ratio = AspectRatio;
    
          double height = size.Width / ratio;
          double width = size.Height * ratio;
    
          if (expand)
          {
             width = Math.Max(width, size.Width);
             height = Math.Max(height, size.Height);
          }
          else
          {
             width = Math.Min(width, size.Width);
             height = Math.Min(height, size.Height);
          }
    
          return new Size(width, height);
       }
    

    I hope it helps someone!