Handling User-Controlled Window Resizing

I gather this is a problem to a few and not-a-problem to many, based on what I find on the web (general web for either XNA or MonoGame) that addresses the problem satisfactorily. I have a solution that works for me, but, given my general ignorance regarding MonoGame (I’m a newb), I need to know whether I’m on the right track or out in left field somewhere.

My interest is in 3D viewing. Here, I’m trying to use the resources of XNA through MonoGame. (Most of my experience is with OpenGL, and I’ve got 48 years with 3D transformations.) I’m using MonoGame 3.4 with Windows 10.

My initial approach was based on an MSDN article on Resizing a Game and a Stack Overflow response to a forum query “How to resize window using XNA”. It involved delegating an event handler to handle the ClientSizeChange event of Game.Window. The event-handling requirements were:

  1. Set the buffer width and height.
  2. Call ApplyChanges() to commit the new viewport.
  3. Call CreatePerspectiveFieldOfView() to define the new viewing frustum.

I tried to handle these few requirements entirely in my OnResize() event handler. But I very quickly found out that my approach caused the stack to overflow. (After all, it was Stack Overflow advice!)

Watching the behavior in the window while I was resizing it (without trying to do everything in OnResize()), I hypothesized that neither draws nor updates were being done so long as my mouse button stayed down; the same buffer was being re-painted. I concluded that what I needed was a resize-complete event. But nothing like that is available, so far as I know.

I thunk about it for a while and then tried an experiment. I did the bare minimum in OnResize(): I set a flag that signals that a resize operation is pending, and I query the window height and width. I store these three things in a simple structure named resizing.

When the user terminates his window resize, there are two places where I could commit a window size change and set the frustum – Draw() or Update(). Both worked. I figured I’d seek someone else’s opinion. I looked at Open Rails source code and saw that they do it in Update(). That’s what I did.

Here’s a quick summary of what I did.

Key class variables in Game1:

    GraphicsDeviceManager graphics;
    SpriteBatch spriteBatch;
    ResizeStatus resizing; // Records status and (width, height) while resizing

Game1’s constructor instantiates resizing (the ResizeStatus structure), allows window resizing, and delegates ClientSizeChange event handling to OnResize():

graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
resizing = new ResizeStatus(graphics);
        Window.AllowUserResizing = true; 
        Window.ClientSizeChanged += new EventHandler<EventArgs>(OnResize);

Update() tests the resizing.Pending flag. If set, the back buffer size is committed and the frustum is adjusted. On completion, the resizing.Pending flag is set false:

        if (resizing.Pending)
        {
            graphics.PreferredBackBufferWidth = resizing.Width;
            graphics.PreferredBackBufferHeight = resizing.Height;
            graphics.ApplyChanges();
            camera.UpdateProjection(resizing.Width, resizing.Height);
            resizing.Pending = false; // Resize is complete (no longer pending)
        }

The Resize() event handler merely records window size changes as they occur and sets the resizing.pending flag:

        resizing.Pending = true; // Resize is pending
        resizing.Width = Window.ClientBounds.Width;
        resizing.Height = Window.ClientBounds.Height;

Finally, my question: Am I on the right track?

Make a new project comment out your game1 then paste this game1 code in or use the game2 code and change program.cs to reflect that, run it play around with it use F1 to F12 take a look at the code.
To see the output of everything it does or you are doing when running it. In vs Go into your debug project settings properties goto application on the left within that click the drop down for application window to change it to console window

https://drive.google.com/folderview?id=0B1zD887frY04RWJMczFLZGg1RXM&usp=drive_web

// goes in the game1 constructor
{
Window.AllowUserResizing = true;

Window.ClientSizeChanged += OnResize;
}

public void OnResize(Object sender, EventArgs e)
{
// Additional code to execute when the user drags the window
// or in the case you programmatically change the screen or windows client screen size.
// code that might directly change the backbuffer width height calling apply changes.
// or passing changes that must occur in other classes or even calling there OnResize methods
// though those methods can simply be added to the Windows event caller

}

1 Like

Will do. Got a nice long weekend coming. Thanks.

Wow! That took me longer than expected.

Once I understood what Will’s code was doing, I went to work adapting his code sequences to my resizing and fullscreen toggling. Resizing was no problem because, there, I had been on the right track. Fullscreen toggling gave me fits. I was getting 4-6 toggles out of one key press. Eventually, it became obvious that I was getting that many Update() calls in the time of one key press (even when I made sure it was a tap). (I didn’t have the burden of work related with logging to the Console.) I adapted some C# code I found on the web to turn any individual key into a single-shot key. Problem solved.

After reading and re-reading MSDN guidance regarding what to put in Update() and what to put in Draw(), I punted. I played it conservatively and put the code for changing device state in Draw(). The set-up code for both resizes and toggles is in Update(), but the commit calls are made from Draw().

A handful of small problems remain, namely:

  • Toggle into fullscreen mode while window is maximized. It’s treating last size as fullscreen and restores to that size.
  • Minimize a window, then restore. It’s saving the minimized height, which is zero.
  • I used the Console for logging while debugging. When VS starts the program, it gives the Console focus, and, if I hit the fullscreen toggle key, the Console goes fullscreen, presenting all-black. I know of no way of forcing the game window to get focus.
  • MonoGame or XNA or something does a good job of restoring an appropriate window position upon a toggle out of fullscreen. But, not having any control over this makes me nervous.

Thanks to Will again for the guidance of his code.