C# Programming

Clockアナログクロック その2(自由な形のウィンドウを作る)

開発環境: Visual Studio 2003

次のような機能のForm アプリケーションを作る。

  • 自由図形のアナログクロック
  • マウスの左ボタンを押しながらドラッグすることにより、ウィンドウを移動する。
  • マウスの右ボタンでコンテキストメニューを表示し、メニューから終了できる。

注意: 透明にならない場合の確認すべき点

透明なウィンドウおよび領域は、Windows 2000 のみでサポートされているというヘルプがありますが、XP でもOKです。
透明にならない場合、Windows 2000 以降かどうか確認してください。

TransparencyKey は、コントロールパネル→画面→画面の色が32bit だと、透明にならない可能性があります。
透明にならない場合には、画面の色を 32bit 以外で試してみてください。

また、http://www.dotnet247.com/247reference/msgs/21/107515.aspx では、フォントのスムージングをしている場合にもNGという説明があります。私の場合には、フォントを滑らかにするかどうかは影響ありませんでしたが、確認してみてください。

Image

See NASA Copyright Notification

作り方

手順 説明
1アナログクロックを作る。基本的な枠組みを作るまではアナログクロックを作る(その1)と同じ。
2自由図形のウィンドウにする。Form のTransparencyKey プロパティで透過色とする色を指定します。
今回の例では、次のようなスペースシャトルの背景を黒で塗りつぶします。 そして、TransparencyKey をBlack とします。
これにより、TransparencyKeyで指定された場所は透過となり、またそこで発生したイベントは、 すべて下のウィンドウへ送られます。 あとは、FormBorderStyle を None として、フレームを表示しないようにします。
3マウスの右ボタンでコンテキストメニューを表示し、終了できるようにする。ツールボックスより、ContextMenu を追加し、Exit メニューを作る。Image
4コンテキストメニューでExit する処理コンテキストメニューのExit が押された時のイベントを追加する。

private void OnExit(object sender, System.EventArgs e)
{
      Application.Exit();
}
5Form からContextMenuを呼び出せるようにする。。Form プロパティの動作から、ContextMenuプロパティに3で作ったコンテキストメニューをセットする。
Image
6そのままでは、アナログクロックの移動ができないので、マウスでドラッグして移動できるようにします。そのために、つぎのようにマウスイベントを処理します。
マウスボタンが押されたときに、押されたことを示す mouseIsDown 変数を持ちます。
この変数は、マウスが放されたときに、クリアします。
また、マウスが移動したときに、そのときの移動量にしたがって、ウィンドウを移動します。
ウィンドウの移動には、Form.Left, Form.Top プロパティを変更するとウィンドウを移動できます。
7起動時に引数で画像を読み込むようにします。main のargs の第1引数から、Image.FromFile( ) で、取得した Image インスタンスをbackgroundImage に渡します。
ファイルが見つからないときのために、Exception をキャッチしておきます。

実行例

次のような画像ファイルを用意します。

10075946.gif
Image

次に、コマンドラインから、
AnalogClock (引数がない場合は、デフォルトのシャトルが表示される)
AnalogClock 10075946.gif
と起動します。

実行結果

Image

以下、ソースコード

日付 修正履歴
2003/12/7VS.2003, .NetFramework V1.1 対応
2002/4/28バックグラウンドイメージの大きさに合わせて、自動的にウィンドウをリサイズするように修正。
Timer コンポーネントではなく、Timer コントロールを使用するように修正。
ContextMenu を追加。
2002/3/3初期バージョン作成

using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
using System.Drawing.Drawing2D;

namespace TransparentAnalogClock
{
    /// <summary>
    /// Form1 の概要の説明です。
    /// </summary>
        
    public class TransparentAnalogClock : System.Windows.Forms.Form
    {
        private System.ComponentModel.IContainer components;

        public TransparentAnalogClock( string[] args )
        {
            //
            // Windows フォーム デザイナ サポートに必要です。
            //
            InitializeComponent();

            //
            // TODO: InitializeComponent 呼び出しの後に、コンストラクタ コードを追加してください。
            //
            if (args.Length == 1) 
            {
                try 
                {
                    this.BackgroundImage = System.Drawing.Image.FromFile(args[0]);
                }
                catch ( Exception e ) 
                {
                    DialogResult res = MessageBox.Show(e.Message, this.Text,
                        MessageBoxButtons.OK, MessageBoxIcon.Error);
                }
            }
        }
        /// <summary>
        /// 使用されているリソースに後処理を実行します。
        /// </summary>
        protected override void Dispose( bool disposing )
        {
            if( disposing )
            {
                if (components != null) 
                {
                    components.Dispose();
                }
            }
            base.Dispose( disposing );
        }

