First, you definitely didn't see "everybody tell you to use on this
forum" that design. Second, that's being suggested in that blog for the
specific purpose of dealing with DirectX, which is the special-case
scenario I mentioned earlier and which doesn't seem to apply to your
scenario.
Well, you may have a different opinion but that doesn't change the
response I got when I asked. I may have asked longer ago than you
approve, but that's the response I got.
Personally, it's my opinion that code of that design is obsolete anyway.
It's the kind of thing that people who learned game implementation
fifteen or twenty years ago, and who never took another look at what the
OS and frameworks offer today and how fast the hardware is, would use.
For people implementing programs where a design like that would have
been desirable a decade ago, IMHO today they should be putting the world
update code in a separate thread, and run screen updates using the
normal messaging API. The context switch is inconsequential compared to
the cost of rendering and especially maintenance of the world state, and
the design can be made to fit the more normal OS API design much better
using threads.
Fair enough, I've seen several and was entertaining the notion of
changing the game loop I used except I've never been comfortable
enough with rendering to the screen to change it. I would probably use
a threaded engine that triggers an event when it needs to update the
display. But I'd like to be a little more comfortable with painting on
the screen before that.
Besides all that, IMHO one definitely should not be trying to use a
game-style rendering loop, and especially not in .NET, until they are
thoroughly familiar and competent with the basic event-driven rendering
model that .NET provides.
I'm not thoroughly familiar with the screen rendering in .NET that's
why I was asking such an open-ended question. I seriously don't
understand why Invalidate() doesn't repaint the screen every time it's
called.
For test code, maybe that's okay. You definitely don't want to be
reading bitmaps from disk in a rendering loop in a real program. And
especially if you don't dispose the old bitmap before loading a new one.
Well, yes. That's why I tried to make substantial reference to the
fact that, in real time, the display will be dynamically generated in
code.
Again, you haven't provided a concise-but-complete code example that
reliably reproduces the problem. So it's still impossible to say what's
wrong with your code.
Then I'm missing what you are asking for. That's pretty much all of my
code.
using System;
using System.Diagnostics;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace Nimals
{
public partial class NimalMainForm : Form
{
GameEngine _nimalEngine;
private bool AppStillIdle
{
get
{
NativeMethods.Message msg;
return !NativeMethods.PeekMessage(out msg,
IntPtr.Zero, 0, 0, 0);
}
}
public void Application_Idle(object sender, EventArgs e)
{
while (AppStillIdle)
{
//Debug.WriteLine("AppIdle - Before");
_nimalEngine.Calculate();
_nimalEngine.Update();
_nimalEngine.Render();
//Debug.WriteLine("AppIdle - After");
this.Invalidate(this.ClientRectangle, true);
}
}
public NimalMainForm()
{
InitializeComponent();
_nimalEngine = new GameEngine(this.Width, this.Height);
}
protected override void OnPaint(PaintEventArgs e)
{
Graphics CurrentGraphics;
CurrentGraphics = this.CreateGraphics();
Debug.WriteLine("DrawImage");
e.Graphics.DrawImage(_nimalEngine.DisplayBitmap, 0, 0);
base.OnPaint(e);
}
}
internal class NativeMethods
{
[StructLayout(LayoutKind.Sequential)]
public struct Message
{
public IntPtr hWnd;
public IntPtr msg;
public IntPtr wParam;
public IntPtr lParam;
public uint time;
public System.Drawing.Point p;
}
[System.Security.SuppressUnmanagedCodeSecurity]
[DllImport("User32.dll", CharSet = CharSet.Auto)]
public static extern bool PeekMessage(out Message msg, IntPtr
hWnd,
uint messageFilterMin,
uint messageFilterMax,
uint flags);
public NativeMethods() { }
}
}
/**********************************************/
/*** GameEngine Class *******************/
using System;
using System.Diagnostics;
using System.Drawing;
namespace Nimals
{
public class GameEngine
{
FoodField _foodField;
Bitmap _displayBitmap;
Bitmap _workBitmap;
public Bitmap DisplayBitmap
{
get
{
return _workBitmap;// _displayBitmap;
}
}
public GameEngine(int Width, int Height)
{
_foodField = new FoodField(Width, Height, .5F);
_displayBitmap = _workBitmap = new Bitmap(Width, Height,
System.Drawing.Imaging.PixelFormat.Format24bppRgb);
}
public void Calculate()
{
//throw new NotImplementedException();
}
public void Update()
{
//throw new NotImplementedException();
}
public void Render()
{
LockUnlockBitsExample();
Debug.WriteLine("Render method");
}
private void LockUnlockBitsExample()
{
Rectangle rect;
System.Drawing.Imaging.BitmapData bmpData;
IntPtr ptr;
int bytes;
byte[] bgrValues;
// Lock the bitmap's bits.
rect = new Rectangle(0, 0, _displayBitmap.Width,
_displayBitmap.Height);
bmpData = _workBitmap.LockBits(rect,
System.Drawing.Imaging.ImageLockMode.ReadWrite,
_workBitmap.PixelFormat);
// Get the address of the first line.
ptr = bmpData.Scan0;
// Declare an array to hold the bytes of the bitmap.
bytes = bmpData.Stride * _workBitmap.Height;
bgrValues = new byte[bytes];
// Copy the BGR values into the array.
System.Runtime.InteropServices.Marshal.Copy(ptr,
bgrValues, 0, bytes);
// Set every third value to 255. A 24bpp bitmap will look
red.
for (int counter = 0; counter < bgrValues.Length; counter
+= 3)
{
bgrValues[counter] = 255;
}
// Copy the RGB values back to the bitmap
System.Runtime.InteropServices.Marshal.Copy(bgrValues, 0,
ptr, bytes);
// Unlock the bits.
_workBitmap.UnlockBits(bmpData);
}
}
}
But your symptoms are typical for a program that has blocked the message
pump. So you should be looking for reasons that your message pump
doesn't get to run. Either because you haven't started it by calling
Application.Run(), or because the thread that calls Application.Run()
has somehow gotten stuck doing something else. A third possibility is
that you've created the form or other controls on the wrong thread,
where there's no message pump, but since you haven't mentioned using
explicit threading at all, that's probably a more remote possibility.
Post a proper concise-but-complete code example if you really want some
reliable, precise help.
Pete
I have never had much luck understanding the rendering/paint events
and why, for example, when I use this.CreateGraphics() the graphics
object I get never seems to be able to draw to the screen. I've
overridden several of the paint events and have gone a long way toward
painting stuff that responds to paint events alone (the way a screen
looks or the way ListViewItems are painted). But when it comes to
dynamically generating images for display I can't seem to figure out
what I'm doing wrong.
Thus the open-ended post and search for some level of illumination. I
hope I haven't insulted or annoyed anyone. I've been programming long
enough to understand some rather complex issues (including the multi-
threaded suggestion you made earlier). But this eludes me.
Tom P.