June 2009 Entries

WPF StaticResource vs DynamicResource

In the process of creating any program, you’ll need to refactor.  As I’m a newbie with WPF and XAML, I’m still learning the in’s and out’s and came across an interesting issue with the designer while refactoring bits of the application to controls.

I have put some resources in my App.xaml.

<Application x:Class="Drunktender.Wpf.App"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    StartupUri="Window1.xaml">
    <Application.Resources>
		<Style x:Key="glowEffect" TargetType="TextBlock">
			<Setter Property="Foreground" Value="#fff" />
			<Setter Property="Effect">
				<Setter.Value>
					<DropShadowEffect ShadowDepth="0" Color="#39f" BlurRadius="4" />
				</Setter.Value>
			</Setter>
		</Style>
		<Style x:Key="glowEffectOnGlass" TargetType="Grid">
			<Setter Property="Effect">
				<Setter.Value>
					<DropShadowEffect ShadowDepth="0" Color="#fff" BlurRadius="5" />
				</Setter.Value>
			</Setter>
		</Style>
	</Application.Resources>
</Application>

And to reference them, you’ll typically do something like this.

<TextBlock Name="textBlock" FontSize="30" Style="{StaticResource glowEffect}">
	Clint is awesomer (true story).
</TextBlock>

Now this works great if you’re in a Window, not a user control.  As soon as this is moved into a user control you’ll get this ever so helpful error in Visual Studio’s design view.  Clicking Reload, no matter how many times will give the same error.

image

And in your error list, you’ll see the following: “Could not create an instance of type 'LoginScreen'.”

To fix this, change StaticResource to DynamicResource and recompile.  One thing also to pay attention to is the little things here as well.  I have a storyboard in a user control that made a StaticResource to itself, this too had to be shifted to a DynamicResource.  Note the “BeginStoryboard”

<UserControl x:Class="Drunktender.Wpf.Controls.LoginScreen"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
   <UserControl.Resources>
		<Storyboard x:Key="textGlowAnim" AutoReverse="True" RepeatBehavior="Forever">
			<DoubleAnimationUsingKeyFrames 
					BeginTime="00:00:00" 
					Storyboard.TargetName="textBlock" 
					Storyboard.TargetProperty="(UIElement.Effect).(DropShadowEffect.BlurRadius)">
				<SplineDoubleKeyFrame  KeyTime="00:00:01" Value="10"/>
			</DoubleAnimationUsingKeyFrames>
		</Storyboard>
	</UserControl.Resources>
	<UserControl.Triggers>
		<EventTrigger RoutedEvent="FrameworkElement.Loaded">
			<BeginStoryboard Storyboard="{DynamicResource textGlowAnim}"/>
		</EventTrigger>
	</UserControl.Triggers>
	<Grid>
		<TextBlock Name="textBlock" FontSize="30" Style="{DynamicResource glowEffect}">
			Use your card to log into the system
		</TextBlock>
	</Grid>
</UserControl>

I don’t know if this will fix everyone’s issue with this error all the time but it fixed mine.

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:

Hardware Complete? The magic is in the software

I’m hardware complete now.  I’ve wired up all 10 of the valves, hooked in the liquid and pressure tubing.  I even added in a pressure sensor to monitor what the pressure is in real time!

Wired up

Now it is time for some software magic.  With WPF, I’m given some pretty nice power to do some stuff like below.  I think the learning curve is well worth it once you start tapping into the power of it.  My “Big Button” is functionally complete, moved into a user control, and now I can easily put code and get it to work.  Here is a quick screen shot of it actually pulling data from the database with correct glasses.

image

One thing I had to do that I was semi-surprised wasn’t in the framework was a string hex value to Color function.  So I had to roll my own.  Basically you can have short hand and long hand hex values for color.  Since a color can be represented in alpha (transparency), red, green, and blue, we must accommodate for this.  So I can get the following requests

  • ARGB
  • RGB
  • AARRGGBB
  • RRGGBB

If I get a RGB or ARGB style entry, it actually duplicates the value.  Viewing an example may help.  If I get 123, the actual color Hex value is 112233.

