Questo blog nasce da una domanda ricorrente:
"Come faccio ad inserire un immagine (ad esempio un logo) nello sfondo di un form MDI?"

Risposte:
1-Usa BackgroundImage()
2-Inserisci un immagine nel form MDI

Controrisposte:
1-L'immagine viene stretchata
2-L'immagine (per il gioco delle 'parentele') e' visibile attraverso i child forms.

Soluzione: Bisogna disegnare l'immagine a runtime nell' evento OnPaint del Form.
Ri-problema: La finestra entro la quale vogliamo disegnare l'immagine e' in realta una child window del form principale ed e' su quella che dobbiamo agire.

Quindi, per procedere bisogna:
1-Ricavare l'handle della child window e recuperarne l'handle
2-Subclassare la child window mediante NativeWindow (ne ho parlato nel mio blog del 16/09) intercettare WM_PAINT e, attraverso GDI+ disegnare l'immagine.
3-Rilasciare eventuali risorse GDI quando non piu' utilizzate.

Soluzione:
1-Utilizziamo EnumChildWindows() per enumerare tutte le finestre child del form MDI principale (per saperne di piu' date un occhiata alla mia sessione al workshop del 17/09), identifichiamo quella contenente "MDICLIENT" nel proprio ClassName (ho trovato il nome utilizzando Spy++) e memorizziamo l'handle.
2-Subclassiamo la child window utilizzando la classe NativeWindow() (ne ho parlato nel mio blog del 16/09...)
3-Intercettiamo il messaggio WM_PAINT e disegnamo l'immagine.
4-Implementiamo IDisposable

Il codice sotto riportato contiene tutto il necessario per realizzare gli steps appena elencati, la classe va utilizzata in questo modo:

MDIPaint mdi=new MDIPaint(this,@"c:\mylogo.gif",100,100);

Ecco il codice (fornito 'As Is')

namespace MDIWin

{

public class MDIPaint:IDisposable

{
// API Declares
delegate bool WndEnumProc (IntPtr hWnd, Int32 param);
[DllImport("user32.dll")]
private static extern bool EnumChildWindows(IntPtr hWndParent,WndEnumProc lpEnumFunc, Int32 lParam);
[DllImport("user32", CharSet = CharSet.Auto)]
private extern static int GetClassName (IntPtr hWnd, StringBuilder lpClassName, int nMaxCount);

//Fields
private IntPtr mChildMDI;
private MyNativeWindow nw;
public MDIPaint(Form MDIForm, string path,Int32 X, Int32 Y)
{
//Recupero l'handle della finestra child
EnumChildWindows(MDIForm.Handle, new WndEnumProc(pWndEnumProc),0);
// Effettuo il subclassing della finestra usando NativeWindow
if (mChildMDI!=IntPtr.Zero) { nw=new MyNativeWindow(mChildMDI,path,X,Y); }
}

~MDIPaint()
{
if (nw!=null) nw.Dispose();
}

private bool pWndEnumProc (IntPtr hWnd, Int32 param)
{
// Funzione che recupera l'handle della child window del form MDI
StringBuilder sb=new StringBuilder(255);
GetClassName(hWnd,sb,sb.Capacity);
string Txt=sb.ToString().ToUpper();
if (Txt.IndexOf("MDICLIENT")>0)
{
// Trovata!
mChildMDI=hWnd;
return false;
}
else return true;
}

#region Native Window
private class MyNativeWindow : NativeWindow
{
//Win32 API Const
private const Int32 WM_PAINT = 0xF;
//Fields
private Bitmap Bmp;
private Int32 x,y;
public MyNativeWindow(IntPtr hWnd, string Path, Int32 X, Int32 Y)
{
base.AssignHandle(hWnd);
//Creo il Bmp
Bmp=new Bitmap(Path);
//Caches
this.x=X;
this.y=Y;
// Forzo il redraw dell'immagine
Message m=Message.Create(hWnd,WM_PAINT,IntPtr.Zero,IntPtr.Zero);
this.WndProc(ref m);
}

protected override void WndProc(ref Message m)
{
switch (m.Msg)
{
case WM_PAINT:
//Dopo aver processato il messaggio eseguo il paint dell'immagine
base.WndProc (ref m);
Graphics g= Graphics.FromHwnd(m.HWnd);
g.DrawImage(Bmp,x,y);
g.Dispose();
m.Result=(IntPtr)0;
break;

default:
base.WndProc(ref m);
break;
}
}

public void Dispose()
{
base.ReleaseHandle();
Bmp.Dispose();
 }
}
#endregion

#region IDisposable Members
public void Dispose()
{
if(nw !=null)
{
nw.Dispose();
nw=
null;
}
GC.SuppressFinalize(
this);
}
#endregion
}
}