The Windows painting model is a good match for your requirements. It separates drawing the background (OnPaintBackground) from the foreground (OnPaint). That however doesn't mean that you can only paint the background once and be done with it. Window surface invalidation invokes both. This is above all required to make anti-aliasing effects work properly, they can only look good against a known background color.
Punt this and draw the Image in the OnPaintBackground() override. You can let Control do this automatically for you by assigning the BackgroundImage property. You'll probably need to set the DoubleBuffer property to true to avoid the flicker you'll see when the background is drawn, temporarily wiping out the foreground pixels. Call Invalidate() if you need to update the foreground.
To be complete, your fantasy is in fact possible. You'd need to overlay the image with a toplevel layered window. That is easy to get with a Form whose TransparencyKey property is set. Here is a sample implementation:
using System;
using System.Drawing;
using System.Windows.Forms;
class OverlayedPictureBox : PictureBox {
private Form mOverlay;
private bool mShown;
public event PaintEventHandler PaintOverlay;
public OverlayedPictureBox() {
mOverlay = new Form();
mOverlay.FormBorderStyle = FormBorderStyle.None;
mOverlay.TransparencyKey = mOverlay.BackColor = Color.Magenta;
mOverlay.ShowInTaskbar = false;
}
protected void OnPaintOverlay(PaintEventArgs e) {
// NOTE: override this or implement the PaintOverlay event
PaintEventHandler handler = PaintOverlay;
if (handler != null) handler(this, e);
}
public void RefreshOverlay() {
// NOTE: call this to force the overlay to be repainted
mOverlay.Invalidate();
}
protected override void Dispose(bool disposing) {
if (disposing) mOverlay.Dispose();
base.Dispose(disposing);
}
protected override void OnVisibleChanged(EventArgs e) {
if (!mShown && !this.DesignMode) {
Control parent = this.Parent;
while (!(parent is Form)) parent = parent.Parent;
parent.LocationChanged += new EventHandler(parent_LocationChanged);
mOverlay.Paint += new PaintEventHandler(mOverlay_Paint);
mOverlay.Show(parent);
mShown = true;
}
base.OnVisibleChanged(e);
}
protected override void OnLocationChanged(EventArgs e) {
mOverlay.Location = this.PointToScreen(Point.Empty);
base.OnLocationChanged(e);
}
protected override void OnSizeChanged(EventArgs e) {
mOverlay.Size = this.Size;
base.OnSizeChanged(e);
}
void parent_LocationChanged(object sender, EventArgs e) {
mOverlay.Location = this.PointToScreen(Point.Empty);
}
private void mOverlay_Paint(object sender, PaintEventArgs e) {
OnPaintOverlay(e);
}
}
One interesting artifact: minimizing the form and restoring it again looks, erm, special.