public static Color FromHex(string hexValue)
{
	if (string.IsNullOrEmpty(hexValue))
		return new Color();

	if (hexValue.StartsWith("#"))
		hexValue = hexValue.TrimStart('#');

	if (hexValue.Length > 8)
		throw new ArgumentException("HexValue is too large", "hexValue");

	short r, g, b, a;
	
	// has alpha
	if (hexValue.Length % 4 == 0)
	{
		var shortHand = hexValue.Length == 4;
		a = FromHex(hexValue, 0, shortHand);
		r = FromHex(hexValue, 1, shortHand);
		g = FromHex(hexValue, 2, shortHand); 
		b = FromHex(hexValue, 3, shortHand);
	}
	// no alpha
	else if (hexValue.Length % 3 == 0)
	{
		var shortHand = hexValue.Length == 3;
		a = 255;
		r = FromHex(hexValue, 0, shortHand);
		g = FromHex(hexValue, 1, shortHand);
		b = FromHex(hexValue, 2, shortHand);
	}
	else
		throw new ArgumentException("HexValue is not of length 3, 4, 6, or 8", "hexValue");

	return Color.FromArgb((byte)a, (byte)r, (byte)g, (byte)b);
}

private static short FromHex(string hexValue, int index, bool isShortHand)
{
	string convertValue = isShortHand ? 
		string.Format("{0}{0}", hexValue.Substring(index, 1)) : 
		hexValue.Substring(index * 2, 2);

	return Int16.Parse(convertValue, System.Globalization.NumberStyles.AllowHexSpecifier);
}

Yes the code is over engineered for something that chances are only I will ever use, but hey, I may not input data into an entry correctly … like me messing up will ever happen.

Transferring Photoshop to XAML

Since my end goal for the Drunktender WPF application is to have it mirror its Win32 application and then some, it has to have some nice glasses.  My friend Ian Hall built them out originally  for my using Photoshop.  This sounds painful, right?

With Expression Blend 3 Preview, I was able to quickly import and continue working with my files.  While the file was done in Photoshop, it used vectors and effects to create the end graphic.  I had to do some magic with the glass work due to the effects (I merged them into a smart object which basically is rasterized), but now I can quickly work with and modify my files in XAML. 

It is super easy to accomplish this by going to File->Import Adobe Photoshop File.

image

Then you’ll be prompted with a screen that looks like this.  You can see what you’ll get in Blend from your original file.  This is the screen where I realized I would have some issues due to the Photoshop effects on the glass work.  To toggle between what you had and will get, just click “Compatibility Layer”

image

And here is one of the glasses I got back in XAML.  Pretty nice eh?

image

Now that I have the objects in XAML, I have to do some Visual Studio work.  In my WPF project, I created 2 folders and copied in the png files and created XAML files.  As you can see, it is still a work in progress.

image

You also must make sure the output is copied else you won’t have access to this.

image

In my XAML file, I copied one of the glass’s xaml’s from Expression Blend to Visual studio then used a regular expression to strip out the names with the Find and Replace tool.  I used this x\:Name=\"[a-zA-Z0-9_]+\" to accomplish it.  I kept 2 names in for the color find / replace.  In addition to this, I had to put in some namespace foo in it as well.

<Canvas xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   Width="153" Height="286">
  <!-- more stuff -->
</Canvas

For the glass image, I had to change tweak the location, since XAML is relative, this was painless.  And to load this in c#, I had to change my code slightly since I no longer needed a StringReader stream.

var element = (UIElement)XamlReader.Load(new FileStream("GlassXaml/XMLFile1.xaml", FileMode.Open));
DyanmicXamlLoad.Children.Add(element);

Now since I know I have 1+ elements that I’ll want to change the color on, I need to change my prior way of finding an element.  Not happy about it but I implemented a naming structure way of doing.  If an element is named replaceColor, or replaceColor with a number after it sequentially, I’ll change the background color, else move on.  Now since I tend to forget stuff and may not check the code 6 months in the future for the numbering system, I have it check for both 0 and 1 before moving on.

Brush brush = new SolidColorBrush(getRandomColor());

if (DyanmicXamlLoad.Children.Count <= 0)
	return;

object element;
int i = -1;
do
{
	// need to check replaceColor, replaceColor0, replaceColor1, and replaceColor2, replaceColor3
	string objectName = string.Format("replaceColor{0}", (i < 0) ? "" : i.ToString());
	element = ((FrameworkElement)DyanmicXamlLoad.Children[DyanmicXamlLoad.Children.Count - 1]).FindName(objectName);

	if (element != null && element is Shape)
		((Shape)element).Fill = brush;
	i++;
} while (element != null || i < 2 );

A dynamically loaded and background color changed whiskey glass.

image

Painless, right?  I think so as I did this all on an airplane with zero internet access so no google bing.com to assist me.

Fixing design decision mistakes

Drunktender may not be the world’s most complex program but it has some thought put into it.  I made a mistake when I designed my database and it cause a bit of complexity for one stored procedure and I decided that when making a drink, brand shouldn’t matter, the type of alcohol is more important.  My system, currently, will never have 10 types of vodka so why should I specify that a Vodka and Sprite should have Grey Goose by default.  That should be made at a later level.  The drink contains Vodka, plain and simple.

So how do I correct a relationship based on ingredients to ingredient type instead?  First, go to the database and correct the relationship.  I renamed IngredientId in DrinkRecipe to IngredientTypeId and updated the foreign key relationship appropriately.  Then in Visual Studio, I corrected my Linq2Sql DBML file to reflect this change.

image

Updating the logic in the code behind was fairly easy due to the abstraction of business layers in my project.  The spots of code I did alter had to do with the drink object and the admin sections that added in drinks since I had to switch the context from ingredients to ingredient types.  One “gotcha!” bug I realized with Linq 2 SQL when I was correcting my data was deleting objects that were connected to the parent object then wanting to reuse them.  My solution was to just clone the list.

// deleting will destroy foreign keys so will need to clone list
var temp = new List<DrinkRecipe>();
drinkRecipes.ForEach(
	dr => temp.Add(new DrinkRecipe
	               	{
	               		Amount = dr.Amount,
	               		PourOrder = dr.PourOrder,
	               		IngredientTypeId = dr.IngredientTypeId
	               	})
	);
DrinkRecipes.DeleteByDrinkId(id);
drinkRecipes = temp;

The stored procedure to return all the drinks that the system could make was made far easier now since I didn’t have to figure out what types of alcohol the Drink contained and what types of alcohol the hookup table contained.  As you can see, it is extremely easy to figure out that data now.  I cut out a few lines of SQL.  It still uses a double negation but gets the job done.

SELECT * FROM drinks as d
WHERE d.Name like '%' + @PartialName + '%' and
d.drinkid NOT IN
(
	SELECT dr.drinkid FROM
	DrinkRecipes dr
	WHERE NOT EXISTS
	(
		select * from IngredientTypes it
		INNER JOIN Ingredients ii ON ii.IngredientTypeId = it.IngredientTypeId
		INNER JOIN hookups h ON ii.IngredientID = h.IngredientId
		AND dr.IngredientTypeId = ii.IngredientTypeId
	)
)
Order By d.Name

Final wiring for Drunktender v3

Final wiring for v3 drunktender hardwareFinal wiring for v3 drunktender hardwareFinal wiring for v3 drunktender hardwareFinal wiring for v3 drunktender hardwareFinal wiring for v3 drunktender hardwareFinal wiring for v3 drunktender hardwareFinal wiring for v3 drunktender hardwareFinal wiring for v3 drunktender hardwareFinal wiring for v3 drunktender hardwareFinal wiring for v3 drunktender hardwareFinal wiring for v3 drunktender hardwareFinal wiring for v3 drunktender hardwareFinal wiring for v3 drunktender hardwareFinal wiring for v3 drunktender hardwareFinal wiring for v3 drunktender hardware

Now on to getting bottles added in, additional drinks in the database, and the WPF application wired up.

WPF Search Grid with Animation in Drunktender

So I’ve been slowly but surely porting the Drunktender system.  I’ve done a bit on dynamically loading XAML and creating the button style I wanted but now I need to start hooking up some plumbing.  I’d first like to give a very massive thank you to Jeremiah Morrill for helping me out when I ran into an issue. 

First thing was I tracked down a flippin sweet grid system with animations.  I grabbed the one from Kevin Moore and his WPF Bag-O-Tricks tool kit.  By using the same dynamic loading XAML layout, I’ve updated it with with the new grid.

<Window x:Class="Drunktender.Wpf.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
	xmlns:atp="clr-namespace:Microsoft.Samples.KMoore.WPFSamples.AnimatingTilePanel;
      assembly=J832.Wpf.BagOTricksLib"
	xmlns:Wpf="clr-namespace:Drunktender.Wpf"
    Title="Window1" Height="696" Width="496">
	<Window.Resources>
		<Style x:Key="SelectedImagesPanel" TargetType="ItemsControl">
			<Setter Property="atp:AnimatingTilePanel.ItemHeight" Value="98" />
			<Setter Property="atp:AnimatingTilePanel.ItemWidth" Value="120" />
			<Setter Property="ItemsControl.ItemsPanel">
				<Setter.Value>
					<ItemsPanelTemplate>
						<atp:AnimatingTilePanel AnimatesNewItem="true" 
						   Attraction="2.0" Dampening="0.20" Variation="1.00" />
					</ItemsPanelTemplate>
				</Setter.Value>
			</Setter>
		</Style>
	</Window.Resources>
    <StackPanel>
		<TextBox 
           Text="{Binding RelativeSource={RelativeSource 
             AncestorType={x:Type Wpf:Window1}}, 
           Path=AlteredText, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" />
		
		<ItemsControl Focusable="false"
           ItemsSource="{Binding RelativeSource=
              {RelativeSource AncestorType={x:Type Wpf:Window1}}, 
           Path=SelectedImages}"
           Style="{StaticResource SelectedImagesPanel}" />
	</StackPanel>
</Window>

To do the binding, I just need a bit of code in the window’s code behind file.

public SortableObservableCollection<Button> SelectedImages
{
	get { return (SortableObservableCollection<Button>)GetValue(SelectedImagesProperty); }
	set { SetValue(SelectedImagesProperty, value); }
}

public static readonly DependencyProperty SelectedImagesProperty =
	DependencyProperty.Register("SelectedImages", typeof(SortableObservableCollection<Button>),
	 typeof(Window1), 
	 new UIPropertyMetadata(new SortableObservableCollection<Button>()));

Now lets make this a bit cooler with the grid dynamically updating.  We’ll use some LINQ and lambda expressions.  In addition, to just listing the items, we’ll want these items sorted as well.  I can do this with a SortableObservableCollection which I found on a MSDN forum which inherits from ObservableCollection.  This will allow the collection to always be sorted rather than be a random mess over time.

public string AlteredText
{
	get { return (string)GetValue(AlteredTextProperty); }
	set { SetValue(AlteredTextProperty, value); }
}

public static readonly DependencyProperty AlteredTextProperty =
	DependencyProperty.Register("AlteredText", typeof(string), 
	typeof(Window1), 
	new UIPropertyMetadata("", AlteredTextChanged));

private static void AlteredTextChanged(DependencyObject sender, 
    DependencyPropertyChangedEventArgs e)
{
	var typedSender = sender as Window1;
	if (typedSender == null)
		return;

	SetGrid(typedSender, e.NewValue as string);
}

private static void SetGrid(Window1 window, string newValue)
{
	var images = window.SelectedImages;

	var newDrinkDataset = Drinks.GetByDrinksPartialName(newValue);

	// removing old
	var drinks = from s in images
				 select ((Drink)s.DataContext);

	var newDrinkIds = from d in newDrinkDataset
					  select d.DrinkId;

	var currentDrinkIds = from d in drinks
						  select d.DrinkId;

	var removeDrinkIds = currentDrinkIds.Except(newDrinkIds);
	var addDrinkIds = newDrinkIds.Except(currentDrinkIds);
	var buttonsToRemove = (from s in images
					where removeDrinkIds.Contains(((Drink)s.DataContext).DrinkId)
					select (s)).ToList();

	var addDrinks = from d in newDrinkDataset
					where addDrinkIds.Contains(d.DrinkId)
					select d;

	buttonsToRemove.ForEach(b => images.Remove(b));

	// adding in good
	addDrinks.ToList().ForEach(d => images.Add(new Button { Content = d.Name, DataContext = d }));

	images.Sort(button => button.Content);
}

You’ll see with the TextBox, I have a few magical words like RelativeSource and UpdateSourceTriggerRelativeSource will let me not have to set a DataContext and UpdateSourceTrigger is telling the TextBox in this context to update soon as the value changes.  By default, the TextBox will only update when it loses focus and I want it to update every time.

We’ll need one last little bit so on load the grid will be pre-populated.  In the constructor for the window, we need to just add in one additional line to accomplish this.

public Window1()
{
	InitializeComponent();
	SetGrid(this, string.Empty);
}

Here it is the extremely basic grid effect with a few snap shots.

Screenshot 1 Screenshot 2 Screenshot 3

Not much now but it is starting to build out nicely.

Building a pour system

Pour systemPour systemPour systemPour systemPour systemPour systemPour systemPour systemPour systemPour system

By using some PVC, a funnel, and some Plumbing Goop, I’ve created a very nice system that all my tubes can feed into and one nice output that is tall enough for a typical glass.  I have a T PVC part that I use to mount to the table mount.  I sealed off the end that will mount to the table mount so then the the liquid will only go to the funnel.  I had to shave down the funnel slightly to fit it in but now the nice thing is I can remove the entire pour assembly and put it in the dish washer!