フォームを半透明にするには Opacity プロパティを使用します。これにより一定の透明度でフォームが表示されます。ではピクセル単位に透明度を持った画像のように、透明度が一定でないフォームを表示させるにはどうすればいいのでしょうか。BackgroundImage プロパティに透明度を持った画像を設定しても透明にはなりません。そこで登場するのがレイヤードウィンドウです。

レイヤードウィンドウ

 レイヤードウィンドウを使用するには、透明度を持つ事ができる 1 ピクセルあたり 32 ビットの形式の画像が必要になります。その画像がそのままフォームとして表示されます。そのため、タイトルバーも表示されなければ、フォーム内のコントロールも表示されません。表示されるのは設定した画像だけです。そして設定した画像の透明度を持っている部分は、そのまま透けて表示されます!

透明度を持った画像     レイヤードウィンドウの表示例
透明度を持った画像
レイヤードウィンドウの表示例

 透明度を持った画像がそのまま透けて表示されるのはいいのですが、コントロールが表示されないのでは通常のフォームのように操作することが出来ません。そのため、レイヤードウィンドウはデスクトップアクセサリのように、表示されるだけのアプリケーションに向いていると思います。フォームの形も設定した画像の形になるので、クールな見た目にするのも簡単です。

レイヤードウィンドウにコントロールを表示する

 レイヤードウィンドウではコントロールが表示されないと言いましたが、コントロールは見えていないだけで実際にはあります。ボタンがある場所をクリックすれば、クリックイベントが発生します。コントロールを必要とするようなレイヤードウィンドウを使用する場合には、レイヤードウィンドウの画像にコントロールを描画してやらなければいけません。ラベルやボタンなどは描画できそうですが、テキストボックスなどの入力系のコントロールは少々の手間では出来そうもありません。

レイヤードウィンドウの使用例 (VB)

 レイヤードウィンドウのコードサンプルは探すと結構見つかります。しかし Visual Basic のサンプルがあまり無いというか、見つけられませんでしたので Visual Basic のコードサンプルを載せたいと思います。

レイヤードウィンドウのサンプル
Imports System.Drawing
Imports System.Runtime.InteropServices

Public Class LayeredForm

#Region " UpdateLayerdWindow 関連 API "

    <DllImport("gdi32.dll", ExactSpelling:=True, SetLastError:=True)> _
    Public Shared Function CreateCompatibleDC(ByVal hDC As IntPtr) As IntPtr
    End Function

    <DllImport("gdi32.dll", ExactSpelling:=True, SetLastError:=True)> _
    Public Shared Function DeleteDC(ByVal hdc As IntPtr) As Boolean
    End Function

    <DllImport("gdi32.dll", ExactSpelling:=True, SetLastError:=True)> _
    Private Shared Function DeleteObject(ByVal hObject As IntPtr) As Boolean
    End Function

    <DllImport("gdi32.dll", ExactSpelling:=True, SetLastError:=True)> _
    Private Shared Function SelectObject(ByVal hDC As IntPtr, ByVal hObject As IntPtr) As IntPtr
    End Function

    <DllImport("user32.dll", ExactSpelling:=True, SetLastError:=True)> _
    Private Shared Function GetDC(ByVal hWnd As IntPtr) As IntPtr
    End Function

    <DllImport("user32.dll", ExactSpelling:=True, SetLastError:=True)> _
    Private Shared Function ReleaseDC(ByVal hWnd As IntPtr, ByVal hDC As IntPtr) As Integer
    End Function

    <DllImport("user32.dll", ExactSpelling:=True, SetLastError:=True)> _
    Private Shared Function UpdateLayeredWindow( _
        ByVal hwnd As IntPtr, _
        ByVal hdcDst As IntPtr, _
        <System.Runtime.InteropServices.In()> _
        ByRef pptDst As Point, _
        <System.Runtime.InteropServices.In()> _
        ByRef psize As Size, _
        ByVal hdcSrc As IntPtr, _
        <System.Runtime.InteropServices.In()> _
        ByRef pptSrc As Point, _
        ByRef crKey As Integer, _
        <System.Runtime.InteropServices.In()> _
        ByRef pblend As BLENDFUNCTION, _
        ByVal dwFlags As Integer _
        ) As Boolean
    End Function

    <StructLayout(LayoutKind.Sequential, Pack:=1)> _
    Private Structure BLENDFUNCTION
        Public BlendOp As Byte
        Public BlendFlags As Byte
        Public SourceConstantAlpha As Byte
        Public AlphaFormat As Byte
    End Structure

    Private Const WS_EX_LAYERED As Integer = &H80000
    Private Const WS_BORDER As Integer = &H800000
    Private Const WS_THICKFRAME As Integer = &H40000
    Private Const AC_SRC_OVER As Byte = 0
    Private Const AC_SRC_ALPHA As Byte = 1
    Private Const ULW_ALPHA As Integer = 2

