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.

Jeremiah Morrill Jun 8, 2009 @ 2:10 PM

# re: WPF Search Grid with Animation in Drunktender
The only problem about asking me for WPF help is getting me to shut up about WPF!

BTW, I do accept thanks in the form of a "cold one".

Take care bud.

-Jer

Clint Jun 8, 2009 @ 2:38 PM

# re: WPF Search Grid with Animation in Drunktender
The Property of Ones! http://www.homestarrunner.com/sbemail39.html
WPF is pretty sweet the more I use it. Just stupid learning curve hurts my head.

I'll be sure it will be a cold one!

Post a Comment

Please add 7 and 8 and type the answer here: