Windows Phoneアプリケーション開発入門

第15回Windows Phoneでグラデーションのボタンを作ってみよう!(2)

はじめの前に

はじめに、Microsoftから発表された新しいモバイルOSについて紹介させてください。

2010年6月17日、Microsoftはビジネス向けのモバイルOSとして「Windows Embedded Handheld」を発表しました。Windows Embedded Handheldの初版は、Windows Mobile 6.5.3がベースですので、Windows Mobile向けに開発されたソフトウェアとも互換性を持っており、今までのソフトウェア財産が無駄にならずに済みます。

Windows Phone 7が発表された際に「Windows Mobile 6.5にも投資を続ける」との発言は、この件があったからなんでしょうか。個人でもWindows Embedded Handheld端末が手に入れば嬉しいのですが……と、楽しみにしています。

Windows Phone 7が発売されているだろう2011年下半期には、Windows Embedded Handheldの次期バージョンとしてWindows Embedded CE 6.x系ベースの「Windows Embedded Compact 7」のリリースが予定されています。

こちらに関しては、Windows Mobile 6.5系の開発環境(Win32、MFC、Compact Framework)に加え、Silverlight for Embeddedが使えますので、今まで通りかそれ以上に面白そうな端末になりそうです。

Visual Studio 2010では、Windows Mobile 6.5の開発は出来ないらしいので、Windows Embedded Handheld向けのアプリケーションを開発する場合もVisual Studio 2008を利用しなければいけないのかどうかが気になりますね。

はじめに

さて、前回の予告どおりグラデーションを使ったさまざまな図形を早速描画してみましょう。

どのような処理を行っているのかをグラデーションさせた円形の描画を例にして、絵を使って説明します。その次に円形以外の図形にグラデーションを適用させてみましょう。

グラデーション掛かった円形を描画するメソッドを実装する

先にソースコードを示します。コメントの部分で数字を打っておきましたので、後程出てくるイメージ画像と合わせてご覧ください。描画関連のメソッドをまとめたクラスですので、クラス名は「GraphicsExtension」としました。

using System;
using System.Linq;
using System.Collections.Generic;
using System.Text;

using System.Drawing;
using System.Drawing.Imaging;

namespace GradientionSample
{
    public class GraphicsExtension
    {
        public static Color KeyColor = Color.FromArgb(255, 0, 255);

        public static void DrawGradientEllipse(Graphics g, Rectangle rc,
            Color startColor, Color endColor, Color borderColor,
            FillDirection gradientType)
        {
            // クリッピング用の色の設定
            Color clippingColor = Color.Green;

            Rectangle rect = new Rectangle(0, 0, rc.Width, rc.Height);

            // (1)グラデーションを描画するイメージの生成
            Bitmap gradientImage = new Bitmap(rc.Width, rc.Height,
                PixelFormat.Format16bppRgb565);
            Graphics gxGradient = Graphics.FromImage(gradientImage);

            // グラデーションを描画する
            GradientFill.Fill(gxGradient, rect,
                startColor, endColor, FillDirection.TopToBottom);

            // (2)任意の図形でグラデーションを切り抜く為のイメージの生成
            Bitmap tempImage = new Bitmap(rc.Width, rc.Height,
                PixelFormat.Format16bppRgb565);

            // クリッピング用に緑色の円形を描画するdelegateを呼ぶ
            Graphics gxTemp = Graphics.FromImage(tempImage);
            gxTemp.Clear(KeyColor);
            gxTemp.FillEllipse(new SolidBrush(clippingColor), rect);
            if (borderColor != Color.Empty)
            {
                gxTemp.DrawEllipse(new Pen(borderColor), rect);
            }

            // グラデーションイメージに対して、
            // クリッピング用のイメージを、緑色の透過色を使用して上書きする
            ImageAttributes clippingAttr = new ImageAttributes();
            clippingAttr.SetColorKey(clippingColor, clippingColor);
            gxGradient.DrawImage(tempImage,
                rect, 0, 0, rect.Width, rect.Height,
                GraphicsUnit.Pixel, clippingAttr);

            // (3)グラデーションの掛かった図形を描画する
            ImageAttributes attrib = new ImageAttributes();
            attrib.SetColorKey(KeyColor, KeyColor);
            g.DrawImage(gradientImage, rc, 0, 0,
                gradientImage.Width, gradientImage.Height,
                GraphicsUnit.Pixel, attrib);

            // Clean up
            gxTemp.Dispose();
            tempImage.Dispose();
            gxGradient.Dispose();
            gradientImage.Dispose();
        }
    }
}

