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
}
}