diff --git a/Ooui.sln b/Ooui.sln index a5e7898..4cdc2c4 100644 --- a/Ooui.sln +++ b/Ooui.sln @@ -1,21 +1,20 @@ - Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 -VisualStudioVersion = 15.0.26124.0 +VisualStudioVersion = 15.0.27130.2003 MinimumVisualStudioVersion = 15.0.26124.0 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ooui", "Ooui\Ooui.csproj", "{DFDFD036-BF48-4D3A-BF99-88CA1EA8E4B9}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ooui", "Ooui\Ooui.csproj", "{DFDFD036-BF48-4D3A-BF99-88CA1EA8E4B9}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Samples", "Samples\Samples.csproj", "{CDF8BB01-40BB-402F-8446-47AA6F1628F3}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Samples", "Samples\Samples.csproj", "{CDF8BB01-40BB-402F-8446-47AA6F1628F3}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests", "Tests\Tests.csproj", "{78F6E9E7-4322-4F87-8CE9-1EEF1B16D268}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tests", "Tests\Tests.csproj", "{78F6E9E7-4322-4F87-8CE9-1EEF1B16D268}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ooui.Forms", "Ooui.Forms\Ooui.Forms.csproj", "{DB819A2F-91E1-40FB-8D48-6544169966B8}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ooui.Forms", "Ooui.Forms\Ooui.Forms.csproj", "{DB819A2F-91E1-40FB-8D48-6544169966B8}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ooui.AspNetCore", "Ooui.AspNetCore\Ooui.AspNetCore.csproj", "{2EDF0328-698B-458A-B10C-AB1B4786A6CA}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ooui.AspNetCore", "Ooui.AspNetCore\Ooui.AspNetCore.csproj", "{2EDF0328-698B-458A-B10C-AB1B4786A6CA}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "PlatformSamples", "PlatformSamples", "{12ADF328-BBA8-48FC-9AF1-F11B7921D9EA}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AspNetCoreMvc", "PlatformSamples\AspNetCoreMvc\AspNetCoreMvc.csproj", "{7C6D477C-3378-4A86-9C31-AAD51204120B}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AspNetCoreMvc", "PlatformSamples\AspNetCoreMvc\AspNetCoreMvc.csproj", "{7C6D477C-3378-4A86-9C31-AAD51204120B}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -26,46 +25,43 @@ Global Release|x64 = Release|x64 Release|x86 = Release|x86 EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {DFDFD036-BF48-4D3A-BF99-88CA1EA8E4B9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {DFDFD036-BF48-4D3A-BF99-88CA1EA8E4B9}.Debug|Any CPU.Build.0 = Debug|Any CPU - {DFDFD036-BF48-4D3A-BF99-88CA1EA8E4B9}.Debug|x64.ActiveCfg = Debug|x64 - {DFDFD036-BF48-4D3A-BF99-88CA1EA8E4B9}.Debug|x64.Build.0 = Debug|x64 - {DFDFD036-BF48-4D3A-BF99-88CA1EA8E4B9}.Debug|x86.ActiveCfg = Debug|x86 - {DFDFD036-BF48-4D3A-BF99-88CA1EA8E4B9}.Debug|x86.Build.0 = Debug|x86 + {DFDFD036-BF48-4D3A-BF99-88CA1EA8E4B9}.Debug|x64.ActiveCfg = Debug|Any CPU + {DFDFD036-BF48-4D3A-BF99-88CA1EA8E4B9}.Debug|x64.Build.0 = Debug|Any CPU + {DFDFD036-BF48-4D3A-BF99-88CA1EA8E4B9}.Debug|x86.ActiveCfg = Debug|Any CPU + {DFDFD036-BF48-4D3A-BF99-88CA1EA8E4B9}.Debug|x86.Build.0 = Debug|Any CPU {DFDFD036-BF48-4D3A-BF99-88CA1EA8E4B9}.Release|Any CPU.ActiveCfg = Release|Any CPU {DFDFD036-BF48-4D3A-BF99-88CA1EA8E4B9}.Release|Any CPU.Build.0 = Release|Any CPU - {DFDFD036-BF48-4D3A-BF99-88CA1EA8E4B9}.Release|x64.ActiveCfg = Release|x64 - {DFDFD036-BF48-4D3A-BF99-88CA1EA8E4B9}.Release|x64.Build.0 = Release|x64 - {DFDFD036-BF48-4D3A-BF99-88CA1EA8E4B9}.Release|x86.ActiveCfg = Release|x86 - {DFDFD036-BF48-4D3A-BF99-88CA1EA8E4B9}.Release|x86.Build.0 = Release|x86 + {DFDFD036-BF48-4D3A-BF99-88CA1EA8E4B9}.Release|x64.ActiveCfg = Release|Any CPU + {DFDFD036-BF48-4D3A-BF99-88CA1EA8E4B9}.Release|x64.Build.0 = Release|Any CPU + {DFDFD036-BF48-4D3A-BF99-88CA1EA8E4B9}.Release|x86.ActiveCfg = Release|Any CPU + {DFDFD036-BF48-4D3A-BF99-88CA1EA8E4B9}.Release|x86.Build.0 = Release|Any CPU {CDF8BB01-40BB-402F-8446-47AA6F1628F3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {CDF8BB01-40BB-402F-8446-47AA6F1628F3}.Debug|Any CPU.Build.0 = Debug|Any CPU - {CDF8BB01-40BB-402F-8446-47AA6F1628F3}.Debug|x64.ActiveCfg = Debug|x64 - {CDF8BB01-40BB-402F-8446-47AA6F1628F3}.Debug|x64.Build.0 = Debug|x64 - {CDF8BB01-40BB-402F-8446-47AA6F1628F3}.Debug|x86.ActiveCfg = Debug|x86 - {CDF8BB01-40BB-402F-8446-47AA6F1628F3}.Debug|x86.Build.0 = Debug|x86 + {CDF8BB01-40BB-402F-8446-47AA6F1628F3}.Debug|x64.ActiveCfg = Debug|Any CPU + {CDF8BB01-40BB-402F-8446-47AA6F1628F3}.Debug|x64.Build.0 = Debug|Any CPU + {CDF8BB01-40BB-402F-8446-47AA6F1628F3}.Debug|x86.ActiveCfg = Debug|Any CPU + {CDF8BB01-40BB-402F-8446-47AA6F1628F3}.Debug|x86.Build.0 = Debug|Any CPU {CDF8BB01-40BB-402F-8446-47AA6F1628F3}.Release|Any CPU.ActiveCfg = Release|Any CPU {CDF8BB01-40BB-402F-8446-47AA6F1628F3}.Release|Any CPU.Build.0 = Release|Any CPU - {CDF8BB01-40BB-402F-8446-47AA6F1628F3}.Release|x64.ActiveCfg = Release|x64 - {CDF8BB01-40BB-402F-8446-47AA6F1628F3}.Release|x64.Build.0 = Release|x64 - {CDF8BB01-40BB-402F-8446-47AA6F1628F3}.Release|x86.ActiveCfg = Release|x86 - {CDF8BB01-40BB-402F-8446-47AA6F1628F3}.Release|x86.Build.0 = Release|x86 + {CDF8BB01-40BB-402F-8446-47AA6F1628F3}.Release|x64.ActiveCfg = Release|Any CPU + {CDF8BB01-40BB-402F-8446-47AA6F1628F3}.Release|x64.Build.0 = Release|Any CPU + {CDF8BB01-40BB-402F-8446-47AA6F1628F3}.Release|x86.ActiveCfg = Release|Any CPU + {CDF8BB01-40BB-402F-8446-47AA6F1628F3}.Release|x86.Build.0 = Release|Any CPU {78F6E9E7-4322-4F87-8CE9-1EEF1B16D268}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {78F6E9E7-4322-4F87-8CE9-1EEF1B16D268}.Debug|Any CPU.Build.0 = Debug|Any CPU - {78F6E9E7-4322-4F87-8CE9-1EEF1B16D268}.Debug|x64.ActiveCfg = Debug|x64 - {78F6E9E7-4322-4F87-8CE9-1EEF1B16D268}.Debug|x64.Build.0 = Debug|x64 - {78F6E9E7-4322-4F87-8CE9-1EEF1B16D268}.Debug|x86.ActiveCfg = Debug|x86 - {78F6E9E7-4322-4F87-8CE9-1EEF1B16D268}.Debug|x86.Build.0 = Debug|x86 + {78F6E9E7-4322-4F87-8CE9-1EEF1B16D268}.Debug|x64.ActiveCfg = Debug|Any CPU + {78F6E9E7-4322-4F87-8CE9-1EEF1B16D268}.Debug|x64.Build.0 = Debug|Any CPU + {78F6E9E7-4322-4F87-8CE9-1EEF1B16D268}.Debug|x86.ActiveCfg = Debug|Any CPU + {78F6E9E7-4322-4F87-8CE9-1EEF1B16D268}.Debug|x86.Build.0 = Debug|Any CPU {78F6E9E7-4322-4F87-8CE9-1EEF1B16D268}.Release|Any CPU.ActiveCfg = Release|Any CPU {78F6E9E7-4322-4F87-8CE9-1EEF1B16D268}.Release|Any CPU.Build.0 = Release|Any CPU - {78F6E9E7-4322-4F87-8CE9-1EEF1B16D268}.Release|x64.ActiveCfg = Release|x64 - {78F6E9E7-4322-4F87-8CE9-1EEF1B16D268}.Release|x64.Build.0 = Release|x64 - {78F6E9E7-4322-4F87-8CE9-1EEF1B16D268}.Release|x86.ActiveCfg = Release|x86 - {78F6E9E7-4322-4F87-8CE9-1EEF1B16D268}.Release|x86.Build.0 = Release|x86 + {78F6E9E7-4322-4F87-8CE9-1EEF1B16D268}.Release|x64.ActiveCfg = Release|Any CPU + {78F6E9E7-4322-4F87-8CE9-1EEF1B16D268}.Release|x64.Build.0 = Release|Any CPU + {78F6E9E7-4322-4F87-8CE9-1EEF1B16D268}.Release|x86.ActiveCfg = Release|Any CPU + {78F6E9E7-4322-4F87-8CE9-1EEF1B16D268}.Release|x86.Build.0 = Release|Any CPU {DB819A2F-91E1-40FB-8D48-6544169966B8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {DB819A2F-91E1-40FB-8D48-6544169966B8}.Debug|Any CPU.Build.0 = Debug|Any CPU {DB819A2F-91E1-40FB-8D48-6544169966B8}.Debug|x64.ActiveCfg = Debug|Any CPU @@ -103,6 +99,15 @@ Global {7C6D477C-3378-4A86-9C31-AAD51204120B}.Release|x86.ActiveCfg = Release|Any CPU {7C6D477C-3378-4A86-9C31-AAD51204120B}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {7C6D477C-3378-4A86-9C31-AAD51204120B} = {12ADF328-BBA8-48FC-9AF1-F11B7921D9EA} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {9370DD6D-816D-4E0F-8356-835F65DCF3AA} + EndGlobalSection GlobalSection(MonoDevelopProperties) = preSolution Policies = $0 $0.TextStylePolicy = $1 @@ -122,7 +127,4 @@ Global $2.SpacingAfterMethodDeclarationName = True $2.SpaceAfterMethodCallName = True EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {7C6D477C-3378-4A86-9C31-AAD51204120B} = {12ADF328-BBA8-48FC-9AF1-F11B7921D9EA} - EndGlobalSection EndGlobal diff --git a/PlatformSamples/AspNetCoreMvc/Properties/launchSettings.json b/PlatformSamples/AspNetCoreMvc/Properties/launchSettings.json new file mode 100644 index 0000000..e94d59b --- /dev/null +++ b/PlatformSamples/AspNetCoreMvc/Properties/launchSettings.json @@ -0,0 +1,27 @@ +{ + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:58785/", + "sslPort": 0 + } + }, + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "AspNetCoreMvc": { + "commandName": "Project", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "applicationUrl": "http://localhost:58786/" + } + } +} \ No newline at end of file diff --git a/Samples/DotMatrixClock/DotMatrixClockPage.cs b/Samples/DotMatrixClock/DotMatrixClockPage.cs new file mode 100644 index 0000000..ae08948 --- /dev/null +++ b/Samples/DotMatrixClock/DotMatrixClockPage.cs @@ -0,0 +1,170 @@ +using System; +using Xamarin.Forms; + +namespace DotMatrixClock +{ + public partial class DotMatrixClockPage : ContentPage + { + // Total dots horizontally and vertically. + const int horzDots = 41; + const int vertDots = 7; + + // 5 x 7 dot matrix patterns for 0 through 9. + static readonly int[, ,] numberPatterns = new int[10, 7, 5] + { + { + { 0, 1, 1, 1, 0}, { 1, 0, 0, 0, 1}, { 1, 0, 0, 1, 1}, { 1, 0, 1, 0, 1}, + { 1, 1, 0, 0, 1}, { 1, 0, 0, 0, 1}, { 0, 1, 1, 1, 0} + }, + { + { 0, 0, 1, 0, 0}, { 0, 1, 1, 0, 0}, { 0, 0, 1, 0, 0}, { 0, 0, 1, 0, 0}, + { 0, 0, 1, 0, 0}, { 0, 0, 1, 0, 0}, { 0, 1, 1, 1, 0} + }, + { + { 0, 1, 1, 1, 0}, { 1, 0, 0, 0, 1}, { 0, 0, 0, 0, 1}, { 0, 0, 0, 1, 0}, + { 0, 0, 1, 0, 0}, { 0, 1, 0, 0, 0}, { 1, 1, 1, 1, 1} + }, + { + { 1, 1, 1, 1, 1}, { 0, 0, 0, 1, 0}, { 0, 0, 1, 0, 0}, { 0, 0, 0, 1, 0}, + { 0, 0, 0, 0, 1}, { 1, 0, 0, 0, 1}, { 0, 1, 1, 1, 0} + }, + { + { 0, 0, 0, 1, 0}, { 0, 0, 1, 1, 0}, { 0, 1, 0, 1, 0}, { 1, 0, 0, 1, 0}, + { 1, 1, 1, 1, 1}, { 0, 0, 0, 1, 0}, { 0, 0, 0, 1, 0} + }, + { + { 1, 1, 1, 1, 1}, { 1, 0, 0, 0, 0}, { 1, 1, 1, 1, 0}, { 0, 0, 0, 0, 1}, + { 0, 0, 0, 0, 1}, { 1, 0, 0, 0, 1}, { 0, 1, 1, 1, 0} + }, + { + { 0, 0, 1, 1, 0}, { 0, 1, 0, 0, 0}, { 1, 0, 0, 0, 0}, { 1, 1, 1, 1, 0}, + { 1, 0, 0, 0, 1}, { 1, 0, 0, 0, 1}, { 0, 1, 1, 1, 0} + }, + { + { 1, 1, 1, 1, 1}, { 0, 0, 0, 0, 1}, { 0, 0, 0, 1, 0}, { 0, 0, 1, 0, 0}, + { 0, 1, 0, 0, 0}, { 0, 1, 0, 0, 0}, { 0, 1, 0, 0, 0} + }, + { + { 0, 1, 1, 1, 0}, { 1, 0, 0, 0, 1}, { 1, 0, 0, 0, 1}, { 0, 1, 1, 1, 0}, + { 1, 0, 0, 0, 1}, { 1, 0, 0, 0, 1}, { 0, 1, 1, 1, 0} + }, + { + { 0, 1, 1, 1, 0}, { 1, 0, 0, 0, 1}, { 1, 0, 0, 0, 1}, { 0, 1, 1, 1, 1}, + { 0, 0, 0, 0, 1}, { 0, 0, 0, 1, 0}, { 0, 1, 1, 0, 0} + }, + }; + + // Dot matrix pattern for a colon. + static readonly int[,] colonPattern = new int[7, 2] + { + { 0, 0 }, { 1, 1 }, { 1, 1 }, { 0, 0 }, { 1, 1 }, { 1, 1 }, { 0, 0 } + }; + + // BoxView colors for on and off. + static readonly Color colorOn = Color.Red; + static readonly Color colorOff = new Color(0.5, 0.5, 0.5, 0.25); + + // Box views for 6 digits, 7 rows, 5 columns. + BoxView[, ,] digitBoxViews = new BoxView[6, 7, 5]; + + public DotMatrixClockPage() + { + InitializeComponent(); + + // BoxView dot dimensions. + double height = 0.85 / vertDots; + double width = 0.85 / horzDots; + + // Create and assemble the BoxViews. + double xIncrement = 1.0 / (horzDots - 1); + double yIncrement = 1.0 / (vertDots - 1); + double x = 0; + + for (int digit = 0; digit < 6; digit++) + { + for (int col = 0; col < 5; col++) + { + double y = 0; + + for (int row = 0; row < 7; row++) + { + // Create the digit BoxView and add to layout. + BoxView boxView = new BoxView(); + digitBoxViews[digit, row, col] = boxView; + absoluteLayout.Children.Add(boxView, + new Rectangle(x, y, width, height), + AbsoluteLayoutFlags.All); + y += yIncrement; + } + x += xIncrement; + } + x += xIncrement; + + // Colons between the hours, minutes, and seconds. + if (digit == 1 || digit == 3) + { + int colon = digit / 2; + + for (int col = 0; col < 2; col++) + { + double y = 0; + + for (int row = 0; row < 7; row++) + { + // Create the BoxView and set the color. + BoxView boxView = new BoxView + { + Color = colonPattern[row, col] == 1 ? + colorOn : colorOff + }; + absoluteLayout.Children.Add(boxView, + new Rectangle(x, y, width, height), + AbsoluteLayoutFlags.All); + y += yIncrement; + } + x += xIncrement; + } + x += xIncrement; + } + } + + // Set the timer and initialize with a manual call. + Device.StartTimer(TimeSpan.FromSeconds(1), OnTimer); + OnTimer(); + } + + void OnPageSizeChanged(object sender, EventArgs args) + { + // No chance a display will have an aspect ratio > 41:7 + absoluteLayout.HeightRequest = vertDots * Width / horzDots; + } + + bool OnTimer() + { + DateTime dateTime = DateTime.Now; + + // Convert 24-hour clock to 12-hour clock. + int hour = (dateTime.Hour + 11) % 12 + 1; + + // Set the dot colors for each digit separately. + SetDotMatrix(0, hour / 10); + SetDotMatrix(1, hour % 10); + SetDotMatrix(2, dateTime.Minute / 10); + SetDotMatrix(3, dateTime.Minute % 10); + SetDotMatrix(4, dateTime.Second / 10); + SetDotMatrix(5, dateTime.Second % 10); + return true; + } + + void SetDotMatrix(int index, int digit) + { + for (int row = 0; row < 7; row++) + for (int col = 0; col < 5; col++) + { + bool isOn = numberPatterns[digit, row, col] == 1; + Color color = isOn ? colorOn : colorOff; + digitBoxViews[index, row, col].Color = color; + } + } + } +} diff --git a/Samples/DotMatrixClock/DotMatrixClockPage.xaml b/Samples/DotMatrixClock/DotMatrixClockPage.xaml new file mode 100644 index 0000000..41ec329 --- /dev/null +++ b/Samples/DotMatrixClock/DotMatrixClockPage.xaml @@ -0,0 +1,10 @@ + + + + diff --git a/Samples/DotMatrixClockSample.cs b/Samples/DotMatrixClockSample.cs new file mode 100644 index 0000000..b901e86 --- /dev/null +++ b/Samples/DotMatrixClockSample.cs @@ -0,0 +1,21 @@ +using Ooui; +using Xamarin.Forms; + +namespace Samples +{ + public class DotMatrixClockSample : ISample + { + public string Title => "Xamarin.Forms DoMatrixClock"; + + public Ooui.Element CreateElement() + { + var page = new DotMatrixClock.DotMatrixClockPage(); + return page.GetOouiElement(); + } + + public void Publish() + { + UI.Publish("/dotmatrixclock", CreateElement); + } + } +} diff --git a/Samples/Program.cs b/Samples/Program.cs index 7a3f706..6e1152a 100644 --- a/Samples/Program.cs +++ b/Samples/Program.cs @@ -30,7 +30,11 @@ namespace Samples new DrawSample ().Publish (); new FilesSample ().Publish (); new DisplayAlertSample ().Publish (); + new DotMatrixClockSample().Publish(); new EditorSample().Publish(); + new TipCalcSample().Publish(); + new WeatherAppSample().Publish(); + new XuzzleSample().Publish(); UI.Present ("/display-alert"); diff --git a/Samples/Samples.csproj b/Samples/Samples.csproj index 72ede52..bdbc539 100644 --- a/Samples/Samples.csproj +++ b/Samples/Samples.csproj @@ -38,6 +38,15 @@ MSBuild:UpdateDesignTimeXaml + + MSBuild:Compile + + + MSBuild:Compile + + + MSBuild:Compile + MSBuild:UpdateDesignTimeXaml @@ -46,6 +55,9 @@ + + + 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); + } + } +} diff --git a/Samples/WeatherApp/Core.cs b/Samples/WeatherApp/Core.cs new file mode 100644 index 0000000..31ee5e9 --- /dev/null +++ b/Samples/WeatherApp/Core.cs @@ -0,0 +1,41 @@ +using System; +using System.Threading.Tasks; + +namespace WeatherApp +{ + public class Core + { + public static async Task GetWeather(string zipCode) + { + //Sign up for a free API key at http://openweathermap.org/appid + string key = "fc9f6c524fc093759cd28d41fda89a1b"; + string queryString = "http://api.openweathermap.org/data/2.5/weather?zip=" + + zipCode + "&appid=" + key; + + var results = await DataService.getDataFromService(queryString).ConfigureAwait(false); + + if (results["weather"] != null) + { + Weather weather = new Weather + { + Title = (string)results["name"], + Temperature = (string)results["main"]["temp"] + " F", + Wind = (string)results["wind"]["speed"] + " mph", + Humidity = (string)results["main"]["humidity"] + " %", + Visibility = (string)results["weather"][0]["main"] + }; + + DateTime time = new DateTime(1970, 1, 1, 0, 0, 0, 0); + DateTime sunrise = time.AddSeconds((double)results["sys"]["sunrise"]); + DateTime sunset = time.AddSeconds((double)results["sys"]["sunset"]); + weather.Sunrise = sunrise.ToString() + " UTC"; + weather.Sunset = sunset.ToString() + " UTC"; + return weather; + } + else + { + return null; + } + } + } +} diff --git a/Samples/WeatherApp/DataService.cs b/Samples/WeatherApp/DataService.cs new file mode 100644 index 0000000..ca1516e --- /dev/null +++ b/Samples/WeatherApp/DataService.cs @@ -0,0 +1,25 @@ +using System.Threading.Tasks; +using Newtonsoft.Json; +using System.Net.Http; +using Newtonsoft.Json.Linq; + +namespace WeatherApp +{ + public class DataService + { + public static async Task getDataFromService(string queryString) + { + HttpClient client = new HttpClient(); + var response = await client.GetAsync(queryString); + + JContainer data = null; + if (response != null) + { + string json = response.Content.ReadAsStringAsync().Result; + data = (JContainer)JsonConvert.DeserializeObject(json); + } + + return data; + } + } +} \ No newline at end of file diff --git a/Samples/WeatherApp/Weather.cs b/Samples/WeatherApp/Weather.cs new file mode 100644 index 0000000..e6e30f4 --- /dev/null +++ b/Samples/WeatherApp/Weather.cs @@ -0,0 +1,26 @@ +namespace WeatherApp +{ + public class Weather + { + public string Title { get; set; } + public string Temperature { get; set; } + public string Wind { get; set; } + public string Humidity { get; set; } + public string Visibility { get; set; } + public string Sunrise { get; set; } + public string Sunset { get; set; } + + public Weather() + { + //Because labels bind to these values, set them to an empty string to + //ensure that the label appears on all platforms by default. + this.Title = " "; + this.Temperature = " "; + this.Wind = " "; + this.Humidity = " "; + this.Visibility = " "; + this.Sunrise = " "; + this.Sunset = " "; + } + } +} \ No newline at end of file diff --git a/Samples/WeatherApp/WeatherPage.xaml b/Samples/WeatherApp/WeatherPage.xaml new file mode 100644 index 0000000..29bc1e0 --- /dev/null +++ b/Samples/WeatherApp/WeatherPage.xaml @@ -0,0 +1,92 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Samples/WeatherApp/WeatherPage.xaml.cs b/Samples/WeatherApp/WeatherPage.xaml.cs new file mode 100644 index 0000000..b1f3c07 --- /dev/null +++ b/Samples/WeatherApp/WeatherPage.xaml.cs @@ -0,0 +1,31 @@ +using System; +using Xamarin.Forms; + +namespace WeatherApp +{ + public partial class WeatherPage : ContentPage + { + public WeatherPage() + { + InitializeComponent(); + this.Title = "Sample Weather App"; + getWeatherBtn.Clicked += GetWeatherBtn_Clicked; + + //Set the default binding to a default object for now + this.BindingContext = new Weather(); + } + + private async void GetWeatherBtn_Clicked(object sender, EventArgs e) + { + if (!String.IsNullOrEmpty(zipCodeEntry.Text)) + { + Weather weather = await Core.GetWeather(zipCodeEntry.Text); + if (weather != null) + { + this.BindingContext = weather; + getWeatherBtn.Text = "Search Again"; + } + } + } + } +} \ No newline at end of file diff --git a/Samples/WeatherAppSample.cs b/Samples/WeatherAppSample.cs new file mode 100644 index 0000000..3119138 --- /dev/null +++ b/Samples/WeatherAppSample.cs @@ -0,0 +1,21 @@ +using Ooui; +using Xamarin.Forms; + +namespace Samples +{ + public class WeatherAppSample : ISample + { + public string Title => "Xamarin.Forms WeatherApp"; + + public Ooui.Element CreateElement() + { + var page = new WeatherApp.WeatherPage(); + return page.GetOouiElement(); + } + + public void Publish() + { + UI.Publish("/weatherapp", CreateElement); + } + } +} diff --git a/Samples/Xuzzle/XuzzlePage.cs b/Samples/Xuzzle/XuzzlePage.cs new file mode 100644 index 0000000..6acf714 --- /dev/null +++ b/Samples/Xuzzle/XuzzlePage.cs @@ -0,0 +1,280 @@ +using System; +using System.Threading.Tasks; +using Xamarin.Forms; + +namespace Xuzzle +{ + public class XuzzlePage : ContentPage + { + // Number of squares horizontally and vertically, + // but if you change it, some code will break. + static readonly int NUM = 4; + + // Array of XuzzleSquare views, and empty row & column. + XuzzleSquare[,] squares = new XuzzleSquare[NUM, NUM]; + int emptyRow = NUM - 1; + int emptyCol = NUM - 1; + + StackLayout stackLayout; + AbsoluteLayout absoluteLayout; + Button randomizeButton; + Label timeLabel; + double squareSize; + bool isBusy; + bool isPlaying; + + public XuzzlePage() + { + // AbsoluteLayout to host the squares. + absoluteLayout = new AbsoluteLayout() + { + HorizontalOptions = LayoutOptions.Center, + VerticalOptions = LayoutOptions.Center + }; + + // Create XuzzleSquare's for all the rows and columns. + string text = "{XAMARIN.FORMS}"; + string winText = "CONGRATULATIONS"; + int index = 0; + + for (int row = 0; row < NUM; row++) + { + for (int col = 0; col < NUM; col++) + { + // But skip the last one! + if (row == NUM - 1 && col == NUM - 1) + break; + + // Instantiate XuzzleSquare. + XuzzleSquare square = new XuzzleSquare(text[index], winText[index], index) + { + Row = row, + Col = col + }; + + // Add tap recognition + TapGestureRecognizer tapGestureRecognizer = new TapGestureRecognizer + { + Command = new Command(OnSquareTapped), + CommandParameter = square + }; + square.GestureRecognizers.Add(tapGestureRecognizer); + + // Add it to the array and the AbsoluteLayout. + squares[row, col] = square; + absoluteLayout.Children.Add(square); + index++; + } + } + + // This is the "Randomize" button. + randomizeButton = new Button + { + Text = "Randomize", + HorizontalOptions = LayoutOptions.Center, + VerticalOptions = LayoutOptions.CenterAndExpand + }; + randomizeButton.Clicked += OnRandomizeButtonClicked; + + // Label to display elapsed time. + timeLabel = new Label + { + FontSize = Device.GetNamedSize(NamedSize.Large, typeof(Label)), + FontAttributes = FontAttributes.Bold, + HorizontalOptions = LayoutOptions.Center, + VerticalOptions = LayoutOptions.CenterAndExpand + }; + + // Put everything in a StackLayout. + stackLayout = new StackLayout + { + Children = { + new StackLayout { + VerticalOptions = LayoutOptions.FillAndExpand, + HorizontalOptions = LayoutOptions.FillAndExpand, + Children = { + randomizeButton, + timeLabel + } + }, + absoluteLayout + } + }; + stackLayout.SizeChanged += OnStackSizeChanged; + + // And set that to the content of the page. + this.Padding = new Thickness(0, Device.RuntimePlatform == Device.iOS ? 20 : 0, 0, 0); + this.Content = stackLayout; + } + + void OnStackSizeChanged(object sender, EventArgs args) + { + double width = stackLayout.Width; + double height = stackLayout.Height; + + if (width <= 0 || height <= 0) + return; + + // Orient StackLayout based on portrait/landscape mode. + stackLayout.Orientation = (width < height) ? StackOrientation.Vertical : + StackOrientation.Horizontal; + + // Calculate square size and position based on stack size. + squareSize = Math.Min(width, height) / NUM; + absoluteLayout.WidthRequest = NUM * squareSize; + absoluteLayout.HeightRequest = NUM * squareSize; + + foreach (View view in absoluteLayout.Children) + { + XuzzleSquare square = (XuzzleSquare)view; + square.SetLabelFont(0.4 * squareSize, FontAttributes.Bold); + + AbsoluteLayout.SetLayoutBounds(square, + new Rectangle(square.Col * squareSize, + square.Row * squareSize, + squareSize, + squareSize)); + } + } + + async void OnSquareTapped(object parameter) + { + if (isBusy) + return; + + isBusy = true; + XuzzleSquare tappedSquare = (XuzzleSquare)parameter; + await ShiftIntoEmpty(tappedSquare.Row, tappedSquare.Col); + isBusy = false; + + // Check for a "win". + if (isPlaying) + { + int index; + + for (index = 0; index < NUM * NUM - 1; index++) + { + int row = index / NUM; + int col = index % NUM; + XuzzleSquare square = squares[row, col]; + if (square == null || square.Index != index) + break; + } + + // We have a winner! + if (index == NUM * NUM - 1) + { + isPlaying = false; + await DoWinAnimation(); + } + } + } + + async Task ShiftIntoEmpty(int tappedRow, int tappedCol, uint length = 100) + { + // Shift columns. + if (tappedRow == emptyRow && tappedCol != emptyCol) + { + int inc = Math.Sign(tappedCol - emptyCol); + int begCol = emptyCol + inc; + int endCol = tappedCol + inc; + + for (int col = begCol; col != endCol; col += inc) + { + await AnimateSquare(emptyRow, col, emptyRow, emptyCol, length); + } + } + // Shift rows. + else if (tappedCol == emptyCol && tappedRow != emptyRow) + { + int inc = Math.Sign(tappedRow - emptyRow); + int begRow = emptyRow + inc; + int endRow = tappedRow + inc; + + for (int row = begRow; row != endRow; row += inc) + { + await AnimateSquare(row, emptyCol, emptyRow, emptyCol, length); + } + } + } + + async Task AnimateSquare(int row, int col, int newRow, int newCol, uint length) + { + // The Square to be animated. + XuzzleSquare animaSquare = squares[row, col]; + + // The destination rectangle. + Rectangle rect = new Rectangle(squareSize * emptyCol, + squareSize * emptyRow, + squareSize, + squareSize); + + // This is the actual animation call. + await animaSquare.LayoutTo(rect, length); + + // Set several variables and properties for new layout. + squares[newRow, newCol] = animaSquare; + animaSquare.Row = newRow; + animaSquare.Col = newCol; + squares[row, col] = null; + emptyRow = row; + emptyCol = col; + } + + async void OnRandomizeButtonClicked(object sender, EventArgs args) + { + Button button = (Button)sender; + button.IsEnabled = false; + Random rand = new Random(); + + isBusy = true; + + // Simulate some fast crazy taps. + for (int i = 0; i < 100; i++) + { + await ShiftIntoEmpty(rand.Next(NUM), emptyCol, 25); + await ShiftIntoEmpty(emptyRow, rand.Next(NUM), 25); + } + button.IsEnabled = true; + + isBusy = false; + + // Prepare for playing. + DateTime startTime = DateTime.Now; + + Device.StartTimer(TimeSpan.FromSeconds(1), () => { + // Round duration and get rid of milliseconds. + TimeSpan timeSpan = (DateTime.Now - startTime) + + TimeSpan.FromSeconds(0.5); + timeSpan = new TimeSpan(timeSpan.Hours, timeSpan.Minutes, timeSpan.Seconds); + + // Display the duration. + if (isPlaying) + timeLabel.Text = timeSpan.ToString("t"); + return isPlaying; + }); + this.isPlaying = true; + } + + async Task DoWinAnimation() + { + // Inhibit all input. + randomizeButton.IsEnabled = false; + isBusy = true; + + for (int cycle = 0; cycle < 2; cycle++) + { + foreach (XuzzleSquare square in squares) + if (square != null) + await square.AnimateWinAsync(cycle == 1); + + if (cycle == 0) + await Task.Delay(1500); + } + + // All input. + randomizeButton.IsEnabled = true; + isBusy = false; + } + } +} diff --git a/Samples/Xuzzle/XuzzleSquare.cs b/Samples/Xuzzle/XuzzleSquare.cs new file mode 100644 index 0000000..203db53 --- /dev/null +++ b/Samples/Xuzzle/XuzzleSquare.cs @@ -0,0 +1,73 @@ +using System.Threading.Tasks; +using Xamarin.Forms; + +namespace Xuzzle +{ + public class XuzzleSquare : ContentView + { + Label label; + string normText, winText; + + public XuzzleSquare(char normChar, char winChar, int index) + { + this.Index = index; + this.normText = normChar.ToString(); + this.winText = winChar.ToString(); + + // A Frame surrounding two Labels. + label = new Label + { + Text = this.normText, + HorizontalOptions = LayoutOptions.Center, + VerticalOptions = LayoutOptions.CenterAndExpand + }; + + Label tinyLabel = new Label + { + Text = (index + 1).ToString(), + FontSize = Device.GetNamedSize(NamedSize.Micro, typeof(Label)), + HorizontalOptions = LayoutOptions.End + }; + + this.Padding = new Thickness(3); + this.Content = new Frame + { + OutlineColor = Color.Accent, + Padding = new Thickness(5, 10, 5, 0), + Content = new StackLayout + { + Spacing = 0, + Children = { + label, + tinyLabel, + } + } + }; + + // Don't let touch pass us by. + this.BackgroundColor = Color.Transparent; + } + + // Retain current Row and Col position. + public int Index { private set; get; } + + public int Row { set; get; } + + public int Col { set; get; } + + public async Task AnimateWinAsync(bool isReverse) + { + uint length = 150; + await Task.WhenAll(this.ScaleTo(3, length), this.RotateTo(180, length)); + label.Text = isReverse ? normText : winText; + await Task.WhenAll(this.ScaleTo(1, length), this.RotateTo(360, length)); + this.Rotation = 0; + } + + public void SetLabelFont(double fontSize, FontAttributes attributes) + { + label.FontSize = fontSize; + label.FontAttributes = attributes; + } + } +} diff --git a/Samples/XuzzleSample.cs b/Samples/XuzzleSample.cs new file mode 100644 index 0000000..10f1d2f --- /dev/null +++ b/Samples/XuzzleSample.cs @@ -0,0 +1,21 @@ +using Ooui; +using Xamarin.Forms; + +namespace Samples +{ + public class XuzzleSample : ISample + { + public string Title => "Xamarin.Forms Xuzzle"; + + public Ooui.Element CreateElement() + { + var page = new Xuzzle.XuzzlePage(); + return page.GetOouiElement(); + } + + public void Publish() + { + UI.Publish("/xuzzle", CreateElement); + } + } +}