        #region Windows Form Designer generated code
        /// <summary>
        /// デザイナ サポートに必要です。このメソッドの内容を
        /// コード エディタで変更しないでください。
        /// </summary>
        private void InitializeComponent()
        {
            this.components = new System.ComponentModel.Container();
            System.Resources.ResourceManager resources = new System.Resources.ResourceManager(typeof(TransparentAnalogClock));
            this.timer1 = new System.Windows.Forms.Timer(this.components);
            this.contextMenu1 = new System.Windows.Forms.ContextMenu();
            this.menuItem1 = new System.Windows.Forms.MenuItem();
            // 
            // timer1
            // 
            this.timer1.Enabled = true;
            this.timer1.Interval = 1000;
            this.timer1.Tick += new System.EventHandler(this.OnTick);
            // 
            // contextMenu1
            // 
            this.contextMenu1.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] {
                                                                                         this.menuItem1});
            // 
            // menuItem1
            // 
            this.menuItem1.Index = 0;
            this.menuItem1.Text = "Exit";
            this.menuItem1.Click += new System.EventHandler(this.OnExit);
            // 
            // TransparentAnalogClock
            // 
            this.AutoScaleBaseSize = new System.Drawing.Size(5, 12);
            this.BackgroundImage = ((System.Drawing.Image)(resources.GetObject("$this.BackgroundImage")));
            this.ClientSize = new System.Drawing.Size(244, 422);
            this.ContextMenu = this.contextMenu1;
            this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
            this.Name = "TransparentAnalogClock";
            this.Text = "C# Analog Clock";
            this.TransparencyKey = System.Drawing.Color.Black;
            this.MouseDown += new System.Windows.Forms.MouseEventHandler(this.OnMouseDown);
            this.Load += new System.EventHandler(this.TransparentAnalogClock_Load);
            this.MouseUp += new System.Windows.Forms.MouseEventHandler(this.OnMouseUp);
            this.Paint += new System.Windows.Forms.PaintEventHandler(this.OnFormPaint);
            this.MouseMove += new System.Windows.Forms.MouseEventHandler(this.OnMouseMove);

        }
        #endregion

        /// <summary>
        /// アプリケーションのメイン エントリ ポイントです。
        /// </summary>
        [STAThread]
        static void Main(string[] args) 
        {
            Application.Run(new TransparentAnalogClock( args ));
        }

        private System.Windows.Forms.Timer timer1;
        private bool mouseIsDown = false;
        private System.Windows.Forms.ContextMenu contextMenu1;
        private System.Windows.Forms.MenuItem menuItem1;
        private Point mouseDownPoint;

        private void OnFormPaint(object sender, System.Windows.Forms.PaintEventArgs e)
        {
            Graphics g = e.Graphics;

            // Translate to the center of the client window.
            g.TranslateTransform(ClientSize.Width/2, ClientSize.Height/2, MatrixOrder.Append);
            // Now Draw it!
            DrawClock( e.Graphics );
        }

        private void DrawClock(Graphics g)
        {
            const double pai = Math.PI;
            Point center = new Point (0, 0);

            DateTime time = DateTime.Now;
            double secAng = 2.0*pai*time.Second/60.0;
            double minAng = 2.0*pai*(time.Minute + time.Second/60.0)/60.0;
            double hourAng = 2.0*pai*(time.Hour + time.Minute/60.0)/12.0;
            int r = Math.Min( ClientSize.Width/2, ClientSize.Height/2 ) ;
            int secHandLength = (int)(0.7*r);
            int minHandLength = (int)(0.9*r);
            int hourHandLength = (int)(0.5*r);

            Point secHand  = new Point((int)(secHandLength*Math.Sin(secAng)),
                (int)(-secHandLength*Math.Cos(secAng)));
            Point minHand  = new Point((int)(minHandLength*Math.Sin(minAng)),
                (int)(-minHandLength*Math.Cos(minAng)));
            Point hourHand = new Point((int)(hourHandLength*Math.Sin(hourAng)),
                (int)(-hourHandLength*Math.Cos(hourAng)));

            Pen YellowPen = new Pen(Color.Yellow, 3);
            g.DrawLine(YellowPen, center, secHand);

            Pen GreenPen = new Pen(Color.Green, 5);
            g.DrawLine(GreenPen, center, minHand);

            Pen RedPen = new Pen(Color.Red, 7);
            g.DrawLine(RedPen, center, hourHand);
        }

        private void OnMouseMove(object sender, System.Windows.Forms.MouseEventArgs e)
        {
            if (mouseIsDown && e.Button == System.Windows.Forms.MouseButtons.Left) 
            {
                this.Left += e.X - mouseDownPoint.X;
                this.Top += e.Y - mouseDownPoint.Y;
            }
        }

        private void OnMouseDown(object sender, System.Windows.Forms.MouseEventArgs e)
        {
            if (e.Button == System.Windows.Forms.MouseButtons.Left) 
            {
                mouseDownPoint = new Point(e.X, e.Y);
                mouseIsDown = true;
            } 
        }

        private void OnMouseUp(object sender, System.Windows.Forms.MouseEventArgs e)
        {
            if (e.Button == System.Windows.Forms.MouseButtons.Left) 
            {
                mouseIsDown = false;
            }
        }

        private void OnTick(object sender, System.EventArgs e)
        {
            Invalidate();
        }

        private void FitToBackgroundImage()
        {               
            this.SetClientSizeCore(this.BackgroundImage.Width, this.BackgroundImage.Height);
        }

        private void OnExit(object sender, System.EventArgs e)
        {
            Application.Exit();
        }

        private void TransparentAnalogClock_Load(object sender, System.EventArgs e)
        {
            FitToBackgroundImage();
        }
    }
}