Friday, October 28, 2011

Write a Glass Window Behavior



The following effect can be achieved using WPF Shell Integration Library and WPF Customizable Window, the purpose of this article is to use the behavior and dwmapi to manager forms.

There are glass behavior in the Expression Gallery. See Link and Link.

By read his code, the author uses the ShaderEffectLibrary.

By reading this artile, we knows how to create a glass window. And by reading the discussion here, we know how to hide the close/max/min button in a form.

In this case my this article will talking about writing a behavior which can make a glass window, Like the following picture.





For .Net 4.0, we can use the code:

var window=new Window();

var handle=new WindowInteropHelper(window).EnsureHandle();

to make sure that the handle will be created. See the discussion here. So if it is .Net 4.0, to create the behavior is pretty simple.

You can download my code here.

or below:

namespace GlassWindowBehaviorTest

{ using System;

using System.Runtime.InteropServices;

using System.Windows;

using System.Windows.Interactivity;

using System.Windows.Interop;

using System.Windows.Media;

///

/// TODO: Update summary.

///

public class GlassWindowBehavior : Behavior<Window>

{

#region Constants and Fields

///

/// The gw l_ style.

///

private const int GWL_STYLE = -16;

///

/// The w s_ sysmenu.

///

private const int WS_SYSMENU = 0x80000;

#endregion

#region Public Methods

///

/// The create glass window.

///

///

/// The window.

///

public void CreateGlassWindow(Window window)

{

window.Background = new SolidColorBrush(Colors.Transparent);

IntPtr windowInteropHelper = new WindowInteropHelper(window).EnsureHandle();

IntPtr myHwnd = windowInteropHelper;

SetWindowLong(myHwnd, GWL_STYLE, GetWindowLong(myHwnd, GWL_STYLE) & ~WS_SYSMENU);

HwndSource mainWindowSrc = HwndSource.FromHwnd(myHwnd);

mainWindowSrc.CompositionTarget.BackgroundColor = Color.FromArgb(0, 0, 0, 0);

var margins = new Margins { cxLeftWidth = -1, cxRightWidth = -1, cyTopHeight = -1, cyBottomHeight = -1 };

DwmExtendFrameIntoClientArea(myHwnd, ref margins);

}

#endregion

#region Methods

///

/// The on attached.

///

protected override void OnAttached()

{

Window window = this.AssociatedObject;

this.AssociatedObject.Loaded += (s, e) => this.CreateGlassWindow(window);

base.OnAttached();

}

///

/// The on detaching.

///

protected override void OnDetaching()

{

base.OnDetaching();

}

///

/// The dwm extend frame into client area.

///

///

/// The hwnd.

///

///

/// The p mar inset.

///

///

/// The dwm extend frame into client area.

///

[DllImport("DwmApi.dll")]

private static extern int DwmExtendFrameIntoClientArea(IntPtr hwnd, ref Margins pMarInset);

///

/// The get window long.

///

///

/// The hwnd.

///

///

/// The n index.

///

///

/// The get window long.

///

[DllImport("user32.dll", SetLastError = true)]

private static extern int GetWindowLong(IntPtr hwnd, int nIndex);

///

/// The set window long.

///

///

/// The hwnd.

///

///

/// The n index.

///

///

/// The dw new long.

///

///

/// The set window long.

///

[DllImport("user32.dll")]

private static extern int SetWindowLong(IntPtr hwnd, int nIndex, int dwNewLong);

#endregion

///

/// The margins.

///

[StructLayout(LayoutKind.Sequential)]

private struct Margins

{

///

/// The cx left width.

///

public int cxLeftWidth;

///

/// The cx right width.

///

public int cxRightWidth;

///

/// The cy top height.

///

public int cyTopHeight;

///

/// The cy bottom height.

///

public int cyBottomHeight;

}

}

}

The problem is for the .net 3.5 or earlier version, there is no EnsureHandle method, WPF guru Pete Brown has a blog on this. I am going to follow his artile to re write my behavior class so that the behavior can be used in .net 3.5 version.


And on msdn,Alexander Yudakov wrote the extension method for EnsureHandle using reflection, this is very cool. So if we have .net 3.5 application, another way to make sure the handle is created, we can use his extension method.

using System.Reflection;
namespace System.Windows.Interop
{
///
/// Provides NetFX 4.0 EnsureHandle method for
/// NetFX 3.5 WindowInteropHelper class.
///

public static class WindowInteropHelperExtensions
{
///
/// Creates the HWND of the window if the HWND has not been created yet.
///

/// An instance of WindowInteropHelper class.
/// An IntPtr that represents the HWND.
/// Use the EnsureHandle method when you want to separate
/// window handle (HWND) creation from the
/// actual showing of the managed Window.

public static IntPtr EnsureHandle(this WindowInteropHelper helper)
{
if (helper == null)
throw new ArgumentNullException("helper");
if (helper.Handle == IntPtr.Zero)
{
var window = (Window)typeof(WindowInteropHelper).InvokeMember("_window",
BindingFlags.GetField | BindingFlags.Instance | BindingFlags.NonPublic,
null, helper, null);
typeof(Window).InvokeMember("SafeCreateWindow",
BindingFlags.InvokeMethod | BindingFlags.Instance | BindingFlags.NonPublic,
null, window, null);
}
return helper.Handle;
}
}
}
Finally what I built a photo frame like this:




No comments: