[WorldClock] Creating basic places page 66/116466/4
authork.stepaniuk <k.stepaniuk@partner.samsung.com>
Thu, 23 Feb 2017 11:06:39 +0000 (12:06 +0100)
committerk.stepaniuk <k.stepaniuk@partner.samsung.com>
Thu, 2 Mar 2017 09:35:22 +0000 (10:35 +0100)
Communication between dependent pages
Adding behaviour for assigning viewModels to views.
Passing navigation mechanizm to viewModels.

Change-Id: I6d00e12f8a5572d9e0016cc4ef9aaaeac3aadbb7

17 files changed:
Clock/Clock.TizenMobile/Clock.TizenMobile.cs
Clock/Clock.TizenMobile/Clock.TizenMobile.csproj
Clock/Clock.TizenMobile/Clock.cs
Clock/Clock.TizenMobile/ViewModel/AlarmViewModel.cs [deleted file]
Clock/Clock.TizenMobile/Views/WorldClockView.xaml
Clock/Clock.TizenMobile/tizen-manifest.xml
Clock/Clock/Clock.csproj
clock/Clock.TizenMobile/Clock.Dependency.cs [new file with mode: 0644]
clock/Clock/Abstractions/IAppControl.cs [new file with mode: 0644]
clock/Clock/Model/ClockLocation.cs [new file with mode: 0644]
clock/Clock/Mvvm/INavigationAware.cs [new file with mode: 0644]
clock/Clock/Mvvm/ViewModelLocationProvider.cs [new file with mode: 0644]
clock/Clock/Mvvm/ViewModelLocator.cs [new file with mode: 0644]
clock/Clock/Tools/Converters/ClockLocationToOffsetConverter.cs [new file with mode: 0644]
clock/Clock/Tools/Converters/ClockLocationToPMDesignationConverter.cs [new file with mode: 0644]
clock/Clock/Tools/Converters/ClockLocationToTimeConverter.cs [new file with mode: 0644]
clock/Clock/ViewModel/WorldClockViewModel.cs [new file with mode: 0644]

index 07f9b9c..d827849 100644 (file)
@@ -1,3 +1,6 @@
+using Clock;
+using Clock.Mvvm;
+using Clock.ViewModel;
 using System;
 
 namespace Clock.TizenMobile
index 66037d3..32aaeb2 100644 (file)
@@ -49,6 +49,7 @@
   <ItemGroup>
     <Compile Include="Clock.cs" />
     <Compile Include="Clock.TizenMobile.cs" />
+    <Compile Include="Clock.Dependency.cs" />
     <Compile Include="Controls\FloatingButtonRenderer.cs" />
     <Compile Include="Properties\AssemblyInfo.cs" />
     <Compile Include="Controls\TimePickerRenderer.cs" />
index a4b777b..68e444f 100644 (file)
@@ -5,16 +5,26 @@ using System.Text;
 
 using Xamarin.Forms;
 using Clock.TizenMobile.Views;
+using Clock.Mvvm;
+using Clock.ViewModel;
+using Clock.Abstractions;
+using Clock.TizenMobile;
 
