using System; using System.Runtime.InteropServices; using System.Windows; using System.Windows.Input; using System.Windows.Interop; using System.Windows.Media; using System.Windows.Media.Animation; namespace Developpez.Dotnet.Windows.Behaviors { /// /// Fournit des propriétés attachées permettant d'ajouter des fonctionnalités à une fenêtre /// public static class WindowBehavior { #region DragMove /// /// Indique si le déplacement automatique à la souris est activé pour la fenêtre /// /// La fenêtre pour laquelle on souhaite obtenir la valeur de la propriété /// true si le déplacement automatique à la souris est activé, sinon false. [AttachedPropertyBrowsableForType(typeof(Window))] public static bool GetEnableDragMove(Window window) { return (bool)window.GetValue(EnableDragMoveProperty); } /// /// Définit si le déplacement automatique à la souris est activé pour la fenêtre /// /// La fenêtre pour laquelle on souhaite définir la propriété /// true pour activer le déplacement automatique à la souris, false pour le désactiver public static void SetEnableDragMove(Window window, bool value) { window.SetValue(EnableDragMoveProperty, value); } /// /// Identifiant de la propriété EnableDragMove /// public static readonly DependencyProperty EnableDragMoveProperty = DependencyProperty.RegisterAttached( "EnableDragMove", typeof(bool), typeof(WindowBehavior), new UIPropertyMetadata(false, OnEnableDragMoveChanged)); static void OnEnableDragMoveChanged(DependencyObject o, DependencyPropertyChangedEventArgs e) { var w = o as Window; if (w == null) return; var ev = e.OfType(); if (ev.OldValue && !ev.NewValue) { w.MouseLeftButtonDown -= w_MouseLeftButtonDown; } if (!ev.OldValue && ev.NewValue) { w.MouseLeftButtonDown += w_MouseLeftButtonDown; } } static void w_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) { var w = sender as Window; if (w == null) return; w.DragMove(); } #endregion #region IsLoadHandlerAttached (private) private static bool GetIsLoadHandlerAttached(DependencyObject obj) { return (bool)obj.GetValue(IsLoadHandlerAttachedProperty); } private static void SetIsLoadHandlerAttached(DependencyObject obj, bool value) { obj.SetValue(IsLoadHandlerAttachedProperty, value); } private static readonly DependencyProperty IsLoadHandlerAttachedProperty = DependencyProperty.RegisterAttached("IsLoadHandlerAttached", typeof(bool), typeof(WindowBehavior), new UIPropertyMetadata(false)); private static void AttachLoadHandler(Window w) { if (!GetIsLoadHandlerAttached(w)) { w.Loaded += w_Loaded; w.Unloaded += w_Unloaded; SetIsLoadHandlerAttached(w, true); } } private static void DetachLoadHandler(Window w) { if (GetIsLoadHandlerAttached(w)) { w.Loaded -= w_Loaded; w.Unloaded -= w_Unloaded; SetIsLoadHandlerAttached(w, false); } } static void w_Unloaded(object sender, RoutedEventArgs e) { Window w = sender as Window; if (w != null) { DetachLoadHandler(w); } } static void w_Loaded(object sender, RoutedEventArgs e) { Window w = sender as Window; if (w != null) { ApplyGlassFrameMargins(w, GetGlassFrameMargins(w)); EnableBlur(w, GetEnableBlur(w)); } } #endregion #region GlassFrameMargins /// /// Obtient les marges de la bordure "Glass" /// /// La fenêtre cible /// Les marges de la bordure "Glass" [AttachedPropertyBrowsableForType(typeof(Window))] public static Thickness GetGlassFrameMargins(Window window) { return (Thickness)window.GetValue(GlassFrameMarginsProperty); } /// /// Définit les marges de la bordure "Glass" /// /// La fenêtre cible /// Les marges à appliquer /// Une bordure uniforme de -1 applique l'effet "Glass" à toute la fenêtre public static void SetGlassFrameMargins(Window window, Thickness value) { window.SetValue(GlassFrameMarginsProperty, value); } /// /// Identifiant de la propriété GlassFrameMargins /// public static readonly DependencyProperty GlassFrameMarginsProperty = DependencyProperty.RegisterAttached( "GlassFrameMargins", typeof(Thickness), typeof(WindowBehavior), new UIPropertyMetadata( new Thickness(), (o, e) => { Window w = o as Window; Thickness newValue = (Thickness)e.NewValue; if (w != null) { if (w.IsLoaded) { ApplyGlassFrameMargins(w, newValue); } else { AttachLoadHandler(w); } } })); private static readonly Thickness FullClientArea = new Thickness(-1); private static void ApplyGlassFrameMargins(Window window, Thickness margins) { if (IsCompositionEnabled()) { // Obtain the window handle for WPF application IntPtr hwnd = new WindowInteropHelper(window).Handle; HwndSource mainWindowSrc = HwndSource.FromHwnd(hwnd); mainWindowSrc.CompositionTarget.BackgroundColor = Color.FromArgb(0, 0, 0, 0); //Color bgColor = Color.FromArgb(0, 0, 0, 0); //if (window.Background is SolidColorBrush) // bgColor = (window.Background as SolidColorBrush).Color; //mainWindowSrc.CompositionTarget.BackgroundColor = bgColor; // Set Margins if (!margins.Equals(FullClientArea)) { System.Drawing.Graphics desktop = System.Drawing.Graphics.FromHwnd(hwnd); float desktopDpiX = desktop.DpiX; float desktopDpiY = desktop.DpiY; margins.Left *= desktopDpiX / 96; margins.Right *= desktopDpiX / 96; margins.Top *= desktopDpiY / 96; margins.Bottom *= desktopDpiY / 96; } Interop.Types.MARGINS mrg = new Interop.Types.MARGINS { Left = (int)margins.Left, Top = (int)margins.Top, Right = (int)margins.Right, Bottom = (int)margins.Bottom }; int hr = Interop.Functions.DwmExtendFrameIntoClientArea(mainWindowSrc.Handle, ref mrg); if (hr < 0) { Exception ex = Marshal.GetExceptionForHR(hr); throw ex; } } } #endregion #region EnableBlur /// /// Indique si l'effet "Blur" est appliqué à la fenêtre /// /// La fenêtre cible /// true si l'effet "Blur" est appliqué à la fenêtre, false sinon. [AttachedPropertyBrowsableForType(typeof(Window))] public static bool? GetEnableBlur(Window window) { return (bool?)window.GetValue(EnableBlurProperty); } /// /// Définit si l'effet "Blur" est appliqué à la fenêtre /// /// La fenêtre cible /// true si l'effet "Blur" doit être appliqué à la fenêtre, false sinon. public static void SetEnableBlur(Window window, bool? value) { window.SetValue(EnableBlurProperty, value); } /// /// Identifiant de la propriété EnableBlur /// public static readonly DependencyProperty EnableBlurProperty = DependencyProperty.RegisterAttached( "EnableBlur", typeof(bool?), typeof(WindowBehavior), new UIPropertyMetadata( null, (o, e) => { bool? newValue = (bool?)e.NewValue; Window w = o as Window; if (w != null) { if (w.IsLoaded && newValue.HasValue) { EnableBlur(w, newValue); } else { AttachLoadHandler(w); } } })); private static void EnableBlur(Window window, bool? enable) { if (enable.HasValue) { if (IsCompositionEnabled()) { IntPtr hwnd = new WindowInteropHelper(window).Handle; HwndSource mainWindowSrc = System.Windows.Interop.HwndSource.FromHwnd(hwnd); mainWindowSrc.CompositionTarget.BackgroundColor = Color.FromArgb(0, 0, 0, 0); Interop.Types.DWM_BLURBEHIND bb = new Interop.Types.DWM_BLURBEHIND(); bb.Enable = enable.Value; bb.BlurRegion = IntPtr.Zero; int hr = Interop.Functions.DwmEnableBlurBehindWindow(hwnd, ref bb); if (hr < 0) { Exception ex = Marshal.GetExceptionForHR(hr); throw ex; } } } } #endregion #region FadeOnClose #region Public DP accessors /// /// Indique si le fondu à la fermeture est activé pour la fenêtre /// /// La fenêtre pour laquelle on souhaite obtenir la valeur de la propriété /// true si le fondu à la fermeture est activé, sinon false [AttachedPropertyBrowsableForType(typeof(Window))] public static bool GetFadeOnClose(Window window) { return (bool)window.GetValue(FadeOnCloseProperty); } /// /// Définit si le fondu à la fermeture est activé pour la fenêtre /// /// La fenêtre pour laquelle on souhaite définir la propriété /// true pour activer le fondu à la fermeture, false pour le désactiver public static void SetFadeOnClose(Window window, bool value) { window.SetValue(FadeOnCloseProperty, value); } /// /// Obtient la durée du fondu pour la fenêtre /// /// La fenêtre pour laquelle on souhaite obtenir la valeur de la propriété /// La durée du fondu [AttachedPropertyBrowsableForType(typeof(Window))] public static Duration GetFadingDuration(Window window) { return (Duration)window.GetValue(FadingDurationProperty); } /// /// Définit la durée du fondu pour la fenêtre /// /// La fenêtre pour laquelle on souhaite définir la propriété /// La durée du fondu pour cette fenêtre public static void SetFadingDuration(Window window, Duration value) { window.SetValue(FadingDurationProperty, value); } #endregion #region Public DP /// /// Identifiant de la propriété attachée FadeOnClose /// public static readonly DependencyProperty FadeOnCloseProperty = DependencyProperty.RegisterAttached( "FadeOnClose", typeof(bool), typeof(WindowBehavior), new UIPropertyMetadata( false, FadeOnCloseChanged)); /// /// Identifiant de la propriété attachée FadingDuration /// public static readonly DependencyProperty FadingDurationProperty = DependencyProperty.RegisterAttached("FadingDuration", typeof(Duration), typeof(WindowBehavior), new UIPropertyMetadata(new Duration(TimeSpan.FromSeconds(1)))); #endregion #region Private members private static bool GetFadingComplete(DependencyObject obj) { return (bool)obj.GetValue(FadingCompleteProperty); } private static void SetFadingComplete(DependencyObject obj, bool value) { obj.SetValue(FadingCompleteProperty, value); } private static readonly DependencyProperty FadingCompleteProperty = DependencyProperty.RegisterAttached("FadingComplete", typeof(bool), typeof(WindowBehavior), new UIPropertyMetadata(false)); private static bool GetFadingStarted(DependencyObject obj) { return (bool)obj.GetValue(FadingStartedProperty); } private static void SetFadingStarted(DependencyObject obj, bool value) { obj.SetValue(FadingStartedProperty, value); } private static readonly DependencyProperty FadingStartedProperty = DependencyProperty.RegisterAttached("FadingStarted", typeof(bool), typeof(WindowBehavior), new UIPropertyMetadata(false)); private static void FadeOnCloseChanged(DependencyObject o, DependencyPropertyChangedEventArgs e) { var window = o as Window; if (window == null) return; var oldValue = (bool)e.OldValue; var newValue = (bool)e.NewValue; if (oldValue && !newValue) { } else if (newValue && !oldValue) { window.Closing += window_Closing; } } static void window_Closing(object sender, System.ComponentModel.CancelEventArgs e) { var window = sender as Window; if (window == null) return; if (!GetFadingStarted(window) && !GetFadingComplete(window)) { var animation = new DoubleAnimation(0, GetFadingDuration(window)); EventHandler handler = null; handler = delegate { SetFadingComplete(window, true); window.Close(); animation.Completed -= handler; }; animation.Completed += handler; SetFadingStarted(window, true); window.BeginAnimation(UIElement.OpacityProperty, animation); e.Cancel = true; } } #endregion #endregion #region Interop declarations /// /// Indique si la composition du bureau est activée. /// /// true si la composition du bureau est activée, sinon false. Renvoie toujours false pour les versions de Windows antérieures à Windows Vista. public static bool IsCompositionEnabled() { try { bool compositionEnabled = false; Interop.Functions.DwmIsCompositionEnabled(ref compositionEnabled); return compositionEnabled; } catch (DllNotFoundException) { return false; } } private static class Interop { public static class Types { [StructLayout(LayoutKind.Sequential)] public struct MARGINS { public MARGINS(int thickness) { Left = thickness; Right = thickness; Top = thickness; Bottom = thickness; } public int Left; // width of left border that retains its size public int Right; // width of right border that retains its size public int Top; // height of top border that retains its size public int Bottom; // height of bottom border that retains its size }; [StructLayout(LayoutKind.Sequential)] public struct DWM_BLURBEHIND { private uint _flags; public uint Flags { get { return _flags; } set { _flags = value; } } [MarshalAs(UnmanagedType.Bool)] private bool _enable; public bool Enable { get { return _enable; } set { _enable = value; _flags |= Constants.DWM_BB_ENABLE; } } private IntPtr _blurRegion; public IntPtr BlurRegion { get { return _blurRegion; } set { _blurRegion = value; _flags |= Constants.DWM_BB_BLURREGION; } } [MarshalAs(UnmanagedType.Bool)] private bool _transitionOnMaximized; public bool TransitionOnMaximized { get { return _transitionOnMaximized; } set { _transitionOnMaximized = value; _flags |= Constants.DWM_BB_TRANSITIONONMAXIMIZED; } } }; } public static class Functions { [DllImport("DwmApi.dll")] public static extern int DwmExtendFrameIntoClientArea(IntPtr hwnd, ref Types.MARGINS pMarInset); [DllImport("DwmApi.dll")] public static extern int DwmEnableBlurBehindWindow(IntPtr hwnd, ref Types.DWM_BLURBEHIND pBlurBehind); [DllImport("DwmApi.dll")] public static extern int DwmIsCompositionEnabled(ref bool enabled); } public static class Constants { public const uint DWM_BB_ENABLE = 0x00000001; public const uint DWM_BB_BLURREGION = 0x00000002; public const uint DWM_BB_TRANSITIONONMAXIMIZED = 0x00000004; } } #endregion } }