PROWAREtech








Xamarin: TableView
	TableView is designed to put data in a form or to display a menu. TableView is really designed to
	work with MVVM and ListView.
Cells
	Like ListView, there are five cells: TextCell ImageCell EntryCell
	SwitchCell ViewCell
TableView displays a list of different cell types. ListView displays a list of the same type of cells.
	The ViewCell is a custom cell. If wanting to use a Picker, like in the example below, then use a
	ViewCell. The other cells are self explanatory.
Example Code
<?xml version="1.0" encoding="utf-8" ?>
<!--TableViewPage.xaml-->
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
			 xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
			 xmlns:local="clr-namespace:ExampleTableView"
			 x:Class="ExampleTableView.TableViewPage">
	<ContentPage.Padding>
		<OnPlatform x:TypeArguments="Thickness" iOS="0, 20, 0, 0" />
	</ContentPage.Padding>
	<StackLayout>
		<TableView Intent="Form" x:Name="tableView">
			<TableView.BindingContext>
				<local:AboutMe />
			</TableView.BindingContext>
			<TableRoot Title="Data Form">
				<TableSection Title="About Me">
					<EntryCell Label="Name:"
								 Text="{Binding Name}"
								 Placeholder="Enter name"
								 Keyboard="Text" />
					<EntryCell Label="Email:"
								 Text="{Binding Email}"
								 Placeholder="Enter email address"
								 Keyboard="Email" />
					<EntryCell Label="Phone:"
								 Text="{Binding Phone}"
								 Placeholder="Enter phone number"
								 Keyboard="Telephone" />
					<EntryCell Label="Age:"
								 Text="{Binding Age}"
								 Placeholder="Enter age"
								 Keyboard="Numeric" />
					<ViewCell>
						<ViewCell.View>
							<StackLayout Orientation="Horizontal"
										 Padding="16, 0">
								<Label Text="Language:"
										 VerticalOptions="Center" />
								<Picker Title="Choose"
										x:Name="languagePicker"
										SelectedIndex="{Binding LanguageIndex}" />
							</StackLayout>
						</ViewCell.View>
					</ViewCell>
					<SwitchCell Text="Business owner?"
								On="{Binding IsBusinessOwner}" />
					<ViewCell>
						<ViewCell.View>
							<StackLayout Orientation="Horizontal"
										 Padding="16, 0">
								<Label Text="Type:"
										 VerticalOptions="Center" />
								<Picker Title="Choose"
										x:Name="businessTypePicker"
										SelectedIndex="{Binding BusinessTypeIndex}"
										IsEnabled="{Binding IsBusinessOwner}" />
							</StackLayout>
						</ViewCell.View>
					</ViewCell>
					<ViewCell>
						<ViewCell.View>
							<StackLayout Padding="16, 0">
								<Label Text="{Binding BusinessType}"
										 HorizontalOptions="Center" />
							</StackLayout>
						</ViewCell.View>
					</ViewCell>
				</TableSection>
			</TableRoot>
		</TableView>
		<Button Text="Submit"
				HorizontalOptions="Center"
				Clicked="OnSubmit" />
	</StackLayout>
</ContentPage>
// TableViewPage.xaml.cs
using Xamarin.Forms;
namespace ExampleTableView
{
	public partial class TableViewPage : ContentPage
	{
		public TableViewPage()
		{
			InitializeComponent();
			foreach(string s in AboutMe.BusinessTypes)
			{
				businessTypePicker.Items.Add(s);
			}
			foreach (string s in AboutMe.Languages)
			{
				languagePicker.Items.Add(s);
			}
		}
		void OnSubmit(object sender, System.EventArgs e)
		{
			AboutMe aboutMe = (AboutMe)tableView.BindingContext;
			DisplayAlert("Name", aboutMe.Name, "OK");
			DisplayAlert("BusinessOwner?", aboutMe.IsBusinessOwner.ToString(), "OK");
			DisplayAlert("BusinessType", aboutMe.BusinessType, "OK");
		}
	}
}
// AboutMeViewModel.cs
using System.Collections;
using System.ComponentModel;
namespace ExampleTableView
{
	class AboutMe : INotifyPropertyChanged
	{
		private string age;
		public string Age
		{
			get
			{
				return age;
			}
			set
			{
				if (value == age)
					return;
				age = value;
				OnPropertyChanged(nameof(Age));
			}
		}
		private string name;
		public string Name
		{
			get
			{
				return name;
			}
			set
			{
				if (value == name)
					return;
				name = value;
				OnPropertyChanged(nameof(Name));
			}
		}
		private string email;
		public string Email
		{
			get
			{
				return email;
			}
			set
			{
				if (value == email)
					return;
				email = value;
				OnPropertyChanged(nameof(Email));
			}
		}
		private string phone;
		public string Phone
		{
			get
			{
				return phone;
			}
			set
			{
				if (value == phone)
					return;
				phone = value;
				OnPropertyChanged(nameof(Phone));
			}
		}
		private static string[] languages = { "English", "Spanish", "French", "Chinese", "German", "Italian", "Other" };
		public static string[] Languages
		{
			get { return languages; }
		}
		public string Language { private set; get; }
		private int languageIndex = -1;
		public int LanguageIndex
		{
			get
			{
				return languageIndex;
			}
			set
			{
				if (value == languageIndex)
					return;
				languageIndex = value;
				OnPropertyChanged(nameof(LanguageIndex));
				if (languageIndex < languages.Length && languageIndex >= 0)
				{
					Language = languages[languageIndex];
					OnPropertyChanged(nameof(Language));
				}
			}
		}
		private bool isBusinessOwner;
		public bool IsBusinessOwner
		{
			get
			{
				return isBusinessOwner;
			}
			set
			{
				if (value == isBusinessOwner)
					return;
				isBusinessOwner = value;
				OnPropertyChanged(nameof(IsBusinessOwner));
			}
		}
		private static string[] businessTypes = { "Services", "Technology", "Finance", "Other" };
		public static string[] BusinessTypes
		{
			get { return businessTypes; }
		}
		public string BusinessType { private set; get; }
		private int businessTypeIndex = -1;
		public int BusinessTypeIndex
		{
			get
			{
				return businessTypeIndex;
			}
			set
			{
				if (value == businessTypeIndex)
					return;
				businessTypeIndex = value;
				OnPropertyChanged(nameof(BusinessTypeIndex));
				if(businessTypeIndex < businessTypes.Length && businessTypeIndex >= 0)
				{
					BusinessType = businessTypes[businessTypeIndex];
					OnPropertyChanged(nameof(BusinessType));
				}
			}
		}
		public event PropertyChangedEventHandler PropertyChanged;
		void OnPropertyChanged(string propName)
		{
			PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName));
		}
	}
}
Phonebook Example
	This small phonebook app is a continuation of this code on the
	ListView article. It implements several of the techonologies on this site.
There are seven files listed here (download zip):
- The ListViewXAML file (a View file).
- The ListViewcode-behind file.
- The TableViewXAML file (a View file).
- The TableViewcode-behind file.
- The ViewModel file.
- The Model file.
- The file for the App's main entry point.
<?xml version="1.0" encoding="utf-8" ?>
<!--PhonebookListView.xaml-->
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
			 xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
			 xmlns:local="clr-namespace:PhonebookMvvm"
			 x:Class="PhonebookMvvm.PhonebookListView"
			 BackgroundColor="#2686DC"
			 x:Name="listViewPage">
	<ContentPage.Padding>
		<OnPlatform x:TypeArguments="Thickness" iOS="10, 20, 10, 0" Android="10, 0" />
	</ContentPage.Padding>
	<StackLayout>
		<Label TextColor="#333399"
				 Text="{Binding MainText}"
				 HorizontalTextAlignment="Center" />
		<!--ListView is bound to People, not just Phonebook-->
		<ListView ItemsSource="{Binding Phonebook.People}"
					BackgroundColor="Transparent">
			<ListView.ItemTemplate>
				<DataTemplate>
					<!--The binding context of ImageCell is Phonebook.People.Person-->
					<ImageCell ImageSource="{Binding ImageUrl}"
								Text="{Binding Name}" TextColor="#333399"
								Detail="{Binding Phone}" DetailColor="#336699">
						<!--User swipes the item to pull up this context menu-->
						<ImageCell.ContextActions>
							<!--Command is bound to the properties of the Person class-->
							<MenuItem Text="Edit"
										Command="{Binding EditCommand}"
										CommandParameter="{x:Reference Name=listViewPage}" />
										<!--Must pass the Page as a parameter so that the
											EditCommand has access to the Navigation object-->
							<MenuItem Text="Remove"
										IsDestructive="True"
										Command="{Binding RemoveCommand}"
										CommandParameter="{x:Reference Name=listViewPage}" />
										<!--Must pass the Page as a parameter so that the
											RemoveCommand has access to PhonebookViewModel-->
						</ImageCell.ContextActions>
					</ImageCell>
				</DataTemplate>
			</ListView.ItemTemplate>
		</ListView>
	</StackLayout>
