如何在无边框表单上绘制平滑的模糊玻璃效果?我已经尝试过使用 C 和 GDI+ 的傻瓜图像处理页面上列出的代码,但我确定这不是我应该使用的。再多的玩弄它并没有产生任何我所追求的结果。
这基本上是我想要实现的目标:
Using Win API like DwmEnableBlurBehindWindow
[DllImport("gdi32")]
private static extern IntPtr CreateEllipticRgn(int nLeftRect, int nTopRect, int nRightRect, int nBottomRect);
[DllImport("dwmapi")]
private static extern int DwmEnableBlurBehindWindow(IntPtr hWnd, ref DwmBlurbehind pBlurBehind);
public struct DwmBlurbehind
{
public int DwFlags;
public bool FEnable;
public IntPtr HRgnBlur;
public bool FTransitionOnMaximized;
}
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
var hr = CreateEllipticRgn(0, 0, Width, Height);
var dbb = new DwmBlurbehind {FEnable = true, DwFlags = 1, HRgnBlur = hr, FTransitionOnMaximized = false};
DwmEnableBlurBehindWindow(Handle, ref dbb);
}
protected override void OnPaint(PaintEventArgs e)
{
e.Graphics.FillRectangle(new SolidBrush(Color.Black), new Rectangle(0, 0, Width, Height));
}
Example 1:
Example 2: You can change the brush color (ex: DarkRed) to get nice effects
Thing to note here is that you can pick a region where this applies and you can have more then 1.
You can use the same approach.
Wpf has much better support for shaders and you can add effect like blur, but thy are more performance heavy. Also your image is most likely from a program created by WPF and Blend.
If i remember correctly you can not use shaders like blur on solid color brush. Therefor following would not give the same effect. Set background 50% see through (Window properties AllowsTransparency="True" and WindowStyle="None")
<Window.Background>
<SolidColorBrush Opacity="0.5" Color="White"/>
</Window.Background>
Wpf support has been described here: http://www.paulrohde.com/native-glass-with-wpf/
I got following result with slightly different approach:
我假设您正在谈论 Windows 7 / Vista,并且您希望以相同的方式实现某些 MS 程序具有的模糊透明区域。对于一般情况,您确实需要一些我不打算介绍的图像处理。
对于我上面提到的情况,你不应该自己做这件事——这有点像重新发明轮子。基本上,您可以通过使用本文描述的方法使用窗口管理器来实现此效果(称为航空玻璃):http: //msdn.microsoft.com/en-us/magazine/cc163435.aspx
我目前只有一台 Windows 8 机器(默认情况下他们取消了这种模糊和透明度),所以我没有测试环境来检查这一点。我将在本周晚些时候找到一个并设置一个示例代码来执行此操作,
使用桌面窗口管理器后,如果您只想模糊顶部(如图像中的部分),请使用 DwmExtendFrameIntoClientArea 将框架(默认为 aero 模糊)扩展到您的窗口中。对于自定义区域,使用 DwmEnableBlurBehindWindow
因此,如果这确实是您正在寻找的(Windows 7 / vista 的解决方案与现有 MS 程序的工作方式相同),请告诉我,我稍后会更新代码。否则,如果您正在寻找通用解决方案(不仅仅是 windows vista / 7),请告诉我省去我编写此代码的工作...
编辑:鉴于您选择手动制作效果,这里有一些基本代码可以帮助您入门
// Code was burrowed from:
// http://stackoverflow.com/questions/19867402/how-can-i-use-enumwindows-to-find-windows-with-a-specific-caption-title
// http://www.dotnetframework.org/default.aspx/4@0/4@0/DEVDIV_TFS/Dev10/Releases/RTMRel/ndp/fx/src/CommonUI/System/Drawing/NativeMethods@cs/1305376/NativeMethods@cs
// http://stackoverflow.com/questions/7292757/how-to-get-screenshot-of-a-window-as-bitmap-object-in-c
// http://stackoverflow.com/questions/798295/how-can-i-use-getnextwindow-in-c */
public static class WinAPI
{
public delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam);
[StructLayout(LayoutKind.Sequential)]
public struct RECT
{
public int left;
public int top;
public int right;
public int bottom;
};
public enum GW
{
GW_HWNDFIRST = 0,
GW_HWNDLAST = 1,
GW_HWNDNEXT = 2,
GW_HWNDPREV = 3,
GW_OWNER = 4,
GW_CHILD = 5,
GW_ENABLEDPOPUP = 6
}
[DllImport("User32.dll")]
public static extern bool EnumWindows(EnumWindowsProc lpEnumFunc, IntPtr lParam);
[DllImport("User32.dll", CharSet = CharSet.Unicode)]
public static extern int GetWindowText(IntPtr hWnd, StringBuilder strText, int maxCount);
[DllImport("User32.dll")]
public static extern int GetWindowTextLength(IntPtr hWnd);
[DllImport("User32.dll")]
public static extern bool IsWindowVisible(IntPtr hWnd);
[DllImport("User32.dll")]
public static extern bool IsIconic(IntPtr hWnd);
[DllImport("User32.dll")]
public static extern bool GetClientRect(IntPtr hWnd, ref RECT lpRect);
[DllImport("User32.dll")]
public static extern bool GetWindowRect(IntPtr hWnd, ref RECT lpRect);
[DllImport("User32.dll")]
public static extern IntPtr GetDesktopWindow();
[DllImport("User32.dll")]
public static extern IntPtr GetTopWindow(IntPtr hWnd);
public static IntPtr GetNextWindow(IntPtr hWnd, GW wCmd)
{
return GetWindow(hWnd, wCmd);
}
public static IntPtr GetWindow(IntPtr hWnd, GW wCmd)
{
return GetWindow(hWnd, (uint)wCmd);
}
[DllImport("User32.dll")]
private static extern IntPtr GetWindow(IntPtr hWnd, uint wCmd);
[DllImport("User32.dll")]
public static extern bool PrintWindow(IntPtr hWnd, IntPtr hdcBlt, uint nFlags);
[DllImport("User32.dll")]
public static extern IntPtr GetDC(IntPtr hWnd);
[DllImport("Gdi32.dll")]
public static extern bool DeleteDC(IntPtr hdc);
[DllImport("User32.dll")]
public static extern IntPtr GetWindowDC(IntPtr hWnd);
[DllImport("User32.dll")]
public static extern int ReleaseDC(IntPtr hWnd, IntPtr hdc);
[DllImport("User32.dll")]
public static extern int GetWindowRgn(IntPtr hWnd, IntPtr hRgn);
[DllImport("Gdi32.dll")]
public static extern IntPtr CreateCompatibleDC(IntPtr hWnd);
[DllImport("Gdi32.dll")]
public static extern IntPtr CreateCompatibleBitmap(IntPtr hdc, int width, int height);
[DllImport("Gdi32.dll")]
public static extern IntPtr CreateBitmap(int width, int height, uint cPlanes, uint cBitsPerPel, IntPtr lpvBits);
[DllImport("Gdi32.dll")]
public static extern bool DeleteObject(IntPtr hGdiObj);
[DllImport("Gdi32.dll")]
public static extern bool SelectObject(IntPtr hdc, IntPtr hGdiObj);
[DllImport("Gdi32.dll")]
public static extern IntPtr CreateRectRgn(int nLeftRect, int nTopRect, int nRightRect, int nBottomRect);
public static StringBuilder GetWindowText(IntPtr hWnd)
{
int length = GetWindowTextLength(hWnd);
// Add another place to allow null terminator
StringBuilder text = new StringBuilder(length + 1);
GetWindowText(hWnd, text, length + 1);
return text;
}
public static bool IsWindowReallyVisible(IntPtr hWnd)
{
if (!IsWindowVisible(hWnd) || IsIconic(hWnd))
return false;
RECT area = new RECT();
if (!GetWindowRect(hWnd, ref area))
return true;
if (area.left == area.right || area.bottom == area.top)
return false;
return true;
}
public enum RegionFlags
{
ERROR = 0,
NULLREGION = 1,
SIMPLEREGION = 2,
COMPLEXREGION = 3,
}
}
public class ScreenShot
{
public static List<IntPtr> GetAllWindows(Func<IntPtr, bool> filter, Func<IntPtr, bool> stop)
{
List<IntPtr> result = new List<IntPtr>();
WinAPI.EnumWindows((wnd, param) =>
{
bool relevant = filter(wnd);
if (relevant)
result.Add(wnd);
bool shouldStop = stop(wnd);
if (shouldStop)
Console.WriteLine("Stop");
return !shouldStop;
}, IntPtr.Zero);
return result;
}
public static IEnumerable<IntPtr> GetWindowsByOrder(Func<IntPtr, bool> filter)
{
List<IntPtr> skip = new List<IntPtr>();
List<IntPtr> result = new List<IntPtr>();
IntPtr desktop = WinAPI.GetDesktopWindow();
for (IntPtr wnd = WinAPI.GetTopWindow(IntPtr.Zero); wnd != IntPtr.Zero; wnd = WinAPI.GetNextWindow(wnd, WinAPI.GW.GW_HWNDNEXT))
{
if (result.Contains(wnd) || skip.Contains(wnd))
break;
else if (filter(wnd))
result.Add(wnd);
else
skip.Add(wnd);
}
result.Add(desktop);
return result;
}
public static Image GetScreenshot(IntPtr hWnd)
{
IntPtr hdcScreen = WinAPI.GetDC(hWnd);
IntPtr hdc = WinAPI.CreateCompatibleDC(hdcScreen);
WinAPI.RECT area = new WinAPI.RECT();
WinAPI.GetWindowRect(hWnd, ref area);
IntPtr hBitmap = WinAPI.CreateCompatibleBitmap(hdcScreen, area.right - area.left, area.bottom - area.top);
WinAPI.SelectObject(hdc, hBitmap);
WinAPI.PrintWindow(hWnd, hdc, 0);
Image resultOpaque = Image.FromHbitmap(hBitmap);
WinAPI.DeleteObject(hBitmap);
WinAPI.DeleteDC(hdc);
IntPtr hRegion = WinAPI.CreateRectRgn(0, 0, 0, 0);
WinAPI.RegionFlags f = (WinAPI.RegionFlags) WinAPI.GetWindowRgn(hWnd, hRegion);
Region region = Region.FromHrgn(hRegion);
WinAPI.DeleteObject(hRegion);
Bitmap result = new Bitmap(resultOpaque.Width, resultOpaque.Height, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
Graphics g = Graphics.FromImage(result);
g.Clear(Color.Transparent);
if (f != WinAPI.RegionFlags.ERROR)
g.SetClip(region, System.Drawing.Drawing2D.CombineMode.Replace);
if (f != WinAPI.RegionFlags.NULLREGION)
g.DrawImageUnscaled(resultOpaque, 0, 0);
g.Dispose();
region.Dispose();
resultOpaque.Dispose();
return result;
}
/* And now for the actual code of getting screenshots of windows */
var windows = ScreenShot.GetWindowsByOrder(this.WindowFilter).Intersect(ScreenShot.GetAllWindows(this.WindowFilter, (wnd) => false)).ToList();
int index = windows.IndexOf((IntPtr)this.Handle); /* Remove all the windows behind your windows */
if (index != -1)
windows.RemoveRange(index, windows.Count - index + 1);
windows.Reverse();
/* Get the images of all the windows */
for (int i = 0; i < windows.Count; ++i )
{
var window = windows[i];
using (var img = ScreenShot.GetScreenshot(window))
{
// Get the actual position of the window
// Draw it's image overlayed on the other windows 9have an accumulating image)
}
}
这在哪里仍然有错误(在我的 Windows 8.1 中的测试套件上)?