[.net] C# and window not refreshing immediately

Started by
8 comments, last by 23yrold3yrold 14 years, 7 months ago
Mmmm, been a while since I posted here .... anyway ... I took up C# a while ago and I've been trying to figure out a strange problem, which is why my windows won't redraw immediately. Example: I open the left window using an option in the Window menu. The main window doesn't refresh the area where the menu opened. It'll refresh if I drag the smaller window onto it, but even then the refresh is always a step behind, and everything leaves a "trail" of gray. I switched to managed DirectX mostly for the speed, but also to see if it would correct this. No dice. :( I've tried calling every redraw, update, and invalidate function I can find, so not sure what I'm doing wrong. I decided to rip out the extra bits to get it down to a core of code I could post; here's the relevant bits of what I got. Anyone got some pointers?

        public bool InitializeDirect3D()
        {
            PresentParameters pps;

            try
            {
                pps = new PresentParameters(Program.maindevicecontext.PresentationParameters);
                pps.BackBufferCount = 1;
                pps.DeviceWindow = drawingwindow;
                pps.DeviceWindowHandle = drawingwindow.Handle;
                pps.BackBufferWidth = drawingwindow.Width;
                pps.BackBufferHeight = drawingwindow.Height;
                m_mainwindowswap = new SwapChain(Program.maindevicecontext, pps);

                pps = new PresentParameters(Program.maindevicecontext.PresentationParameters);
                pps.BackBufferCount = 1;
                pps.DeviceWindow = currenttiles;
                pps.DeviceWindowHandle = currenttiles.Handle;
                pps.BackBufferWidth  = currenttiles.Width;
                pps.BackBufferHeight = currenttiles.Height;
                m_currtilesswap = new SwapChain(Program.maindevicecontext, pps);

                return true;
            }
            catch (DirectXException e)
            {
                MessageBox.Show(e.ToString(), "Error from main window!!");
                return false;
            }
        }

        public MainWindow()
        {
            scrollx = scrolly = 0;
            InitializeComponent();

            PresentParameters pps = new PresentParameters();
            pps.Windowed = true;
            pps.SwapEffect = SwapEffect.Discard;
            Program.maindevicecontext = new Device(0, DeviceType.Hardware, this, CreateFlags.SoftwareVertexProcessing, pps);

            if (this.InitializeDirect3D() == false)
            {
                MessageBox.Show("Could not initialize Direct3D.", "Error");
                return;
            }

            PaintBigWindow();
            PaintSmallWindow();
        }

        private void PaintBigWindow()
        {
            if (m_mainwindowswap.Disposed)
                return;

            Surface surf = m_mainwindowswap.GetBackBuffer(0, BackBufferType.Mono);
            surf.Device.SetRenderTarget(0, surf);

            // start drawing
            surf.Device.BeginScene();
            surf.Device.Clear(ClearFlags.Target, Color.Black, 1.0f, 0); // Clear the window

            // DirectX bits go here, commented out for now

            surf.Device.EndScene();
            m_mainwindowswap.Present();
        }

        private void PaintSmallWindow()
        {
            if (m_currtilesswap.Disposed)
                return;

            Surface surf = m_currtilesswap.GetBackBuffer(0, BackBufferType.Mono);
            surf.Device.SetRenderTarget(0, surf);

            // start drawing
            surf.Device.BeginScene();
            surf.Device.Clear(ClearFlags.Target, Color.Black, 1.0f, 0); // Clear the window

            // DirectX bits go here, commented out for now

            surf.Device.EndScene();
            m_currtilesswap.Present();
        }

        private void pictureBox1_Paint(object sender, PaintEventArgs e)
        {
            PaintBigWindow();
        }

        private void currenttiles_Paint(object sender, PaintEventArgs e)
        {
            PaintSmallWindow();
        }

        private void MainResize()
        {
            m_mainwindowswap.Dispose();
            m_currtilesswap.Dispose();

            // snip snip
            
            InitializeDirect3D();

            PaintBigWindow();
            PaintSmallWindow();
        }

        private void MainWindow_Resize(object sender, EventArgs e)
        {
            MainResize();
        }

        private void tileWindowToolStripMenuItem_Click(object sender, EventArgs e)
        {
            smallwindow = new SmallWindow();

            smallwindow.Show();
        }

        private void MainWindow_Paint(object sender, PaintEventArgs e)
        {
            PaintBigWindow();
            PaintSmallWindow();
        }
    }
}

