Creating Non-Rectangular Splash Screen - using Alpha Channel
For one of my apps that I created and I will be posting that soon, I wanted to have a splash screen just like the common desktop apps have. So that began my quest for a non-rectangular window. Fortunately WPF does have the capability, but my first attempt was using a Win32 Form and it came out pretty well. It takes a 32 bit bitmap and displays it within a form. So depending on the shape of the image you have a window. So lets look at the code.
class Splash : Form{public Splash(Bitmap bitmap){ // Window settings this.TopMost = true; this.ShowInTaskbar = false; this.Size = bitmap.Size; this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; this.Show(); // Must be called before setting bitmap this.SelectBitmap(bitmap); this.BackColor = Color.Red; this.Click += new EventHandler(Splash_Click); this.MouseClick += new MouseEventHandler(Splash_MouseClick);}void Splash_MouseClick(object sender, MouseEventArgs e){ this.Close();}void Splash_Click(object sender, EventArgs e){}// Sets the current bitmappublic void SelectBitmap(Bitmap bitmap) { // Does this bitmap contain an alpha channel? if (bitmap.PixelFormat != PixelFormat.Format32bppArgb) { throw new ApplicationException("The bitmap must be 32bpp with alpha-channel."); } // Get device contexts IntPtr screenDc = APIHelp.GetDC(IntPtr.Zero); IntPtr memDc = APIHelp.CreateCompatibleDC(screenDc); IntPtr hBitmap = IntPtr.Zero; IntPtr hOldBitmap = IntPtr.Zero; try { // Get handle to the new bitmap and select it into the current device context hBitmap = bitmap.GetHbitmap(Color.FromArgb(0)); hOldBitmap = APIHelp.SelectObject(memDc, hBitmap); // Set parameters for layered window update APIHelp.Size newSize = new APIHelp.Size(bitmap.Width, bitmap.Height); // Size window to match bitmap APIHelp.Point sourceLocation = new APIHelp.Point(0, 0); APIHelp.Point newLocation = new APIHelp.Point(this.Left, this.Top); // Same as this window APIHelp.BLENDFUNCTION blend = new APIHelp.BLENDFUNCTION(); blend.BlendOp = APIHelp.AC_SRC_OVER; // Only works with a 32bpp bitmap blend.BlendFlags = 0; // Always 0 blend.SourceConstantAlpha = 255; // Set to 255 for per-pixel alpha values blend.AlphaFormat = APIHelp.AC_SRC_ALPHA; // Only works when the bitmap contains an alpha channel // Update the window APIHelp.UpdateLayeredWindow(Handle, screenDc, ref newLocation, ref newSize, memDc, ref sourceLocation, 0, ref blend, APIHelp.ULW_ALPHA); } finally { // Release device context APIHelp.ReleaseDC(IntPtr.Zero, screenDc); if (hBitmap != IntPtr.Zero) { APIHelp.SelectObject(memDc, hOldBitmap); APIHelp.DeleteObject(hBitmap); // Remove bitmap resources } APIHelp.DeleteDC(memDc); }}protected override CreateParams CreateParams { get { // Add the layered extended style (WS_EX_LAYERED) to this window CreateParams createParams = base.CreateParams; createParams.ExStyle |= APIHelp.WS_EX_LAYERED; return createParams; }}// Let Windows drag this window for us (thinks its hitting the title bar of the window)protected override void WndProc(ref Message message) { if (message.Msg == APIHelp.WM_NCHITTEST) { // Tell Windows that the user is on the title bar (caption) message.Result= (IntPtr)APIHelp.HTCAPTION; } else { base.WndProc(ref message); }}} // Class to assist with Win32 API callsclass APIHelp {// Required constantspublic const Int32 WS_EX_LAYERED = 0x80000;public const Int32 HTCAPTION = 0x02;public const Int32 WM_NCHITTEST = 0x84;public const Int32 ULW_ALPHA = 0x02;public const byte AC_SRC_OVER = 0x00;public const byte AC_SRC_ALPHA = 0x01; public enum Bool { False = 0, True = 1} [StructLayout(LayoutKind.Sequential)]public struct Point { public Int32 x; public Int32 y; public Point(Int32 x, Int32 y) { this.x = x; this.y = y; }} [StructLayout(LayoutKind.Sequential)]public struct Size { public Int32 cx; public Int32 cy; public Size(Int32 cx, Int32 cy) { this.cx = cx; this.cy = cy; }} [StructLayout(LayoutKind.Sequential, Pack = 1)]struct ARGB { public byte Blue; public byte Green; public byte Red; public byte Alpha;} [StructLayout(LayoutKind.Sequential, Pack = 1)]public struct BLENDFUNCTION { public byte BlendOp; public byte BlendFlags; public byte SourceConstantAlpha; public byte AlphaFormat;} [DllImport("user32.dll", ExactSpelling = true, SetLastError = true)]public static extern Bool UpdateLayeredWindow(IntPtr hwnd, IntPtr hdcDst,
ref Point pptDst, ref Size psize, IntPtr hdcSrc, ref Point pprSrc, Int32 crKey,
ref BLENDFUNCTION pblend, Int32 dwFlags);[DllImport("gdi32.dll", ExactSpelling = true, SetLastError = true)]public static extern IntPtr CreateCompatibleDC(IntPtr hDC);[DllImport("user32.dll", ExactSpelling = true, SetLastError = true)]public static extern IntPtr GetDC(IntPtr hWnd);[DllImport("user32.dll", ExactSpelling = true)]public static extern int ReleaseDC(IntPtr hWnd, IntPtr hDC);[DllImport("gdi32.dll", ExactSpelling = true, SetLastError = true)]public static extern Bool DeleteDC(IntPtr hdc);[DllImport("gdi32.dll", ExactSpelling = true)]public static extern IntPtr SelectObject(IntPtr hDC, IntPtr hObject);[DllImport("gdi32.dll", ExactSpelling = true, SetLastError = true)]public static extern Bool DeleteObject(IntPtr hObject);}
The code has comments which are self explanatory. Its much simpler in Avalon but before delving into it, I wanted to show how it can be possibly done using alpha channels.
PS:the above code is taken from one of the bloggers. Cant seem to remember the person.. Blame my memory .. ;)