Not your father's ListBox

[Edited: I changed the code to use one template this time to emphasize the proper use of styling. I also moved the the definition for the style to a class to make the code cleaner.]

After only a couple of days  hacking away at WPF using the SDK docs and Google as reference, I may finally be gaining a toehold into the vast and exciting world of Avalon.  So many things about WPF are new e.g.  FrameworkElementFactory, DependencyProperty, DataTemplate and the list goes on.  If you are not careful you'll loose yourself following the inheritance heirarchy. 

I finally gave up doing that and decided that the best way to learn WPF was to leave all those fancy stuff first - like transformations, animation, 3D etc. - and work on the normal stuff like take some data from a service object and display the  information on the screen.  So to get the ball rolling, I whipped up a couple of rudimentary classes - Customer and CustomerService.  After spending half a day pounding the keyboard, studying compiler error messages, checking the docs, googling, cursing - here's what i managed to do with the ListBox:

ListBox Demo

Although styling and templating are indisputably easier to accomplish in XAML, I believe programmers must also know how to do these things in pure code. Knowing how things are done under the hood will prove invaluable in the long haul. The days when Web developers would smirk at the sight of our user interfaces are about to end.

import System
import System.Windows
import System.Windows.Controls
import System.Windows.Controls.Primitives
import System.Windows.Data
import System.Windows.Documents
import System.Windows.Input
import System.Windows.Markup
import System.Windows.Media
import System.Windows.Navigation
import System.Windows.Shapes
import System.Globalization

class Customer:
	[property(Name)] _name = ""
	[property(Address)] _addr = ""
	
	def constructor(name as string, addr as string):
		_name = name
		_addr = addr
	
	override def ToString() as string:
		return _name

class CustomerService:
	static def GetCustomers():
		yield Customer("brent","davao")
		yield Customer("manny","gsc")
		yield Customer("robert","cagayan")
		yield Customer("george","cebu")
		yield Customer("thomas","iloilo")
		yield Customer("lelanie","tagum")
		yield Customer("bong","digos")
		yield Customer("hazel","bicol")		
			
class CustomerTemplate(DataTemplate):
	def constructor():
		super(Customer)
		panel = FrameworkElementFactory(StackPanel)		
		child1 = FrameworkElementFactory(TextBlock)
		child1.SetValue(TextBlock.TextProperty, Binding("Name"))		
		child1.SetValue(TextBlock.FontSizeProperty, 20.0)
		child2 = FrameworkElementFactory(Label)
		child2.SetValue(Label.ContentProperty, Binding("Address"))		
		panel.AppendChild(child1)
		panel.AppendChild(child2)	
		self.VisualTree = panel
		
class CustomerStyle(Style):
	def constructor():
		super(ListBoxItem)
		setter1 = Setter(ListBoxItem.HorizontalContentAlignmentProperty, HorizontalAlignment.Stretch)
		setter2 = Setter(ListBoxItem.MarginProperty, Thickness(2))
		setter3 = Setter(ListBoxItem.BorderThicknessProperty, Thickness(1))
		setter4 = Setter(ListBox.BorderBrushProperty, Brushes.Gold)
		setter5 = Setter(ListBox.BackgroundProperty, Brushes.Bisque)
		setter6 = Setter(TextBlock.ForegroundProperty, Brushes.Orange)
		self.Setters.Add(setter1)
		self.Setters.Add(setter2)
		self.Setters.Add(setter3)
		self.Setters.Add(setter4)
		self.Setters.Add(setter5)
		self.Setters.Add(setter6)


class Home(Grid):
	def constructor():	
		self.ColumnDefinitions.Add(ColumnDefinition(Width:GridLength(200.0)))
		self.ColumnDefinitions.Add(ColumnDefinition(Width:GridLength(10.0)))
		self.ColumnDefinitions.Add(ColumnDefinition(Width:GridLength(400.0)))
		self.RowDefinitions.Add(RowDefinition(Height:GridLength(20.0)))
		self.RowDefinitions.Add(RowDefinition(Height:GridLength(100.0)))
		self.RowDefinitions.Add(RowDefinition(Height:GridLength(10.0)))
		self.RowDefinitions.Add(RowDefinition(Height:GridLength(100.0)))
		self.RowDefinitions.Add(RowDefinition(Height:GridLength(10.0)))
		self.RowDefinitions.Add(RowDefinition(Height:GridLength(200.0)))
		self.RowDefinitions.Add(RowDefinition(Height:GridLength(10.0)))
		
		regularListBox = ListBox()
		regularListBox.ItemsSource = CustomerService.GetCustomers()
		Grid.SetColumn(regularListBox,2)
		Grid.SetRow(regularListBox,1)
		
		templatedListBox = ListBox()
		templatedListBox.ItemTemplate = CustomerTemplate()	
		templatedListBox.ItemsSource = CustomerService.GetCustomers()
		Grid.SetColumn(templatedListBox,2)
		Grid.SetRow(templatedListBox,3)
		
		styledListBox = ListBox()			
		styledListBox.ItemContainerStyle = CustomerStyle()
		styledListBox.ItemTemplate = CustomerTemplate()
		styledListBox.ItemsSource = CustomerService.GetCustomers()						
		Grid.SetColumn(styledListBox,2)
		Grid.SetRow(styledListBox,5)
		
		text1 = TextBlock(Run("A Regular ListBox "))
		text1.FontSize = 14
		text1.HorizontalAlignment = HorizontalAlignment.Right
		text2 = TextBlock(Run("A Templated ListBox "))
		text2.FontSize = 14
		text2.HorizontalAlignment = HorizontalAlignment.Right
		text3 = TextBlock(Run("A Styled Templated ListBox "))
		text3.FontSize = 14
		text3.HorizontalAlignment = HorizontalAlignment.Right
		Grid.SetRow(text1,1)
		Grid.SetRow(text2,3)
		Grid.SetRow(text3,5)	
			
		self.Children.Add(regularListBox)
		self.Children.Add(templatedListBox)
		self.Children.Add(styledListBox)
		self.Children.Add(text1)
		self.Children.Add(text2)
		self.Children.Add(text3)				

[STAThread]
def Main():
	# define main window
	win = Window(Title:"ListBox Demo")	
	win.Content = Home()
	# define application
	app = Application()
	app.Startup += { win.Show() }
	app.Run()

Published 09-28-2006 1:08 AM by smash
Filed under: , ,