Making some awesome UI’s with WPF

I’ve been messing with WPF for sometime porting my application from Win32 to WPF.  I’ve transformed into a lover of WPF since you can do some extremely nice UIs with just using a text editor and quickly alter how they look.  Now here are some key things about this.  I used Aero glass, a drop shadow effect, and did everything here using just notepad.   Now it is resizable and stuff stays where it should.  This UI only took me about an hour to fully crank together, another additional hour to refactor bits and pieces.  Simple but good looking in my opinion.

First, you’ll set up a grid to keep everything.

<Grid>
		<Grid.RowDefinitions>
			<RowDefinition Height="40"/>
			<RowDefinition Height="*"/>
			<RowDefinition Height="*"/>
			<RowDefinition Height="20"/>
		</Grid.RowDefinitions>
		<Grid.ColumnDefinitions>
			<ColumnDefinition Width="*"/>
			<ColumnDefinition Width="*"/>
		</Grid.ColumnDefinitions>
	<!-- Stuff goes in here! -->
</Grid>

This gives me the nice little blue lines.

image

But we have the effect and we want to use the same look and feel on everything.  We can do this by adding stuff to our App.xaml file.

<Application.Resources>
	<Style x:Key="glowEffect" TargetType="TextBlock">
		<Setter Property="Foreground" Value="#CCFFFF" />
		<Setter Property="Effect">
			<Setter.Value>
				<DropShadowEffect ShadowDepth="0" Color="#000" BlurRadius="5" />
			</Setter.Value>
		</Setter>
	</Style>
</Application.Resources>

Now anything that I give the style of glowEffect to will have the same foreground solid brush and the Drop Shadow Effect.  You’ll see how I had to use a Setter.Value so I could use a resource file to apply an effect to the style too.

The one bummer with WPF is you can’t set multiple styles to an element BUT you can do use the BaseOn with a different Style so you can create derivative styles.  This is nice but I think you can easily get into nesting issues here.  I’d rather have the ability to set multiple styles on the same element.

Since we have a grid, I just need to declare the row and column I’ll use and bam, I’m good to go.  I’ll use the code sample for the bottom right corner.  I used a StackPanel to keep everything nice and orderly.  You also can see how I used the VerticalAlignment and HorizontalAlignment to keep items where I want them instead of the center.

<StackPanel VerticalAlignment="Top" HorizontalAlignment="Left" Margin="10" Grid.Row="2"  Grid.Column="1">
	<Button Name="d" Width="200" Height="200">
		x
	</Button>
	<TextBlock Style="{StaticResource glowEffect}" FontSize="15" TextAlignment="Right">
		Search
	</TextBlock>
</StackPanel>

With some additional cut and copying, I get the look I want BUT I don’t get that sexy glass look I get with Aero.  So what I did was create a helper class to get this since I have to do some pinvoke goodness.

using System;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Interop;
using System.Windows.Media;

namespace Drunktender.Wpf.Classes
{
	public class WpfHelper
	{
		[StructLayout(LayoutKind.Sequential)]
		private struct Margins
		{
			public int cxLeftWidth;
			public int cxRightWidth;
			public int cyTopHeight;
			public int cyBottomHeight;
		}

		[DllImport("DwmApi.dll")]
		private static extern int DwmExtendFrameIntoClientArea(IntPtr hwnd, ref Margins pMarInset);

		[DllImport("dwmapi.dll", PreserveSig = false)]
		static extern bool DwmIsCompositionEnabled();
		
		public static void MakeGlass(Window window)
		{
			var originalBackground = window.Background;

			try
			{
				var mainWindowSrc = HwndSource.FromHwnd(new WindowInteropHelper(window).Handle);
				if (mainWindowSrc == null || !DwmIsCompositionEnabled())
					return;

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

				window.Background = Brushes.Transparent;
				mainWindowSrc.CompositionTarget.BackgroundColor = Color.FromArgb(0, 0, 0, 0);
				DwmExtendFrameIntoClientArea(mainWindowSrc.Handle, ref margins);
			}
			// If not Vista, paint background white.
			catch (DllNotFoundException)
			{
				window.Background = originalBackground;
			}
		}
	}
}

As you can see, the function is static so I can call it in the OnLoad event with a single line.  The Code Project file shows you how to do it with an Property which I think is pretty neat.  Chances are I’ll switch to that since I like the WPF idea of being able to do most stuff via XAML instead of code behind for user interface look/feel.

WpfHelper.MakeGlass(this);

Do all that and you’ll get something that looks like this.  This is roughly what it will look like after you logged on to the system.  I still need to tweak the buttons but it is coming along nicely.

image

Resources used:

No comments posted yet.

Post a Comment

Please add 8 and 6 and type the answer here: