レイヤードウィンドウの作成 (不定形かつ透明度に変化のあるフォームの作成) - C#

レイヤードウィンドウを作成します。
レイヤードウィンドウはアルファ値付きの画像をウィンドウに適用することで、不定形かつ透明度の変化のあるフォームを表示できます。

説明

フォームの作成

はじめに、CreateParamsをオーバーライドしてフォーム作成時のパラメータを変更します。
cp.ExStyle = cp.ExStyle | WindowsConst.WS_EX_LAYERED;
にてフォーム作成時のパラメータの ExStyleにWS_EX_LAYEREDパラメータを追加します。WS_EX_LAYEREDを追加することでフォームをレイヤードフォームに設定できます。
また、フォームにタイトルバーやリサイズ枠があるスタイルの場合はこれらを無効にします。

UpdateLayeredWindowを呼び出すに当たり、以下の処理をします。

アルファつきビットマップの読み込み

アルファ値のついたビットマップを読み込みます。 以下のコードで読み込んだ場合はアルファ値が無視されてしまうためこちらで紹介した方法でビットマップを読み込みます。 アルファ値のついたPNGファイルを読み込む場合は通常のコンストラクタにファイル名を指定する処理でアルファつきの画像として読み込めます。
  Bitmap bmp = new Bitmap("ファイル名")

デバイスコンテキストの取得

UpdateLayeredWindowを呼び出す場合にはウィンドウのハンドルとウィンドウのサーフェイスのハンドルが必要なためこれらを取得します。
 screenDc = GetDC(IntPtr.Zero);
 memDc = CreateCompatibleDC(screenDc);
また、すでに別の画像がレイヤードウィンドウに適用されている場合はその画像を置き換えて古い画像を開放する必要があるため、 設定しようとする画像のハンドルとウィンドウサーフェイスの画像のハンドルを取得します。
 hBitmap = srcBitmap.GetHbitmap(Color.FromArgb(0));
 hOldBitmap = SelectObject(memDc, hBitmap);

BLENDFUNCTION構造体の初期化

ブレンドの設定を決めるBLENDFUNCTION構造体を初期化します。
 BLENDFUNCTION blend = new BLENDFUNCTION();
 blend.BlendOp = AC_SRC_OVER;
 blend.BlendFlags = 0;
 blend.SourceConstantAlpha = 255;
 blend.AlphaFormat = AC_SRC_ALPHA;

UpdateLayeredWindowを呼び出します。レイヤードウィンドウの画面の位置とウィンドウサイズも指定する必要があります。画面の位置は現在の位置と同じ位置とし、ウィンドウのサイズも現在のウィンドウサイズと同じとします。
 this.Size = new Size(srcBitmap.Width, srcBitmap.Height);
 Point pptDst = new Point(this.Left, this.Top);
 Size psize = new Size(this.Width, this.Height);
 Point pptSrc = new Point(0, 0);
 UpdateLayeredWindow(this.Handle, screenDc, ref pptDst, ref psize, memDc, ref pptSrc, 0, ref blend, ULW_ALPHA);

コード

フォーム (FormSub5Layered.cs)
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.IO;
using System.Drawing.Imaging;