I'm coming back to this personal project after hacking at this problem for a while and then leaving it for a couple of months, so if you see something particularly odd, no, I don't remember why I did it ... if you need any more info let me know.

Jesus saves ... the rest of you take 2d4 fire damage.

Advertisement
Try calling Invalidate method on your Window after you've opened the menuitem (A bit harsh but maybe it works).

I don't know if their is an event that you've closed the menuitem, but you should try and find an event like that. In that event you force a redraw of your black windows.
Sprinkle a few calls to Application.DoEvents and see if it fixes the issue.

Also, make sure that your new window is "owned" by the main window, because otherwise each window will steal events from the other one (WinForms is supposed to detect this and throw an exception but there are many ways to break the detection).

[OpenTK: C# OpenGL 4.4, OpenGL ES 3.0 and OpenAL 1.1. Now with Linux/KMS support!]

There are several ways of accomplishing what you want to do. If your control is going to display continous animation, the most common way of resolving the problem is via the Application.Idle event. This event is triggered when the application thread has completed processing windows messages and is about to become idle. Because of this, the event is continously raised and makes a good candidate for forcing the updating of rendering code.

// Were yourControl is the control or form that you are drawing on.Application.Idle += EventHandler( yourControl.OnApplicationIdle );void OnApplicationIdle (object sender, EventArgs e){     // Forces the control to repaint itself. This will cause the OnPaint method to be called.    // Another option is to directly inject the DirectX rendering code here     // (Although the control and its child controls would not be redrawn).    Invalidate();}


On a side note, Managed DirectX is now a deprecated project. Microsoft is no longer maintaining it. You should consider migrating to XNA or SlimDX.
Oxidda: I've already tried peppering every event with Invalidate() and Refresh(), no effect. I'm figuring the problem is something more specific with a more elegant solution than that. :)

Fiddler: I don't think it's something that Application.DoEvents will fix; my code is doing very little worth waiting on. But checking that the children know who is the parent isn't a bad idea. I don't see anywhere the parent is explicitly assigned in the generated code. I'm going to have to figure out how C# sets/gets parents and check on that ...

Billr17: Yeah, no animation (yet). Again, I was hoping for a more elegant solution. I'm not interested in XNA right now because this isn't for a game, but I might look into SlimDX if I find this lacking, thanks.

Jesus saves ... the rest of you take 2d4 fire damage.

Quote:Original post by 23yrold3yrold
Fiddler: I don't think it's something that Application.DoEvents will fix;


One thing to keep in mind is that the Application.DoEvents method allocates quite a bit of garbage that needs collected. Tom Miller had an interesting post quite a while ago on why you would not want to use this method in a render loop. Its probably still worth taking a look at: http://blogs.msdn.com/tmiller/archive/2003/11/07/57524.aspx

The solution I posted is what Microsoft generally recommends. Tom Miller had posted this as a solution for getting Managed DirectX working within Windows Forms back when he was still working on MDX. Also Microsoft used this same solution in thier official XNA examples for showing how to use XNA with Windows Forms.

From what I see, it appears that your control is properly redrawing itself. However, the DirectX surface is not being redrawn onto the control. Have you tried hooking the Invalidated event for the control your drawing on? That way you can force it to draw when the control becomes invalidated. Also, have you tried to set a break point in you paint event handlers to see if the code is ever hit when the control becomes invalidated?
Playing with it now; Application.Idle works as intended but my whole application is flickering nastily, even when I'm not clearing to the background colour. I think the parent thinks it's supposed to refresh itself over the child window ...

Jesus saves ... the rest of you take 2d4 fire damage.

Quote:Original post by 23yrold3yrold
Playing with it now; Application.Idle works as intended but my whole application is flickering nastily, even when I'm not clearing to the background colour. I think the parent thinks it's supposed to refresh itself over the child window ...


Are you invalidating the form each time the idle event is raised? Invalidating the form would cause the flickering you are seeing. If so, only invalidate the child controls you are drawing on.
Yeah, I'm only invalidating the two child windows. Flickers like mad. If I just call my custom "repaint these controls" functions in Application.Idle then the original problem persists.

Jesus saves ... the rest of you take 2d4 fire damage.

I think I found the problem; the Application.Idle doesn't fire if that dialog window is open ... for whatever reason. Confirmed by adding the MessageBox.Show() call; it spams my screen until I open that dialog. Gonna have to find a way to deal with that ...

Jesus saves ... the rest of you take 2d4 fire damage.

This topic is closed to new replies.

Advertisement