Sunday 3 March 2013

Part - 2: Data Binding in ListBox

This post is the second in the series of ListBox control. In previous post, we learned some basic of ListBox control, adding items manually to ListBox control in XAML and code behind, and examining some properties of ListBox control.

Here is the reference of all the post in this series for quick reference.

Part - 1: ListBox basics - Adding Items manually to ListBox and understanding properties of ListBox

Part - 2: Data Binding in ListBox (Binding ListBox to data source)

Part - 3: Binding ListBox to XML

Part - 4: Applying style to ListBox

Part - 5: Applying style to ListBox, Continued

Part - 6: ListBox custom ControlTemplate

 

Part - 2: Data Binding in ListBox

In last post we learnt how to add static items to ListBox in XAML and code. But Most of the times we need to populate ListBox control by binding the control to some data source, instead of adding items manually. Data source can be xml, business object, DataTable and some other type that contains data.

Binding ListBox to Custom Business Object

Let’s create a simple class “Fruit” to hold fruit information.

public class Fruit
{
public string Name { get; set; }
public string ImagePath { get; set; }
public Int32 Calories { get; set; }
public string Vitamins { get; set; }
}


Add simple ListBox control to XAML

<ListBox Name="lstFruits"></ListBox>


In code behind (constructor), Create a list of Fruits and bind to ListBox control.

List<Fruit> myFruits = new List<Fruit>()
{
new Fruit() { Name = "Apple", ImagePath = "Images\\Apple.png", Calories = 61, Vitamins = "A,C" },
new Fruit() { Name = "Orange", ImagePath = "Images\\Orange.png", Calories = 51, Vitamins = "A,B1,C" },
new Fruit() { Name = "Grape", ImagePath = "Images\\Grapes.png", Calories = 40, Vitamins = "C" },
new Fruit() { Name = "Mango", ImagePath = "Images\\Mango.png", Calories = 80, Vitamins = "A,B1,C" }
};

lstFruits.ItemsSource = myFruits;


ItemSource property can be bound to any data source that implements IEnumerable. Generic List is one of them that implements IEnumerable.


Output:



In above image, it is visible that ListBox control is correctly bound to the data source as it shows 4 items in the list. Each item in ListBox is bound to an instance of Fruit class. As we have not specified any property of Fruit class, by default ToString() method of Fruit object is called which returns “ListboxSample.Fruit” value. i.e <Namespace.ClassName>


Modify XAML to bind to Name property by adding DisplayMemberPath

<ListBox Name="lstFruits" DisplayMemberPath="Name"></ListBox>

and here is the output



Now we examine the properties as we did in post 1.



One noticeable difference is that “SelectedItem” and “SelectedValue” property returns type “ListBoxSample.Fruit” instead of “ListBox.ListBoxItem” as each item is bound to instance of Fruit class. So to access properties we type cast to Fruit class.


Now let’s add one more property “SelectedValuePath”.

<ListBox Name="lstFruits" DisplayMemberPath="Name" SelectedValuePath="ImagePath"></ListBox>

Examine the difference.



lstFruits.SelectedValue now directly returns value of “ImagePath” property from Fruit class instead of an instance of Fruit class. Many times its quite useful to bind “SelectedValuePath” property to Unique Identifier (ID) of class so we can directly get Unique Identifier of selected item.


Some more fun with ListBox DataBinding.


Add Image control below ListBox control. Bind Image control Source property to SelectedValue property of ListBox control. This is called Element to Element binding.

<Image Grid.Row="1" Source="{Binding ElementName=lstFruits, Path=SelectedValue}" Stretch="Uniform"></Image>


Run the application and select items in ListBox.



As we have specified SelectedValuePath property of ListBox to ImagePath property of Fruit class, when any item is selected in ListBox control, SelectedValue property  of ListBox will return ImagePath of selected fruit item. Now we have bound Source property of Image control to SelectedValue property of ListBox control which would return ImagePath. Hence selecting fruit in ListBox will show image below.


ListBox also provides an alternative way of achieving same result.


IsSynchronizedWithCurrentItem


When ListBox control is bound to Data Source, internally an instance of ICollectionView is created depending on the type of of Data Source and then this view is bound to ListBox. If IsSynchronizedWithCurrentItem of ListBox is set to True, when user select any item in ListBox control, same item is set as current item in underlying view. This way if any other control (in our case Image control) is bound to any property of same data source, then it would reflect value from currently selected item in ListBox.


Let’s see what it means?


First of all we need to bind both ListBox control and Image control to same data source. Here DataContext property becomes more useful. DataContext property is present in every control and can be set to any data source. When DataContext is set in parent control, all child controls can inherit that data source from parent control. Hence we set DataContext property of entire Window.

List<Fruit> myFruits = new List<Fruit>()
{
new Fruit() { Name = "Apple", ImagePath = "Images\\Apple.png", Calories = 61, Vitamins = "A,C" },
new Fruit() { Name = "Orange", ImagePath = "Images\\Orange.png", Calories = 51, Vitamins = "A,B1,C" },
new Fruit() { Name = "Grape", ImagePath = "Images\\Grapes.png", Calories = 40, Vitamins = "C" },
new Fruit() { Name = "Mango", ImagePath = "Images\\Mango.png", Calories = 80, Vitamins = "A,B1,C" }
};

this.DataContext = myFruits;


DataContext of entire window is set to list of fruits. Hence ListBox control and Image control in Window would inherit data source from Window.

<ListBox Name="lstFruits" ItemsSource="{Binding}" DisplayMemberPath="Name" SelectedValuePath="ImagePath" IsSynchronizedWithCurrentItem="True"></ListBox>

<Image Grid.Row="1" Stretch="Uniform" Source="{Binding ImagePath}"></Image>


As ListBox inherits data source from parent, ItemSource for ListBox is set to empty Binding. IsSynchronizedWithCurrentItem = True will make sure that any item selected in ListBox will be the current item in ICollectionView.


Image control is bound to ImagePath property of Fruit class. hmm, that sounds interesting. how come any ContentControl like Image control can be bound to collection. That’s beauty of WPF DataBinding. When any property of ContentControl is bound to a property in list, current item (by default first item) in the list will be used for binding.


Now run the application and see the action.




When application is run, by default first item is the current item in ICollectionView. Hence “Apple” is selected in ListBox and Apple is displayed in Image control.


Now as we change the selection in ListBox, current item in ICollectionView is updated and hence image gets displayed for selected item.



This is very handy feature and useful in different scenarios.


I hope this would provide you a good insight into DataBinding.

No comments:

Post a Comment