给WPF程序增加玻璃效果

在Vista中增加了一种Aero新界面,也就是我们所说的玻璃效果,这种效果比较酷,我也常常喜欢在自己写的小程序中加入这种效果。

    

实现这种效果并不难,也就是两个API,MSDN的文章将玻璃框扩展到 WPF 应用程序详细介绍了如何实现这种效果。在google上拿"C# DwmExtendFrameIntoClientArea"做关键字,也能搜到一大把。

我一般是用的CodeProject的文章Adding Glass Effect to WPF using Attached Properties上所述的那样,通过依赖属性注入这种效果,只要在窗口中加一句话src:GlassEffect.IsEnabled="True"即可使能这种效果,用起来非常方便。通过这种方式实现起来的效果如下:

    

看起来一起都非常简单,但这种方法只是仅仅实现了玻璃效果,还有几个不完善的地方:

  1. 这种方法只能实现全屏玻璃效果,不能实现部分玻璃效果。
  2. 实现玻璃效果后,我们往往还需要隐藏标题栏中的图标和文字。
  3. 那些玻璃化的部分不能像标题栏那样通过鼠标拖动窗口。

因此,我将那个代码改动了一下,增加了上述功能,代码如下:  

代码
    public class GlassInfo: System.Windows.Markup.MarkupExtension
    {
        
public int TopMargin { getset; }
        
public bool ShowIcon { getset; }
        
public bool ShowTitle { getset; }

        
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(0000);

                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 设置标题栏属性

        
/// <summary>
        
/// Do Not Draw The Caption (Text)
        
/// </summary>
        public static uint WTNCA_NODRAWCAPTION = 0x00000001;
        
/// <summary>
        
/// Do Not Draw the Icon
        
/// </summary>
        public static uint WTNCA_NODRAWICON = 0x00000002;
        
/// <summary>
        
/// Do Not Show the System Menu
        
/// </summary>
        public static uint WTNCA_NOSYSMENU = 0x00000004;
        
/// <summary>
        
/// Do Not Mirror the Question mark Symbol
        
/// </summary>
        public static uint WTNCA_NOMIRRORHELP = 0x00000008;

        
/// <summary>
        
/// The Options of What Attributes to Add/Remove
        
/// </summary>
        [StructLayout(LayoutKind.Sequential)]
        
struct WTA_OPTIONS
        {
            
public uint Flags;
            
public uint Mask;
        }

        
/// <summary>
        
/// What Type of Attributes? (Only One is Currently Defined)
        
/// </summary>
        enum WindowThemeAttributeType
        {
            WTA_NONCLIENT 
= 1,
        };

        
/// <summary>
        
/// Set The Window's Theme Attributes
        
/// </summary>
        
/// <param name="hWnd">The Handle to the Window</param>
        
/// <param name="wtype">What Type of Attributes</param>
        
/// <param name="attributes">The Attributes to Add/Remove</param>
        
/// <param name="size">The Size of the Attributes Struct</param>
        
/// <returns>If The Call Was Successful or Not</returns>
        [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}">

最终效果如下:

  

原文地址:https://www.cnblogs.com/TianFang/p/1669275.html