-namespace Clock.TizenMobile
+namespace Clock
 {
     public class App : Application
     {
         public App()
         {
+
             MainPage = new NavigationPage(new MainView());
         }
 
+        static App()
+        {
+            ViewModelsModule.RegisterViewModels();
+        }
+
         protected override void OnStart()
         {
             // Handle when your app starts
diff --git a/Clock/Clock.TizenMobile/ViewModel/AlarmViewModel.cs b/Clock/Clock.TizenMobile/ViewModel/AlarmViewModel.cs
deleted file mode 100644 (file)
index f00a78c..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-
-namespace Clock.ViewModel
-{
-    public class AlarmViewModel
-    {
-    }
-}
index d5e1065..3a4c933 100644 (file)
@@ -1,23 +1,33 @@
 <?xml version="1.0" encoding="utf-8" ?>
-<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
+<ContentPage x:Class="Clock.TizenMobile.Views.WorldClockView"
+             xmlns="http://xamarin.com/schemas/2014/forms"
              xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
              xmlns:controls="clr-namespace:Clock.TizenMobile.Controls;assembly=Clock.TizenMobile"
-             x:Class="Clock.TizenMobile.Views.WorldClockView">
-
-  <AbsoluteLayout>
+             xmlns:converters="clr-namespace:Clock.Tools.Converters;assembly=Clock"
+             xmlns:mvvm="clr-namespace:Clock.Mvvm;assembly=Clock"
+             mvvm:ViewModelLocator.AutoWireViewModel="True">
+    <ContentPage.Resources>
+        <ResourceDictionary>
+            <converters:ClockLocationToTimeConverter x:Key="LocationToTimeConverter"/>
+            <converters:ClockLocationToPMDesignationConverter x:Key="LocationToPmDesignationConverter"/>
+            <converters:ClockLocationToOffsetConverter x:Key="ClockLocationToOffsetConverter"/>
+        </ResourceDictionary>
+    </ContentPage.Resources>
+    
+    <AbsoluteLayout>
     <Image Source="images/clock_world_map_01.png" AbsoluteLayout.LayoutBounds="0, 0.025, 1, 0.37" AbsoluteLayout.LayoutFlags="All"/>
     <Button BackgroundColor="Transparent" Image="images/clock_icon_world_clock_arrow_left.png" AbsoluteLayout.LayoutBounds="0.015, 0.205, 0.08, 0.073" AbsoluteLayout.LayoutFlags="All"/>
     <Button BackgroundColor="Transparent" Image="images/clock_icon_world_clock_arrow_right.png" AbsoluteLayout.LayoutBounds="0.99, 0.205, 0.08, 0.073" AbsoluteLayout.LayoutFlags="All"/>
 
     <StackLayout Orientation="Horizontal" HorizontalOptions="Center" AbsoluteLayout.LayoutBounds="0.5, 0.47, 1, 0.086" AbsoluteLayout.LayoutFlags="All">
-      <Label Text="7:50" FontSize="36" TextColor="White"
+      <Label Text="{Binding Location, Converter={StaticResource LocationToTimeConverter}}" FontSize="36" TextColor="White"
            VerticalTextAlignment="Center" HorizontalTextAlignment="Center" VerticalOptions="EndAndExpand"/>
-      <Label Text="a.m." FontSize="24" TextColor="White" Margin="10,0,0,4"
+      <Label Text="{Binding Location, Converter={StaticResource LocationToPmDesignationConverter}}" FontSize="24" TextColor="White" Margin="10,0,0,4"
            VerticalTextAlignment="Center" HorizontalTextAlignment="Center" VerticalOptions="EndAndExpand"/>
-      <Label Text="1h behind" FontSize="24" TextColor="White" Margin="10,0,0,4"
+      <Label Text="{Binding Location, Converter={StaticResource ClockLocationToOffsetConverter}}" FontSize="24" TextColor="White" Margin="10,0,0,4"
            VerticalTextAlignment="Center" HorizontalTextAlignment="Center" VerticalOptions="EndAndExpand"/>
     </StackLayout>
-    <Label Text="Belijning, Denpasar, Guangssho" FontSize="24" TextColor="White" AbsoluteLayout.LayoutBounds="0.5, 0.55, 1, 0.047" AbsoluteLayout.LayoutFlags="All"
+    <Label Text="{Binding Location.Place}" FontSize="24" TextColor="White" AbsoluteLayout.LayoutBounds="0.5, 0.55, 1, 0.047" AbsoluteLayout.LayoutFlags="All"
            VerticalTextAlignment="Center" HorizontalTextAlignment="Center"/>
 
     <BoxView BackgroundColor="White" AbsoluteLayout.LayoutBounds="0, 1, 1, 0.38" AbsoluteLayout.LayoutFlags="All"/>
@@ -25,7 +35,7 @@
            AbsoluteLayout.LayoutBounds="0, 1, 1, 0.38" AbsoluteLayout.LayoutFlags="All"
                  VerticalTextAlignment="Center" HorizontalTextAlignment="Center" VerticalOptions="Center" HorizontalOptions="Center"/>
     <AbsoluteLayout x:Name="FloatingButtonSpacer" AbsoluteLayout.LayoutBounds=".5, 1, 1, .17" AbsoluteLayout.LayoutFlags="All">
-      <controls:FloatingButton AbsoluteLayout.LayoutBounds=".5, .5, 1, 1" AbsoluteLayout.LayoutFlags="All"/>
+      <controls:FloatingButton AbsoluteLayout.LayoutBounds=".5, .5, 1, 1" AbsoluteLayout.LayoutFlags="All" Command="{Binding AddCommand}"/>
     </AbsoluteLayout>
   </AbsoluteLayout>
 </ContentPage>
\ No newline at end of file
index 6821975..a3f6164 100644 (file)
@@ -6,4 +6,7 @@
     <label>Clock</label>
   </ui-application>
   <shortcut-list />
+  <privileges>
+    <privilege>http://tizen.org/privilege/appmanager.launch</privilege>
+  </privileges>
 </manifest>
index e2ed5f9..820559a 100644 (file)
     <ConsolePause>false</ConsolePause>
   </PropertyGroup>
   <ItemGroup>
+    <Compile Include="Abstractions\IAppControl.cs" />
+    <Compile Include="Model\ClockLocation.cs" />
     <Compile Include="Model\Counter.cs" />
     <Compile Include="Model\StopWatch.cs" />
+    <Compile Include="Mvvm\INavigationAware.cs" />
+    <Compile Include="Mvvm\ViewModelLocationProvider.cs" />
+    <Compile Include="Mvvm\ViewModelLocator.cs" />
+    <Compile Include="Tools\Converters\ClockLocationToOffsetConverter.cs" />
+    <Compile Include="Tools\Converters\ClockLocationToPMDesignationConverter.cs" />
+    <Compile Include="Tools\Converters\ClockLocationToTimeConverter.cs" />
     <Compile Include="ViewModel\AlarmViewModel.cs" />
     <Compile Include="ViewModel\StopWatchViewModel.cs" />
     <Compile Include="Properties\AssemblyInfo.cs" />
+    <Compile Include="ViewModel\WorldClockViewModel.cs" />
   </ItemGroup>
   <ItemGroup>
     <!-- <None Include="GettingStarted.Xamarin" /> -->
@@ -58,6 +67,7 @@
       <Private>True</Private>
     </Reference>
   </ItemGroup>
+  <ItemGroup />
   <Import Project="$(MSBuildExtensionsPath32)\Microsoft\Portable\$(TargetFrameworkVersion)\Microsoft.Portable.CSharp.targets" />
   <Import Project="..\..\packages\Xamarin.Forms.2.3.3.175\build\portable-win+net45+wp80+win81+wpa81+MonoAndroid10+Xamarin.iOS10+xamarinmac20\Xamarin.Forms.targets" Condition="Exists('..\..\packages\Xamarin.Forms.2.3.3.175\build\portable-win+net45+wp80+win81+wpa81+MonoAndroid10+Xamarin.iOS10+xamarinmac20\Xamarin.Forms.targets')" />
   <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
diff --git a/clock/Clock.TizenMobile/Clock.Dependency.cs b/clock/Clock.TizenMobile/Clock.Dependency.cs
new file mode 100644 (file)
index 0000000..c1a134b
--- /dev/null
@@ -0,0 +1,76 @@
+using Clock.Abstractions;
+using Clock.TizenMobile.Views;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Xamarin.Forms;
+using Clock.TizenMobile;
+using Clock.Mvvm;
+using Clock.ViewModel;
+using Clock.Model;
+using static Tizen.Applications.AppControl;
+
+[assembly: Dependency(typeof(TizenAppControl))]
+namespace Clock.TizenMobile
+{
+    public class ViewModelsModule
+    {
+        public static void RegisterViewModels()
+        {
+            ViewModelLocationProvider.Register<WorldClockView>(() => new WorldClockViewModel(DependencyService.Get<IAppControl>()));
+        }
+    }
+
+    class TizenAppControl : IAppControl
+    {
+        public const string AppControlOperationPick = "http://tizen.org/appcontrol/operation/pick";
+        public const string WorldclockId = "org.tizen.worldclock-efl";
+
+        public void RequestLocationsModule(Action<ClockLocation> action)
+        {
+            Tizen.Applications.AppControl appControl = new Tizen.Applications.AppControl();
+            appControl.Operation = AppControlOperationPick;
+            appControl.ApplicationId = WorldclockId;
+            appControl.LaunchMode = Tizen.Applications.AppControlLaunchMode.Group;
+            Tizen.Applications.AppControl.SendLaunchRequest(appControl, (launchRequest, replyRequest, result) =>
+            {
+                if (result == Tizen.Applications.AppControlReplyResult.Succeeded && replyRequest != null)
+                {
+                    try
+                    {
+                        action(CreateLocation(replyRequest.ExtraData));
+                    }
+                    catch (FormatException ex)
+                    {
+                        Tizen.Log.Error(nameof(IAppControl), "FormatException while creating location: " + ex.Message);
+                    }
+                    catch (Exception ex)
+                    {
+                        Tizen.Log.Error(nameof(IAppControl), "Exception while creating location: " + ex.Message);
+                    }
+                }
+            });
+        }
+
+        private ClockLocation CreateLocation(ExtraDataCollection collection)
+        {
+            ClockLocation result = new ClockLocation(collection.Get("city_name") as string
+                , collection.Get("country_name") as string
+                , GetOffsetMinutes(collection.Get("timezone") as string)
+                , 0
+                , 0);
+            result.TimeZonePath = collection.Get("tzpath") as string;
+            return result;
+        }
+
+        private int GetOffsetMinutes(string timeZone)
+        {
+            IEnumerable<string> timeNumbers = timeZone.Substring(1)?.Split(':');
+            int hours = Convert.ToInt32(timeNumbers.FirstOrDefault());
+            int minutes = Convert.ToInt32(timeNumbers.Skip(1)?.FirstOrDefault());
+            return (60 * hours + minutes) * ((timeZone[0] == '-') ? (-1) : (1));
+        }
+    }
+}
diff --git a/clock/Clock/Abstractions/IAppControl.cs b/clock/Clock/Abstractions/IAppControl.cs
new file mode 100644 (file)
index 0000000..f88e823
--- /dev/null
@@ -0,0 +1,14 @@
+using Clock.Model;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Clock.Abstractions
+{
+    public interface IAppControl
+    {
+        void RequestLocationsModule(Action<ClockLocation> action);
+    }
+}
diff --git a/clock/Clock/Model/ClockLocation.cs b/clock/Clock/Model/ClockLocation.cs
new file mode 100644 (file)
index 0000000..21fc7ad
--- /dev/null
@@ -0,0 +1,149 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Clock.Model
+{
+    public class ClockLocation
+    {
+        #region Location Collection
+        public static List<ClockLocation> Locations = new List<ClockLocation>
+        {
+            new ClockLocation( "Midway Atoll", "USA", -11 * 60, 43, 218 ),
+            new ClockLocation( "Pago Pago", "American Samoa", -11 * 60, 54, 230 ),
+            new ClockLocation( "Honolulu", "USA", -10 * 60, 57, 223 ),
+            new ClockLocation( "Anchorage", "USA", -9 * 60, 74, 144 ),
+            new ClockLocation( "Los Angeles", "USA", -8 * 60, 131, 196 ),
+            new ClockLocation( "San Francisco", "USA", -8 * 60, 126, 191 ),
+            new ClockLocation( "Vancouver", "Canada", -8 * 60, 122, 166 ),
+            new ClockLocation( "Denver", "USA", -7 * 60, 156, 185 ),
+            new ClockLocation( "Austin", "USA", -6 * 60, 168, 199 ),
+            new ClockLocation( "Chicago", "USA", -6 * 60, 187, 181 ),
+            new ClockLocation( "Guatemala City", "Guatemala", -6 * 60, 181, 233 ),
+            new ClockLocation( "Mexico City", "Mexico", -6 * 60, 167, 244 ),
+            new ClockLocation( "Bogota", "Colombia", -5 * 60, 212, 253 ),
+            new ClockLocation( "Lima", "Peru", -5 * 60, 206, 286 ),
+            new ClockLocation( "Miami", "USA", -5 * 60, 200, 211 ),
+            new ClockLocation( "New York", "USA", -5 * 60, 211, 184 ),
+            new ClockLocation( "Ottawa", "Canada", -5 * 60, 208, 173 ),
+            new ClockLocation( "Washington DC", "USA", -5 * 60, 205, 189 ),
+            new ClockLocation( "Santiago", "Chile", -4 * 60, 217, 327 ),
+            new ClockLocation( "Santo Domingo", "Dominican Republic", -4 * 60, 218, 226 ),
+            new ClockLocation( "Brasilia", "Brazil", -3 * 60, 257, 290 ),
+            new ClockLocation( "Buenos Aires", "Argentina", -3 * 60, 239, 329 ),
+            new ClockLocation( "Nuuk", "Greenland", -3 * 60, 256, 143 ),
+            new ClockLocation( "Sao Paulo", "Brazil", -3 * 60, 260, 308 ),
+            new ClockLocation( "Mid-Atlantic", "", -2 * 60, 296, 266 ),
+            new ClockLocation( "Grytvieken", "South Georgia", -2 * 60, 276, 364 ),
+            new ClockLocation( "Azores", "Portugal", -1 * 60, 308, 188 ),
+            new ClockLocation( "Ponta Delaga", "Portugal", -1 * 60, 300, 190 ),
+            new ClockLocation( "Accra", "Ghana", 0, 343, 250 ),
+            new ClockLocation( "Dakar", "Senegal", 0, 314, 234 ),
+            new ClockLocation( "Lisbon", "Portugal", 0, 327, 187 ),
+            new ClockLocation( "London", "United Kingdom", 0, 343, 162 ),
+            new ClockLocation( "Reykjavik", "Iceland", 0, 306, 138 ),
+            new ClockLocation( "Algiers", "Algeria", 1 * 60, 349, 190 ),
+            new ClockLocation( "Bracelona", "Spain", 1 * 60, 346, 181 ),
+            new ClockLocation( "Berlin", "Germany", 1 * 60, 367, 162 ),
+            new ClockLocation( "Luanda", "Angola", 1 * 60, 367, 278 ),
+            new ClockLocation( "Madrid", "Spain", 1 * 60, 337, 185 ),
+            new ClockLocation( "Paris", "France", 1 * 60, 349, 168 ),
+            new ClockLocation( "Rome", "Italy", 1 * 60, 365, 180 ),
+            new ClockLocation( "Stockholm", "Sweden", 1 * 60, 375, 147 ),
+            new ClockLocation( "Athens", "Greece", 2 * 60, 385, 188 ),
+            new ClockLocation( "Cairo", "Egypt", 2 * 60, 400, 205 ),
+            new ClockLocation( "Cape Town", "South Africa", 2 * 60, 376, 326 ),
+            new ClockLocation( "Harare", "Zimbabwe", 2 * 60, 403, 293 ),
+            new ClockLocation( "Istanbul", "Turkey", 2 * 60, 395, 183 ),
+            new ClockLocation( "Doha", "Qatar", 3 * 60, 437, 213 ),
+            new ClockLocation( "Nairobi", "Kenia", 3 * 60, 409, 263 ),
+            new ClockLocation( "Moscow", "Russia", 3 * 60, 410, 155 ),
+            new ClockLocation( "Tehran", "Iran", 3 * 60 + 30, 435, 192 ),
+            new ClockLocation( "Izhevsk", "Russia", 4 * 60, 437, 152 ),
+            new ClockLocation( "Dubai", "United Arab Emirates", 4 * 60, 441, 213 ),
+            new ClockLocation( "Kabul", "Afghanistan", 4 * 60 + 30, 464, 194 ),
+            new ClockLocation( "Islamabad", "Pakistan", 5 * 60, 467, 196 ),
+            new ClockLocation( "Tashkent", "Uzbekistan", 5 * 60, 467, 182 ),
+            new ClockLocation( "Delhi", "India", 5 * 60 + 30, 481, 203 ),
+            new ClockLocation( "Kathmandu", "Nepal", 5 * 60 + 45, 494, 204 ),
+            new ClockLocation( "Almaty", "Kazakhstan", 6 * 60, 481, 177 ),
+            new ClockLocation( "Bishkek", "Kyrgyzstan", 6 * 60, 477, 179 ),
+            new ClockLocation( "Dhaka", "Bangladesh", 6 * 60, 504, 213 ),
+            new ClockLocation( "Omsk", "Russia", 6 * 60, 474, 156 ),
+            new ClockLocation( "Bangkok", "Thailand", 7 * 60, 522, 235 ),
+            new ClockLocation( "Hanoi", "Vietnam", 7 * 60, 532, 220 ),
+            new ClockLocation( "Jakarta", "Indonesia", 7 * 60, 532, 274 ),
+            new ClockLocation( "Beijing", "China", 8 * 60, 550, 185 ),
+            new ClockLocation( "Kuala Lumpur", "Malaysia", 8 * 60, 523, 255 ),
+            new ClockLocation( "Manila", "Philipines", 8 * 60, 559, 234 ),
+            new ClockLocation( "Perth", "Australia", 8 * 60, 550, 322 ),
+            new ClockLocation( "Singapore", "Republic of Singapore", 8 * 60, 528, 258 ),
+            new ClockLocation( "Ulan Bator", "Mongolia", 8 * 60, 535, 168 ),
+            new ClockLocation( "Seoul", "South Korea", 9 * 60, 570, 190 ),
+            new ClockLocation( "Tokyo", "Japan", 9 * 60, 592, 193 ),
+            new ClockLocation( "Sydney", "Australia", 10 * 60, 613, 326 ),
+            new ClockLocation( "Noumea", "New Caledonia", 11 * 60, 639, 304 ),
+            new ClockLocation( "Khabarowsk", "Russia", 11 * 60, 584, 169 ),
+            new ClockLocation( "Auckland", "New Zealand", 12 * 60, 654, 328 ),
+            new ClockLocation( "Nuku'alofa", "Tonga", 13 * 60, 668, 304 ),
+            new ClockLocation( "Caracas", "Venezuela", -4 * 60 - 30, 219, 247 ),
+            new ClockLocation( "St John's", "Newfoundland", -3 * 60 - 30, 249, 166 ),
+            new ClockLocation( "Yangon", "Myanmar", 6 * 60 + 30, 516, 232 ),
+            new ClockLocation( "Adelaide", "Australia", 9 * 60 + 30, 603, 332 )
+        };
+        #endregion
+
+        public ClockLocation(string place, string country, int gmtMinutesOffset, int x, int y)
+        {
+            Place = place;
+            Country = country;
+            GmtMinutesOffset = gmtMinutesOffset;
+            X = x;
+            Y = y;
+        }
+
+        public string Place
+        {
+            get;
+            set;
+        }
+
+        public string Country
+        {
+            get;
+            set;
+        }
+
+        public int GmtMinutesOffset
+        {
+            get;
+            set;
+        }
+
+        public int X
+        {
+            get;
+            set;
+        }
+
+        public int Y
+        {
+            get;
+            set;
+        }
+
+        public bool IsSummertime
+        {
+            get;
+            set;
+        }
+
+        public string TimeZonePath
+        {
+            get;
+            set;
+        }
+    }
+}
diff --git a/clock/Clock/Mvvm/INavigationAware.cs b/clock/Clock/Mvvm/INavigationAware.cs
new file mode 100644 (file)
index 0000000..a002cff
--- /dev/null
@@ -0,0 +1,18 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Xamarin.Forms;
+
+namespace Clock.Mvvm
+{
+    public interface INavigationAware
+    {
+        INavigation Navigation
+        {
+            get;
+            set;
+        }
+    }
+}
diff --git a/clock/Clock/Mvvm/ViewModelLocationProvider.cs b/clock/Clock/Mvvm/ViewModelLocationProvider.cs
new file mode 100644 (file)
index 0000000..793e1ee
--- /dev/null
@@ -0,0 +1,193 @@
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Reflection;
+
+namespace Clock.Mvvm
+{
+    /// <summary>
+    /// The ViewModelLocationProvider class locates the view model for the view that has the AutoWireViewModelChanged attached property set to true.
+    /// The view model will be located and injected into the view's DataContext. To locate the view, two strategies are used: First the ViewModelLocationProvider
+    /// will look to see if there is a view model factory registered for that view, if not it will try to infer the view model using a convention based approach.
+    /// This class also provide methods for registering the view model factories,
+    /// and also to override the default view model factory and the default view type to view model type resolver.
+    /// </summary>
+    // Documentation on using the MVVM pattern is at http://go.microsoft.com/fwlink/?LinkID=288814&clcid=0x409
+
+    public static class ViewModelLocationProvider
+    {
+        /// <summary>
+        /// A dictionary that contains all the registered factories for the views.
+        /// </summary>
+        static Dictionary<string, Func<object>> _factories = new Dictionary<string, Func<object>>();
+
+        /// <summary>
+        /// A dictionary that contains all the registered ViewModel types for the views.
+        /// </summary>
+        static Dictionary<string, Type> _typeFactories = new Dictionary<string, Type>();
+
+        /// <summary>
+        /// The default view model factory which provides the ViewModel type as a parameter.
+        /// </summary>
+        static Func<Type, object> _defaultViewModelFactory = type => Activator.CreateInstance(type);
+
+        /// <summary>
+        /// ViewModelfactory that provides the View instance and ViewModel type as parameters.
+        /// </summary>
+        static Func<object, Type, object> _defaultViewModelFactoryWithViewParameter;
+
+        /// <summary>
+        /// Default view type to view model type resolver, assumes the view model is in same assembly as the view type, but in the "ViewModels" namespace.
+        /// </summary>
+        static Func<Type, Type> _defaultViewTypeToViewModelTypeResolver =
+            viewType =>
+            {
+                var viewName = viewType.FullName;
+                viewName = viewName.Replace(".Views.", ".ViewModels.");
+                var viewAssemblyName = viewType.GetTypeInfo().Assembly.FullName;
+                var suffix = viewName.EndsWith("View") ? "Model" : "ViewModel";
+                var viewModelName = String.Format(CultureInfo.InvariantCulture, "{0}{1}, {2}", viewName, suffix, viewAssemblyName);
+                return Type.GetType(viewModelName);
+            };
+
+        /// <summary>
+        /// Sets the default view model factory.
+        /// </summary>
+        /// <param name="viewModelFactory">The view model factory which provides the ViewModel type as a parameter.</param>
+        public static void SetDefaultViewModelFactory(Func<Type, object> viewModelFactory)
+        {
+            _defaultViewModelFactory = viewModelFactory;
+        }
+
+        /// <summary>
+        /// Sets the default view model factory.
+        /// </summary>
+        /// <param name="viewModelFactory">The view model factory that provides the View instance and ViewModel type as parameters.</param>
+        public static void SetDefaultViewModelFactory(Func<object, Type, object> viewModelFactory)
+        {
+            _defaultViewModelFactoryWithViewParameter = viewModelFactory;
+        }
+
+        /// <summary>
+        /// Sets the default view type to view model type resolver.
+        /// </summary>
+        /// <param name="viewTypeToViewModelTypeResolver">The view type to view model type resolver.</param>
+        public static void SetDefaultViewTypeToViewModelTypeResolver(Func<Type, Type> viewTypeToViewModelTypeResolver)
+        {
+            _defaultViewTypeToViewModelTypeResolver = viewTypeToViewModelTypeResolver;
+        }
+
+        /// <summary>
+        /// Automatically looks up the viewmodel that corresponds to the current view, using two strategies:
+        /// It first looks to see if there is a mapping registered for that view, if not it will fallback to the convention based approach.
+        /// </summary>
+        /// <param name="view">The dependency object, typically a view.</param>
+        /// <param name="setDataContextCallback">The call back to use to create the binding between the View and ViewModel</param>
+        public static void AutoWireViewModelChanged(object view, Action<object, object> setDataContextCallback)
+        {
+            // Try mappings first
+            object viewModel = GetViewModelForView(view);
+
+            // try to use ViewModel type
+            if (viewModel == null)
+            {
+                //check type mappings
+                var viewModelType = GetViewModelTypeForView(view.GetType());
+
+                // fallback to convention based
+                if (viewModelType == null)
+                {
+                    viewModelType = _defaultViewTypeToViewModelTypeResolver(view.GetType());
+                }
+
+                if (viewModelType == null)
+                {
+                    return;
+                }
+
+                viewModel = _defaultViewModelFactoryWithViewParameter != null ? _defaultViewModelFactoryWithViewParameter(view, viewModelType) : _defaultViewModelFactory(viewModelType);
+            }
+
+
+            setDataContextCallback(view, viewModel);
+        }
+
+        /// <summary>
+        /// Gets the view model for the specified view.
+        /// </summary>
+        /// <param name="view">The view that the view model wants.</param>
+        /// <returns>The ViewModel that corresponds to the view passed as a parameter.</returns>
+        private static object GetViewModelForView(object view)
+        {
+            var viewKey = view.GetType().ToString();
+
+            // Mapping of view models base on view type (or instance) goes here
+            if (_factories.ContainsKey(viewKey))
+            {
+                return _factories[viewKey]();
+            }
+
+            return null;
+        }
+
+        /// <summary>
+        /// Gets the ViewModel type for the specified view.
+        /// </summary>
+        /// <param name="view">The View that the ViewModel wants.</param>
+        /// <returns>The ViewModel type that corresponds to the View.</returns>
+        private static Type GetViewModelTypeForView(Type view)
+        {
+            var viewKey = view.ToString();
+
+            if (_typeFactories.ContainsKey(viewKey))
+            {
+                return _typeFactories[viewKey];
+            }
+
+            return null;
+        }
+
+        /// <summary>
+        /// Registers the ViewModel factory for the specified view type.
+        /// </summary>
+        /// <typeparam name="T">The View</typeparam>
+        /// <param name="factory">The ViewModel factory.</param>
+        public static void Register<T>(Func<object> factory)
+        {
+            Register(typeof(T).ToString(), factory);
+        }
+
+        /// <summary>
+        /// Registers the ViewModel factory for the specified view type name.
+        /// </summary>
+        /// <param name="viewTypeName">The name of the view type.</param>
+        /// <param name="factory">The ViewModel factory.</param>
+        public static void Register(string viewTypeName, Func<object> factory)
+        {
+            _factories[viewTypeName] = factory;
+        }
+
+        /// <summary>
+        /// Registers a ViewModel type for the specified view type.
+        /// </summary>
+        /// <typeparam name="T">The View</typeparam>
+        /// <typeparam name="VM">The ViewModel</typeparam>
+        public static void Register<T, VM>()
+        {
+            var viewType = typeof(T);
+            var viewModelType = typeof(VM);
+
+            Register(viewType.ToString(), viewModelType);
+        }
+
+        /// <summary>
+        /// Registers a ViewModel type for the specified view.
+        /// </summary>
+        /// <param name="viewTypeName">The View type name</param>
+        /// <param name="viewModelType">The ViewModel type</param>
+        public static void Register(string viewTypeName, Type viewModelType)
+        {
+            _typeFactories[viewTypeName] = viewModelType;
+        }
+    }
+}
\ No newline at end of file
diff --git a/clock/Clock/Mvvm/ViewModelLocator.cs b/clock/Clock/Mvvm/ViewModelLocator.cs
new file mode 100644 (file)
index 0000000..8ce948a
--- /dev/null
@@ -0,0 +1,63 @@
+using Xamarin.Forms;
+
+namespace Clock.Mvvm
+{
+    /// <summary>
+    /// This class defines the attached property and related change handler that calls the <see cref="Prism.Mvvm.ViewModelLocationProvider"/>.
+    /// </summary>
+    public static class ViewModelLocator
+    {
+        /// <summary>
+        /// Instructs Prism whether or not to automatically create an instance of a ViewModel using a convention, and assign the associated View's <see cref="Xamarin.Forms.BindableObject.BindingContext"/> to that instance.
+        /// </summary>
+        public static readonly BindableProperty AutoWireViewModelProperty =
+            BindableProperty.CreateAttached("AutoWireViewModel", typeof(bool?), typeof(ViewModelLocator), null, propertyChanged: OnAutoWireViewModelChanged);
+
+        /// <summary>
+        /// Gets the AutowireViewModel property value.
+        /// </summary>
+        /// <param name="bindable"></param>
+        /// <returns></returns>
+        public static bool? GetAutoWireViewModel(BindableObject bindable)
+        {
+            return (bool?)bindable.GetValue(ViewModelLocator.AutoWireViewModelProperty);
+        }
+
+        /// <summary>
+        /// Sets the AutowireViewModel property value.  If <c>true</c>, creates an instance of a ViewModel using a convention, and sets the associated View's <see cref="Xamarin.Forms.BindableObject.BindingContext"/> to that instance.
+        /// </summary>
+        /// <param name="bindable"></param>
+        /// <param name="value"></param>
+        public static void SetAutowireViewModel(BindableObject bindable, bool? value)
+        {
+            bindable.SetValue(ViewModelLocator.AutoWireViewModelProperty, value);
+        }
+
+        private static void OnAutoWireViewModelChanged(BindableObject bindable, object oldValue, object newValue)
+        {
+            bool? bNewValue = (bool?)newValue;
+            if (bNewValue.HasValue && bNewValue.Value)
+            {
+                ViewModelLocationProvider.AutoWireViewModelChanged(bindable, Bind);
+            }
+        }
+
+        /// <summary>
+        /// Sets the <see cref="Xamarin.Forms.BindableObject.BindingContext"/> of a View
+        /// </summary>
+        /// <param name="view">The View to set the <see cref="Xamarin.Forms.BindableObject.BindingContext"/> on</param>
+        /// <param name="viewModel">The object to use as the <see cref="Xamarin.Forms.BindableObject.BindingContext"/> for the View</param>
+        static void Bind(object view, object viewModel)
+        {
+            BindableObject element = view as BindableObject;
+            if (element != null)
+            {
+                element.BindingContext = viewModel;
+            }
+            if (viewModel is INavigationAware && view is VisualElement)
+            {
+                (viewModel as INavigationAware).Navigation = (view as VisualElement).Navigation;
+            }
+        }
+    }
+}
diff --git a/clock/Clock/Tools/Converters/ClockLocationToOffsetConverter.cs b/clock/Clock/Tools/Converters/ClockLocationToOffsetConverter.cs
new file mode 100644 (file)
index 0000000..dfbce7f
--- /dev/null
@@ -0,0 +1,41 @@
+using Clock.Model;
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Xamarin.Forms;
+
+namespace Clock.Tools.Converters
+{
+    public class ClockLocationToOffsetConverter : IValueConverter
+    {
+        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+        {
+            StringBuilder builder = new StringBuilder();
+            var location = value as ClockLocation;
+            if (location == null || location.GmtMinutesOffset == 0)
+            {
+                builder.Append("same as local");
+            }
+            else
+            {
+                var hours = Math.Abs(location.GmtMinutesOffset) / 60;
+                builder.Append(hours);
+                builder.Append("h");
+                if (location.GmtMinutesOffset - hours * 60 >= 30)
+                {
+                    builder.Append(" 30m");
+                }
+                builder.Append(location.GmtMinutesOffset > 0 ? " ahead" : " behind");
+            }
+            return builder.ToString();
+        }
+
+        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+        {
+            throw new NotImplementedException();
+        }
+    }
+}
diff --git a/clock/Clock/Tools/Converters/ClockLocationToPMDesignationConverter.cs b/clock/Clock/Tools/Converters/ClockLocationToPMDesignationConverter.cs
new file mode 100644 (file)
index 0000000..3ded811
--- /dev/null
@@ -0,0 +1,26 @@
+using Clock.Model;
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Xamarin.Forms;
+
+namespace Clock.Tools.Converters
+{
+    public class ClockLocationToPMDesignationConverter : IValueConverter
+    {
+        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+        {
+            var location = value as ClockLocation;
+            var time = DateTime.Now.AddMinutes(location?.GmtMinutesOffset ?? 0);
+            return time.Hour < 12 ? "a.m." : "p.m.";
+        }
+
+        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+        {
+            throw new NotImplementedException();
+        }
+    }
+}
diff --git a/clock/Clock/Tools/Converters/ClockLocationToTimeConverter.cs b/clock/Clock/Tools/Converters/ClockLocationToTimeConverter.cs
new file mode 100644 (file)
index 0000000..b7dacf9
--- /dev/null
@@ -0,0 +1,26 @@
+using Clock.Model;
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Xamarin.Forms;
+
+namespace Clock.Tools.Converters
+{
+    public class ClockLocationToTimeConverter : IValueConverter
+    {
+        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+        {
+            var location = value as ClockLocation;
+            var time = DateTime.Now.AddMinutes(location?.GmtMinutesOffset ?? 0);
+            return string.Format("{0:hh:mm}", time);
+        }
+
+        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+        {
+            throw new NotImplementedException();
+        }
+    }
+}
diff --git a/clock/Clock/ViewModel/WorldClockViewModel.cs b/clock/Clock/ViewModel/WorldClockViewModel.cs
new file mode 100644 (file)
index 0000000..fc10e55
--- /dev/null
@@ -0,0 +1,55 @@
+using Clock.Abstractions;
+using Clock.Model;
+using Clock.Mvvm;
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Input;
+using Xamarin.Forms;
+
+namespace Clock.ViewModel
+{
+    public class WorldClockViewModel : INotifyPropertyChanged, INavigationAware
+    {
+        public event PropertyChangedEventHandler PropertyChanged;
+        private IAppControl _appControl = null;
+
+        public WorldClockViewModel( IAppControl appControl )
+        {
+            AddCommand = new Command(Add);
+            _appControl = appControl;
+        }
+
+        public ICommand AddCommand
+        {
+            get;
+            set;
+        }
+
+        public INavigation Navigation
+        {
+            get;
+            set;
+        }
+
+        public ClockLocation Location
+        {
+            get;
+            private set;
+        }
+
+        private void Add()
+        {
+            _appControl?.RequestLocationsModule(OnLocationsResult);
+        }
+
+        private void OnLocationsResult( ClockLocation location )
+        {
+            Location = location;
+            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Location)));
+        }
+    }
+}