背景が透けているウィンドウを作る

今回はウィンドウの背景色を透明にして、デスクトップが透ける効果のあるウィンドウデザインを紹介します。タイトルバーを消すと(というか消さざるを得ないのですが)ガジェットのような雰囲気が出ます。

Windowコントロールの設定

ウィンドウ背景の透過効果を有効にするには次のWindowコントロールのプロパティ設定が必要です。

  1. AllowsTransparency を True にする
  2. WindowStyle を None にする(必須)
  3. Background を Transparentにする(あるいはOpacityを1.0未満に設定するなど透明度を設定する)

また、ウィンドウの外形や背景効果をカスタマイズするために背景用の要素を配置します。今回は角丸の四角形にするためRectangleを使いました。お好みでPathを使って星型にするなどできますのでカスタマイズの自由度は高いと思います。

<Window x:Class="WpfApplication1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" 
        Loaded="Window_Loaded" MouseRightButtonDown="Window_MouseRightButtonDown"
        SizeToContent="WidthAndHeight" Background="Transparent"
        WindowStyle="None" AllowsTransparency="True" >
    
    <Grid>
        
        <!--ウィンドウのドラッグ移動用のThumb-->
        <Thumb Name="WindowBackground" DragDelta="WindowBackground_DragDelta">
            <Thumb.Template>
                <ControlTemplate>
                    <!--ウィンドウ背景のデザイン-->
                    <Rectangle RadiusX="20" RadiusY="20"
                               Fill="Black" Opacity="0.5"/>
                </ControlTemplate>
            </Thumb.Template>
        </Thumb>
        
        <!--通常のウィンドウコンテンツ-->
        <Grid>
            <TextBlock Name="TimeDisplay" Foreground="LightGreen"
                       FontFamily="Century" FontSize="40" 
                       HorizontalAlignment="Center"
                       VerticalAlignment="Center" 
                       Margin="50,30"
                       Text="12:34:56">
                <TextBlock.Effect>
                    <DropShadowEffect />
                </TextBlock.Effect>
            </TextBlock>
        </Grid>

    </Grid>
</Window>

ウィンドウ操作

上の設定でタイトルバーが(表示したくても)表示されなくなります。このためウィンドウの移動や閉じるなどの標準操作ができなくなりますので個別に代替手段を提供してやる必要があります。
今回はドラッグでウィンドウ移動をできるように背景図形を兼ねたThumbコントロールを入れています。また右クリックでウィンドウを閉じてアプリを終了していますが、本来であれば右クリックメニューや閉じるボタンがある方が適切かもしれません。

        //ウィンドウのドラッグでウィンドウ移動
        private void WindowBackground_DragDelta(object sender, DragDeltaEventArgs e) {
            Left += e.HorizontalChange;
            Top += e.VerticalChange;
        }

        //右クリックでアプリケーション終了
        private void Window_MouseRightButtonDown(object sender, MouseButtonEventArgs e) {
            this.Close();
        } 
    }

サンプルの作成

今回の完成イメージはガジェット風のデジタル時計に仕立ててみました。本題では無いので少々大雑把です。

        private void Window_Loaded(object sender, RoutedEventArgs e) {
            //時計更新処理のスレッドをテキトーに作る
            var t = new Thread(() => {
                while (true) {
                    Dispatcher.Invoke((Action)(() => TimeDisplay.Text = DateTime.Now.ToLongTimeString()), null);
                    Thread.Sleep(1000);
                }
            });
            Closing += (_, __) => t.Abort();    //ウィンドウ閉鎖時に更新スレッドを停止する
            t.Start();
        }
別スレッドからのコントロールプロパティの更新処理

時計表示を1秒おきに更新するよう簡単にスレッドを立ち上げています。アニメーション効果を入れるなら、アニメーションの完了イベントを利用しても良いと思います。

なお、WPFやWinFormでコントロールのプロパティ変更などはメインスレッドでのみ許されています。WPFで別スレッドから更新する場合にはDispatcherクラスのInvokeやBeginInvokeメソッドを使えば良いです。

Delegate型の引数でのラムダ式の利用

Invokeメソッドの第一引数はDelegate型です。ラムダ式や匿名メソッド〔delegate(〜){〜}〕が直接使えません。次のようなコンパイルエラーが出ます。

ラムダ式 はデリゲート型ではないため、型 'System.Delegate' に変換できません。

この場合には適当なデリゲート型に明示的にキャストすれば良いようです。つまりラムダ式からDelegete型には暗黙の型変換をしてくれないということのようです。
ControlクラスのInvokeメソッドで匿名メソッドを使うには?[2.0のみ、C#] - @IT

Closingイベント

ウィンドウを閉じた際に時計更新用のスレッドを停止させないとアプリケーションが起動したままになってしまいます。停止の処理は1行だけで単純なのでこのようなイベントハンドラの登録にしてみました。