読者です 読者をやめる 読者になる 読者になる

SourceChord

C#とXAML好きなプログラマの備忘録。最近はWPF系の話題が中心です。

クライアント領域までAero効果が適用されたウィンドウを作る

最近、WPF 4 UNLEASHEDを読んで、WPFの勉強をしてます。
洋書で1000ページ近い分厚い本ですが、割と読みやすい英語で書かれていて、サクサク読めます。
フルカラーで、色々とサンプルコードも多数載ってるので、楽しみながら読める本です。


解説もすごく詳しくて、いまいち仕組みが分からなかった部分とか、
アプリを作るときによく使いそうなTips、よく間違えそうな部分の解説とか、役に立つ情報がビッシリ詰まってます。
(こんな本が日本語でも出てほしい。。。。)

WPF 4 Unleashed

WPF 4 Unleashed

この本のp.251あたりで、Aeroのグラス効果が適用されたウィンドウを作るための、ヘルパークラスの実装例が載っていました。
この本のサンプルでは、複数のstaticメソッドからなるヘルパークラスとなっていて、Aeroの半透過効果を適用するウィンドウ側でも、メソッドのオーバーライドなどをしないといけない構造になっていました。


そこで、使いまわしやすいように、以下のようなクラスを作ってみました。
・継承すると、Aero効果が適用され、クライアント領域の背景も半透過のウィンドウになるクラス


クライアント領域全域に適用すると以下のような感じ。

ただ、WPFだとあまりWindowクラスを継承した構造にしないっぽいので、もっとキレイな実装方法もあるのかも。。。


一応、コードは以下のような感じ

AeroWindow.cs

Aeroのグラス効果を適用したいウィンドウは、このクラスを継承するようにする。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Interop;
using System.Runtime.InteropServices;
using System.Windows.Media;

namespace WpfApplication1
{
    [StructLayout(LayoutKind.Sequential)]
    struct MARGINS
    {
        public MARGINS(Thickness t)
        {
            Left = (int)t.Left;
            Right = (int)t.Right;
            Top = (int)t.Top;
            Bottom = (int)t.Bottom;
        }
        public int Left;
        public int Right;
        public int Top;
        public int Bottom;
    }
    public partial class AeroWindow : Window
    {
        private const int WM_DWMCOMPOSITIONCHANGED= 0x031E;

        [DllImport("dwmapi.dll", PreserveSig=false)]
        static extern void DwmExtendFrameIntoClientArea(IntPtr hWnd, ref MARGINS pMarInset);

        [DllImport("dwmapi.dll", PreserveSig = false)]
        static extern bool DwmIsCompositionEnabled();

        // 
        /// <summary>
        /// 半透過領域のマージ量指定
        /// -1を指定すると、全部が半透過になる。
        /// デフォルト値はThickness(-1)を指定
        /// </summary>
        public Thickness GlassMargins
        {
            get { return (Thickness)GetValue(GlassMarginsProperty); }
            set { SetValue(GlassMarginsProperty, value); }
        }

        // Using a DependencyProperty as the backing store for GlassMargins.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty GlassMarginsProperty =
            DependencyProperty.Register("GlassMargins", typeof(Thickness), typeof(AeroWindow), new UIPropertyMetadata(new Thickness(-1)));

        
        
        public AeroWindow()
        {
        }

        public bool ExtendGlassFrame()
        {
            if (!DwmIsCompositionEnabled())
                return false;

            IntPtr hwnd = new WindowInteropHelper(this).Handle;
            if (hwnd == IntPtr.Zero)
                throw new InvalidOperationException("The Window must be shown before extending glass.");

            // WPF/Win32の両方の背景を透明にする
            this.Background = Brushes.Transparent;
            HwndSource.FromHwnd(hwnd).CompositionTarget.BackgroundColor = Colors.Transparent;

            MARGINS margins = new MARGINS(GlassMargins);
            DwmExtendFrameIntoClientArea(hwnd, ref margins);
            return true;
        }

        protected override void OnSourceInitialized(EventArgs e)
        {
            base.OnSourceInitialized(e);

            // このメソッドは、SourceInitializedイベントより前で実行してはいけない。
            ExtendGlassFrame();

            IntPtr hwnd = new WindowInteropHelper(this).Handle;
            HwndSource.FromHwnd(hwnd).AddHook(new HwndSourceHook(WndProc));
        }

        private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
        {
            if (msg == WM_DWMCOMPOSITIONCHANGED)
            {
                ExtendGlassFrame();
                handled = true;
            }
            return IntPtr.Zero;
        }
    }
}
MainWindow.xaml

ルートのタグをWindowからAeroWindowに変更。
ローカルの名前空間にあるものなので、「xmlns:local="clr-namespace:WpfApplication1"」などの記述を追加。

<local:AeroWindow x:Class="WpfApplication1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:WpfApplication1"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
    </Grid>
</local:AeroWindow>
MainWindow.xaml.cs

MainWindowを、先ほど作成したAeroWindowクラスから派生させる。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace WpfApplication1
{
    /// <summary>
    /// MainWindow.xaml の相互作用ロジック
    /// </summary>
    public partial class MainWindow : AeroWindow
    {
        public MainWindow()
        {
            InitializeComponent();
        }
    }
}

使用例

このウィンドウ上にコントロールを配置する際に、コントロールの背景色に透明度を持たせたりすると、コントロールも透過します。