ふとデスクトップに現在時刻を描画する自作アプリをつくってみたいと思い、やってみたのですが、意外に苦戦してしまいました。

残念ですが失敗です

最初に思いついた方法はディスプレイのデバイスコンテキストのハンドルを取得し、そこに現在時刻を描画するという方法です。

しかしこの方法では前回描画された文字のうえにさらに描画がおこなわれてしまうので文字が重なってしまいます。前回描画された文字列を消してから描画しなければなりません。DllImport 属性をもちいてdll関数の呼び出しをするという面倒なことをしたのですが、残念な結果となりました。

背景が透明なフォームに文字を表示させる

ではこの方法はどうでしょうか? 以前、フォームに穴をあけて画面録画できるようにするでフォームに穴をあける方法を紹介しました。これを使うと以下のようなコードになります。

タイトルバーの非表示と終了させるための別の手段

これだと一応、クライアント領域は透明になり、うまくいきます。ただタイトルバーやウィンドウの境界線が透明にならないのでもうすこし工夫します。

タイトルバーが透明になってしまうと移動させたり終了させることができなくなってしまいます。そこで別の方法でこれらができるようにしておかなければなりません。

それからこのままではタスクバーで[デスクトップを表示]を選択すると、このフォーム自体も最小化されてしまい表示が消えてしまいます。これでは困るのでthis.MinimizeBox = false; として他のウィンドウをすべて最小化したときに見えなくなってしまうことを防いでいます。

以下のコードではthis.FormBorderStyle = FormBorderStyle.Noneとすることでタイトルバーと境界線を非表示にします。そして通知領域にアイコンを表示させ、これをクリックすると終了させるためのメニューが表示されます。そして終了を選択するとアプリケーションが終了します。

表示色や表示場所の変更

一応、これで表示はされるのですが、表示色や表示場所を選択したい、他のウィンドウがあるときはそのウィンドウを隠さないようにしたいなどの設定もできるようにします。

フォームの場所を変更するためには透明でない部分をドラッグすればよいのですが、それだと使いにくいので一時的に背景を白で描画するようにします。ドラッグでフォームを移動させるための処理は後述します。

また文字色を変化させるときはColorDialogをつかって設定しますが、そのときにColor.Magentaが選択されてしまうと、文字まで透明になってしまいます。そこでその場合はColor.Magentaから少しズラした色を使うようにします。

フォームを最前面にするのであればthis.TopMost = true;とすればいいのですが、最後面にするにはどうすればいいのでしょうか? ここではWindows API関数のSetWindowPos関数を使います。第2引数にHWND_BOTTOMを使い、第3から第6引数まで無視するために第7引数にSWP_NOSIZE | SWP_NOMOVEを使います。

ドラッグアンドドロップでフォームを移動させる処理

ドラッグでフォームを移動する処理を示します。

ドラッグが開始されたらマウスキャプチャしてマウスポインタの座標を記憶します。そしてマウスキャプチャされているときにマウスが移動したら、移動分だけフォームの座標を移動させる処理を繰り返します。最後にマウスボタンが離されたらマウスキャプチャを解除します。