クライアント領域までAero効果が適用されたウィンドウを作る
最近、WPF 4 UNLEASHEDを読んで、WPFの勉強をしてます。
洋書で1000ページ近い分厚い本ですが、割と読みやすい英語で書かれていて、サクサク読めます。
フルカラーで、色々とサンプルコードも多数載ってるので、楽しみながら読める本です。
解説もすごく詳しくて、いまいち仕組みが分からなかった部分とか、
アプリを作るときによく使いそうなTips、よく間違えそうな部分の解説とか、役に立つ情報がビッシリ詰まってます。
(こんな本が日本語でも出てほしい。。。。)
- 作者: Adam Nathan
- 出版社/メーカー: Sams
- 発売日: 2010/06/04
- メディア: ペーパーバック
- 購入: 3人 クリック: 13回
- この商品を含むブログ (4件) を見る
この本の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(); } } }