在Vista中增加了一种Aero新界面,也就是我们所说的玻璃效果,这种效果比较酷,我也常常喜欢在自己写的小程序中加入这种效果。
实现这种效果并不难,也就是两个API,MSDN的文章将玻璃框扩展到 WPF 应用程序详细介绍了如何实现这种效果。在google上拿"C# DwmExtendFrameIntoClientArea"做关键字,也能搜到一大把。
我一般是用的CodeProject的文章Adding Glass Effect to WPF using Attached Properties上所述的那样,通过依赖属性注入这种效果,只要在窗口中加一句话src:GlassEffect.IsEnabled="True"即可使能这种效果,用起来非常方便。通过这种方式实现起来的效果如下:
看起来一起都非常简单,但这种方法只是仅仅实现了玻璃效果,还有几个不完善的地方:
-
这种方法只能实现全屏玻璃效果,不能实现部分玻璃效果。
-
实现玻璃效果后,我们往往还需要隐藏标题栏中的图标和文字。
-
那些玻璃化的部分不能像标题栏那样通过鼠标拖动窗口。
因此,我将那个代码改动了一下,增加了上述功能,代码如下:
代码
public
class
GlassInfo: System.Windows.Markup.MarkupExtension
{
public int TopMargin { get ; set ; }
public bool ShowIcon { get ; set ; }
public bool ShowTitle { get ; set ; }
public GlassInfo()
{
TopMargin = - 1 ;
ShowIcon = true ;
ShowTitle = true ;
}
public override object ProvideValue(IServiceProvider serviceProvider)
{
return this ;
}
}
public class GlassEffect
{
public static GlassInfo GetInfo(DependencyObject obj)
{
return (GlassInfo)obj.GetValue(InfoProperty);
}
public static void SetInfo(DependencyObject obj, GlassInfo value)
{
obj.SetValue(InfoProperty, value);
}
public static readonly DependencyProperty InfoProperty =
DependencyProperty.RegisterAttached( " Info " , typeof (GlassInfo), typeof (GlassEffect), new FrameworkPropertyMetadata(OnIsEnabledChanged));
public static void OnIsEnabledChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
{
if (System.ComponentModel.DesignerProperties.GetIsInDesignMode(obj))
return ;
(obj as Window).Loaded += (s, e) => ApplyGlassEffect(obj as Window, args.NewValue as GlassInfo);
EnableMouseDrag(obj as Window,(args.NewValue as GlassInfo).TopMargin);
}
static void ApplyGlassEffect(Window window, GlassInfo info)
{
if ( ! DwmIsCompositionEnabled())
return ;
var originalBackground = window.Background;
window.Background = Brushes.Transparent;
try
{
IntPtr mainWindowPtr = new WindowInteropHelper(window).Handle;
HwndSource mainWindowSrc = HwndSource.FromHwnd(mainWindowPtr);
mainWindowSrc.CompositionTarget.BackgroundColor = Color.FromArgb( 0 , 0 , 0 , 0 );
var margins = info.TopMargin == - 1 ? new MARGINS( - 1 , - 1 , - 1 , - 1 ) : new MARGINS { Top = info.TopMargin };
DwmExtendFrameIntoClientArea(mainWindowPtr, ref margins);
ApplyWindowThemeAttribute(info, mainWindowPtr);
}
catch (DllNotFoundException)
{
window.Background = originalBackground;
}
}
static void ApplyWindowThemeAttribute(GlassInfo info, IntPtr mainWindowPtr)
{
WTA_OPTIONS ops = new WTA_OPTIONS();
// We Want To Hide the Caption and the Icon
ops.Flags = WTNCA_NODRAWCAPTION | WTNCA_NODRAWICON | WTNCA_NOSYSMENU;
// If we set the Mask to the same value as the Flags, the Flags are Added. If Not They are Removed
if ( ! info.ShowTitle)
{
ops.Mask |= WTNCA_NODRAWCAPTION;
}
if ( ! info.ShowIcon)
{
ops.Mask |= WTNCA_NODRAWICON | WTNCA_NOSYSMENU;
}
// Set It, The Marshal.Sizeof() stuff is to get the right size of the custom struct, and in UINT/DWORD Form
SetWindowThemeAttribute(mainWindowPtr, WindowThemeAttributeType.WTA_NONCLIENT,
ref ops, ( uint )Marshal.SizeOf( typeof (WTA_OPTIONS)));
}
#region 设置任意位置拖动效果API
const int WM_NCLBUTTONDOWN = 0xA1 ;
const int HT_CAPTION = 0x2 ;
[DllImportAttribute( " user32.dll " )]
static extern int SendMessage(IntPtr hWnd,
int Msg, int wParam, int lParam);
[DllImportAttribute( " user32.dll " )]
static extern bool ReleaseCapture();
static void EnableMouseDrag(Window window, int topMargin)
{
window.MouseLeftButtonDown += (s, e) =>
{
var win = s as Window;
var point = e.GetPosition(win);
if (point.Y > topMargin)
return ;
Console.WriteLine(e.OriginalSource);
var mainWindowPtr = new WindowInteropHelper(win).Handle;
ReleaseCapture();
SendMessage(mainWindowPtr, WM_NCLBUTTONDOWN, HT_CAPTION, 0 );
};
}
#endregion
#region 设置玻璃效果API
[StructLayout(LayoutKind.Sequential)]
struct MARGINS
{
public int Left;
public int Right;
public int Top;
public int Bottom;
public MARGINS( int left, int right, int top, int bottom)
{
this .Left = left;
this .Right = right;
this .Top = top;
this .Bottom = bottom;
}
public MARGINS(Thickness margin)
{
this .Left = ( int )margin.Left;
this .Right = ( int )margin.Right;
this .Top = ( int )margin.Top;
this .Bottom = ( int )margin.Bottom;
}
};
[DllImport( " DwmApi.dll " )]
static extern int DwmExtendFrameIntoClientArea(IntPtr hwnd, ref MARGINS pMarInset);
[DllImport( " dwmapi.dll " , PreserveSig = false )]
static extern bool DwmIsCompositionEnabled();
public static readonly DependencyProperty IsEnabledProperty =
DependencyProperty.RegisterAttached( " IsEnabled " ,
typeof (Boolean),
typeof (GlassEffect),
new FrameworkPropertyMetadata(OnIsEnabledChanged));
#endregion
#region 设置标题栏属性
///
/// Do Not Draw The Caption (Text)
///
public static uint WTNCA_NODRAWCAPTION = 0x00000001 ;
///
/// Do Not Draw the Icon
///
public static uint WTNCA_NODRAWICON = 0x00000002 ;
///
/// Do Not Show the System Menu
///
public static uint WTNCA_NOSYSMENU = 0x00000004 ;
///
/// Do Not Mirror the Question mark Symbol
///
public static uint WTNCA_NOMIRRORHELP = 0x00000008 ;
///
/// The Options of What Attributes to Add/Remove
///
[StructLayout(LayoutKind.Sequential)]
struct WTA_OPTIONS
{
public uint Flags;
public uint Mask;
}
///
/// What Type of Attributes? (Only One is Currently Defined)
///
enum WindowThemeAttributeType
{
WTA_NONCLIENT = 1 ,
};
///
/// Set The Window's Theme Attributes
///
/// The Handle to the Window
/// What Type of Attributes
/// The Attributes to Add/Remove
/// The Size of the Attributes Struct
///
If The Call Was Successful or Not
[DllImport( " UxTheme.dll " )]
static extern int SetWindowThemeAttribute(IntPtr hWnd, WindowThemeAttributeType wtype, ref WTA_OPTIONS attributes, uint size);
#endregion
}
{
public int TopMargin { get ; set ; }
public bool ShowIcon { get ; set ; }
public bool ShowTitle { get ; set ; }
public GlassInfo()
{
TopMargin = - 1 ;
ShowIcon = true ;
ShowTitle = true ;
}
public override object ProvideValue(IServiceProvider serviceProvider)
{
return this ;
}
}
public class GlassEffect
{
public static GlassInfo GetInfo(DependencyObject obj)
{
return (GlassInfo)obj.GetValue(InfoProperty);
}
public static void SetInfo(DependencyObject obj, GlassInfo value)
{
obj.SetValue(InfoProperty, value);
}
public static readonly DependencyProperty InfoProperty =
DependencyProperty.RegisterAttached( " Info " , typeof (GlassInfo), typeof (GlassEffect), new FrameworkPropertyMetadata(OnIsEnabledChanged));
public static void OnIsEnabledChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
{
if (System.ComponentModel.DesignerProperties.GetIsInDesignMode(obj))
return ;
(obj as Window).Loaded += (s, e) => ApplyGlassEffect(obj as Window, args.NewValue as GlassInfo);
EnableMouseDrag(obj as Window,(args.NewValue as GlassInfo).TopMargin);
}
static void ApplyGlassEffect(Window window, GlassInfo info)
{
if ( ! DwmIsCompositionEnabled())
return ;
var originalBackground = window.Background;
window.Background = Brushes.Transparent;
try
{
IntPtr mainWindowPtr = new WindowInteropHelper(window).Handle;
HwndSource mainWindowSrc = HwndSource.FromHwnd(mainWindowPtr);
mainWindowSrc.CompositionTarget.BackgroundColor = Color.FromArgb( 0 , 0 , 0 , 0 );
var margins = info.TopMargin == - 1 ? new MARGINS( - 1 , - 1 , - 1 , - 1 ) : new MARGINS { Top = info.TopMargin };
DwmExtendFrameIntoClientArea(mainWindowPtr, ref margins);
ApplyWindowThemeAttribute(info, mainWindowPtr);
}
catch (DllNotFoundException)
{
window.Background = originalBackground;
}
}
static void ApplyWindowThemeAttribute(GlassInfo info, IntPtr mainWindowPtr)
{
WTA_OPTIONS ops = new WTA_OPTIONS();
// We Want To Hide the Caption and the Icon
ops.Flags = WTNCA_NODRAWCAPTION | WTNCA_NODRAWICON | WTNCA_NOSYSMENU;
// If we set the Mask to the same value as the Flags, the Flags are Added. If Not They are Removed
if ( ! info.ShowTitle)
{
ops.Mask |= WTNCA_NODRAWCAPTION;
}
if ( ! info.ShowIcon)
{
ops.Mask |= WTNCA_NODRAWICON | WTNCA_NOSYSMENU;
}
// Set It, The Marshal.Sizeof() stuff is to get the right size of the custom struct, and in UINT/DWORD Form
SetWindowThemeAttribute(mainWindowPtr, WindowThemeAttributeType.WTA_NONCLIENT,
ref ops, ( uint )Marshal.SizeOf( typeof (WTA_OPTIONS)));
}
#region 设置任意位置拖动效果API
const int WM_NCLBUTTONDOWN = 0xA1 ;
const int HT_CAPTION = 0x2 ;
[DllImportAttribute( " user32.dll " )]
static extern int SendMessage(IntPtr hWnd,
int Msg, int wParam, int lParam);
[DllImportAttribute( " user32.dll " )]
static extern bool ReleaseCapture();
static void EnableMouseDrag(Window window, int topMargin)
{
window.MouseLeftButtonDown += (s, e) =>
{
var win = s as Window;
var point = e.GetPosition(win);
if (point.Y > topMargin)
return ;
Console.WriteLine(e.OriginalSource);
var mainWindowPtr = new WindowInteropHelper(win).Handle;
ReleaseCapture();
SendMessage(mainWindowPtr, WM_NCLBUTTONDOWN, HT_CAPTION, 0 );
};
}
#endregion
#region 设置玻璃效果API
[StructLayout(LayoutKind.Sequential)]
struct MARGINS
{
public int Left;
public int Right;
public int Top;
public int Bottom;
public MARGINS( int left, int right, int top, int bottom)
{
this .Left = left;
this .Right = right;
this .Top = top;
this .Bottom = bottom;
}
public MARGINS(Thickness margin)
{
this .Left = ( int )margin.Left;
this .Right = ( int )margin.Right;
this .Top = ( int )margin.Top;
this .Bottom = ( int )margin.Bottom;
}
};
[DllImport( " DwmApi.dll " )]
static extern int DwmExtendFrameIntoClientArea(IntPtr hwnd, ref MARGINS pMarInset);
[DllImport( " dwmapi.dll " , PreserveSig = false )]
static extern bool DwmIsCompositionEnabled();
public static readonly DependencyProperty IsEnabledProperty =
DependencyProperty.RegisterAttached( " IsEnabled " ,
typeof (Boolean),
typeof (GlassEffect),
new FrameworkPropertyMetadata(OnIsEnabledChanged));
#endregion
#region 设置标题栏属性
///
/// Do Not Draw The Caption (Text)
///
public static uint WTNCA_NODRAWCAPTION = 0x00000001 ;
///
/// Do Not Draw the Icon
///
public static uint WTNCA_NODRAWICON = 0x00000002 ;
///
/// Do Not Show the System Menu
///
public static uint WTNCA_NOSYSMENU = 0x00000004 ;
///
/// Do Not Mirror the Question mark Symbol
///
public static uint WTNCA_NOMIRRORHELP = 0x00000008 ;
///
/// The Options of What Attributes to Add/Remove
///
[StructLayout(LayoutKind.Sequential)]
struct WTA_OPTIONS
{
public uint Flags;
public uint Mask;
}
///
/// What Type of Attributes? (Only One is Currently Defined)
///
enum WindowThemeAttributeType
{
WTA_NONCLIENT = 1 ,
};
///
/// Set The Window's Theme Attributes
///
/// The Handle to the Window
/// What Type of Attributes
/// The Attributes to Add/Remove
/// The Size of the Attributes Struct
///
[DllImport( " UxTheme.dll " )]
static extern int SetWindowThemeAttribute(IntPtr hWnd, WindowThemeAttributeType wtype, ref WTA_OPTIONS attributes, uint size);
#endregion
}
使用示例如下:
<Window x:Class="WpfApplication.MainWindow"
… …
src:GlassEffect.Info="{util:GlassInfo TopMargin=32, ShowIcon=False, ShowTitle=False}">
最终效果如下: