Last active
August 29, 2015 14:13
-
-
Save RichardD2/ef4ee339ce3dcc10264b to your computer and use it in GitHub Desktop.
WPF Aero Glass Effect
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
internal static class SafeNativeMethods | |
{ | |
[DllImport("dwmapi", SetLastError = true)] | |
private static extern int DwmIsCompositionEnabled([MarshalAs(UnmanagedType.Bool)] out bool isEnabled); | |
public static bool SafeDwmIsCompositionEnabled() | |
{ | |
bool result = false; | |
if (Environment.OSVersion.Version.Major >= 6) | |
{ | |
DwmIsCompositionEnabled(out result); | |
} | |
return result; | |
} | |
[StructLayout(LayoutKind.Sequential)] | |
private struct DwmMargins | |
{ | |
private readonly int Left; | |
private readonly int Right; | |
private readonly int Top; | |
private readonly int Bottom; | |
public DwmMargins(Thickness value) | |
{ | |
Left = (int)value.Left; | |
Right = (int)value.Right; | |
Top = (int)value.Top; | |
Bottom = (int)value.Bottom; | |
} | |
} | |
[DllImport("dwmapi", SetLastError = true)] | |
private static extern int DwmExtendFrameIntoClientArea(IntPtr hWnd, ref DwmMargins margins); | |
public static bool SafeDwmExtendFrameIntoClientArea(IntPtr hWnd, Thickness margins) | |
{ | |
if (hWnd == IntPtr.Zero) throw new ArgumentException("Invalid window handle.", "hWnd"); | |
bool result = false; | |
if (SafeDwmIsCompositionEnabled()) | |
{ | |
var dwmMargins = new DwmMargins(margins); | |
int hr = DwmExtendFrameIntoClientArea(hWnd, ref dwmMargins); | |
result = (hr == 0); | |
} | |
return result; | |
} | |
[Flags] | |
private enum DwmBlurBehindDwFlags : uint | |
{ | |
DWM_BB_ENABLE = 0x00000001, | |
DWM_BB_BLURREGION = 0x00000002, | |
} | |
[StructLayout(LayoutKind.Sequential)] | |
private struct DWM_BLURBEHIND | |
{ | |
public DwmBlurBehindDwFlags dwFlags; | |
public bool fEnable; | |
public IntPtr hRgnBlur; | |
private readonly bool fTransitionOnMaximized; | |
}; | |
[DllImport("dwmapi", SetLastError = true)] | |
private static extern int DwmEnableBlurBehindWindow(IntPtr hWnd, ref DWM_BLURBEHIND bb); | |
public static void SafeResetBlur(IntPtr hWnd) | |
{ | |
if (hWnd == IntPtr.Zero) throw new ArgumentException("Invalid window handle.", "hWnd"); | |
if (SafeDwmIsCompositionEnabled()) | |
{ | |
var blurBehind = new DWM_BLURBEHIND | |
{ | |
fEnable = false, | |
hRgnBlur = IntPtr.Zero, | |
dwFlags = DwmBlurBehindDwFlags.DWM_BB_ENABLE | DwmBlurBehindDwFlags.DWM_BB_BLURREGION | |
}; | |
DwmEnableBlurBehindWindow(hWnd, ref blurBehind); | |
} | |
} | |
} | |
public static class DesktopWindowManager | |
{ | |
private static readonly Thickness EmptyThickness = new Thickness(0); | |
private static readonly Thickness SheetOfGlass = new Thickness(-1); | |
public static bool IsDesktopCompositionEnabled | |
{ | |
get { return SafeNativeMethods.SafeDwmIsCompositionEnabled(); } | |
} | |
private static readonly DependencyProperty OriginalBackgroundProperty = DependencyProperty.RegisterAttached( | |
"OriginalBackground", typeof(Brush), typeof(DesktopWindowManager), | |
new PropertyMetadata(null)); | |
private static Brush GetOriginalBackground(DependencyObject element) | |
{ | |
if (element == null) throw new ArgumentNullException("element"); | |
return (Brush)element.GetValue(OriginalBackgroundProperty); | |
} | |
private static void SetOriginalBackground(DependencyObject element, Brush value) | |
{ | |
if (element == null) throw new ArgumentNullException("element"); | |
element.SetValue(OriginalBackgroundProperty, value); | |
} | |
internal static void ResetBlurCore(IntPtr hWnd) | |
{ | |
if (hWnd != IntPtr.Zero && IsDesktopCompositionEnabled) | |
{ | |
SafeNativeMethods.SafeResetBlur(hWnd); | |
} | |
} | |
public static void ResetBlur(Window window) | |
{ | |
if (window == null) throw new ArgumentNullException("window"); | |
ResetBlurCore(new WindowInteropHelper(window).Handle); | |
} | |
public static void ResetBlur(WindowInteropHelper window) | |
{ | |
if (window == null) throw new ArgumentNullException("window"); | |
ResetBlurCore(window.Handle); | |
} | |
public static void ResetBlur(IWin32Window window) | |
{ | |
if (window == null) throw new ArgumentNullException("window"); | |
ResetBlurCore(window.Handle); | |
} | |
internal static bool ResetFrameCore(IntPtr hWnd, Control window) | |
{ | |
if (hWnd == IntPtr.Zero) return false; | |
bool result = false; | |
if (IsDesktopCompositionEnabled) | |
{ | |
if (window != null) | |
{ | |
Brush background = GetOriginalBackground(window); | |
if (background != null) | |
{ | |
window.ClearValue(OriginalBackgroundProperty); | |
window.Background = background; | |
} | |
else | |
{ | |
window.ClearValue(Control.BackgroundProperty); | |
} | |
} | |
result = SafeNativeMethods.SafeDwmExtendFrameIntoClientArea(hWnd, EmptyThickness); | |
} | |
return result; | |
} | |
public static bool ResetFrame(Window window) | |
{ | |
if (window == null) throw new ArgumentNullException("window"); | |
IntPtr hWnd = new WindowInteropHelper(window).Handle; | |
return ResetFrameCore(hWnd, window); | |
} | |
public static bool ResetFrame(WindowInteropHelper window) | |
{ | |
if (window == null) throw new ArgumentNullException("window"); | |
return ResetFrameCore(window.Handle, null); | |
} | |
public static bool ResetFrame(IWin32Window window) | |
{ | |
if (window == null) throw new ArgumentNullException("window"); | |
return ResetFrameCore(window.Handle, null); | |
} | |
internal static bool ExtendFrameIntoClientAreaCore(IntPtr hWnd, Control window, Thickness margins) | |
{ | |
if (hWnd == IntPtr.Zero) return false; | |
bool result = false; | |
if (IsDesktopCompositionEnabled) | |
{ | |
if (window != null && !Brushes.Transparent.Equals(window.Background)) | |
{ | |
SetOriginalBackground(window, window.Background); | |
window.Background = Brushes.Transparent; | |
} | |
var source = HwndSource.FromHwnd(hWnd); | |
if (source != null && source.CompositionTarget != null) | |
{ | |
source.CompositionTarget.BackgroundColor = Colors.Transparent; | |
} | |
result = SafeNativeMethods.SafeDwmExtendFrameIntoClientArea(hWnd, margins); | |
} | |
return result; | |
} | |
public static bool ExtendFrameIntoClientArea(Window window, Thickness margins) | |
{ | |
if (window == null) throw new ArgumentNullException("window"); | |
IntPtr hWnd = new WindowInteropHelper(window).Handle; | |
return ExtendFrameIntoClientAreaCore(hWnd, window, margins); | |
} | |
public static bool ExtendFrameIntoClientArea(Window window) | |
{ | |
return ExtendFrameIntoClientArea(window, SheetOfGlass); | |
} | |
public static bool ExtendFrameIntoClientArea(WindowInteropHelper window, Thickness margins) | |
{ | |
if (window == null) throw new ArgumentNullException("window"); | |
return ExtendFrameIntoClientAreaCore(window.Handle, null, margins); | |
} | |
public static bool ExtendFrameIntoClientArea(WindowInteropHelper window) | |
{ | |
return ExtendFrameIntoClientArea(window, SheetOfGlass); | |
} | |
public static bool ExtendFrameIntoClientArea(IWin32Window window, Thickness margins) | |
{ | |
if (window == null) throw new ArgumentNullException("window"); | |
return ExtendFrameIntoClientAreaCore(window.Handle, null, margins); | |
} | |
public static bool ExtendFrameIntoClientArea(IWin32Window window) | |
{ | |
return ExtendFrameIntoClientArea(window, SheetOfGlass); | |
} | |
} | |
public static class Glass | |
{ | |
private static readonly Thickness DefaultThickness = new Thickness(-1, 0, 0, 0); | |
private static readonly DependencyPropertyKey IsAppliedPropertyKey = DependencyProperty.RegisterAttachedReadOnly( | |
"IsApplied", typeof(bool), typeof(Glass), | |
new FrameworkPropertyMetadata(false)); | |
public static readonly DependencyProperty IsAppliedProperty = IsAppliedPropertyKey.DependencyProperty; | |
[AttachedPropertyBrowsableForType(typeof(Window))] | |
public static bool GetIsApplied(Window element) | |
{ | |
if (element == null) throw new ArgumentNullException("element"); | |
return (bool)element.GetValue(IsAppliedProperty); | |
} | |
private static void SetIsApplied(Window element, bool value) | |
{ | |
if (element == null) throw new ArgumentNullException("element"); | |
element.SetValue(IsAppliedPropertyKey, value); | |
} | |
public static readonly DependencyProperty IsEnabledProperty = DependencyProperty.RegisterAttached( | |
"IsEnabled", typeof(bool), typeof(Glass), | |
new FrameworkPropertyMetadata(false, OnGlassChanged)); | |
[AttachedPropertyBrowsableForType(typeof(Window))] | |
public static bool GetIsEnabled(Window element) | |
{ | |
if (element == null) throw new ArgumentNullException("element"); | |
return (bool)element.GetValue(IsEnabledProperty); | |
} | |
public static void SetIsEnabled(Window element, bool value) | |
{ | |
if (element == null) throw new ArgumentNullException("element"); | |
element.SetValue(IsEnabledProperty, value); | |
} | |
public static readonly DependencyProperty FrameThicknessProperty = DependencyProperty.RegisterAttached( | |
"FrameThickness", typeof(Thickness), typeof(Glass), | |
new FrameworkPropertyMetadata(DefaultThickness, OnGlassChanged)); | |
[AttachedPropertyBrowsableForType(typeof(Window))] | |
public static Thickness GetFrameThickness(Window element) | |
{ | |
if (element == null) throw new ArgumentNullException("element"); | |
return (Thickness)element.GetValue(FrameThicknessProperty); | |
} | |
public static void SetFrameThickness(Window element, Thickness value) | |
{ | |
if (element == null) throw new ArgumentNullException("element"); | |
element.SetValue(FrameThicknessProperty, value); | |
} | |
private static void OnGlassChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) | |
{ | |
var window = d as Window; | |
if (window != null) Manager.UpdateGlassEffect(window); | |
} | |
private sealed class Manager | |
{ | |
private readonly Window _window; | |
private IntPtr _windowHandle; | |
private Manager(Window window) | |
{ | |
_window = window; | |
_window.SourceInitialized += WindowSourceInitialized; | |
WindowSourceInitialized(null, EventArgs.Empty); | |
} | |
private void WindowSourceInitialized(object sender, EventArgs e) | |
{ | |
_windowHandle = new WindowInteropHelper(_window).Handle; | |
UpdateGlassEffectCore(); | |
} | |
public static void UpdateGlassEffect(Window window) | |
{ | |
var manager = (Manager)window.GetValue(ManagerProperty); | |
if (manager == null) | |
{ | |
manager = new Manager(window); | |
window.SetValue(ManagerProperty, manager); | |
} | |
else | |
{ | |
manager.UpdateGlassEffectCore(); | |
} | |
} | |
private void UpdateGlassEffectCore() | |
{ | |
bool result = false; | |
if (_windowHandle != IntPtr.Zero && DesktopWindowManager.IsDesktopCompositionEnabled) | |
{ | |
DesktopWindowManager.ResetBlurCore(_windowHandle); | |
if (GetIsEnabled(_window)) | |
{ | |
result = DesktopWindowManager.ExtendFrameIntoClientAreaCore(_windowHandle, _window, GetFrameThickness(_window)); | |
} | |
else if (GetIsApplied(_window)) | |
{ | |
DesktopWindowManager.ResetFrameCore(_windowHandle, _window); | |
} | |
} | |
SetIsApplied(_window, result); | |
} | |
} | |
private static readonly DependencyProperty ManagerProperty = DependencyProperty.RegisterAttached( | |
"Manager", typeof(Manager), typeof(Glass), | |
new PropertyMetadata(null)); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment