Reputation: 649
So I've been looking around on this question quite a bit, but the results always keep coming back to either setting BackgroundImageLayout
to "Stretch". Which, of course, doesn't do anything about maintaining the aspect ratio of the original image or to tutorials on how to scale a PictureBox
or other loaded images manually while retaining the aspect ratio, but I cannot (very surprisingly to me) find anything about how to get a WinForm to manage a background image that's always centered on the window, stretched to cover it, but maintaining the same aspect ratio as the original.
I tried this:
public class CustomForm : Form
{
protected Size _scale;
protected Size _originalSize;
protected float _aspectRatio;
public CustomForm() : base()
{
this.DoubleBuffered = true;
}
private float AspectRatio() { return this.AspectRatio(this._originalSize); }
private float AspectRatio(int width, int height) { return (float)width / height; }
private float AspectRatio(Size value) { return this.AspectRatio(value.Width, value.Height); }
protected override void OnBackgroundImageChanged(EventArgs e)
{
this._originalSize = new Size(this.BackgroundImage.Width, this.BackgroundImage.Height);
this._aspectRatio = this.AspectRatio(this._originalSize);
base.OnBackgroundImageChanged(e);
}
protected override void OnPaintBackground(PaintEventArgs e)
{
if (this.BackgroundImage == null) base.OnPaintBackground(e);
else
{
e.Graphics.FillRectangle(new SolidBrush(this.BackColor), ClientRectangle);
int x = (this._scale.Width > this.Width) ? (int)Math.Round((this._scale.Width - this.Width) / 2.0) : 0;
int y = (this._scale.Height > this.Height) ? (int)Math.Round((this._scale.Height - this.Height) / 2.0) : 0;
e.Graphics.DrawImage(this.BackgroundImage, new Rectangle(x, y, this._scale.Width, this._scale.Height));
}
}
protected override void OnSizeChanged(EventArgs e)
{
if ( this._aspectRatio > this.AspectRatio(this.Size))
{
this._scale.Width = this.Width;
this._scale.Height = (int)(this.Width / this._aspectRatio);
}
else
{
this._scale.Height = this.Height;
this._scale.Width = (int)(this.Height * this._aspectRatio);
}
base.OnSizeChanged(e);
}
}
But all it does is fix the image in the top-left corner of the form at it's basic dimensions, regardless of how the form is (re)sized...
Can someone please point me in the direction of a proper solution, or to what I've got / done wrong thus far?
Thanks!
Upvotes: 1
Views: 563
Reputation: 649
Okay, after a bunch more research, and with some bodging, I finally got this sorted out! Posting this here for anyone who may come looking for it in the future!
public class CustomForm : Form
{
protected Size _scale;
protected Size _originalSize;
public CustomForm() : base()
{
this.InitializeComponent();
}
protected override void OnBackgroundImageChanged(EventArgs e)
{
this._originalSize = new Size(this.BackgroundImage.Width, this.BackgroundImage.Height);
base.OnBackgroundImageChanged(e);
}
protected override void OnPaintBackground(PaintEventArgs e)
{
if ((BackgroundImage != null) && (BackgroundImageLayout == ImageLayout.None))
{
Point p = new Point(
(int)((this.Width - this._scale.Width) / 2),
(int)((this.Height - this._scale.Height) / 2)
);
e.Graphics.DrawImage(this.BackgroundImage, new Rectangle(p, this._scale));
}
else
base.OnPaintBackground(e);
}
protected override void OnSizeChanged(EventArgs e)
{
if ((BackgroundImage != null) && (BackgroundImageLayout == ImageLayout.None))
{
float rX = (float) this.Width / this._originalSize.Width;
float rY = (float) this.Height / this._originalSize.Height;
float r = (rX < rY) ? rY : rX;
this._scale.Height = (int)(this._originalSize.Height * r);
this._scale.Width = (int)(this._originalSize.Width * r);
}
base.OnSizeChanged(e);
}
private void InitializeComponent()
{
this._originalSize = new Size(0, 0);
this._scale = new Size(0, 0);
this.SuspendLayout();
//
// CustomForm
//
this.Name = "CustomForm";
this.DoubleBuffered = true; // minimizes "flicker" when resizing
this.ResizeRedraw = true; // forces a full redraw when resized!
this.ResumeLayout(false);
}
}
Upvotes: 2