Xamarin Forms Sketch demonstrating data binding (without strings, to nested objects, to unlimited number of fields) and common app/sketch code. Note: remove the .cs from the file name, it is only there to make GitHub format it as C#
using Xamarin.Forms; | |
// Additional guidance: see http://vincenth.net/blog/archive/2014/11/27/how-to-share-xamarin-forms-data-binding-code-across-xamarin-sketches-and-apps-without-using-strings.aspx | |
// NOTE: Once support for creating classes is added to Xamarin Sketches, | |
// there is no need for this Tuple + enum + BindName + regular expression workaround; | |
// you can then simply create design data classes in the Sketch and bind to that using | |
// the same syntax in both projects and sketches, e.g.: | |
// SetBinding(..., (Person boundPerson) => boundPerson.Name) | |
// BindName helper function for use with binding to design data in Sketches. | |
// Usage: SetBinding(..., BindName(SomeEnum.SomeValue)) | |
// Maps an enum property name value to a Tuple ItemX property name. | |
// Maps 7th and later properties to nested Tuples in Item7. | |
// | |
// Use this regular expression to convert SetBinding() code from sketch to project: | |
// E.g. SetBinding(..., BindName(Person.Name) will become SetBinding(..., (Person boundPerson) => boundPerson.Name | |
// Search: SetBinding\s*\((?<bindableProperty>[^,]+)\s*,\s*BindName\((?<class>[^\.\)]*)\.(?<property>[^\.\)]*)\) | |
// Replace: SetBinding(${bindableProperty}, (${class} bound${class}) => bound${class}.${property} | |
// | |
// Use this regular expression to convert SetBinding() code from project to sketch: | |
// E.g. SetBinding(..., (Person boundPerson) => boundPerson.Name will become SetBinding(..., BindName(Person.Name) | |
// Search: SetBinding\s*\((?<bindableProperty>[^,]+)\s*,\s*\(\s*(?<class>\w*)\s+bound\k<class>\s*\)\s*=>\s*bound\k<class>\.(?<property>\w*) | |
// Replace: SetBinding(${bindableProperty}, BindName(${class}.${property}) | |
Func<Enum, string> BindName = (Enum propertyName) => | |
{ | |
const int MaxTupleItems = 7; | |
int fieldIndex = int.Parse(propertyName.ToString("D")); | |
string bindName = ""; | |
while (fieldIndex >= MaxTupleItems) | |
{ | |
bindName += "Item" + MaxTupleItems.ToString() + "."; | |
fieldIndex -= (MaxTupleItems - 1); | |
} | |
bindName += "Item" + fieldIndex.ToString(); | |
return bindName; | |
}; | |
// Names of bindable classes with properties as enum names with values for Sketch design data | |
// Set the first value in each enum to 1 to map to Tuple Item1 | |
// Subsequent items will map to subsequent Tuple fields | |
// You can overcome the limit of 7 properties in a Tuple by making the 7th property a nested Tuple, | |
// which can then contain properties 7 through 12. You can keep nesting tuples to add more properties. | |
enum Name | |
{ | |
First = 1, | |
Last | |
}; | |
enum Person | |
{ | |
Image = 1, | |
Name, | |
Description, | |
Property4, | |
Property5, | |
Property6, | |
Property7, | |
Property8, | |
Property9 | |
}; | |
// Design data for Sketch. | |
// Each tuple in the list represents a Person class with a nested Name class, which is represented by a nested Tuple. | |
// Property7..9 are represented by a nested Tuple in Item7, demonstrating how to overcome the limit of 7 properties in a Tuple. | |
var persons = new List<Tuple<UriImageSource,Tuple<string, string>,string, string, string, string, Tuple<string, string, string>>> { | |
Tuple.Create(new UriImageSource { Uri = new Uri("http://bit.ly/1s07h2W") }, new Tuple<string, string>("Miguel", "de Icaza"), "CTO and Co-founder", "4", "5", "6", new Tuple<string, string, string>("7", "8", "9th bound property value\nNo strings, just code :-)")), | |
Tuple.Create(new UriImageSource { Uri = new Uri("http://bit.ly/1rYGvGU") }, new Tuple<string, string>("Nat", "Friedman"), "CEO and Co-founder", "4", "5", "6", new Tuple<string, string, string>("7", "8", "9th bound property value\nUse Class.Property")), | |
Tuple.Create(new UriImageSource { Uri = new Uri("http://photos1.meetupstatic.com/photos/member/2/4/a/6/member_74169382.jpeg") }, new Tuple<string, string>("Vincent", "Hoogendoorn"), "Developer", "4", "5", "6", new Tuple<string, string, string>("7", "8", "9th bound property value\nCompiler checks name")), | |
Tuple.Create(new UriImageSource { Uri = new Uri("http://bit.ly/1EhFsao") }, new Tuple<string, string>("Stephanie", "Schatz"), "SVP of Sales and Customer Success", "4", "5", "6", new Tuple<string, string, string>("7", "8", "9th bound property value\nDot notate: Class.Property")), | |
Tuple.Create(new UriImageSource { Uri = new Uri("http://bit.ly/1oIYpso") }, new Tuple<string, string>("Joanne", ""), "VP of Marketing", "4", "5", "6", new Tuple<string, string, string>("7", "8", "9th bound field value")), | |
Tuple.Create(new UriImageSource { Uri = new Uri("http://bit.ly/10CbVZE") }, new Tuple<string, string>("Joseph", ""), "Director of Developer Relations and Co-founder", "4", "5", "6", new Tuple<string, string, string>("7", "8", "9th bound field value")), | |
Tuple.Create(new UriImageSource { Uri = new Uri("http://bit.ly/1vCRbKh ") }, new Tuple<string, string>("Rob", ""), "VP of Business Development", "4", "5", "6", new Tuple<string, string, string>("7", "8", "9th bound property value")), | |
Tuple.Create(new UriImageSource { Uri = new Uri("http://bit.ly/1rPp1vm") }, new Tuple<string, string>("Bryan", ""), "Vice President of Education Services", "4", "5", "6", new Tuple<string, string, string>("7", "8", "9th bound property value")), | |
Tuple.Create(new UriImageSource { Uri = new Uri("http://bit.ly/1sXguu1") }, new Tuple<string, string>("Jason", ""), "Team Lead - Xamarin.Forms", "4", "5", "6", new Tuple<string, string, string>("7", "8", "9th bound property value")), | |
}; | |
var personTemplate = new DataTemplate(() => | |
{ | |
// Start of common App/Sketch code fragment #1 ***************************** | |
var image = new Image { | |
WidthRequest = 100, | |
HeightRequest = 100 | |
}; | |
image.SetBinding(Image.SourceProperty, BindName(Person.Image)); | |
var firstNameLabel = new Label(); | |
firstNameLabel.SetBinding(Label.BindingContextProperty, BindName(Person.Name)); | |
firstNameLabel.SetBinding(Label.TextProperty, BindName(Name.First)); | |
var lastNameLabel = new Label { | |
TextColor = Color.Gray | |
}; | |
lastNameLabel.SetBinding(Label.BindingContextProperty, BindName(Person.Name)); | |
lastNameLabel.SetBinding(Label.TextProperty, BindName(Name.Last)); | |
var titleLabel = new Label(); | |
titleLabel.SetBinding(Label.TextProperty, BindName(Person.Description)); | |
var ninthFieldLabel = new Label { | |
TextColor = Color.White, | |
BackgroundColor = Color.Purple | |
}; | |
ninthFieldLabel.SetBinding(Label.TextProperty, BindName(Person.Property9)); | |
var nameLayout = new StackLayout { | |
Children = { firstNameLabel, lastNameLabel }, | |
Orientation = StackOrientation.Horizontal | |
}; | |
var textLayout = new StackLayout { | |
Children = { nameLayout, titleLabel, ninthFieldLabel } | |
}; | |
var cellLayout = new StackLayout { | |
Padding = 10, | |
Orientation = StackOrientation.Horizontal, | |
Children = { image, textLayout } | |
}; | |
// End of common App/Sketch code fragment #1 ******************************* | |
var cell = new ViewCell { | |
View = cellLayout, | |
}; | |
return cell; | |
}); | |
var page = new ContentPage(); | |
// Start of common App/Sketch code fragment #2 ***************************** | |
var searchBar = new SearchBar { Placeholder = "Search ..." }; | |
var listView = new ListView { | |
ItemTemplate = personTemplate, | |
ItemsSource = persons, | |
RowHeight = 110 | |
}; | |
var layout = new StackLayout { | |
Children = { | |
searchBar, | |
listView | |
} | |
}; | |
page.Padding = new Thickness(0, 20, 0, 0); | |
page.Content = layout; | |
// End of common App/Sketch code fragment #2 ******************************* | |
RootPage.Children.Add(page); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment