PROWAREtech

articles » archived » xamarin » tableview-class

Xamarin: TableView

How to use the TableView class.

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):

  1. The ListView XAML file (a View file).
  2. The ListView code-behind file.
  3. The TableView XAML file (a View file).
  4. The TableView code-behind file.
  5. The ViewModel file.
  6. The Model file.
  7. 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
		}
	}
}

PROWAREtech

Hello there! How can I help you today?
Ask any question

PROWAREtech

This site uses cookies. Cookies are simple text files stored on the user's computer. They are used for adding features and security to this site. Read the privacy policy.
ACCEPT REJECT