</ContentPage>
// PhonebookListView.xaml.cs
using Xamarin.Forms;
namespace PhonebookMvvm
{
	public partial class PhonebookListView : ContentPage
	{
		public PhonebookListView()
		{
			InitializeComponent();
		}
	}
}
<?xml version="1.0" encoding="utf-8" ?>
<!--PhonebookTableView.xaml-->
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
			 xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
			 xmlns:local="clr-namespace:PhonebookMvvm"
			 x:Class="PhonebookMvvm.PhonebookTableView"
			 BackgroundColor="#2686DC">
	<ContentPage.Padding>
		<OnPlatform x:TypeArguments="Thickness" iOS="0, 20, 0, 0" />
	</ContentPage.Padding>
	<StackLayout BackgroundColor="#2686DC">
		<TableView Intent="Form" BackgroundColor="Transparent">
			<TableRoot Title="Data Form">
				<TableSection Title="Edit Phonebook Entry">
					<TextCell Text="{Binding Name}"
								TextColor="#333399" />
					<EntryCell Label="Last Name:"
								 Text="{Binding LastName}"
								 Placeholder="Enter last name"
								 Keyboard="Text" />
					<EntryCell Label="First Name:"
								 Text="{Binding FirstName}"
								 Placeholder="Enter first name"
								 Keyboard="Text" />
					<EntryCell Label="Phone:"
								 Text="{Binding Phone}"
								 Placeholder="Enter phone number"
								 Keyboard="Telephone" />
					<EntryCell Label="Image URL:"
								 Text="{Binding ImageUrl}"
								 Placeholder="Enter URL"
								 Keyboard="Url" />
					<ViewCell>
						<Image Source="{Binding ImageUrl}"
								 Aspect="AspectFit" />
					</ViewCell>
				</TableSection>
			</TableRoot>
		</TableView>
		<Button Text="Back"
				TextColor="White"
				BackgroundColor="Navy"
				HorizontalOptions="Fill"
				Clicked="OnBackClick" />
	</StackLayout>
</ContentPage>
// PhonebookTableView.xaml.cs
using Xamarin.Forms;
namespace PhonebookMvvm
{
	public partial class PhonebookTableView : ContentPage
	{
		public PhonebookTableView()
		{
			InitializeComponent();
		}
		async void OnBackClick(object sender, System.EventArgs e)
		{
			await Navigation.PopModalAsync();
		}
	}
}
	This code was modified to not set the Phonebook property of the Person class because it has been removed.
	Also, a serializer/deserializer has been added for saving the application state and a method that downloads the phonebook from the
	Internet has been added. This code uses LINQ, see LINQ Tutorial.
// PhonebookViewModel.cs
using System.ComponentModel;
using System.IO;
using System.Net;
using System.Xml.Serialization;
namespace PhonebookMvvm
{
	public class PhonebookViewModel : INotifyPropertyChanged
	{
		Phonebook phonebook = new Phonebook();
		string mainText;
		public string MainText
		{
			get
			{
				return mainText;
			}
			set
			{
				if (value == mainText)
					return;
				mainText = value;
				OnPropertyChanged(nameof(MainText));
			}
		}
		public Phonebook Phonebook
		{
			get
			{
				return phonebook;
			}
			private set
			{
				if (value == phonebook)
					return;
				phonebook = value;
				OnPropertyChanged(nameof(Phonebook));
			}
		}
		public void DownloadPhonebook()
		{
			HttpWebRequest request =
				WebRequest.CreateHttp("http://www.prowaretech.com/_phonebook.xml");
			request.BeginGetResponse((arg) =>
			{
				try
				{
					using (WebResponse response = request.EndGetResponse(arg))
					{
						using (Stream stream = response.GetResponseStream())
						{
							using (StreamReader reader = new StreamReader(stream))
							{
								XmlSerializer xml = new XmlSerializer(typeof(Phonebook));
								Phonebook = xml.Deserialize(reader) as Phonebook;
								Phonebook.SortPeople();
							}
						}
					}
				}
				catch (System.Exception ex)
				{
					MainText = ex.Message;
				}
			}, null);
		}
		public string Serialize()
		{
			XmlSerializer xmlSerial = new XmlSerializer(typeof(PhonebookViewModel));
			using (StringWriter strWriter = new StringWriter())
			{
				xmlSerial.Serialize(strWriter, this);
				return strWriter.GetStringBuilder().ToString();
			}
		}
		public static PhonebookViewModel Deserialize(string xmlString)
		{
			XmlSerializer xmlSerial = new XmlSerializer(typeof(PhonebookViewModel));
			using (StringReader strReader = new StringReader(xmlString))
			{
				return (PhonebookViewModel)xmlSerial.Deserialize(strReader);
			}
		}
		public event PropertyChangedEventHandler PropertyChanged;
		void OnPropertyChanged(string propName)
		{
			PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName));
		}
	}
}
	The Person class no longer has a Phonebook property as it did in the ListView article.
	A SortPeople() method was added to Phonebook.
