5

How do I draw a smooth blurred-glass effect on a border-less Form? I have tried the code listed on Image Processing for Dummies with C and GDI+ page but I'm sure it's not what I should be using. No amount of playing around with it has yielded any kind of result that is what I'm after.

This is basically what I'm trying to achieve:

enter image description here

Flexo
  • 87,323
  • 22
  • 191
  • 272
jay_t55
  • 11,362
  • 28
  • 103
  • 174
  • Pretty odd that your original question was about entity frameworks and now it's about a gui interface with a +400 bounty on it. Why wouldn't you just write this as a new question? – LarsTech Sep 10 '14 at 15:18
  • @LarsTech, I did originally write a new post a couple days ago but when I refreshed the page it said it doesn't exist. So I tried again and when I finished and clicked "Ask Question" it just kept loading and never got anywhere so then I decided to just edit an old question that is basically crap and hasn't had any activity for a while and then put a bounty on this new question. – jay_t55 Sep 10 '14 at 15:22
  • Regardless of what else is going on please please don't totally change questions again in the future - it creates a bit of a mess and confuses things needlessly. – Flexo Sep 11 '14 at 11:07
  • (And FYI your attempt at asking this as a new question worked just fine: http://stackoverflow.com/questions/25720906/how-do-i-create-a-blurred-glass-effect-on-a-border-less-form) – Flexo Sep 11 '14 at 11:10
  • 1
    Do you care about the language? I have a C example – γηράσκω δ' αεί πολλά διδασκόμε Sep 11 '14 at 15:45
  • @valter I kind of do care about the language but since I am more comfortable with C/C++ I can learn from the C one and rewrite it in C#. So if you have anything in C it would be very helpful! – jay_t55 Sep 11 '14 at 23:49
  • You should really add the different windows versions you want this to run on: XP/Vista/7/8. Do you only need a solution for winforms or WPF? – ASA Sep 12 '14 at 09:01
  • Yep, a custom drawn effect. I didn't think windows versions was necessary because I indicated border less form, and did not mention Aero and gdi+ – jay_t55 Sep 12 '14 at 09:58

2 Answers2

1

I'll assume you are talking about Windows 7 / Vista, and you'd like to achieve the blurry transparent areas that some MS programs have in the same way. For the general case, you do need some image processing which I'm not going to cover.

For the case I mentioned above, you are not supposed to do this yourself - this is sort of re-inventing the wheel. Basically, you can use the window manager to achieve this effect (which is called aero glass) by using the methods described by this article: http://msdn.microsoft.com/en-us/magazine/cc163435.aspx

I currently have only a windows 8 machine (in which they cancelled this blurring and transparency by default) so I don't have a test environment to check this. I'll get to one later this week and set-up a sample code to do this,

Once you are using the desktop window manager, if you want just to blur the top part (like in your image), use DwmExtendFrameIntoClientArea to extend the frame (which is aero blurred by default) into your window. For a custom area, use DwmEnableBlurBehindWindow

So, in case this is really what you are looking for (solution for windows 7 / vista in the same way existing MS programs work), tell me and I'll update with code later. Otherwise, if you are looking for a general solution (not just windows vista / 7), tell me to save me the effort of coding this...


EDIT: Given the fact that you chose to manually make the effect, here is some basic code to get you started

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

Where does this still have bugs (on my testsuite in Windows 8.1)?

  • I'm still having troubles with transparency - windows which are semi-transparent inside their clipping region still get painted black
  • I have some unknown black windows (probably transparent) that still appear
  • The taskbar doesn't paint correctly
Barak Itkin
  • 4,872
  • 1
  • 22
  • 29
  • Thank you @Lightning I appreciate your effort. I hate to be pedantic but that's really not what I'm looking for. I don't want "Aero"... I want my own glass. And in Windows 8 and 8.1 you can't get the Aero glass effect it just remains opaque which is why I asked this question. I want a custom drawn glass effect. – jay_t55 Sep 10 '14 at 15:31
  • 1
    @baeltazor: ok, I had a suspicion that a 400+ bounty was not that easy. In that case, you basically need to gather a screenshot of all the windows under your own window and compose these as a bitmap. Once done, blur the bitmap and put it as the background of your own window. This is indeed much harder - I'll give it a shot, but I doubt I know enough to do it – Barak Itkin Sep 10 '14 at 15:43
  • Thanks so much @Lightning, I was thinking about just taking a "screenshot" of the area of the desktop directly beneath the window but I'm not sure if it only grabs the wallpaper or if it also bra a everything else that's there (like as you said, other windows, etc) – jay_t55 Sep 10 '14 at 15:46
  • 1
    @baeltazor: I added all my code for you to experiment with. I have the screenshot concept 70% implemented already. Hope this helps ;) – Barak Itkin Sep 13 '14 at 16:35
1

Winform

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:

enter image description here

Example 2: You can change the brush color (ex: DarkRed) to get nice effects

enter image description here

Thing to note here is that you can pick a region where this applies and you can have more then 1.

Wpf

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>

Native glass windows

Wpf support has been described here: http://www.paulrohde.com/native-glass-with-wpf/

I got following result with slightly different approach: enter image description here

Margus
  • 19,694
  • 14
  • 55
  • 103
  • How similar kind of elements are created: http://blogs.msdn.com/b/wpfsdk/archive/2008/09/08/custom-window-chrome-in-wpf.aspx – Margus Sep 12 '14 at 09:05