#End Region

#Region " コンストラクタ "

    Public Sub New(ByVal bmp As Bitmap)

        ' この呼び出しは、Windows フォーム デザイナで必要です。 
        InitializeComponent()

        ' InitializeComponent() 呼び出しの後で初期化を追加します。 
        Me.SetBackground(bmp)
    End Sub

#End Region

#Region " オーバーライド "

    Protected Overrides ReadOnly Property CreateParams() As CreateParams
        Get
            ' レイヤードウィンドウスタイルを適用 
            Dim cp As CreateParams = MyBase.CreateParams
            cp.ExStyle = cp.ExStyle Or WS_EX_LAYERED
            cp.Style = cp.Style And (Not WS_BORDER)
            cp.Style = cp.Style And (Not WS_THICKFRAME)
            Return cp
        End Get
    End Property

#End Region

#Region " Public メソッド "

    Public Sub SetBackground(ByVal srcBitmap As Bitmap)

        ' デバイスコンテキストを取得 
        Dim screenDc As IntPtr = GetDC(IntPtr.Zero)
        Dim memDc As IntPtr = CreateCompatibleDC(screenDc)
        Dim hBitmap As IntPtr = IntPtr.Zero
        Dim hOldBitmap As IntPtr = IntPtr.Zero
        Try
            hBitmap = srcBitmap.GetHbitmap(Color.FromArgb(0))
            hOldBitmap = SelectObject(memDc, hBitmap)

            ' BLENDFUNCTION を初期化 
            Dim blend As New BLENDFUNCTION
            blend.BlendOp = AC_SRC_OVER
            blend.BlendFlags = 0
            blend.SourceConstantAlpha = 255
            blend.AlphaFormat = AC_SRC_ALPHA

            ' レイヤードウィンドウを更新 
            Dim r As Boolean = UpdateLayeredWindow( _
                Me.Handle, screenDc, Me.Location, New Size(srcBitmap.Width, srcBitmap.Height), _
                memDc, New Point(0, 0), 0, blend, ULW_ALPHA _
            )

        Finally
            ReleaseDC(IntPtr.Zero, screenDc)
            If hBitmap <> IntPtr.Zero Then
                SelectObject(memDc, hOldBitmap)
                DeleteObject(hBitmap)
            End If
            DeleteDC(memDc)

        End Try

    End Sub

#End Region

End Class
レイヤードウィンドウの呼び出し
Dim f As New LayeredForm(New Bitmap("hoge.png"))
f.Show()

 コードサンプルで一つだけ注意するとすれば、WS_BORDER と WS_THICKFRAME スタイルを設定していることです。もしこのスタイルを適用しないと、フォームタイトルバーの墨が丸まっているように表示されてしまいます。

 レイヤードウィンドウの機能は Windows 2000 から既に登場しています。そんな古い機能を私は最近知ったわけですが、今でも衰えを感じないような機能だと思っています。できれば API を使用しないで .NET の世界だけで実現してほしいと思いますが、Opacity プロパティと同時に使用することができないので、無いのかもしれません。レイヤードウィンドウ専用のフォームを用意してもらってもいいと思います。