Sunday, March 18, 2007

First off, I've returned from a great 7 weeks holiday in New Zealand. I will not bore you with 1500 pictures, although I am perfectly capable of doing just that. On top of that, 3 hours of film was shot. Yikes. It was just that incredible!

For picking up the blogging slack, i'll share with you guys a mechanism for extending objects with attached properties. Although the technique is well documented, I didn't find any resources online with an example of a checked listbox. I did find a post by Josh Smith about it, but he created the checked listbox using the Checked and Unchecked events, which I don't feel is very 'WPF' like.. Not when you are using WPF! So, let's first look at the problem at hand, and then get on to the solution, which is so simple, it feels like an anti-climax.

We have an ObservableCollection<T> with domainobjects, or in our case simple Strings. We wish to show these in a listbox, and give the user the abiliby to select multiple items using checkboxes. We also provide a button, which will just select all of them. Obviously, we want to be able to get to the checked items.
In the past, I would probably have used checked and unchecked events to keep track of the checked items, or I would have extended my domainobject with a 'IsSelected' property and bind to it. The latter would have made me extremely unhappy, because it would mean my userinterface was invading my domainobject.

An attached property, basically, is a property that belongs to another object, but can be set on any dependency object you choose. So, I will introduce a boolean attached property (for instance: IsGeselecteerd, dutch for IsSelected). Then I create the datatemplate for my objects, with a checkbox in it, and a binding to the attachedproperty:

 

<Window.Resources> <DataTemplate x:Key="lbitems"> <StackPanel Orientation="Horizontal"> <CheckBox Name="checkbox" l:Window1.Geselecteerd="{Binding Path=IsChecked, RelativeSource={RelativeSource Self}, Mode=TwoWay }" /> <Label Content="{Binding}" /> </StackPanel> </DataTemplate> </Window.Resources> <StackPanel> <ListBox Name="lv" ItemsSource="{Binding Path=Lijst}" ItemTemplate="{StaticResource lbitems}"> </ListBox> <Button Click="SelectAll">Select all</Button> <Button Click="LeesUit">show selected items in debug.output</Button> </StackPanel> </Window>

 

The binding of interest is: l:Window1.Geselecteerd="{Binding Path=IsChecked, RelativeSource={RelativeSource Self}, Mode=TwoWay }", on the checkbox.
It means that the IsChecked property of itself (the checkbox) is bound to the property Geselecteerd, which lives on the Window1 class.

That's basically all there is to it. Setting the checkboxes from procedural code is somewhat harder then I would like it to be, but only because I couldn't find an easy way to get to the checkbox from code:

 

foreach(object dataitem in lv.Items) { // get the visual container, belonging to the dataitem ListBoxItem lbitem = (ListBoxItem)lv.ItemContainerGenerator.ContainerFromItem(dataitem); // get to the checkbox CheckBox c = (CheckBox) GeefChildHelper(lbitem, "checkbox"); // set the attached property SetGeselecteerd(c, true); }

Point of interest is the setting of the attached property, which will tell the binding of the checkbox to do the appropriate thing and the Helper method, to get to the checkbox:

 

private object GeefChildHelper(ListBoxItem lbitem, string naam) { Border border = VisualTreeHelper.GetChild(lbitem, 0) as Border; ContentPresenter contentPresenter = VisualTreeHelper.GetChild(border, 0) as ContentPresenter; return lv.ItemTemplate.FindName(naam, contentPresenter); }

Here you see the need to first find a contentpresenter, before the FindName method will work..... That's because apparently that's the thing the template is bound to.

Reading the values is just as easy:

 

foreach(string dataitem in Lijst) { ListBoxItem lbitem = (ListBoxItem)lv.ItemContainerGenerator.ContainerFromItem(dataitem); CheckBox c = (CheckBox)GeefChildHelper(lbitem, "checkbox"); Debug.WriteLine(String.Format("item: {0} is {1}", dataitem, GetGeselecteerd(c).ToString())); }

I hope this sample helps someone!

AttachedList.zip (11.99 KB)
Monday, March 19, 2007 12:10:33 PM (Romance Standard Time, UTC+01:00)
Hey Ruurd,

Good to see you're back :-)

- Waseem
Waseem
Monday, March 19, 2007 6:05:21 PM (Romance Standard Time, UTC+01:00)
;-)
Ruurd
Wednesday, October 17, 2007 9:48:11 AM (Romance Standard Time, UTC+01:00)
Did you try getting ItemTemplate with 2 or more ListBox DataTemplates? Try It ....
Thursday, November 25, 2010 12:44:47 PM (Romance Standard Time, UTC+01:00)
Did you try getting ItemTemplate with 2 or more ListBox DataTemplates? Try It ....
Thursday, December 02, 2010 3:49:27 AM (Romance Standard Time, UTC+01:00)
This article was Very helpful. Actually, I am fond of reading online punjabi news. Thanks for writing such a complete ..
thank you for sharing.
Saturday, December 04, 2010 2:47:55 AM (Romance Standard Time, UTC+01:00)
The way in which it pays may be questionnaire done hourly or pay per analysis taken. You may be requested questions about their products which you employ and you'll really need to provide answers to it. The answers can be in variety of "yes or no". It's possible you'll be asked questions like"how countless days do you drink coca cola inside a month? What exactly is favored Nike footwear? These institutions distribute their surveys through third bash agencies and you simply shouldn't count on to visit their sites to seek out the surveys to take them.
Saturday, December 04, 2010 2:54:05 AM (Romance Standard Time, UTC+01:00)
No matter if you happen to be wearing sandals, boots or working shoes it's important that your feet are supported and at ease. Shoes with assistance and shock absorption can help reduce pounding for the relaxation of your respective body as you walk or run.
Comments are closed.