NetXpert
NetXpert

Reputation: 649

Trying to set a background image that always covers the form, but has a static/fixed aspect ratio

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

Answers (1)

NetXpert
NetXpert

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

Related Questions