任意の形のウィンドウを作る

デスクトップアクセサリや、ガジェットを作成する場合は、ウインドウを任意の形に変更したいと考えることが多いと思います。ここでは、2通りの方法を説明します。

System.Windows.Forms.Form.TransparencyKeyプロパティを使う

おそらく最も簡単な方法だと思います。
背景画像を指定し、透過させる色を指定すると、透過色の部分が描画されなくなります。
この画像の黒い部分を透明化します。
実コードは以下となります。

    public partial class FormTransparensyKey : Form
    {
        public FormTransparensyKey()
        {
            InitializeComponent();

            this.DoubleBuffered = true;
        }

        private void FormTransparensyKey_Load(object sender, EventArgs e)
        {
            this.FormBorderStyle = FormBorderStyle.None;
            Bitmap bmp = new Bitmap(@"RegionVSTransparensyKey.bmp");
            Color transColor = bmp.GetPixel(0, 0);
            this.Size = bmp.Size;
            bmp.MakeTransparent(transColor);
            this.BackgroundImage = bmp;
            this.BackColor = transColor;
            this.TransparencyKey = transColor;
        }


実際に動かしてみると、背景色部分が大きいせいもありますが、若干のちらつきがあります。
また、描画速度は、概ね80ms程度でした。

System.Windows.Forms.Control.Regionプロパティを使う

描画領域をあらかじめ指定しておくと、指定領域の中のみが描画されます。

実行結果は左のようになります(背景はデスクトップの壁紙です)。
ここでは、描画する領域を、100×100の矩形に収まる円にしています。


実コードは以下となります。

    public partial class FormControlRegion : Form
    {
        public FormControlRegion()
        {
            InitializeComponent();

            this.DoubleBuffered = true;
        }

        private void FormControlRegion_Load(object sender, EventArgs e)
        {
            System.Drawing.Drawing2D.GraphicsPath path =
                new System.Drawing.Drawing2D.GraphicsPath();
            path.AddEllipse(new Rectangle(0, 0, 100, 100));
            this.Region = new Region(path);
        }

実際に動かしてみると、背景部分の再描画が無いせいか、特にちらつきはありませんでした。
描画速度は、概ね70ms程度でした。

画像からリージョンを生成する(System.Windows.Forms.Control.Regionプロパティを使う)

System.Windows.Forms.Form.TransparencyKeyプロパティを使う場合、描画領域が自動的に認識されるため、任意の画像で透過処理が可能ですが、背景色部分の描き直しが発生するせいか、若干のちらつきが出てしまいます。
System.Windows.Forms.Control.Regionプロパティを使う場合、ちらつきは発生しませんが、描画領域を、リージョンのデータとして渡してやる必要があります。これを手で作るのは困難です。
そこで、元画像を解析し、リージョンのデータを自動的に生成するコードを追加しました。
実コードは以下となります。

        private void FormControlRegion_Load(object sender, EventArgs e)
        {
            this.Region = GetRegionFromBitmap(@"RegionVSTransparensyKey.bmp");
        }

        private Region GetRegionFromBitmap(string filename)
        {
            bitmap = new Bitmap(filename);

            Color transColor = this.bitmap.GetPixel(0, 0);
            this.Size = this.bitmap.Size;
            this.BackgroundImage = this.bitmap;

            System.Drawing.Drawing2D.GraphicsPath gpath
                = new System.Drawing.Drawing2D.GraphicsPath();

            unsafe
            {
                BitmapData dataRgb = this.bitmap.LockBits(
                    new Rectangle(0, 0, bitmap.Width, bitmap.Height),
                    ImageLockMode.ReadOnly,
                    PixelFormat.Format24bppRgb);

                int width = this.bitmap.Width;
                int height = this.bitmap.Height;
                int stride = dataRgb.Stride;
                int maxX = stride / 3;
                int modX = stride % 3;
                byte* currentData = (byte*)dataRgb.Scan0 - 1;
                for (int y = 0; y < height; ++y)
                {
                    for (int x = 0; x < maxX; x++)
                    {
                        Color pixcel = Color.FromArgb((int)*++currentData,
                            (int)*++currentData, (int)*++currentData);
                        if (pixcel != transColor)
                        {
                            gpath.AddRectangle(new Rectangle(x, y, 1, 1));
                        }
                    }
                    currentData += modX;
                }
                this.bitmap.UnlockBits(dataRgb);
            }

            return new Region(gpath);
        }

Regionクラス(System.Drawing)は、GraphicsPathクラス(System.Drawing.Drawing2D)で定義された、接続された一連の図形からなる領域です。GraphicsPathクラスは、任意の数の図形の集合で、複雑な図形を表現します。ここでは、描画するピクセルを1つずつ、GraphicsPathクラスに追加していくことで、描画領域を定義しています。

実行結果は左のようになります(背景はデスクトップの壁紙です)。
描画に要する時間は、概ね99ms程度でした。

ちらつきの解消(System.Windows.Forms.Form.TransparencyKeyプロパティを使う)

Form.FormBoderStyleにNoneを指定すると、ベースとなるフォームの描画を行わないので、ちらつきが無くなります。
描画速度は、ちらつき対策を行う前と同様に、概ね80ms程度でした。

まとめ

今回の試行では、System.Windows.Forms.Form.TransparencyKeyプロパティを使う方が若干速い、という結果になりましたが、複数の画像を切り替えて表示させる場合などでは、一度生成したリージョンを使いまわすことが出来るSystem.Windows.Forms.Control.Regionプロパティを使用した方が速くなるケースもあるかと思います。