図形に対してのグラデーションの適用

まず例として最初に円形のグラデーションの描画をします。

Windows Mobileでは、グラデーションを矩形でしか描画することができません。ですので、少し手間ですが数ステップ踏んで描画します。

(1)ベースになるグラデーションイメージを用意する

赤から黒への単純なグラデーションイメージを用意しました。このグラデーションイメージが、最終的にグラデーションの掛かった図形になります。

ベースになるグラデーション
ベースになるグラデーション

(2)グラデーションをクリッピングするイメージを作る

背景をマゼンタで塗り潰し、グラデーションさせたい領域を緑色で描画します(この画像では分かりやすいように円形を黒線で囲っています⁠⁠。

クリッピングイメージ
クリッピングイメージ

透過させる色に緑色を設定して、グラデーションイメージに対して、クリッピングイメージ(マゼンタ背景&緑図形)を描画すると、このように緑色の部分が透過されます。

グラデーションイメージの上に乗せたクリッピングイメージ
グラデーションイメージの上に乗せたクリッピングイメージ

マゼンタを背景としたグラデーション掛かった円形のイメージの完成です。

グラデーションイメージ
グラデーションイメージ

(3)円形のグラデーションを描画する

次に透過色をマゼンタに設定して、グラデーションイメージをFormのOnPaintイベントのe.Graphicsに描画します。これでグラデーション掛かった円形を描画することができました。

描画されたグラデーションの掛かった円形
描画されたグラデーションの掛かった円形

様々な図形のグラデーションを描画するための準備

前記のDrawGradientEllipseメソッドの処理では円形にしか対応していません。今後簡単に図形を足して行けるようにGraphicsExtensionクラスを書き換えてみました。

  using System;
  using System.Linq;
  using System.Collections.Generic;
  using System.Text;
  
  using System.Drawing;
  using System.Drawing.Imaging;
  
  namespace GradientionSample
  {
      public class GraphicsExtension
      {
          // 透過用のキーカラー
          public static Color KeyColor = Color.FromArgb(255, 0, 255);

          // クリッピング用画像描画デリゲート定義
          private delegate void InnderDrawFunction(Graphics g, 
              Rectangle rc, Color fillColor, Color borderColor);

          // グラデーション描画メソッド(共通処理)
          private static void InnerDrawGradient(Graphics g, Rectangle rc, 
              Color startColor, Color endColor, Color borderColor, 
              FillDirection gradientType, InnderDrawFunction func)
          {
              // クリッピング用の色の設定
              Color clippingColor = Color.Green;

              Rectangle rect = new Rectangle(0, 0, rc.Width, rc.Height);

              // グラデーションを描画するイメージの生成
              Bitmap gradientImage = new Bitmap(rc.Width, rc.Height,
                  PixelFormat.Format16bppRgb565);
              Graphics gxGradient = Graphics.FromImage(gradientImage);

              // グラデーションを描画する
              GradientFill.Fill(gxGradient, rect,
                  startColor, endColor, FillDirection.TopToBottom);

              // 任意の図形でグラデーションを切り抜く為のイメージの生成
              Bitmap tempImage = new Bitmap(rc.Width, rc.Height,
                  PixelFormat.Format16bppRgb565);

              // クリッピング用の図形を描画するdelegateを呼ぶ
              Graphics gxTemp = Graphics.FromImage(tempImage);
              func(gxTemp, rect, clippingColor, borderColor);

              // グラデーションイメージに対して、
              // クリッピング用のイメージを、緑色の透過色を使用して上書きする
              ImageAttributes clippingAttr = new ImageAttributes();
              clippingAttr.SetColorKey(clippingColor, clippingColor);
              gxGradient.DrawImage(tempImage,
                  rect, 0, 0, rect.Width, rect.Height,
                  GraphicsUnit.Pixel, clippingAttr);

              // グラデーションの掛かった図形を描画する
              ImageAttributes attrib = new ImageAttributes();
              attrib.SetColorKey(KeyColor, KeyColor);
              g.DrawImage(gradientImage, rc, 0, 0, 
                  gradientImage.Width, gradientImage.Height,
                  GraphicsUnit.Pixel, attrib);

              // Clean up
              gxTemp.Dispose();
              tempImage.Dispose();
              gxGradient.Dispose();
              gradientImage.Dispose();
          }

          // グラデーションが掛かった円形を描画する
          public static void DrawGradientEllipse(
              Graphics g, Rectangle rc,
              Color startColor, Color endColor, Color borderColor,
              FillDirection gradientType)
          {
              InnerDrawGradient(g, rc, startColor, 
                  endColor, borderColor, gradientType, 
                  new InnderDrawFunction(_DrawEllipse));
          }

          // クリッピング用の図形を描画するメソッド
          private static void _DrawEllipse(Graphics g, 
              Rectangle rc, Color fillColor, Color borderColor)
          {
              // 描画用オブジェクトの取得
              SolidBrush fillBrush = new SolidBrush(fillColor);
              Pen borderPen = new Pen(borderColor);

              // 背景は透過色でクリアしておく
              g.Clear(KeyColor);

              // 円を描く
              g.FillEllipse(fillBrush, rc);

              // ボーダーを描画する
              if (borderColor != Color.Empty)
              {
                  g.DrawEllipse(borderPen, rc);
              }

              // 描画用オブジェクトの解放
              fillBrush.Dispose();
              fillBrush = null;
              borderPen.Dispose();
              borderPen = null;
          }
      }
  }

publicにするDrawXXXメソッドと、内部で緑色のクリッピング用の図形を描画するprivateな_DraXXXメソッドを書けば、円形と同様にグラデーションの掛かった図形を描画することができます。

グラデーションの掛かった星形を描画する

描画用メソッドとクリッピング用の図形を描画するメソッドを用意します。下記のメソッドをGraphicsExtensionクラスに追加してください。

// グラデーションが掛かった円形を描画する
public static void DrawGradientStar(
    Graphics g, Rectangle rc,
    Color startColor, Color endColor, Color borderColor,
    FillDirection gradientType)
{
    InnerDrawGradient(g, rc, startColor,
        endColor, borderColor, gradientType,
        new InnderDrawFunction(_DrawStar));
}

// クリッピング用の図形を描画するメソッド
private static void _DrawStar(Graphics g,
    Rectangle rc, Color fillColor, Color borderColor)
{
    // 描画用オブジェクトの取得
    SolidBrush fillBrush = new SolidBrush(fillColor);
    Pen borderPen = new Pen(borderColor);

    // 背景は透過色でクリアしておく
    g.Clear(KeyColor);

    // 星形を描画する
    Point[] apt = new Point[5];
    for (int i = 0; i < apt.Length; i++)
    {
        double dAngle = (i * 0.8 - 0.5) * Math.PI;
       
        apt[i] = new Point(
            (int)(rc.Width * (0.50 + 0.48 * Math.Cos(dAngle))),  
            (int)(rc.Height * (0.50 + 0.48 * Math.Sin(dAngle))));

    }
    g.FillPolygon(fillBrush, apt);

    // ボーダーを描画する
    if (borderColor != Color.Empty)
    {
        g.DrawPolygon(borderPen, apt);
    }

    // 描画用オブジェクトの解放
    fillBrush.Dispose();
    fillBrush = null;
    borderPen.Dispose();
    borderPen = null;
}

呼び出し元のForm.OnPaintメソッド内にて、DrawGradientStarメソッドを呼び出しています。

protected override void OnPaint(PaintEventArgs e)
{
    // グラデーションを描画する図形を設定する
    Rectangle rect = new Rectangle();
    rect.Width = this.Width / 2;
    rect.Height = this.Height / 2;
    rect.X = (this.Width - rect.Width) / 2;
    rect.Y = (this.Height - rect.Height) / 2;

    // グラデーション開始色
    Color startColor = Color.Red;
    // グラデーション終了色
    Color endColor = Color.Black;
    // グラデーションの方向
    FillDirection type = FillDirection.TopToBottom;

    // グラデーションの掛かった星形を描く
    GraphicsExtension.DrawGradientStar(e.Graphics,
        rect, startColor, endColor, Color.Empty, type);
}

実行の結果、画面中央にグラデーションの掛かった星形が描画されていれば完成です。

グラデーションの掛かった星形
グラデーションの掛かった星形

さいごに

サンプルコードを実行しながら、.NET Compact Frameworkからどうやってグラデーションを使えばよいのかを知って頂ければ幸いです。特に今回のグラデーションが掛かった任意の図形の作り方は、活用のしがいがあると思います。

次回は、今回のことを応用して角丸ボタンの見た目の部分を作りたいと思います。

以上で今回は終わりです。ありがとうございました。

おすすめ記事

記事・ニュース一覧