// PhonebookModel.cs
using System.ComponentModel;
using System.Xml.Serialization;
using System.Windows.Input;
using System.Collections.ObjectModel;
using System.Linq;
namespace PhonebookMvvm
{
	public class Phonebook : INotifyPropertyChanged
	{
		ObservableCollection<Person> people = new ObservableCollection<Person>();
		public ObservableCollection<Person> People
		{
			get
			{
				return people;
			}
			set
			{
				if (value == people)
					return;
				people = value;
				OnPropertyChanged(nameof(People));
			}
		}
		public void SortPeople()
		{
			if(People.Count > 1)
			{
				// use Linq to sort People (Person collection)
				var query = from pb in People
							orderby pb.Name
							select pb;
				ObservableCollection<Person> p =
					new ObservableCollection<Person>(query);
				People.Clear();
				People = p;
			}
		}
		public event PropertyChangedEventHandler PropertyChanged;
		void OnPropertyChanged(string propName)
		{
			PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName));
		}
	}
	public class Person : INotifyPropertyChanged
	{
		string lastName, firstName, phone, imageUrl;
		public Person()
		{
			EditCommand = new Xamarin.Forms.Command(async (page) =>
			{
				await ((PhonebookListView)page).Navigation.PushModalAsync(new PhonebookTableView() { BindingContext = this });
			});
			RemoveCommand = new Xamarin.Forms.Command((page) =>
			{
				((PhonebookViewModel)((PhonebookListView)page).BindingContext).Phonebook.People.Remove(this);
			});
		}
		public string ImageUrl
		{
			get
			{
				return imageUrl;
			}
			set
			{
				if (value == imageUrl)
					return;
				imageUrl = value;
				OnPropertyChanged(nameof(ImageUrl));
			}
		}
		[XmlIgnore]
		public string Name
		{
			get
			{
				System.Text.StringBuilder name = new System.Text.StringBuilder(100);
				if (lastName == null && firstName == null)
				{
					name.Append("UNDEFINED");
				}
				else if (lastName == null)
				{
					name.Append(firstName);
				}
				else if (firstName == null)
				{
					name.Append(lastName);
				}
				else
				{
					name.Append(lastName);
					name.Append(", ");
					name.Append(firstName);
				}
				return name.ToString();
			}
		}
		public string LastName
		{
			get
			{
				return lastName;
			}
			set
			{
				if (value == lastName)
					return;
				lastName = value;
				OnPropertyChanged(nameof(LastName));
				OnPropertyChanged(nameof(Name));
			}
		}
		public string FirstName
		{
			get
			{
				return firstName;
			}
			set
			{
				if (value == firstName)
					return;
				firstName = value;
				OnPropertyChanged(nameof(FirstName));
				OnPropertyChanged(nameof(Name));
			}
		}
		public string Phone
		{
			get
			{
				return phone;
			}
			set
			{
				if (value == phone)
					return;
				phone = value;
				OnPropertyChanged(nameof(Phone));
			}
		}
		[XmlIgnore]
		public ICommand EditCommand { private set; get; }
		[XmlIgnore]
		public ICommand RemoveCommand { private set; get; }
		public event PropertyChangedEventHandler PropertyChanged;
		void OnPropertyChanged(string propName)
		{
			PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName));
		}
	}
}
Added code to save the application state. When the phonebook is empty then it will download the one on the Internet.
// App.cs
using Xamarin.Forms;
namespace PhonebookMvvm
{
	public class App : Application
	{
		public App()
		{
			PhonebookViewModel viewModel;
			if (Properties.ContainsKey(nameof(PhonebookViewModel)))
			{
				viewModel = PhonebookViewModel.Deserialize((string)Properties[nameof(PhonebookViewModel)]);
			}
			else
			{
				viewModel = new PhonebookViewModel();
			}
			if (viewModel.Phonebook.People.Count == 0)
			{
				viewModel.DownloadPhonebook();
			}
			MainPage = new PhonebookListView()
			{
				BindingContext = viewModel
			};
		}
		protected override void OnStart()
		{
			// Handle when your app starts
		}
		protected override void OnSleep()
		{
			// Handle when your app sleeps
			PhonebookViewModel viewModel = (PhonebookViewModel)((PhonebookListView)MainPage).BindingContext;
			viewModel.Phonebook.SortPeople();
			Properties[nameof(PhonebookViewModel)] = viewModel.Serialize();
		}
		protected override void OnResume()
		{
			// Handle when your app resumes
		}
	}
}