WP7 Master - Detail Navigation with Repository Pattern
published on: 4/29/2011 | Views: N/A | Tags: Navigation
by WindowsPhoneGeek
In this article I am going to talk about Master/Detail Navigation in Windows Phone 7 applications.
It is a common scenario to have some kind of composite navigation when building a WP7 application. However before we begin we have to decide how to handle the following situations:
Question 1.When navigating between Master - > Detail pages how to implement the connection so that for each record in the Master page a correct Detail information is shows?
Solution: We will pass a parameter during the navigation and will extract the right Detail data using this parameter.
Question 2. How to pass data between pages? We will need some kind of unique "Key" or "ID" which determine the right data.
Solution: We will use/pass an unique "ID" and will use it to extract the real data that will be shown in the Detail view. The assist way to accomplish this is to implement a Repository which exposes GetCountryByID() method.
So in this post I will demonstrate how to perform Master/Detail Navigation using Repository Pattern. Lets first create a sample Windows Phone 7 application project.
Implementing the Data Repository
"Conceptually, a Repository encapsulates the set of objects persisted in a data store and the operations performed over them, providing a more object-oriented view of the persistence layer. Repository also supports the objective of achieving a clean separation and one-way dependency between the domain and data mapping layers" (Martin Fowler). For more information visit :The Repository Pattern on MSDN
The first thing we need to do when implementing Repository Pattern is to create the main interface with two methods: GetCountryList() and GetCountryById(int id). It will represent a country repository abstraction which will allow changing the implementation of a country repository with one that reads country data from different data sources like for example a database, Isolated Storage, etc. :
public interface ICountryRepository
{
IList<CountryData> GetCountryList();
CountryData GetCountryById(int id);
}
After that we can begin with the real repository by simply creating a class that implement ICountryRepository.
- Example1: Creating a XmlCountryRepository that uses data from a XML file
In this example the data is stored in a XML file called "CountriesXML.xml" (we have added this file into our project):
<?xml version="1.0" encoding="utf-8" ?>
<Countries>
<Country>
<Name>Germany</Name>
<Flag>../Images/Germany.png</Flag>
<ID>1</ID>
<Description>Germany Description</Description>
<Capital>Berlin</Capital>
</Country>
<Country>
<Name>Grece</Name>
<Flag>../Images/Greece.png</Flag>
<ID>2</ID>
<Description>Grece Description</Description>
<Capital>Athens</Capital>
</Country>
...
</Countries>
NOTE: We use a set of Images placed in Image folder. You can find them attached in the sample project at the end of this article.
In order to be able to use this data in an easy way we will have to add a CountryData data class like for example:
public class CountryData
{
public string Name
{
get;
set;
}
public string Flag
{
get;
set;
}
public int ID
{
get;
set;
}
public string Capital
{
get;
set;
}
public string Description
{
get;
set;
}
}
and finally here is how our XmlCountryRepository should look like:
public class XmlCountryRepository : ICountryRepository
{
private static List<CountryData> countryList = null;
static XmlCountryRepository()
{
XDocument loadedData = XDocument.Load("CountriesXML.xml");
var data = from query in loadedData.Descendants("Country")
select new CountryData
{
Name = (string)query.Element("Name"),
Flag = (string)query.Element("Flag"),
Description = (string)query.Element("Description"),
Capital = (string)query.Element("Capital"),
ID = (int)query.Element("ID"),
};
countryList = data.ToList();
}
public IList<CountryData> GetCountryList()
{
return countryList;
}
public CountryData GetCountryById(int id)
{
return countryList.FirstOrDefault(c => c.ID == id);
}
}
NOTE: In order to read the information from the XML files we use XDocument so you will fat first need to add reference to System.Xml.Linq.dll and after that include the using System.Xml.Linq;! For more info take a look at our article: WP7 working with XML: reading, filtering and databinding
- Example 2: A very simple SimpleCountryRepository repository that keeps information in a static list
In this example the data is stored in a static List. We will use the previously created CountryData as data class. Here is how our SimpleCountryRepository should look like:
public class SimpleCountryRepository : ICountryRepository
{
private static List<CountryData> countryList = null;
static SimpleCountryRepository()
{
countryList = new List<CountryData>();
countryList.Add(new CountryData() { Name = "Germany", Flag = new Uri(@"../Images/Germany.png", UriKind.Relative).ToString(), ID = 1, Capital = "Berlin", Description = "Germany Description" });
.
}
public IList<CountryData> GetCountryList()
{
return countryList;
}
public CountryData GetCountryById(int id)
{
return countryList.FirstOrDefault(c => c.ID == id);
}
}
Implementing the Navigation Logic
We will add the following two pages to our project: MainPage.xaml (master page) and CountryDetails.xaml (detail page):
Master Page
In our MainPage.xaml we will add a ListBox with the following ItemTemplate:
<ListBox x:Name="list" SelectionChanged="list_SelectionChanged">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Image Source="{Binding Flag}" Stretch="None"/>
<TextBlock Text="{Binding Name}" FontSize="30"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Here is how we will populate the ListBox using the previously created XmlCountryRepository repository.(In exactly the same way you can populate the list using SimpleCountryRepository):
public MainPage()
{
InitializeComponent();
//ICountryRepository countryRepository = new SimpleCountryRepository();
//this.list.ItemsSource = countryRepository.GetCountryList();
ICountryRepository countryRepository = new XmlCountryRepository();
this.list.ItemsSource = countryRepository.GetCountryList();
}
We will perform the navigation in SelectionChanged handler of the ListBox. Here is how we will pas the "ID" as a parameter:
private void list_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
CountryData selectedItemData = (sender as ListBox).SelectedItem as CountryData;
if(selectedItemData != null)
{
NavigationService.Navigate(new Uri(string.Format("/CountryDetails.xaml?parameter={0}",selectedItemData.ID ), UriKind.Relative));
}
}
Detail Page
In our CountryDetails.xaml we will add some UIElements and the following Binding:
<StackPanel x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<TextBlock Text="{Binding Name}" FontSize="50"/>
<TextBlock Text="Flag:" FontSize="30"/>
<Image Source="{Binding Flag}" Stretch="None" HorizontalAlignment="Left"/>
<TextBlock Text="Capital:" FontSize="30"/>
<TextBlock Text="{Binding Capital}" />
<TextBlock Text="Short Description:" FontSize="30"/>
<TextBlock Text="{Binding Description}"/>
</StackPanel>
The most important part in the whole navigation is not to forget to override OnNavigatedTo in the CountryDetails.xaml code behind.
Here we get the passed parameter and again use the repository to call GetCountryById. Finally the data is set as DataContext of the current page:
protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
{
base.OnNavigatedTo(e);
string parameter = this.NavigationContext.QueryString["parameter"];
CountryData country = null;
int countryId = -1;
if (int.TryParse(parameter, out countryId))
{
//ICountryRepository countryRepository = new SimpleCountryRepository();
ICountryRepository countryRepository = new XmlCountryRepository();
country = countryRepository.GetCountryById(countryId);
}
this.DataContext = country;
}
Here is how the final result should look like:
That was all about how to implement Master/Detail Navigation in WP7 using Repository Pattern. Here is the full source code:
I hope that the article was helpful.
You can also follow us on Twitter @winphonegeek
Comments
interesting approach.
posted by: Thomas on 4/30/2011 12:36:29 PM
Interesting approach. I will give it a try.
Ole
posted by: kremqun@gmail.com on 5/30/2011 3:12:28 AM
Hey, thank you for the tutorial and source code. I think this was all I need atm.
I am too new at coding and working on an online XML reader and I tried to implement this into my project.
But, I couldn't figure out the XmlCountryRepository build, It looks too confusing for me :D. Could you give me a hand on how to adapt it. Here is the DownloadString side of my projects.
private void Feed(object Sender, DownloadStringCompletedEventArgs e)
{
XElement xml;
try
{
if (!e.Cancelled)
{
_xml = XElement.Parse(e.Result);
Results.Items.Clear();
foreach (XElement value in _xml.Elements("Items").Elements("Item"))
{
FeedItem _item = new FeedItem();
_item.Fname = value.Element("fname").Value;
_item.Lname = value.Element("lname").Value;
_item.Photo = value.Element("photo").Value;
_item.Age = value.Element("age").Value;
_item.Bio = value.Element("bio").Value;
Results.Items.Add(item);
Thank you
posted by: Rob on 6/1/2011 12:32:53 AM
Thank you thank you thank you!!! I have been looking everywhere for a master/detail tutorial and not been able to find one. You officially just saved my life!
Thank you
posted by: james87 on 6/13/2011 7:23:31 AM
Thank you, very helpful ^^
A better WP7 navigation alternative
posted by: Adrian Aisemberg on 7/5/2011 1:28:37 PM
Kick-ass WP7 navigation without passing any strings. Any object type is supported: http://www.sharpregion.com/easy-windows-phone-7-navigation/
Gracias es muy bueno!
posted by: Marco on 1/10/2012 5:21:21 PM
Hola un saludo, y es justo lo que necesitaba este tutorial.
loadedData.Descendants...
posted by: Gustavo on 1/13/2012 11:52:34 PM
When I'm building XmlCountryRepository, loadedData.Descendants("Country") shows an error saying cuold not find an implementation of the query pattern for source type 'System.Collection.Generic.IENumerable
I added the reference for System.Linq...any ideas ?
Question
posted by: Gustavo on 1/19/2012 11:28:22 PM
I know this is a simple question, but what's the purpose of
ICountryRepository countryRepository = new XmlCountryRepository();
in MainPage.xaml.cs ? Why create the interface instead of going straight for XmCountryRepository()?
multiple navigation
posted by: malar on 2/27/2012 11:45:59 AM
when i click first page list.i want to move to second page list.then i click the list i need description page.please help how to parse XML file like this.
With Webclient
posted by: Battousai90 on 4/10/2012 8:19:49 PM
i'm trying to use your method with webclient Because i don't have a local XML, but a PHP Zend rest adress. The probleme is in this code :
WebClient annonce = new WebClient();
annonce.DownloadStringCompleted += new DownloadStringCompletedEventHandler(annonce_DownloadStringCompleted);
annonce.DownloadStringAsync(new Uri("http://Localhost/TestWebservice/rest/serveur.php?method=Annonces"));
The Handler can't be static. Please can you help to adapt your code for online XML?
Thanx
posted by: TechnoTalkative on 5/4/2012 2:24:31 PM
Thanx a lot for your time and code example.
Thanx,
Our Top Articles & Free books
- Our FREE e-book: "Windows Phone Toolkit In Depth" 2nd edition
- 400+ Windows Phone Development articles in our Article Index
- 21 WP7 Toolkit in Depth articles covering all controls
- 12 WP7 Coding4Fun Toolkit in Depth articles covering all controls
- Performance Tips when creating WP7 apps
- Creating a WP7 Custom Control in 7 Steps
- WP7 working with VisualStates: How to make a ToggleSwitch from CheckBox
- What makes a WP7 App successful
- Creating theme friendly UI in WP7 using OpacityMask
- Implementing Windows Phone 7 DataTemplateSelector and CustomDataTemplateSelector
- All about Splash Screens in WP7 – Creating animated Splash Screen
- Getting Started with Unit Testing in Silverlight for WP7
- WP7 WatermarkedTextBox custom control
Our Top Tips & Samples
- All about WP7 Isolated Storage series
- WP7 Dynamically Generating DataTemplate in code
- 5 tips for a successful WP7 Marketplace submission
- WP7: Navigating to a page in different assembly
- WP7 ContextMenu: answers to popular questions
- WP7 ListBox: answers to popular questions
- WP7 working with Images: Content vs Resource build action
- WP7 Element Binding samples
- WP7 working with XML: reading, filtering and databinding
- Drawing in WP7: #2 Drawing shapes with finger
- WP7 TextBox Light theme problems - the solution
- Changing the WP7 Panorama Background Image dynamically with Animation
