diff --git a/Samples/Program.cs b/Samples/Program.cs
index f6c8837..99b39ad 100644
--- a/Samples/Program.cs
+++ b/Samples/Program.cs
@@ -31,6 +31,7 @@ namespace Samples
new FilesSample ().Publish ();
new DisplayAlertSample ().Publish ();
new EditorSample().Publish();
+ new TipCalcSample().Publish();
new XuzzleSample().Publish();
UI.Present ("/display-alert");
diff --git a/Samples/Samples.csproj b/Samples/Samples.csproj
index 08c02e8..cf5d6b4 100644
--- a/Samples/Samples.csproj
+++ b/Samples/Samples.csproj
@@ -38,6 +38,9 @@
MSBuild:UpdateDesignTimeXaml
+
+ MSBuild:Compile
+
MSBuild:UpdateDesignTimeXaml
@@ -46,10 +49,7 @@
-
-
-
-
+
Exe
diff --git a/Samples/TipCalc/DoubleRoundingConverter.cs b/Samples/TipCalc/DoubleRoundingConverter.cs
new file mode 100644
index 0000000..ce6494c
--- /dev/null
+++ b/Samples/TipCalc/DoubleRoundingConverter.cs
@@ -0,0 +1,33 @@
+using System;
+using System.Globalization;
+using Xamarin.Forms;
+
+namespace TipCalc
+{
+ public class DoubleRoundingConverter : IValueConverter
+ {
+ public object Convert(object value, Type targetType,
+ object parameter, CultureInfo culture)
+ {
+ return Round((double)value, parameter);
+ }
+
+ public object ConvertBack(object value, Type targetType,
+ object parameter, CultureInfo culture)
+ {
+ return Round((double)value, parameter);
+ }
+
+ double Round(double number, object parameter)
+ {
+ double precision = 1;
+
+ // Assume parameter is string encoding precision.
+ if (parameter != null)
+ {
+ precision = Double.Parse((string)parameter);
+ }
+ return precision * Math.Round(number / precision);
+ }
+ }
+}
diff --git a/Samples/TipCalc/DoubleToStringConverter.cs b/Samples/TipCalc/DoubleToStringConverter.cs
new file mode 100644
index 0000000..447d488
--- /dev/null
+++ b/Samples/TipCalc/DoubleToStringConverter.cs
@@ -0,0 +1,32 @@
+using System;
+using System.Globalization;
+using Xamarin.Forms;
+
+namespace TipCalc
+{
+ public class DoubleToStringConverter : IValueConverter
+ {
+ public object Convert(object value, Type targetType,
+ object parameter, CultureInfo culture)
+ {
+ // Assumes value is double.
+ double number = (double)value;
+
+ // Return empty string for a zero (good for Entry views).
+ if (number == 0)
+ {
+ return "";
+ }
+
+ return number.ToString();
+ }
+
+ public object ConvertBack(object value, Type targetType,
+ object parameter, CultureInfo culture)
+ {
+ double number = 0;
+ Double.TryParse((string)value, out number);
+ return number;
+ }
+ }
+}
diff --git a/Samples/TipCalc/TipCalcModel.cs b/Samples/TipCalc/TipCalcModel.cs
new file mode 100644
index 0000000..f3164f7
--- /dev/null
+++ b/Samples/TipCalc/TipCalcModel.cs
@@ -0,0 +1,111 @@
+using System;
+using System.ComponentModel;
+
+namespace TipCalc
+{
+ class TipCalcModel : INotifyPropertyChanged
+ {
+ double subTotal, postTaxTotal, tipPercent, tipAmount, total;
+
+ public event PropertyChangedEventHandler PropertyChanged;
+
+ public double SubTotal
+ {
+ set
+ {
+ if (subTotal != value)
+ {
+ subTotal = value;
+ OnPropertyChanged("SubTotal");
+ Recalculate();
+ }
+ }
+ get
+ {
+ return subTotal;
+ }
+ }
+
+ public double PostTaxTotal
+ {
+ set
+ {
+ if (postTaxTotal != value)
+ {
+ postTaxTotal = value;
+ OnPropertyChanged("PostTaxTotal");
+ Recalculate();
+ }
+ }
+ get
+ {
+ return postTaxTotal;
+ }
+ }
+
+ public double TipPercent
+ {
+ set
+ {
+ if (tipPercent != value)
+ {
+ tipPercent = value;
+ OnPropertyChanged("TipPercent");
+ Recalculate();
+ }
+ }
+ get
+ {
+ return tipPercent;
+ }
+ }
+
+ public double TipAmount
+ {
+ set
+ {
+ if (tipAmount != value)
+ {
+ tipAmount = value;
+ OnPropertyChanged("TipAmount");
+ }
+ }
+ get
+ {
+ return tipAmount;
+ }
+ }
+
+ public double Total
+ {
+ set
+ {
+ if (total != value)
+ {
+ total = value;
+ OnPropertyChanged("Total");
+ }
+ }
+ get
+ {
+ return total;
+ }
+ }
+
+ void Recalculate()
+ {
+ this.TipAmount = Math.Round(this.TipPercent * this.SubTotal / 100, 2);
+
+ // Round total to nearest quarter.
+ this.Total = Math.Round(4 * (this.PostTaxTotal + this.TipAmount)) / 4;
+ }
+
+ protected void OnPropertyChanged(string propertyName)
+ {
+ if (PropertyChanged != null)
+ {
+ PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
+ }
+ }
+ }
+}
diff --git a/Samples/TipCalc/TipCalcPage.xaml b/Samples/TipCalc/TipCalcPage.xaml
new file mode 100644
index 0000000..f8b69cb
--- /dev/null
+++ b/Samples/TipCalc/TipCalcPage.xaml
@@ -0,0 +1,55 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Samples/TipCalc/TipCalcPage.xaml.cs b/Samples/TipCalc/TipCalcPage.xaml.cs
new file mode 100644
index 0000000..fd9823e
--- /dev/null
+++ b/Samples/TipCalc/TipCalcPage.xaml.cs
@@ -0,0 +1,10 @@
+namespace TipCalc
+{
+ public partial class TipCalcPage
+ {
+ public TipCalcPage()
+ {
+ InitializeComponent();
+ }
+ }
+}
diff --git a/Samples/TipCalcSample.cs b/Samples/TipCalcSample.cs
new file mode 100644
index 0000000..a3440d7
--- /dev/null
+++ b/Samples/TipCalcSample.cs
@@ -0,0 +1,21 @@
+using Ooui;
+using Xamarin.Forms;
+
+namespace Samples
+{
+ public class TipCalcSample : ISample
+ {
+ public string Title => "Xamarin.Forms TipCalc";
+
+ public Ooui.Element CreateElement()
+ {
+ var page = new TipCalc.TipCalcPage();
+ return page.GetOouiElement();
+ }
+
+ public void Publish()
+ {
+ UI.Publish("/tipcalc", CreateElement);
+ }
+ }
+}