Windows Phone Toolkit MultiselectList in depth| Part2: Data Binding
published on: 11/3/2011 | Views: 7091 | Tags: WP7ToolkitRate Now!
by WindowsPhoneGeek
This is the second article about the new MultiselectList control from the latest release of the Windows Phone Toolkit - August 2011 (7.1 SDK). This time I am going to talk about data binding and using MultiselectList in more complex scenarios.
NOTE: In Part1 we talked about key properties, methods, events and the main features of the Windows Phone MultiselectList control. You can take a look at it for reference.
- Windows Phone Toolkit MultiselectList in depth| Part1: key concepts and API
- Windows Phone Toolkit MultiselectList in depth| Part2: Data Binding
Data Binding MultiselectList Step by Step
This example demonstrates how to populate the MultiselectList control with data using data binding. In this example the MultiselectList control is used to enable the user to select multiple ingredients from a list in order to create a custom pizza.
- Defining the Data Source
Here are the steps we will follow in order to create a data source:
Step1. Define the business/data class:
The first step is to define the data class. Let's create a "PizzaOption" class which exposes the following properties:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
public
class
PizzaOption
{
public
PizzaOption(
string
name)
{
this
.Name = name;
}
public
string
Name
{
get
;
set
;
}
public
string
Note
{
get
;
set
;
}
public
bool
IsSelected
{
get
;
set
;
}
}
|
Step2. Create a sample collection with items of type TileItem:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
public
MainPage()
{
//...
List<PizzaOption> pizzaOptions =
new
List<PizzaOption>()
{
new
PizzaOption(
"Olives"
),
new
PizzaOption(
"Mozzarella"
) { Note =
"NEW"
},
new
PizzaOption(
"Mushrooms"
),
new
PizzaOption(
"Ham"
),
new
PizzaOption(
"Bacon"
),
new
PizzaOption(
"Pepperoni"
),
new
PizzaOption(
"Salami"
) { Note =
"SOON"
},
new
PizzaOption(
"Tomatoes"
),
new
PizzaOption(
"Onions"
)
};
//...
}
|
- Data binding the MultiselectList
Step1. Define a custom ItemTemplate in the page Resources.
1
2
3
4
5
|
<
phone:PhoneApplicationPage.Resources
>
<
DataTemplate
x:Key
=
"CustomItemTemplate"
>
<
TextBlock
Text
=
"{Binding Name}"
/>
</
DataTemplate
>
</
phone:PhoneApplicationPage.Resources
>
|
Step2. Define a custom ItemInfoTemplate in the page Resources.
1
2
3
4
5
|
<
phone:PhoneApplicationPage.Resources
>
<
DataTemplate
x:Key
=
"CustomItemInfoTemplate"
>
<
TextBlock
Text
=
"{Binding Note}"
/>
</
DataTemplate
>
</
phone:PhoneApplicationPage.Resources
>
|
Step3. Define a MultiselectList in XAML and set its ItemTemplate and ItemInfoTemplate:
1
2
3
|
<
toolkit:MultiselectList
Grid.Row
=
"1"
x:Name
=
"multiSelectList"
ItemTemplate
=
"{StaticResource CustomItemTemplate}"
ItemInfoTemplate
=
"{StaticResource CustomItemInfoTemplate}"
/>
|
Step4. Set the MultiselectList ItemsSource
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
ICollectionView pizzaOptionsCollectionView;
public
MainPage()
{
//...
CollectionViewSource pizzaOptionSource =
new
CollectionViewSource();
pizzaOptionSource.Source = pizzaOptions;
this
.pizzaOptionsCollectionView = pizzaOptionSource.View;
using
(
this
.pizzaOptionsCollectionView.DeferRefresh())
{
this
.pizzaOptionsCollectionView.SortDescriptions.Add(
new
SortDescription(
"IsSelected"
, ListSortDirection.Descending));
this
.pizzaOptionsCollectionView.SortDescriptions.Add(
new
SortDescription(
"Name"
, ListSortDirection.Descending));
}
this
.multiSelectList.ItemsSource =
this
.pizzaOptionsCollectionView;
}
|
The CollectionViewSource in the code above is used to sort the list by the values of the IsSelected and Name properties.
Step5. Add the following ApplicationButtons in code behind:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
private
ApplicationBarIconButton selectButton;
private
ApplicationBarIconButton acceptButton;
private
ApplicationBarIconButton cancelButton;
private
ApplicationBarMenuItem selectAllMenuItem;
private
ApplicationBarMenuItem unselectAllMenuItem;
// .
public
MainPage()
{
InitializeComponent();
this
.selectButton =
new
ApplicationBarIconButton();
this
.selectButton.IconUri =
new
Uri(
"/Toolkit.Content/ApplicationBar.Select.png"
, UriKind.RelativeOrAbsolute);
this
.selectButton.Text =
"select"
;
this
.selectButton.Click +=
new
EventHandler(selectButton_Click);
this
.acceptButton =
new
ApplicationBarIconButton();
this
.acceptButton.IconUri =
new
Uri(
"/Toolkit.Content/ApplicationBar.Check.png"
, UriKind.RelativeOrAbsolute);
this
.acceptButton.Text =
"accept"
;
this
.acceptButton.Click +=
new
EventHandler(acceptButton_Click);
this
.cancelButton =
new
ApplicationBarIconButton();
this
.cancelButton.IconUri =
new
Uri(
"/Toolkit.Content/ApplicationBar.Cancel.png"
, UriKind.RelativeOrAbsolute);
this
.cancelButton.Text =
"cancel"
;
this
.cancelButton.Click +=
new
EventHandler(cancelButton_Click);
this
.selectAllMenuItem =
new
ApplicationBarMenuItem();
this
.selectAllMenuItem.Text =
"select all"
;
this
.selectAllMenuItem.Click +=
new
EventHandler(selectAllMenuItem_Click);
this
.unselectAllMenuItem =
new
ApplicationBarMenuItem();
this
.unselectAllMenuItem.Text =
"unselect all"
;
this
.unselectAllMenuItem.Click +=
new
EventHandler(unselectAllMenuItem_Click);
}
|
Step6. Create a new method that will configure the application bar for the normal state:
1
2
3
4
5
6
7
|
private
void
ShowNormalStateAppBar()
{
this
.ApplicationBar.Buttons.Clear();
this
.ApplicationBar.MenuItems.Clear();
this
.ApplicationBar.Buttons.Add(
this
.selectButton);
this
.pizzaOptionsCollectionView.Filter =
this
.IsSelectedFilter;
}
|
Step7. Call "ShowNormalStateAppBar" in the constructor of MainPage after InitializeComponent():
1
2
3
4
5
6
|
public
MainPage()
{
InitializeComponent();
this
.ShowNormalStateAppBar();
//...
}
|
Step8. Subscribe to the MultiselectList IsSelectionEnabledChanged event and add the following code in its handler:
1
2
3
4
5
|
public
MainPage()
{
//...
this
.multiSelectList.IsSelectionEnabledChanged +=
new
DependencyPropertyChangedEventHandler(multiSelectList_IsSelectionEnabledChanged);
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
void
multiSelectList_IsSelectionEnabledChanged(
object
sender, DependencyPropertyChangedEventArgs e)
{
if
(
this
.multiSelectList.IsSelectionEnabled)
{
if
(
this
.updateSelectedState)
{
IEnumerable<PizzaOption> pizzaOptions =
this
.pizzaOptionsCollectionView.SourceCollection
as
IEnumerable<PizzaOption>;
this
.SetOptionsSelected(pizzaOptions,
true
, (pizzaOption) => pizzaOption.IsSelected);
}
this
.ShowSelectionStateAppBar();
}
else
{
this
.ShowNormalStateAppBar();
}
}
|
NOTE: It is important to handle the IsSelectionEnabledChanged event since selection mode can be triggered by user interaction also, and not only by setting the IsSelectionEnabled property. |
Step9. Add a helper method that sets the IsSelected property of MultiselectItems:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
private
void
SetOptionsSelected(IEnumerable<PizzaOption> pizzaOptions,
bool
selected, Predicate<PizzaOption> predicate)
{
if
(pizzaOptions ==
null
)
{
return
;
}
if
(predicate ==
null
)
{
predicate = (pizzaOption) =>
true
;
}
ItemContainerGenerator itemContainerGenerator =
this
.multiSelectList.ItemContainerGenerator;
foreach
(PizzaOption pizzaOption
in
pizzaOptions)
{
if
(pizzaOption !=
null
&& predicate(pizzaOption))
{
DependencyObject visualItem = itemContainerGenerator.ContainerFromItem(pizzaOption);
MultiselectItem multiselectItem = visualItem
as
MultiselectItem;
if
(multiselectItem !=
null
)
{
// NOTE: this will also add an item to the SelectedItems collection
multiselectItem.IsSelected = selected;
}
}
}
}
|
Step10. Create a method that will configure the application bar for the selection state:
1
2
3
4
5
6
7
8
9
10
|
private
void
ShowSelectionStateAppBar()
{
this
.ApplicationBar.Buttons.Clear();
this
.ApplicationBar.MenuItems.Clear();
this
.ApplicationBar.Buttons.Add(
this
.acceptButton);
this
.ApplicationBar.Buttons.Add(
this
.cancelButton);
this
.ApplicationBar.MenuItems.Add(
this
.selectAllMenuItem);
this
.ApplicationBar.MenuItems.Add(
this
.unselectAllMenuItem);
this
.pizzaOptionsCollectionView.Filter =
null
;
}
|
Step11. Create a method that is used to filter the collection in the normal state so that only selected items are displayed:
1
2
3
4
5
6
7
8
9
|
private
bool
IsSelectedFilter(
object
item)
{
PizzaOption pizzaOption = item
as
PizzaOption;
if
(pizzaOption !=
null
)
{
return
pizzaOption.IsSelected;
}
return
false
;
}
|
Step12. Override OnBackKeyPress and add the following code:
1
2
3
4
5
6
7
8
9
|
protected
override
void
OnBackKeyPress(CancelEventArgs e)
{
base
.OnBackKeyPress(e);
if
(
this
.multiSelectList.IsSelectionEnabled)
{
this
.multiSelectList.IsSelectionEnabled =
false
;
e.Cancel =
true
;
}
}
|
Step13. Add the following code in the application buttons event handlers, defined in Step5:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
|
void
cancelButton_Click(
object
sender, EventArgs e)
{
this
.multiSelectList.IsSelectionEnabled =
false
;
}
void
acceptButton_Click(
object
sender, EventArgs e)
{
IEnumerable<PizzaOption> pizzaOptions =
this
.pizzaOptionsCollectionView.SourceCollection
as
IEnumerable<PizzaOption>;
foreach
(PizzaOption pizzaOption
in
pizzaOptions)
{
pizzaOption.IsSelected =
false
;
}
foreach
(
object
item
in
this
.multiSelectList.SelectedItems)
{
PizzaOption pizzaOption = item
as
PizzaOption;
if
(pizzaOption !=
null
)
{
pizzaOption.IsSelected =
true
;
}
}
this
.multiSelectList.IsSelectionEnabled =
false
;
}
void
selectButton_Click(
object
sender, EventArgs e)
{
this
.multiSelectList.IsSelectionEnabled =
true
;
}
private
bool
updateSelectedState =
true
;
void
unselectAllMenuItem_Click(
object
sender, EventArgs e)
{
IEnumerable<PizzaOption> pizzaOptions =
this
.pizzaOptionsCollectionView.SourceCollection
as
IEnumerable<PizzaOption>;
this
.SetOptionsSelected(pizzaOptions,
false
,
null
);
// IMPORTANT NOTE:
// when all items are unselected the selection mode automatically turns off
// is this a bug???
this
.updateSelectedState =
false
;
this
.multiSelectList.IsSelectionEnabled =
true
;
this
.updateSelectedState =
true
;
}
void
selectAllMenuItem_Click(
object
sender, EventArgs e)
{
IEnumerable<PizzaOption> pizzaOptions =
this
.pizzaOptionsCollectionView.SourceCollection
as
IEnumerable<PizzaOption>;
this
.SetOptionsSelected(pizzaOptions,
true
,
null
);
}
|
Here is how the final result should look like:
That was all about data binding MultiselectList from the Windows Phone Toolkit - August 2011 (7.1 SDK) in depth.
Here is the full source code:
I hope that the post was helpful.
You can also follow us on Twitter @winphonegeek
Comments
posted by: LS on 3/31/2012 6:28:55 AM
How about a listbox with data binded content from an external source?
What if you have more than "30" Pizzas Topping???
posted by: G Cupertino on 6/3/2012 11:01:39 PM
Hi, it's a great post, but if you add more items to the list, let's say you have like 60 items and you click select all, only the first 20 items will be selected. Could it be memory performance? It doesn't work when the line is not visible. If the line is not visible, returns null. Any idea?