I'm using the Winforms sample on the app hub and everything is working fine except my spritebatch won't draw anything unless I call Invalidate in the Draw method. I have this in my initialize method:

Application.Idle += delegate { Invalidate(); }; 

I used a breakpoint and it is indeed invalidating my program and it is calling my draw method. I get no errors with the spritebatch and all the textures are loaded I just don't see anything on the screen. Here's the code I have:

    protected override void Draw()
    {
        GraphicsDevice.Clear(Color.CornflowerBlue);
        spriteBatch.Begin();
        tileSheet.Draw(spriteBatch);
        foreach (Image img in selector)
            img.Draw(spriteBatch);
        spriteBatch.End();
    }

But when I do this:

    protected override void Draw()
    {
        GraphicsDevice.Clear(Color.CornflowerBlue);
        spriteBatch.Begin();
        tileSheet.Draw(spriteBatch);
        foreach (Image img in selector)
            img.Draw(spriteBatch);
        spriteBatch.End();
        Invalidate();
    }

then all of a sudden the drawing starts to work! but the problem is that it freezes everything else and only that control gets updated. What can I do to fix this? It's really frustrating.

  • 1
    Please don't double post. If your question hasn't been answered, try editing it to include more information. Your alternative is to ask somewhere else. (You are getting free help after all, don't demand too much). – MichaelHouse Sep 20 '13 at 13:27
  • It seems that your program never runs the line: Application.Idle += delegate { Invalidate(); }; . Did you check this with your debugger? – 0xBADF00D Sep 24 '13 at 10:55

To get continuous updates without degrading the UI performance, you'll need the following trick to hook into the windows message queue:

public static class NativeMethods
{
    [StructLayout(LayoutKind.Sequential)]
    public struct Message
    {
        public IntPtr hWnd;
        public UInt32 msg;
        public IntPtr wParam;
        public IntPtr lParam;
        public UInt32 time;
        public Point p;
    }

    [SuppressUnmanagedCodeSecurity] // We won't use this maliciously
    [DllImport("User32.dll", CharSet = CharSet.Auto)]
    public static extern bool PeekMessage(out Message msg, IntPtr hWnd, uint messageFilterMin, uint messageFilterMax, uint flags);
}

The usage is like so (for bonus points I've added my fixed time step code):

    // Hook up idle event (in Form load or wherever)
    Application.Idle += delegate { Application_Idle(); }; 

    void Application_Idle(object sender, EventArgs e)
    {
        while (AppStillIdle())
            Tick(); // This contains your Update() and Draw() loop
    }

    // Check if any messages are waiting on the queue
    // We only want to keep ticking whilst there are none.
    private bool AppStillIdle()
    {
        NativeMethods.Message msg;
        return !NativeMethods.PeekMessage(out msg, Handle, 0, 0, 0);
    }

    // Tick function handles the fixed timestep and Update/Draw
    const float TARGET_ELAPSED_TIME = 0.0166667f;
    float elapsedSum = 0.0f;
    private void Tick()
    {
        float elapsed = (float)timer.Elapsed.TotalSeconds;
        timer.Reset();
        timer.Start();

        elapsedSum += elapsed;
        if (elapsedSum >= TARGET_ELAPSED_TIME)
        {
            // Game update and draw
            Update(TARGET_ELAPSED_TIME);
            Draw();

            // INVALIDATE GRAPHICS DEVICE CONTROLS
            gdcLevel.Invalidate();

            elapsedSum %= TARGET_ELAPSED_TIME;
        }
        // Return control to UI thread in case more messages arrive
        System.Threading.Thread.Sleep(0);
    }

My implementation is based on Tom Miller's blog post: http://blogs.msdn.com/b/tmiller/archive/2005/05/05/415008.aspx

Also floAr's answer references Shawn's blog which contains pretty much the same code :http://blogs.msdn.com/b/shawnhar/archive/2010/12/06/when-winforms-met-game-loop.aspx

There is a very good post by Shawn Hargreaves about XNA with WinForms you should definitly check it out.

In addition here are two tutorials aout XNA in WinForms and WPF, with a bit more complex event handling (both are in german)

Maybe try to set up a timer and rebuild XNAs fixed time step. This will queue the timer in your UI thread processing and update the drawing. Invalidate will be called whenever a new frame is prepared and ready.

timer = new Timer();
    timer.Interval = (int)TargetElapsedTime.TotalMilliseconds;
    timer.Tick += Tick;
    timer.Start();

    Stopwatch stopWatch = Stopwatch.StartNew();

    readonly TimeSpan TargetElapsedTime = TimeSpan.FromTicks(TimeSpan.TicksPerSecond / 60);
    readonly TimeSpan MaxElapsedTime = TimeSpan.FromTicks(TimeSpan.TicksPerSecond / 10);

    TimeSpan accumulatedTime;
    TimeSpan lastTime;


    void Tick(object sender, EventArgs e)
    {
        TimeSpan currentTime = stopWatch.Elapsed;
        TimeSpan elapsedTime = currentTime - lastTime;
        lastTime = currentTime;

        if (elapsedTime > MaxElapsedTime)
        {
            elapsedTime = MaxElapsedTime;
        }

        accumulatedTime += elapsedTime;

        bool updated = false;

        while (accumulatedTime >= TargetElapsedTime)
        {
            Update();
            Draw();

            accumulatedTime -= TargetElapsedTime;
            updated = true;
        }

        if (updated)
        {
            Invalidate();
        }
    }

I hope this gets you startet!

  • Answers should address the issue directly, not just supply related information. Maybe make this a comment instead. – MichaelHouse Sep 20 '13 at 13:28
  • Added a proposal, which might solve the problem – floAr Sep 20 '13 at 14:04
  • 1
    The timer of the .net framework has a limite frequency. I have never get it to work above 40 fps. – Emir Lima Nov 19 '13 at 16:15

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.