namespace App
{
  public partial class FormSub5Layered : Form
  {
    [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    public static extern IntPtr GetDC(IntPtr hWnd);

    [DllImport("gdi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    public static extern IntPtr CreateCompatibleDC(IntPtr hdc);

    [DllImport("gdi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    public static extern IntPtr SelectObject(IntPtr hdc, IntPtr hgdiobj);

    [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    public static extern int ReleaseDC(IntPtr hWnd, IntPtr hDC);

    [DllImport("gdi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    public static extern int DeleteObject(IntPtr hobject);

    [DllImport("gdi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    public static extern int DeleteDC(IntPtr hdc);

    public const byte AC_SRC_OVER = 0;
    public const byte AC_SRC_ALPHA = 1;
    public const int ULW_ALPHA = 2;

    [StructLayout(LayoutKind.Sequential, Pack = 1)]
    public struct BLENDFUNCTION
    {
      public byte BlendOp;
      public byte BlendFlags;
      public byte SourceConstantAlpha;
      public byte AlphaFormat;
    }

    [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    public static extern int UpdateLayeredWindow(
        IntPtr hwnd,
        IntPtr hdcDst,
        [System.Runtime.InteropServices.In()]
            ref Point pptDst,
        [System.Runtime.InteropServices.In()]
            ref Size psize,
        IntPtr hdcSrc,
        [System.Runtime.InteropServices.In()]
            ref Point pptSrc,
        int crKey,
        [System.Runtime.InteropServices.In()]
            ref BLENDFUNCTION pblend,
        int dwFlags);

    public void SetLayeredWindow(Bitmap srcBitmap)
    {
      // GetDeviceContext
      IntPtr screenDc = IntPtr.Zero;
      IntPtr memDc = IntPtr.Zero;
      IntPtr hBitmap = IntPtr.Zero;
      IntPtr hOldBitmap = IntPtr.Zero;
      try {
        screenDc = GetDC(IntPtr.Zero);
        memDc = CreateCompatibleDC(screenDc);
        hBitmap = srcBitmap.GetHbitmap(Color.FromArgb(0));
        hOldBitmap = SelectObject(memDc, hBitmap);

        // init BLENDFUNCTION
        BLENDFUNCTION blend = new BLENDFUNCTION();
        blend.BlendOp = AC_SRC_OVER;
        blend.BlendFlags = 0;
        blend.SourceConstantAlpha = 255;
        blend.AlphaFormat = AC_SRC_ALPHA;

        // Update Layered Window
        this.Size = new Size(srcBitmap.Width, srcBitmap.Height);
        Point pptDst = new Point(this.Left, this.Top);
        Size psize = new Size(this.Width, this.Height);
        Point pptSrc = new Point(0, 0);
        UpdateLayeredWindow(this.Handle, screenDc, ref pptDst, ref psize, memDc,
          ref pptSrc, 0, ref blend, ULW_ALPHA);

      }
      finally {
        if (screenDc != IntPtr.Zero) {
          ReleaseDC(IntPtr.Zero, screenDc);
        }
        if (hBitmap != IntPtr.Zero) {
          SelectObject(memDc, hOldBitmap);
          DeleteObject(hBitmap);
        }
        if (memDc != IntPtr.Zero) {
          DeleteDC(memDc);
        }
      }
    }

    public FormSub5Layered()
    {
      InitializeComponent();
    }

    protected override CreateParams CreateParams
    {
      get
      {
        System.Windows.Forms.CreateParams cp = base.CreateParams;
        
        cp.ExStyle = cp.ExStyle | WindowsConst.WS_EX_LAYERED;
        //必要に応じて WS_EX_TRANSPARENT をつける
        if (this.FormBorderStyle != FormBorderStyle.None) {
          cp.Style = cp.Style & (~WindowsConst.WS_BORDER);
          cp.Style = cp.Style & (~WindowsConst.WS_THICKFRAME);
        }
        
        return cp;
      }
    }

    private void FormSub5Layered_Load(object sender, EventArgs e)
    {
      Bitmap bmp = new Bitmap(@"c:\map\chn2.bmp");

      Rectangle bmBounds = new Rectangle(0,0,bmp.Width,bmp.Height);
      BitmapData bmpd = bmp.LockBits(bmBounds, ImageLockMode.ReadOnly,
        bmp.PixelFormat);

      Bitmap bmp2 = new Bitmap(bmpd.Width, bmpd.Height, bmpd.Stride,
        PixelFormat.Format32bppArgb, bmpd.Scan0);

      bmp.UnlockBits(bmpd);
      SetLayeredWindow(bmp2);
    }
  }
}
定数定義 (WindowsConst.cs)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace MultiFormApp
{
  public static class WindowsConst
  {
    public const int WM_NCHITTEST     = 0x0084;

    /*
     * Window Styles
     */
    public const int  WS_OVERLAPPED   = 0x00000000;
    public const long WS_POPUP        = 0x80000000L; //unchecked((int) WS_POPUP) 
    public const int  WS_CHILD        = 0x40000000;
    public const int  WS_MINIMIZE     = 0x20000000;
    public const int  WS_VISIBLE      = 0x10000000;
    public const int  WS_DISABLED     = 0x08000000;
    public const int  WS_CLIPSIBLINGS = 0x04000000;
    public const int  WS_CLIPCHILDREN = 0x02000000;
    public const int  WS_MAXIMIZE     = 0x01000000;
    public const int  WS_CAPTION      = 0x00C00000;/* WS_BORDER | WS_DLGFRAME  */
    public const int  WS_BORDER       = 0x00800000;
    public const int  WS_DLGFRAME     = 0x00400000;
    public const int  WS_VSCROLL      = 0x00200000;
    public const int  WS_HSCROLL      = 0x00100000;
    public const int  WS_SYSMENU      = 0x00080000;
    public const int  WS_THICKFRAME   = 0x00040000;
    public const int  WS_GROUP        = 0x00020000;
    public const int  WS_TABSTOP      = 0x00010000;

    public const int  WS_MINIMIZEBOX  = 0x00020000;
    public const int  WS_MAXIMIZEBOX  = 0x00010000;

    public const int  WS_TILED        = WS_OVERLAPPED;
    public const int  WS_ICONIC       = WS_MINIMIZE;
    public const int  WS_SIZEBOX      = WS_THICKFRAME;
    public const int  WS_TILEDWINDOW  = WS_OVERLAPPEDWINDOW;

    /*
     * Common Window Styles
    */
    public const int WS_OVERLAPPEDWINDOW = (WS_OVERLAPPED |
                                     WS_CAPTION |
                                     WS_SYSMENU |
                                     WS_THICKFRAME |
                                     WS_MINIMIZEBOX |
                                     WS_MAXIMIZEBOX);

    public const long WS_POPUPWINDOW = (WS_POPUP |
                                     WS_BORDER |
                                     WS_SYSMENU);

    public const int WS_CHILDWINDOW = (WS_CHILD);


    /*
     * Extended Window Styles
     */
    public const int WS_EX_DLGMODALFRAME = 0x00000001;
    public const int WS_EX_NOPARENTNOTIFY = 0x00000004;
    public const int WS_EX_TOPMOST = 0x00000008;
    public const int WS_EX_ACCEPTFILES = 0x00000010;
    public const int WS_EX_TRANSPARENT = 0x00000020;
    public const int WS_EX_MDICHILD = 0x00000040;
    public const int WS_EX_TOOLWINDOW = 0x00000080;
    public const int WS_EX_WINDOWEDGE = 0x00000100;
    public const int WS_EX_CLIENTEDGE = 0x00000200;
    public const int WS_EX_CONTEXTHELP = 0x00000400;

    public const int WS_EX_RIGHT = 0x00001000;
    public const int WS_EX_LEFT = 0x00000000;
    public const int WS_EX_RTLREADING = 0x00002000;
    public const int WS_EX_LTRREADING = 0x00000000;
    public const int WS_EX_LEFTSCROLLBAR = 0x00004000;
    public const int WS_EX_RIGHTSCROLLBAR = 0x00000000;

    public const int WS_EX_CONTROLPARENT = 0x00010000;
    public const int WS_EX_STATICEDGE = 0x00020000;
    public const int WS_EX_APPWINDOW = 0x00040000;


    public const int WS_EX_OVERLAPPEDWINDOW = (WS_EX_WINDOWEDGE | WS_EX_CLIENTEDGE);
    public const int WS_EX_PALETTEWINDOW
      = (WS_EX_WINDOWEDGE | WS_EX_TOOLWINDOW | WS_EX_TOPMOST);
    public const int WS_EX_LAYERED = 0x00080000;

    public const int WS_EX_NOINHERITLAYOUT = 0x00100000; 
      // Disable inheritence of mirroring by children
    public const int WS_EX_LAYOUTRTL = 0x00400000; // Right to left mirroring

    public const int WS_EX_COMPOSITED = 0x02000000;
    public const int WS_EX_NOACTIVATE = 0x08000000;

    /*
     * WM_NCHITTEST and MOUSEHOOKSTRUCT Mouse Position Codes
     */
    public const int HTERROR = (-2);
    public const int HTTRANSPARENT = (-1);
    public const int HTNOWHERE = 0;
    public const int HTCLIENT = 1;
    public const int HTCAPTION = 2;
    public const int HTSYSMENU = 3;
    public const int HTGROWBOX = 4;
    public const int HTSIZE = HTGROWBOX;
    public const int HTMENU = 5;
    public const int HTHSCROLL = 6;
    public const int HTVSCROLL = 7;
    public const int HTMINBUTTON = 8;
    public const int HTMAXBUTTON = 9;
    public const int HTLEFT = 10;
    public const int HTRIGHT = 11;
    public const int HTTOP = 12;
    public const int HTTOPLEFT = 13;
    public const int HTTOPRIGHT = 14;
    public const int HTBOTTOM = 15;
    public const int HTBOTTOMLEFT = 16;
    public const int HTBOTTOMRIGHT = 17;
    public const int HTBORDER = 18;
    public const int HTREDUCE = HTMINBUTTON;
    public const int HTZOOM = HTMAXBUTTON;
    public const int HTSIZEFIRST = HTLEFT;
    public const int HTSIZELAST = HTBOTTOMRIGHT;

    public const int HTOBJECT = 19;
    public const int HTCLOSE = 20;
    public const int HTHELP = 21;

  }
}

実行結果1

画像の準備をします。白の画像を用意します。


アルファチャネルマスクを編集します。マスクは下図のとおりです。作成後画像をBMP形式で保存します。32ビットARGB形式で保存します。


この画像を読み込み実行すると、下図の実行結果となります。


実行結果2

アルファチャネルマスクを下図のようにした場合


下図の実行結果となります。


著者
iPentecのメインプログラマー
C#, ASP.NET の開発がメイン、少し前まではDelphiを愛用
掲載日: 2010-07-13
iPentec all rights reserverd.