Ver Fonte

Merge branch 'master' into InvalidCharInput

Jurjen Biewenga há 8 anos atrás
pai
commit
e9b61be182
100 ficheiros alterados com 5591 adições e 1865 exclusões
  1. 1 0
      .gitignore
  2. 5 0
      .ncrunch/Avalonia.Designer.HostApp.v3.ncrunchproject
  3. 5 0
      .ncrunch/RemoteTest.v3.ncrunchproject
  4. 1 2
      .travis.yml
  5. 218 3
      Avalonia.sln
  6. 0 4
      appveyor.yml
  7. 1 1
      build/MonoMac.props
  8. 0 5
      build/Serilog.Sinks.Trace.props
  9. 3 1
      build/Serilog.props
  10. 0 5
      build/Splat.props
  11. 8 8
      build/XUnit.props
  12. 0 3
      docs/.gitignore
  13. 0 21
      docs/README.md
  14. 0 3
      docs/build.cmd
  15. 0 55
      docs/docfx.json
  16. 0 43
      docs/guidelines/build.md
  17. 0 60
      docs/guidelines/contributing.md
  18. 0 4
      docs/guidelines/toc.yml
  19. BIN
      docs/images/avalonia-video.png
  20. BIN
      docs/images/cross-platform.png
  21. BIN
      docs/images/hello-world-xaml.png
  22. BIN
      docs/images/inspection-support.png
  23. BIN
      docs/images/screen.png
  24. 0 37
      docs/index.md
  25. 0 225
      docs/intro.md
  26. 0 2
      docs/serve.cmd
  27. 0 101
      docs/spec/architecture.md
  28. 0 156
      docs/spec/binding-from-code.md
  29. 0 99
      docs/spec/binding-from-xaml.md
  30. 0 199
      docs/spec/defining-properties.md
  31. 0 54
      docs/spec/logging.md
  32. 0 111
      docs/spec/styles.md
  33. 0 14
      docs/spec/toc.yml
  34. 0 102
      docs/spec/working-with-properties.md
  35. 0 13
      docs/template/partials/footer.tmpl.partial
  36. 0 10
      docs/toc.yml
  37. 0 161
      docs/tutorial/from-wpf.md
  38. 0 10
      docs/tutorial/gettingstarted.md
  39. BIN
      docs/tutorial/images/add-dialogs.png
  40. 0 86
      docs/tutorial/nuget.md
  41. 0 6
      docs/tutorial/toc.yml
  42. 33 4
      packages.cake
  43. 21 41
      readme.md
  44. 1 12
      samples/BindingTest/App.xaml.cs
  45. 0 2
      samples/BindingTest/BindingTest.csproj
  46. 0 1
      samples/ControlCatalog.Desktop/ControlCatalog.Desktop.csproj
  47. 6 15
      samples/ControlCatalog.Desktop/Program.cs
  48. 6 11
      samples/ControlCatalog.NetCore/Program.cs
  49. 6 0
      samples/ControlCatalog/ControlCatalog.csproj
  50. 1 0
      samples/ControlCatalog/MainView.xaml
  51. 47 0
      samples/ControlCatalog/Pages/CalendarPage.xaml
  52. 28 0
      samples/ControlCatalog/Pages/CalendarPage.xaml.cs
  53. 9 1
      samples/ControlCatalog/Pages/CarouselPage.xaml
  54. 4 1
      samples/ControlCatalog/Pages/CarouselPage.xaml.cs
  55. 19 6
      samples/ControlCatalog/Pages/CheckBoxPage.xaml
  56. 1 1
      samples/ControlCatalog/Pages/DialogsPage.xaml.cs
  57. 18 6
      samples/ControlCatalog/Pages/RadioButtonPage.xaml
  58. 3 3
      samples/ControlCatalog/SideBar.xaml
  59. 6 0
      samples/Previewer/App.xaml
  60. 14 0
      samples/Previewer/App.xaml.cs
  61. 19 0
      samples/Previewer/Center.cs
  62. 12 0
      samples/Previewer/MainWindow.xaml
  63. 86 0
      samples/Previewer/MainWindow.xaml.cs
  64. 27 0
      samples/Previewer/Previewer.csproj
  65. 13 0
      samples/Previewer/Program.cs
  66. 53 0
      samples/RemoteTest/Program.cs
  67. 25 0
      samples/RemoteTest/RemoteTest.csproj
  68. 1 13
      samples/RenderTest/Program.cs
  69. 0 2
      samples/RenderTest/RenderTest.csproj
  70. 3 3
      samples/RenderTest/SideBar.xaml
  71. 1 12
      samples/VirtualizationTest/Program.cs
  72. 1 1
      samples/VirtualizationTest/ViewModels/MainWindowViewModel.cs
  73. 0 2
      samples/VirtualizationTest/VirtualizationTest.csproj
  74. 0 2
      samples/interop/Direct3DInteropSample/Direct3DInteropSample.csproj
  75. 3 2
      samples/interop/Direct3DInteropSample/MainWindow.cs
  76. 4 5
      scripts/ReplaceNugetCache.ps1
  77. 3 4
      scripts/ReplaceNugetCache.sh
  78. 4 5
      scripts/ReplaceNugetCacheRelease.ps1
  79. 1 1
      src/Android/Avalonia.Android/AndroidThreadingInterface.cs
  80. 0 1
      src/Android/Avalonia.AndroidTestApplication/Avalonia.AndroidTestApplication.csproj
  81. 2 1
      src/Avalonia.Base/AvaloniaObject.cs
  82. 1 1
      src/Avalonia.Base/AvaloniaObjectExtensions.cs
  83. 11 6
      src/Avalonia.Base/Data/IndexerBinding.cs
  84. 101 54
      src/Avalonia.Base/Data/InstancedBinding.cs
  85. 2 1
      src/Avalonia.Base/Platform/IPlatformThreadingInterface.cs
  86. 19 0
      src/Avalonia.Base/Threading/Dispatcher.cs
  87. 4 14
      src/Avalonia.Base/Threading/DispatcherTimer.cs
  88. 2 1
      src/Avalonia.Controls/Avalonia.Controls.csproj
  89. 2132 0
      src/Avalonia.Controls/Calendar/Calendar.cs
  90. 215 0
      src/Avalonia.Controls/Calendar/CalendarBlackoutDatesCollection.cs
  91. 193 0
      src/Avalonia.Controls/Calendar/CalendarButton.cs
  92. 79 0
      src/Avalonia.Controls/Calendar/CalendarDateRange.cs
  93. 253 0
      src/Avalonia.Controls/Calendar/CalendarDayButton.cs
  94. 21 0
      src/Avalonia.Controls/Calendar/CalendarExtensions.cs
  95. 1264 0
      src/Avalonia.Controls/Calendar/CalendarItem.cs
  96. 155 0
      src/Avalonia.Controls/Calendar/DateTimeHelper.cs
  97. 361 0
      src/Avalonia.Controls/Calendar/SelectedDatesCollection.cs
  98. 1 1
      src/Avalonia.Controls/Classes.cs
  99. 53 24
      src/Avalonia.Controls/Control.cs
  100. 1 1
      src/Avalonia.Controls/Design.cs

+ 1 - 0
.gitignore

@@ -175,3 +175,4 @@ artifacts/
 nuget
 Avalonia.XBuild.sln
 project.lock.json
+.idea/*

+ 5 - 0
.ncrunch/Avalonia.Designer.HostApp.v3.ncrunchproject

@@ -0,0 +1,5 @@
+<ProjectConfiguration>
+  <Settings>
+    <IgnoreThisComponentCompletely>True</IgnoreThisComponentCompletely>
+  </Settings>
+</ProjectConfiguration>

+ 5 - 0
.ncrunch/RemoteTest.v3.ncrunchproject

@@ -0,0 +1,5 @@
+<ProjectConfiguration>
+  <Settings>
+    <IgnoreThisComponentCompletely>True</IgnoreThisComponentCompletely>
+  </Settings>
+</ProjectConfiguration>

+ 1 - 2
.travis.yml

@@ -1,7 +1,6 @@
 language: csharp
 os:
   - linux
-  - osx
 dist: trusty
 osx_image: xcode8.3
 env:
@@ -9,7 +8,7 @@ env:
     - DOTNET_SKIP_FIRST_TIME_EXPERIENCE=1
     - DOTNET_CLI_TELEMETRY_OPTOUT=1
 mono:
-  - latest
+  - 5.2.0
 dotnet: 2.0.0
 script:
   - ./build.sh --target "Travis" --platform "Mono" --configuration "Release"

+ 218 - 3
Avalonia.sln

@@ -1,6 +1,7 @@
+
 Microsoft Visual Studio Solution File, Format Version 12.00
 # Visual Studio 15
-VisualStudioVersion = 15.0.26730.3
+VisualStudioVersion = 15.0.27004.2008
 MinimumVisualStudioVersion = 10.0.40219.1
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Base", "src\Avalonia.Base\Avalonia.Base.csproj", "{B09B78D8-9B26-48B0-9149-D64A2F120F3F}"
 EndProject
@@ -154,7 +155,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Props", "Props", "{F3AC8BC1
 		build\ReactiveUI.props = build\ReactiveUI.props
 		build\Rx.props = build\Rx.props
 		build\Serilog.props = build\Serilog.props
-		build\Serilog.Sinks.Trace.props = build\Serilog.Sinks.Trace.props
 		build\SharpDX.props = build\SharpDX.props
 		build\SkiaSharp.Desktop.props = build\SkiaSharp.Desktop.props
 		build\SkiaSharp.props = build\SkiaSharp.props
@@ -178,10 +178,22 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Win32.Interop", "s
 EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Skia.RenderTests", "tests\Avalonia.RenderTests\Avalonia.Skia.RenderTests.csproj", "{E1582370-37B3-403C-917F-8209551B1634}"
 EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Remote.Protocol", "src\Avalonia.Remote.Protocol\Avalonia.Remote.Protocol.csproj", "{D78A720C-C0C6-478B-8564-F167F9BDD01B}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RemoteTest", "samples\RemoteTest\RemoteTest.csproj", "{E2999E4A-9086-401F-898C-AEB0AD38E676}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tools", "Tools", "{4ED8B739-6F4E-4CD4-B993-545E6B5CE637}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Designer.HostApp", "src\tools\Avalonia.Designer.HostApp\Avalonia.Designer.HostApp.csproj", "{050CC912-FF49-4A8B-B534-9544017446DD}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Previewer", "samples\Previewer\Previewer.csproj", "{F40FC0A2-1BC3-401C-BFC1-928EC4D4A9CE}"
+EndProject
 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "OSX", "OSX", "{A59C4C0A-64DF-4621-B450-2BA00D6F61E2}"
 EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.MonoMac", "src\OSX\Avalonia.MonoMac\Avalonia.MonoMac.csproj", "{CBFD5788-567D-401B-9DFA-74E4224025A0}"
 EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Designer.HostApp.NetFX", "src\tools\Avalonia.Designer.HostApp.NetFX\Avalonia.Designer.HostApp.NetFX.csproj", "{4ADA61C8-D191-428D-9066-EF4F0D86520F}"
+EndProject
 Global
 	GlobalSection(SharedMSBuildProjectFiles) = preSolution
 		src\Shared\RenderHelpers\RenderHelpers.projitems*{3c4c0cb4-0c0f-4450-a37b-148c84ff905f}*SharedItemsImports = 13
@@ -2355,6 +2367,166 @@ Global
 		{E1582370-37B3-403C-917F-8209551B1634}.Release|Mono.Build.0 = Release|Any CPU
 		{E1582370-37B3-403C-917F-8209551B1634}.Release|x86.ActiveCfg = Release|Any CPU
 		{E1582370-37B3-403C-917F-8209551B1634}.Release|x86.Build.0 = Release|Any CPU
+		{D78A720C-C0C6-478B-8564-F167F9BDD01B}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
+		{D78A720C-C0C6-478B-8564-F167F9BDD01B}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
+		{D78A720C-C0C6-478B-8564-F167F9BDD01B}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
+		{D78A720C-C0C6-478B-8564-F167F9BDD01B}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
+		{D78A720C-C0C6-478B-8564-F167F9BDD01B}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
+		{D78A720C-C0C6-478B-8564-F167F9BDD01B}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
+		{D78A720C-C0C6-478B-8564-F167F9BDD01B}.Ad-Hoc|Mono.ActiveCfg = Debug|Any CPU
+		{D78A720C-C0C6-478B-8564-F167F9BDD01B}.Ad-Hoc|Mono.Build.0 = Debug|Any CPU
+		{D78A720C-C0C6-478B-8564-F167F9BDD01B}.Ad-Hoc|x86.ActiveCfg = Debug|Any CPU
+		{D78A720C-C0C6-478B-8564-F167F9BDD01B}.Ad-Hoc|x86.Build.0 = Debug|Any CPU
+		{D78A720C-C0C6-478B-8564-F167F9BDD01B}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU
+		{D78A720C-C0C6-478B-8564-F167F9BDD01B}.AppStore|Any CPU.Build.0 = Debug|Any CPU
+		{D78A720C-C0C6-478B-8564-F167F9BDD01B}.AppStore|iPhone.ActiveCfg = Debug|Any CPU
+		{D78A720C-C0C6-478B-8564-F167F9BDD01B}.AppStore|iPhone.Build.0 = Debug|Any CPU
+		{D78A720C-C0C6-478B-8564-F167F9BDD01B}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU
+		{D78A720C-C0C6-478B-8564-F167F9BDD01B}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU
+		{D78A720C-C0C6-478B-8564-F167F9BDD01B}.AppStore|Mono.ActiveCfg = Debug|Any CPU
+		{D78A720C-C0C6-478B-8564-F167F9BDD01B}.AppStore|Mono.Build.0 = Debug|Any CPU
+		{D78A720C-C0C6-478B-8564-F167F9BDD01B}.AppStore|x86.ActiveCfg = Debug|Any CPU
+		{D78A720C-C0C6-478B-8564-F167F9BDD01B}.AppStore|x86.Build.0 = Debug|Any CPU
+		{D78A720C-C0C6-478B-8564-F167F9BDD01B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{D78A720C-C0C6-478B-8564-F167F9BDD01B}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{D78A720C-C0C6-478B-8564-F167F9BDD01B}.Debug|iPhone.ActiveCfg = Debug|Any CPU
+		{D78A720C-C0C6-478B-8564-F167F9BDD01B}.Debug|iPhone.Build.0 = Debug|Any CPU
+		{D78A720C-C0C6-478B-8564-F167F9BDD01B}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
+		{D78A720C-C0C6-478B-8564-F167F9BDD01B}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
+		{D78A720C-C0C6-478B-8564-F167F9BDD01B}.Debug|Mono.ActiveCfg = Debug|Any CPU
+		{D78A720C-C0C6-478B-8564-F167F9BDD01B}.Debug|Mono.Build.0 = Debug|Any CPU
+		{D78A720C-C0C6-478B-8564-F167F9BDD01B}.Debug|x86.ActiveCfg = Debug|Any CPU
+		{D78A720C-C0C6-478B-8564-F167F9BDD01B}.Debug|x86.Build.0 = Debug|Any CPU
+		{D78A720C-C0C6-478B-8564-F167F9BDD01B}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{D78A720C-C0C6-478B-8564-F167F9BDD01B}.Release|Any CPU.Build.0 = Release|Any CPU
+		{D78A720C-C0C6-478B-8564-F167F9BDD01B}.Release|iPhone.ActiveCfg = Release|Any CPU
+		{D78A720C-C0C6-478B-8564-F167F9BDD01B}.Release|iPhone.Build.0 = Release|Any CPU
+		{D78A720C-C0C6-478B-8564-F167F9BDD01B}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
+		{D78A720C-C0C6-478B-8564-F167F9BDD01B}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
+		{D78A720C-C0C6-478B-8564-F167F9BDD01B}.Release|Mono.ActiveCfg = Release|Any CPU
+		{D78A720C-C0C6-478B-8564-F167F9BDD01B}.Release|Mono.Build.0 = Release|Any CPU
+		{D78A720C-C0C6-478B-8564-F167F9BDD01B}.Release|x86.ActiveCfg = Release|Any CPU
+		{D78A720C-C0C6-478B-8564-F167F9BDD01B}.Release|x86.Build.0 = Release|Any CPU
+		{E2999E4A-9086-401F-898C-AEB0AD38E676}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
+		{E2999E4A-9086-401F-898C-AEB0AD38E676}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
+		{E2999E4A-9086-401F-898C-AEB0AD38E676}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
+		{E2999E4A-9086-401F-898C-AEB0AD38E676}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
+		{E2999E4A-9086-401F-898C-AEB0AD38E676}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
+		{E2999E4A-9086-401F-898C-AEB0AD38E676}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
+		{E2999E4A-9086-401F-898C-AEB0AD38E676}.Ad-Hoc|Mono.ActiveCfg = Debug|Any CPU
+		{E2999E4A-9086-401F-898C-AEB0AD38E676}.Ad-Hoc|Mono.Build.0 = Debug|Any CPU
+		{E2999E4A-9086-401F-898C-AEB0AD38E676}.Ad-Hoc|x86.ActiveCfg = Debug|Any CPU
+		{E2999E4A-9086-401F-898C-AEB0AD38E676}.Ad-Hoc|x86.Build.0 = Debug|Any CPU
+		{E2999E4A-9086-401F-898C-AEB0AD38E676}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU
+		{E2999E4A-9086-401F-898C-AEB0AD38E676}.AppStore|Any CPU.Build.0 = Debug|Any CPU
+		{E2999E4A-9086-401F-898C-AEB0AD38E676}.AppStore|iPhone.ActiveCfg = Debug|Any CPU
+		{E2999E4A-9086-401F-898C-AEB0AD38E676}.AppStore|iPhone.Build.0 = Debug|Any CPU
+		{E2999E4A-9086-401F-898C-AEB0AD38E676}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU
+		{E2999E4A-9086-401F-898C-AEB0AD38E676}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU
+		{E2999E4A-9086-401F-898C-AEB0AD38E676}.AppStore|Mono.ActiveCfg = Debug|Any CPU
+		{E2999E4A-9086-401F-898C-AEB0AD38E676}.AppStore|Mono.Build.0 = Debug|Any CPU
+		{E2999E4A-9086-401F-898C-AEB0AD38E676}.AppStore|x86.ActiveCfg = Debug|Any CPU
+		{E2999E4A-9086-401F-898C-AEB0AD38E676}.AppStore|x86.Build.0 = Debug|Any CPU
+		{E2999E4A-9086-401F-898C-AEB0AD38E676}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{E2999E4A-9086-401F-898C-AEB0AD38E676}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{E2999E4A-9086-401F-898C-AEB0AD38E676}.Debug|iPhone.ActiveCfg = Debug|Any CPU
+		{E2999E4A-9086-401F-898C-AEB0AD38E676}.Debug|iPhone.Build.0 = Debug|Any CPU
+		{E2999E4A-9086-401F-898C-AEB0AD38E676}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
+		{E2999E4A-9086-401F-898C-AEB0AD38E676}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
+		{E2999E4A-9086-401F-898C-AEB0AD38E676}.Debug|Mono.ActiveCfg = Debug|Any CPU
+		{E2999E4A-9086-401F-898C-AEB0AD38E676}.Debug|Mono.Build.0 = Debug|Any CPU
+		{E2999E4A-9086-401F-898C-AEB0AD38E676}.Debug|x86.ActiveCfg = Debug|Any CPU
+		{E2999E4A-9086-401F-898C-AEB0AD38E676}.Debug|x86.Build.0 = Debug|Any CPU
+		{E2999E4A-9086-401F-898C-AEB0AD38E676}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{E2999E4A-9086-401F-898C-AEB0AD38E676}.Release|Any CPU.Build.0 = Release|Any CPU
+		{E2999E4A-9086-401F-898C-AEB0AD38E676}.Release|iPhone.ActiveCfg = Release|Any CPU
+		{E2999E4A-9086-401F-898C-AEB0AD38E676}.Release|iPhone.Build.0 = Release|Any CPU
+		{E2999E4A-9086-401F-898C-AEB0AD38E676}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
+		{E2999E4A-9086-401F-898C-AEB0AD38E676}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
+		{E2999E4A-9086-401F-898C-AEB0AD38E676}.Release|Mono.ActiveCfg = Release|Any CPU
+		{E2999E4A-9086-401F-898C-AEB0AD38E676}.Release|Mono.Build.0 = Release|Any CPU
+		{E2999E4A-9086-401F-898C-AEB0AD38E676}.Release|x86.ActiveCfg = Release|Any CPU
+		{E2999E4A-9086-401F-898C-AEB0AD38E676}.Release|x86.Build.0 = Release|Any CPU
+		{050CC912-FF49-4A8B-B534-9544017446DD}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU
+		{050CC912-FF49-4A8B-B534-9544017446DD}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU
+		{050CC912-FF49-4A8B-B534-9544017446DD}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU
+		{050CC912-FF49-4A8B-B534-9544017446DD}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU
+		{050CC912-FF49-4A8B-B534-9544017446DD}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU
+		{050CC912-FF49-4A8B-B534-9544017446DD}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU
+		{050CC912-FF49-4A8B-B534-9544017446DD}.Ad-Hoc|Mono.ActiveCfg = Release|Any CPU
+		{050CC912-FF49-4A8B-B534-9544017446DD}.Ad-Hoc|Mono.Build.0 = Release|Any CPU
+		{050CC912-FF49-4A8B-B534-9544017446DD}.Ad-Hoc|x86.ActiveCfg = Release|Any CPU
+		{050CC912-FF49-4A8B-B534-9544017446DD}.Ad-Hoc|x86.Build.0 = Release|Any CPU
+		{050CC912-FF49-4A8B-B534-9544017446DD}.AppStore|Any CPU.ActiveCfg = Release|Any CPU
+		{050CC912-FF49-4A8B-B534-9544017446DD}.AppStore|Any CPU.Build.0 = Release|Any CPU
+		{050CC912-FF49-4A8B-B534-9544017446DD}.AppStore|iPhone.ActiveCfg = Release|Any CPU
+		{050CC912-FF49-4A8B-B534-9544017446DD}.AppStore|iPhone.Build.0 = Release|Any CPU
+		{050CC912-FF49-4A8B-B534-9544017446DD}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU
+		{050CC912-FF49-4A8B-B534-9544017446DD}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU
+		{050CC912-FF49-4A8B-B534-9544017446DD}.AppStore|Mono.ActiveCfg = Release|Any CPU
+		{050CC912-FF49-4A8B-B534-9544017446DD}.AppStore|Mono.Build.0 = Release|Any CPU
+		{050CC912-FF49-4A8B-B534-9544017446DD}.AppStore|x86.ActiveCfg = Release|Any CPU
+		{050CC912-FF49-4A8B-B534-9544017446DD}.AppStore|x86.Build.0 = Release|Any CPU
+		{050CC912-FF49-4A8B-B534-9544017446DD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{050CC912-FF49-4A8B-B534-9544017446DD}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{050CC912-FF49-4A8B-B534-9544017446DD}.Debug|iPhone.ActiveCfg = Debug|Any CPU
+		{050CC912-FF49-4A8B-B534-9544017446DD}.Debug|iPhone.Build.0 = Debug|Any CPU
+		{050CC912-FF49-4A8B-B534-9544017446DD}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
+		{050CC912-FF49-4A8B-B534-9544017446DD}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
+		{050CC912-FF49-4A8B-B534-9544017446DD}.Debug|Mono.ActiveCfg = Debug|Any CPU
+		{050CC912-FF49-4A8B-B534-9544017446DD}.Debug|Mono.Build.0 = Debug|Any CPU
+		{050CC912-FF49-4A8B-B534-9544017446DD}.Debug|x86.ActiveCfg = Debug|Any CPU
+		{050CC912-FF49-4A8B-B534-9544017446DD}.Debug|x86.Build.0 = Debug|Any CPU
+		{050CC912-FF49-4A8B-B534-9544017446DD}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{050CC912-FF49-4A8B-B534-9544017446DD}.Release|Any CPU.Build.0 = Release|Any CPU
+		{050CC912-FF49-4A8B-B534-9544017446DD}.Release|iPhone.ActiveCfg = Release|Any CPU
+		{050CC912-FF49-4A8B-B534-9544017446DD}.Release|iPhone.Build.0 = Release|Any CPU
+		{050CC912-FF49-4A8B-B534-9544017446DD}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
+		{050CC912-FF49-4A8B-B534-9544017446DD}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
+		{050CC912-FF49-4A8B-B534-9544017446DD}.Release|Mono.ActiveCfg = Release|Any CPU
+		{050CC912-FF49-4A8B-B534-9544017446DD}.Release|Mono.Build.0 = Release|Any CPU
+		{050CC912-FF49-4A8B-B534-9544017446DD}.Release|x86.ActiveCfg = Release|Any CPU
+		{050CC912-FF49-4A8B-B534-9544017446DD}.Release|x86.Build.0 = Release|Any CPU
+		{F40FC0A2-1BC3-401C-BFC1-928EC4D4A9CE}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU
+		{F40FC0A2-1BC3-401C-BFC1-928EC4D4A9CE}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU
+		{F40FC0A2-1BC3-401C-BFC1-928EC4D4A9CE}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU
+		{F40FC0A2-1BC3-401C-BFC1-928EC4D4A9CE}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU
+		{F40FC0A2-1BC3-401C-BFC1-928EC4D4A9CE}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU
+		{F40FC0A2-1BC3-401C-BFC1-928EC4D4A9CE}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU
+		{F40FC0A2-1BC3-401C-BFC1-928EC4D4A9CE}.Ad-Hoc|Mono.ActiveCfg = Release|Any CPU
+		{F40FC0A2-1BC3-401C-BFC1-928EC4D4A9CE}.Ad-Hoc|Mono.Build.0 = Release|Any CPU
+		{F40FC0A2-1BC3-401C-BFC1-928EC4D4A9CE}.Ad-Hoc|x86.ActiveCfg = Release|Any CPU
+		{F40FC0A2-1BC3-401C-BFC1-928EC4D4A9CE}.Ad-Hoc|x86.Build.0 = Release|Any CPU
+		{F40FC0A2-1BC3-401C-BFC1-928EC4D4A9CE}.AppStore|Any CPU.ActiveCfg = Release|Any CPU
+		{F40FC0A2-1BC3-401C-BFC1-928EC4D4A9CE}.AppStore|Any CPU.Build.0 = Release|Any CPU
+		{F40FC0A2-1BC3-401C-BFC1-928EC4D4A9CE}.AppStore|iPhone.ActiveCfg = Release|Any CPU
+		{F40FC0A2-1BC3-401C-BFC1-928EC4D4A9CE}.AppStore|iPhone.Build.0 = Release|Any CPU
+		{F40FC0A2-1BC3-401C-BFC1-928EC4D4A9CE}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU
+		{F40FC0A2-1BC3-401C-BFC1-928EC4D4A9CE}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU
+		{F40FC0A2-1BC3-401C-BFC1-928EC4D4A9CE}.AppStore|Mono.ActiveCfg = Release|Any CPU
+		{F40FC0A2-1BC3-401C-BFC1-928EC4D4A9CE}.AppStore|Mono.Build.0 = Release|Any CPU
+		{F40FC0A2-1BC3-401C-BFC1-928EC4D4A9CE}.AppStore|x86.ActiveCfg = Release|Any CPU
+		{F40FC0A2-1BC3-401C-BFC1-928EC4D4A9CE}.AppStore|x86.Build.0 = Release|Any CPU
+		{F40FC0A2-1BC3-401C-BFC1-928EC4D4A9CE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{F40FC0A2-1BC3-401C-BFC1-928EC4D4A9CE}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{F40FC0A2-1BC3-401C-BFC1-928EC4D4A9CE}.Debug|iPhone.ActiveCfg = Debug|Any CPU
+		{F40FC0A2-1BC3-401C-BFC1-928EC4D4A9CE}.Debug|iPhone.Build.0 = Debug|Any CPU
+		{F40FC0A2-1BC3-401C-BFC1-928EC4D4A9CE}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
+		{F40FC0A2-1BC3-401C-BFC1-928EC4D4A9CE}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
+		{F40FC0A2-1BC3-401C-BFC1-928EC4D4A9CE}.Debug|Mono.ActiveCfg = Debug|Any CPU
+		{F40FC0A2-1BC3-401C-BFC1-928EC4D4A9CE}.Debug|Mono.Build.0 = Debug|Any CPU
+		{F40FC0A2-1BC3-401C-BFC1-928EC4D4A9CE}.Debug|x86.ActiveCfg = Debug|Any CPU
+		{F40FC0A2-1BC3-401C-BFC1-928EC4D4A9CE}.Debug|x86.Build.0 = Debug|Any CPU
+		{F40FC0A2-1BC3-401C-BFC1-928EC4D4A9CE}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{F40FC0A2-1BC3-401C-BFC1-928EC4D4A9CE}.Release|Any CPU.Build.0 = Release|Any CPU
+		{F40FC0A2-1BC3-401C-BFC1-928EC4D4A9CE}.Release|iPhone.ActiveCfg = Release|Any CPU
+		{F40FC0A2-1BC3-401C-BFC1-928EC4D4A9CE}.Release|iPhone.Build.0 = Release|Any CPU
+		{F40FC0A2-1BC3-401C-BFC1-928EC4D4A9CE}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
+		{F40FC0A2-1BC3-401C-BFC1-928EC4D4A9CE}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
+		{F40FC0A2-1BC3-401C-BFC1-928EC4D4A9CE}.Release|Mono.ActiveCfg = Release|Any CPU
+		{F40FC0A2-1BC3-401C-BFC1-928EC4D4A9CE}.Release|Mono.Build.0 = Release|Any CPU
+		{F40FC0A2-1BC3-401C-BFC1-928EC4D4A9CE}.Release|x86.ActiveCfg = Release|Any CPU
+		{F40FC0A2-1BC3-401C-BFC1-928EC4D4A9CE}.Release|x86.Build.0 = Release|Any CPU
 		{CBFD5788-567D-401B-9DFA-74E4224025A0}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
 		{CBFD5788-567D-401B-9DFA-74E4224025A0}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
 		{CBFD5788-567D-401B-9DFA-74E4224025A0}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
@@ -2395,6 +2567,46 @@ Global
 		{CBFD5788-567D-401B-9DFA-74E4224025A0}.Release|Mono.Build.0 = Release|Any CPU
 		{CBFD5788-567D-401B-9DFA-74E4224025A0}.Release|x86.ActiveCfg = Release|Any CPU
 		{CBFD5788-567D-401B-9DFA-74E4224025A0}.Release|x86.Build.0 = Release|Any CPU
+		{4ADA61C8-D191-428D-9066-EF4F0D86520F}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU
+		{4ADA61C8-D191-428D-9066-EF4F0D86520F}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU
+		{4ADA61C8-D191-428D-9066-EF4F0D86520F}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU
+		{4ADA61C8-D191-428D-9066-EF4F0D86520F}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU
+		{4ADA61C8-D191-428D-9066-EF4F0D86520F}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU
+		{4ADA61C8-D191-428D-9066-EF4F0D86520F}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU
+		{4ADA61C8-D191-428D-9066-EF4F0D86520F}.Ad-Hoc|Mono.ActiveCfg = Release|Any CPU
+		{4ADA61C8-D191-428D-9066-EF4F0D86520F}.Ad-Hoc|Mono.Build.0 = Release|Any CPU
+		{4ADA61C8-D191-428D-9066-EF4F0D86520F}.Ad-Hoc|x86.ActiveCfg = Release|Any CPU
+		{4ADA61C8-D191-428D-9066-EF4F0D86520F}.Ad-Hoc|x86.Build.0 = Release|Any CPU
+		{4ADA61C8-D191-428D-9066-EF4F0D86520F}.AppStore|Any CPU.ActiveCfg = Release|Any CPU
+		{4ADA61C8-D191-428D-9066-EF4F0D86520F}.AppStore|Any CPU.Build.0 = Release|Any CPU
+		{4ADA61C8-D191-428D-9066-EF4F0D86520F}.AppStore|iPhone.ActiveCfg = Release|Any CPU
+		{4ADA61C8-D191-428D-9066-EF4F0D86520F}.AppStore|iPhone.Build.0 = Release|Any CPU
+		{4ADA61C8-D191-428D-9066-EF4F0D86520F}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU
+		{4ADA61C8-D191-428D-9066-EF4F0D86520F}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU
+		{4ADA61C8-D191-428D-9066-EF4F0D86520F}.AppStore|Mono.ActiveCfg = Release|Any CPU
+		{4ADA61C8-D191-428D-9066-EF4F0D86520F}.AppStore|Mono.Build.0 = Release|Any CPU
+		{4ADA61C8-D191-428D-9066-EF4F0D86520F}.AppStore|x86.ActiveCfg = Release|Any CPU
+		{4ADA61C8-D191-428D-9066-EF4F0D86520F}.AppStore|x86.Build.0 = Release|Any CPU
+		{4ADA61C8-D191-428D-9066-EF4F0D86520F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{4ADA61C8-D191-428D-9066-EF4F0D86520F}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{4ADA61C8-D191-428D-9066-EF4F0D86520F}.Debug|iPhone.ActiveCfg = Debug|Any CPU
+		{4ADA61C8-D191-428D-9066-EF4F0D86520F}.Debug|iPhone.Build.0 = Debug|Any CPU
+		{4ADA61C8-D191-428D-9066-EF4F0D86520F}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
+		{4ADA61C8-D191-428D-9066-EF4F0D86520F}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
+		{4ADA61C8-D191-428D-9066-EF4F0D86520F}.Debug|Mono.ActiveCfg = Debug|Any CPU
+		{4ADA61C8-D191-428D-9066-EF4F0D86520F}.Debug|Mono.Build.0 = Debug|Any CPU
+		{4ADA61C8-D191-428D-9066-EF4F0D86520F}.Debug|x86.ActiveCfg = Debug|Any CPU
+		{4ADA61C8-D191-428D-9066-EF4F0D86520F}.Debug|x86.Build.0 = Debug|Any CPU
+		{4ADA61C8-D191-428D-9066-EF4F0D86520F}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{4ADA61C8-D191-428D-9066-EF4F0D86520F}.Release|Any CPU.Build.0 = Release|Any CPU
+		{4ADA61C8-D191-428D-9066-EF4F0D86520F}.Release|iPhone.ActiveCfg = Release|Any CPU
+		{4ADA61C8-D191-428D-9066-EF4F0D86520F}.Release|iPhone.Build.0 = Release|Any CPU
+		{4ADA61C8-D191-428D-9066-EF4F0D86520F}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
+		{4ADA61C8-D191-428D-9066-EF4F0D86520F}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
+		{4ADA61C8-D191-428D-9066-EF4F0D86520F}.Release|Mono.ActiveCfg = Release|Any CPU
+		{4ADA61C8-D191-428D-9066-EF4F0D86520F}.Release|Mono.Build.0 = Release|Any CPU
+		{4ADA61C8-D191-428D-9066-EF4F0D86520F}.Release|x86.ActiveCfg = Release|Any CPU
+		{4ADA61C8-D191-428D-9066-EF4F0D86520F}.Release|x86.Build.0 = Release|Any CPU
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE
@@ -2448,7 +2660,10 @@ Global
 		{638580B0-7910-40EF-B674-DCB34DA308CD} = {A0CC0258-D18C-4AB3-854F-7101680FC3F9}
 		{CBC4FF2F-92D4-420B-BE21-9FE0B930B04E} = {B39A8919-9F95-48FE-AD7B-76E08B509888}
 		{E1582370-37B3-403C-917F-8209551B1634} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
-		{CBFD5788-567D-401B-9DFA-74E4224025A0} = {A59C4C0A-64DF-4621-B450-2BA00D6F61E2}
+		{E2999E4A-9086-401F-898C-AEB0AD38E676} = {9B9E3891-2366-4253-A952-D08BCEB71098}
+		{050CC912-FF49-4A8B-B534-9544017446DD} = {4ED8B739-6F4E-4CD4-B993-545E6B5CE637}
+		{F40FC0A2-1BC3-401C-BFC1-928EC4D4A9CE} = {9B9E3891-2366-4253-A952-D08BCEB71098}
+		{4ADA61C8-D191-428D-9066-EF4F0D86520F} = {4ED8B739-6F4E-4CD4-B993-545E6B5CE637}
 	EndGlobalSection
 	GlobalSection(ExtensibilityGlobals) = postSolution
 		SolutionGuid = {87366D66-1391-4D90-8999-95A620AD786A}

+ 0 - 4
appveyor.yml

@@ -23,10 +23,6 @@ before_build:
 - git submodule update --init
 build_script:
 - ps: .\build.ps1 -Target "AppVeyor" -Platform "$env:platform" -Configuration "$env:configuration"
-after_build:
-- "SET PATH=C:\\Python34;C:\\Python34\\Scripts;%PATH%"
-- pip install codecov
-- codecov -f "./artifacts/coverage.xml"
 
 test: off
 artifacts:

+ 1 - 1
build/MonoMac.props

@@ -1,5 +1,5 @@
 <Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
   <ItemGroup>
-    <PackageReference Include="MonoMac.NetStandard" Version="0.0.3" />
+    <PackageReference Include="MonoMac.NetStandard" Version="0.0.4" />
   </ItemGroup>
 </Project>

+ 0 - 5
build/Serilog.Sinks.Trace.props

@@ -1,5 +0,0 @@
-<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
-  <ItemGroup>
-     <PackageReference Include="Serilog.Sinks.Trace" Version="2.1.0" />
-  </ItemGroup>
-</Project>

+ 3 - 1
build/Serilog.props

@@ -1,5 +1,7 @@
 <Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
   <ItemGroup>
-     <PackageReference Include="Serilog" Version="2.4.0" />
+    <PackageReference Include="Serilog" Version="2.5.0" />
+    <PackageReference Include="Serilog.Sinks.Trace" Version="2.1.0" />
+    <PackageReference Include="Serilog.Sinks.Debug" Version="1.0.0" />
   </ItemGroup>
 </Project>

+ 0 - 5
build/Splat.props

@@ -1,5 +0,0 @@
-<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
-  <ItemGroup>
-     <PackageReference Include="Splat" Version="2.0.0" />
-  </ItemGroup>
-</Project>

+ 8 - 8
build/XUnit.props

@@ -1,14 +1,14 @@
 <Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
   <ItemGroup>
-    <PackageReference Include="xunit" Version="2.3.0-beta5-build3769" />
+    <PackageReference Include="xunit" Version="2.3.0" />
     <PackageReference Include="xunit.abstractions" Version="2.0.1" />
-    <PackageReference Include="xunit.assert" Version="2.3.0-beta5-build3769" />
-    <PackageReference Include="xunit.core" Version="2.3.0-beta5-build3769" />
-    <PackageReference Include="xunit.extensibility.core" Version="2.3.0-beta5-build3769" />
-    <PackageReference Include="xunit.extensibility.execution" Version="2.3.0-beta5-build3769" />
-    <PackageReference Include="xunit.runner.console" Version="2.3.0-beta5-build3769" />
-    <PackageReference Include="xunit.runner.visualstudio" Version="2.3.0-beta5-build3769" />
-    <DotNetCliToolReference Include="dotnet-xunit" Version="2.3.0-beta5-build3769" />
+    <PackageReference Include="xunit.assert" Version="2.3.0" />
+    <PackageReference Include="xunit.core" Version="2.3.0" />
+    <PackageReference Include="xunit.extensibility.core" Version="2.3.0" />
+    <PackageReference Include="xunit.extensibility.execution" Version="2.3.0" />
+    <PackageReference Include="xunit.runner.console" Version="2.3.0" />
+    <PackageReference Include="xunit.runner.visualstudio" Version="2.3.0" />
+    <DotNetCliToolReference Include="dotnet-xunit" Version="2.3.0" />
   </ItemGroup>
   <ItemGroup Condition="'$(TargetFramework)' == 'netcoreapp2.0'">
     <PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.0.0" />

+ 0 - 3
docs/.gitignore

@@ -1,3 +0,0 @@
-#docfx
-_site
-api

+ 0 - 21
docs/README.md

@@ -1,21 +0,0 @@
-# Avalonia Documentation
-
-* [API Reference](http://avalonia.github.io/)
-
-## Building
-
-Download and unzip `docfx.zip` into `Documentation` folder from [DocFX project site](https://github.com/dotnet/docfx/releases).
-
-**Step 1.** To create `_site` documentation folder run build script
-
-```
-build.cmd
-```
-
-**Step 2.** To browse `_site` documentation folder run serve script
-
-```
-serve.cmd
-```
-
-And you can view the generated website in your browser `http://localhost:8080`.

+ 0 - 3
docs/build.cmd

@@ -1,3 +0,0 @@
-@echo off
-docfx metadata
-docfx build

+ 0 - 55
docs/docfx.json

@@ -1,55 +0,0 @@
-{
-  "metadata": [
-    {
-      "src": [
-        {
-          "files": [
-            "/src/Gtk/Avalonia.Cairo/Avalonia.Cairo.csproj",
-            "/src/Gtk/Avalonia.Gtk/Avalonia.Gtk.csproj",
-            "/src/Markup/Avalonia.Markup/Avalonia.Markup.csproj",
-            "/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj",
-            "/src/Avalonia.Animation/Avalonia.Animation.csproj",
-            "/src/Avalonia.Application/Avalonia.Application.csproj",
-            "/src/Avalonia.Base/Avalonia.Base.csproj",
-            "/src/Avalonia.Controls/Avalonia.Controls.csproj",
-            "/src/Avalonia.Diagnostics/Avalonia.Diagnostics.csproj",
-            "/src/Avalonia.HtmlRenderer/Avalonia.HtmlRenderer.csproj",
-            "/src/Avalonia.Input/Avalonia.Input.csproj",
-            "/src/Avalonia.Interactivity/Avalonia.Interactivity.csproj",
-            "/src/Avalonia.Layout/Avalonia.Layout.csproj",
-            "/src/Avalonia.ReactiveUI/Avalonia.ReactiveUI.csproj",
-            "/src/Avalonia.Visuals/Avalonia.Visuals.csproj",
-            "/src/Avalonia.Styling/Avalonia.Styling.csproj",
-            "/src/Avalonia.Themes.Default/Avalonia.Themes.Default.csproj",
-            "/src/Skia/Avalonia.Skia.Desktop/Avalonia.Skia.Desktop.csproj",
-            "/src/Windows/Avalonia.Designer/Avalonia.Designer.csproj",
-            "/src/Windows/Avalonia.Direct2D1/Avalonia.Direct2D1.csproj",
-            "/src/Windows/Avalonia.Win32/Avalonia.Win32.csproj",
-          ],
-          "exclude": [ "**/bin/**", "**/obj/**" ], 
-          "cwd": ".."
-        }
-      ],
-      "dest": "api"
-    },
-  ],
-  "build": {
-    "content":
-      [
-        {
-          "files": ["**/*.yml", "index.md", "tutorial/*.md", "guidelines/*.md", "spec/*.md"],
-        }
-      ],
-    "resource": [
-        {
-          "files": ["images/**", "tutorial/images/**", "guidelines/images/**", "spec/images/**"]
-        }
-    ],
-    "overwrite": "apidoc/*.md",
-    "globalMetadata": {
-      "_appTitle": "Avalonia Website"
-    },
-    "dest": "_site",
-    "template": [ "default", "template"]
-  }
-}

+ 0 - 43
docs/guidelines/build.md

@@ -1,43 +0,0 @@
-# Building Avalonia
-
-## Windows
-
-Avalonia requires at least Visual Studio 2017 and .NET Core SDK 2.0 to build on Windows.
-
-### Clone the Avalonia repository
-
-```
-git clone https://github.com/AvaloniaUI/Avalonia.git
-git submodule update --init
-```
-
-### Open in Visual Studio
-
-Open the `Avalonia.sln` solution in Visual Studio 2015 or newer. The free Visual Studio Community
-edition works fine. Run the `Samples\ControlCatalog.Desktop` project to see the sample application.
-
-## Linux/OSX
-
-It's *not* possible to build the *whole* project on Linux/OSX. You can only build the subset targeting .NET Standard and .NET Core (which is, however, sufficient to get UI working on Linux/OSX). If you want to something that involves changing platform-specific APIs you'll need a Windows machine.
-
-MonoDevelop, Xamarin Studio and Visual Studio for Mac aren't capable of properly opening our solution. You can use Rider (at least 2017.2 EAP) or VSCode instead. They will fail to load most of platform specific projects, but you don't need them to run on .NET Core.
-
-### Install the latest version of .NET Core
-
-Go to https://www.microsoft.com/net/core and follow instructions for your OS. You need SDK (not just "runtime") package.
-
-### Clone the Avalonia repository
-
-```
-git clone https://github.com/AvaloniaUI/Avalonia.git
-git submodule update --init --recursive
-```
-
-### Build and Run Avalonia
-
-```
-samples/ControlCatalog.NetCore
-dotnet restore
-dotnet run
-```
-

+ 0 - 60
docs/guidelines/contributing.md

@@ -1,60 +0,0 @@
-# Contributing #
-
-## Before You Start ##
-
-Drop into our [gitter chat room](https://gitter.im/Avalonia/Avalonia) and let us know what you're thinking of doing. We might be able to give you guidance or let you know if someone else is already working on the feature.
-
-## Style ##
-
-The codebase uses [.net core](https://github.com/dotnet/corefx/blob/master/Documentation/coding-guidelines/coding-style.md) coding style. 
-
-Try to keep lines of code around 100 characters in length or less, though this is not a hard limit.
-If you're a few characters over then don't worry too much. 
-
-**DO NOT USE #REGIONS** full stop.
-
-## Pull requests ##
-
-A single pull request should be submitted for each change. If you're making more than one change,
-please submit separate pull requests for each change for easy review. Rebase your changes to make 
-sense, so a history that looks like:
-
-* Add class A
-* Feature A didn't set Foo when Bar was set
-* Fix spacing
-* Add class B
-* Sort using statements
-
-Should be rebased to read:
-
-* Add class A
-* Add class B
-
-Again, this makes review much easier.
-
-Please try not to submit pull requests that don't add new features (e.g. moving stuff around) 
-unless you see something that is obviously wrong or that could be written in a more terse or 
-idiomatic style. It takes time to review each pull request - time that I'd prefer to spend writing 
-new features!
-
-Prefer terseness to verbosity but don't try to be too clever.
-
-## Tests ##
-
-There are two types of tests currently in the codebase; unit tests and render tests.
-
-Unit tests should be contained in a class name that mirrors the class being tested with the suffix
--Tests, e.g.
-
-    Avalonia.Controls.UnitTests.Presenters.TextPresenterTests
-
-Where Avalonia.Controls.UnitTests is the name of the project.
-
-Unit test methods should be named in a sentence style, separated by underscores, that describes in
-English what the test is testing, e.g.
-
-    void Calling_Foo_Should_Increment_Bar()
-
-Render tests should describe what the produced image is:
-
-    void Rectangle_2px_Stroke_Filled()

+ 0 - 4
docs/guidelines/toc.yml

@@ -1,4 +0,0 @@
-- name: Building Avalonia 
-  href: build.md
-- name: Contributing
-  href: contributing.md

BIN
docs/images/avalonia-video.png


BIN
docs/images/cross-platform.png


BIN
docs/images/hello-world-xaml.png


BIN
docs/images/inspection-support.png


BIN
docs/images/screen.png


+ 0 - 37
docs/index.md

@@ -1,37 +0,0 @@
-# The Avalonia UI Framework
-
-Cross platform .NET UI Framework with bindings and XAML
-
-## Current status
-
-We're pleased to announce that Avalonia is now in alpha!
-
-What does alpha mean? Well, it means that it's now at a stage where you can have a play and hopefully create simple applications. There's now a Visual Studio Extension containing project and item templates that will help you get started, and there's an initial complement of controls. There's still a lot missing, and you will find bugs, and the API will change, but this represents the first time where we've made it somewhat easy to have a play and experiment with the framework.
-
-## How do I try it out
-
-The easiest way to try out Avalonia is to install the [Visual Studio Extension](https://marketplace.visualstudio.com/items?itemName=AvaloniaTeam.AvaloniaforVisualStudio).
-
-This will add a Avalonia project template and a Window template to the standard Visual Studo "Add" dialog (yes, icons still to come :) ):
-
-![](images/add-dialogs.png)
-
-Creating a Avalonia Project will give you a simple project with a single XAML window. We even have a simple designer:
-
-![](images/hello-world-xaml.png)
-
-You can also find the project [on GitHub](https://github.com/AvaloniaUI/Avalonia/)
-
-## News
-
-You can read news about Avalonia on [Groky's blog](http://grokys.github.io/)
-
-## Cross Platform
-
-Fow now we can run on Windows, Linux and Mac.
-
-![](images/cross-platform.png)
-
-## Inspection support
-
-![](images/inspection-support.png)

+ 0 - 225
docs/intro.md

@@ -1,225 +0,0 @@
-# Avalonia #
-
-...a next generation WPF?
-
-## Background ##
-
-As everyone who's involved in client-side .NET development knows, the past half decade have been a 
-very sad time. Where WPF started off as a game-changer, it now seems to have been all but forgotten.
-WinRT came along and took many of the lessons of WPF but it's currently not usable on the desktop.
-
-After a few months of trying to reverse-engineer WPF with the [Avalonia Project](https://github.com/grokys/Avalonia) I began to come to the same conclusion that I imagine Microsoft
-came to internally: for all its groundbreaking-ness at the time, WPF at its core is a dated mess,
-written for .NET 1 and barely updated to even bring it up-to-date with .NET 2 features such as
-generics.
-
-So I began to think: what if we were to start anew with modern C# features such as *(gasp)* 
-Generics, Observables, async, etc etc. The result of that thought is Avalonia 
-(https://github.com/grokys/Avalonia).
-
-##### DISCLAIMER
-This is really **early development pre-alpha-alpha** stuff. Everything is subject to 
-change, I'm not even sure if the performance characteristics of Rx make Observables suitable for 
-binding throughout a framework. *I'm writing this only to see if the idea of exploring these ideas 
-appeals to anyone else.*
-
-So what can it do so far? Not a whole lot right now. Here's the demo application:
-
-![](screen.png)
-
-## AvaloniaProperty ##
-
-AvaloniaProperty is the equivalent of WPF's DependencyProperty. 
-
-I'm not a big fan of DependencyProperty. My first thought was that I'd rather not have something 
-like this at all and just use basic INPC but DPs give you two important features: Inheritance and 
-Attached Properties. So the challenge became to improve it.
-
-Declaring a DependencyProperty in WPF looks something like this:
-
-```csharp
-public static readonly DependencyProperty PropertyDeclaration =
-DependencyProperty.Register(
-    "PropertyName",
-    typeof(PropertyType),
-    typeof(OwnerClass),
-    new FrameworkPropertyMetadata(
-        default(PropertyType),
-        FrameworkPropertyMetadataOptions.Inherits));
-
-public PropertyType PropertyName
-{
-    get { return (PropertyType)this.GetValue(PropertyDeclaration); }
-    set { this.SetValue(PropertyDeclaration, value); }
-}
-```
-
-Eww! All that just to declare a single property. There's **A LOT** of boilerplate there. With 
-generics and default parameters we can at least make it look a bit nicer:
-
-```csharp
-public static readonly AvaloniaProperty<PropertyType> PropertyDeclaration =
-AvaloniaProperty.Register<OwnerClass, PropertyType>("PropertyName", inherits: true);
-
-public PropertyType PropertyName
-{
-    get { return this.GetValue(PropertyDeclaration); }
-    set { this.SetValue(PropertyDeclaration, value); }
-}
-```
-
-What can we see here?
-
-- AvaloniaProperties are typed, so no more having to cast in the getter.
-- We pass the property type and owner class as a generic type to `Register()` so we don't have to 
-write `typeof()` twice.
-- We used default parameter values in `Register()` so that defaults don't have to be restated.
-
-*(ASIDE: maybe Roslyn will give us [var for fields](http://blogs.msdn.com/b/ericlippert/archive/2009/01/26/why-no-var-on-fields.aspx)...)? Lets hope...*
-
-## Binding
-
-Binding in Avalonia uses Reactive Extensions' [IObservable](http://msdn.microsoft.com/library/dd990377.aspx). To bind an IObservable to a property, use the `Bind()` method:
-
-```csharp
-control.Bind(BorderProperty, someObject.SomeObservable());
-```
-
-Note that because AvaloniaProperty is typed, we can check that the observable is of the correct type.
-
-To get the value of a property as an observable, call `GetObservable()`:
-
-```csharp
-var observable = control.GetObservable(Control.FooProperty);
-```
-
-## Attached Properties and Binding Pt 2
-
-Attached properties are set just like in WPF, using `SetValue()`. But what about the `[]` operator, also called [index initializer](https://roslyn.codeplex.com/wikipage?title=Language%20Feature%20Status&referringTitle=Home) (see *C# feature descriptions*)? The upcoming version of C# will provide a new feature that allows us to use `[]` array subscripts in object initializers. An example on how to make use of it will look like this:
-
-```csharp
-var control = new Control
-{
-    Property1 = "Foo",
-    [Attached.Property] = "Bar",
-}
-```
-
-
-Nice... Lets take this further:
-
-```csharp
-var control = new Control
-{
-    Property1 = "Foo",
-    [Attached.Property] = "Bar",
-    [!Property2] = something.SomeObservable,
-}
-```
-
-Yep, by putting a bang in front of the property name you can **bind** to a property (attached or 
-otherwise) from the object initializer.
-
-Binding to a property on another control? Easy:
-
-```csharp
-var control = new Control
-{
-    Property1 = "Foo",
-    [Attached.Property] = "Bar",
-    [!Property2] = anotherControl[!Property1],
-}
-```
-
-Two way binding? Just add two bangs:
-
-```csharp
-var control = new Control
-{
-    Property1 = "Foo",
-    [Attached.Property] = "Bar",
-    [!!Property2] = anotherControl[!!Property1],
-}
-```
-
-## Visual and Logical trees
-
-Avalonia uses the same visual/logical tree separation that is used by WPF (and to some extent HTML 
-is moving in this direction with the Shadow DOM). The manner of accessing the two trees is slightly
-different however. Rather than using Visual/LogicalTreeHelper you can cast any control to an 
-`IVisual` or `ILogical` to reveal the tree operations. There's also the VisualExtensions class which
-provides some useful extension methods such as `GetVisualAncestor<T>(this IVisual visual)` or 
-`GetVisualAt(this IVisual visual, Point p)`.
-
-Again like WPF, Controls are lookless with the visual appearance being defined by the Template 
-property, which is determined by...
-
-## Styles
-
-Styles in Avalonia diverge from styles in WPF quite a lot, and move towards a more CSS-like system.
-It's probably easiest to show in an example. Here is the default style for the CheckBox control:
-
-```csharp
-new Style(x => x.OfType<CheckBox>())
-{
-	Setters = new[]
-	{
-	    new Setter(Button.TemplateProperty, ControlTemplate.Create<CheckBox>(this.Template))
-	}
-};
-
-new Style(x => x.OfType<CheckBox>().Template().Id("checkMark"))
-{
-	Setters = new[]
-	{
-	    new Setter(Shape.IsVisibleProperty, false)
-	}
-};
-	
-new Style(x => x.OfType<CheckBox>().Class(":checked").Template().Id("checkMark"))
-{
-	Setters = new[]
-	{
-	    new Setter(Shape.IsVisibleProperty, true)
-	}
-};
-```
-
-Let's see what's happening here:
-
-```csharp
-new Style(x => x.OfType<CheckBox>())
-```
-
-The constructor for the Style class defines the selector. Here we're saying "*this style applies to 
-all controls in the the visual tree of type CheckBox*". A more complex selector:
-
-```csharp
-new Style(x => x.OfType<CheckBox>().Class(":checked").Template().Id("checkMark"))
-```
-
-This selector matches *"all controls with Id == "checkMark" in the template of a CheckBox with the
-class `:checked`"*. Each control has an Id property, and Ids in templates are considered to be in a
-separate namespace.
-
-Inside the Style class we then have a collection of setters similar to WPF. 
-
-This system means that there's no more need for WPF's Triggers - the styling works with classes 
-(which are arbitrary strings) similar to CSS. Similar to CSS, classes with a leading `:` are set
-by the control itself in response to events like `mouseover` and `click`.
-
-Similar to WPF, styles can be defined on each control, with a global application style collection
-at the root. This means that different subsections of the visual tree can have a completely 
-different look-and-feel.
-
-## XAML
-
-As you can see, all of the examples here are defined in code - but a XAML implementation is being
-worked on. The current progress can be reviewed at [https://github.com/SuperJMN/Avalonia](https://github.com/SuperJMN/Avalonia).
-
-## That's all for now
-
-There's a lot more to see, and even more to do, so if you want to have a play you can get the code 
-here: [https://github.com/grokys/Avalonia](https://github.com/grokys/Avalonia)
-
-Feedback is always welcome!

+ 0 - 2
docs/serve.cmd

@@ -1,2 +0,0 @@
-@echo off
-docfx serve _site

+ 0 - 101
docs/spec/architecture.md

@@ -1,101 +0,0 @@
-# Avalonia Architecture
-
-At the highest level, avalonia is split up into a "core" and two "subsystems".
-
-* The core is a set of Portable Class Libraries that can run anywhere.
-* The Windowing subsystem is responsible for creating windows, handling input and scheduling timers.
-* The Rendering subsystem is responsible for drawing.
-
-There are currently two Windowing and two Rendering subsystems:
-
-## Windowing Subsystems
-
-* Avalonia.Win32 uses the Win32 API (this also works on 64-bit windows).
-* Avalonia.Gtk uses the GTK2 toolkit and can be run on both Windows and *nix.
-
-## Rendering Subsystems
-
-* Avalonia.Direct2D1 uses Microsoft's Direct2D1 API.
-* Avalonia.Cairo uses Cairo for rendering and Pango for text layout.
-
-## Core
-
-The Avalonia core is split up into several assemblies. Note that they're not separated like this 
-because you will want to use them separately; they are separate to maintain separation of concerns 
-and a layered architecture. It is fully possible that they will be ILMerged into a single assembly 
-for distribution.
-
-The assemblies are as follows, from lowest to highest level:
-
-### Avalonia.Base
-
-The main classes in this assembly are `AvaloniaObject` and `AvaloniaProperty`.
-
-These are Avalonia's versions of XAML's `DependencyObject` and `DependencyProperty`. It also 
-defines a `AvaloniaDispatcher` which is - surprise - our version of XAML's `Dispatcher`.
-
-### Avalonia.Animation
-
-The main class in the assembly is `Animatable`.
-
-Allows AvaloniaProperties to be animated and provides various utilities related to animation.
-
-### Avalonia.Visuals
-
-The main class in this assembly is `Visual` and its interface `IVisual`.
-
-Defines the "Visual" layer which is a 2D scene graph, with each node being a `IVisual`/`Visual`. 
-Also defines primitives such as `Point`/`Rect`/`Matrix`, plus `Geometry`, `Bitmap`, `Brush` and 
-whatever else you might need in order to draw to the screen.
-
-### Avalonia.Styling
-
-The main interface in this assembly is `IStyleable`.
-
-Defines a CSS-like system for styling controls.
-
-### Avalonia.Layout
-
-The main class in this assembly is `Layoutable`.
-
-Defines a XAML-like layout system using `Measure` and `Arrange`. Also defines `LayoutManager` which 
-carries out the actual layout.
-
-### Avalonia.Interactivity
-
-The main class in this assembly is `Interactive`.
-
-Defines a system of routed events similar to those found in XAML.
-
-### Avalonia.Input
-
-The main class in this assembly is `InputElement`.
-
-Handles input from various devices such as `MouseDevice` and `KeyboardDevice`, together with
-`FocusManager` for tracking input focus. Input is sent from the windowing subsystem to 
-`InputManager` in the form of "Raw" events which are then translated into routed events for the 
-controls.
-
-### Avalonia.Controls
-
-There are many important classes in this assembly, but the root of them is `Control`.
-
-Finally defines the actual controls. The `Control` class is analogous to WPF's `FrameworkElement`
-whereas the `TemplatedControl` class is our version of WPF's `Control`. This is also where you'll 
-find all of the basic controls you'd expect.
-
-### Avalonia.Themes.Default
-
-Defines a default theme using a set of styles and control templates.
-
-### Avalonia.Application
-
-The main class in this assembly is `Application`.
-
-This ties everything together, setting up the service locator, defining the default theme etc.
-
-### Avalonia.Diagnostics
-
-Adds utilities for debugging avalonia applications, such as `DevTools` which provides a Snoop/WPF
-Inspector/Browser DevTools window for inspecting the state of the application.
-

+ 0 - 156
docs/spec/binding-from-code.md

@@ -1,156 +0,0 @@
-# Binding from Code
-
-Avalonia binding from code works somewhat differently to WPF/UWP. At the low level, Avalonia's
-binding system is based on Reactive Extensions' `IObservable` which is then built upon by XAML
-bindings (which can also be instantiated in code).
-
-## Binding to an observable
-
-You can bind a property to an observable using the `AvaloniaObject.Bind` method:
-
-```csharp
-// We use an Rx Subject here so we can push new values using OnNext
-var source = new Subject<string>();
-var textBlock = new TextBlock();
-
-// Bind TextBlock.Text to source
-textBlock.Bind(TextBlock.TextProperty, source);
-
-// Set textBlock.Text to "hello"
-source.OnNext("hello");
-// Set textBlock.Text to "world!"
-source.OnNext("world!");
-```
-
-## Binding priorities
-
-You can also pass a priority to a binding. *Note: Priorities only apply to styled properties: they*
-*are ignored for direct properties.*
-
-The priority is passed using the `BindingPriority` enum, which looks like this:
-
-```csharp
-/// <summary>
-/// The priority of a binding.
-/// </summary>
-public enum BindingPriority
-{
-    /// <summary>
-    /// A value that comes from an animation.
-    /// </summary>
-    Animation = -1,
-
-    /// <summary>
-    /// A local value: this is the default.
-    /// </summary>
-    LocalValue = 0,
-
-    /// <summary>
-    /// A triggered style binding.
-    /// </summary>
-    /// <remarks>
-    /// A style trigger is a selector such as .class which overrides a
-    /// <see cref="TemplatedParent"/> binding. In this way, a basic control can have
-    /// for example a Background from the templated parent which changes when the
-    /// control has the :pointerover class.
-    /// </remarks>
-    StyleTrigger,
-
-    /// <summary>
-    /// A binding to a property on the templated parent.
-    /// </summary>
-    TemplatedParent,
-
-    /// <summary>
-    /// A style binding.
-    /// </summary>
-    Style,
-
-    /// <summary>
-    /// The binding is uninitialized.
-    /// </summary>
-    Unset = int.MaxValue,
-}
-```
-
-Bindings with a priority with a smaller number take precedence over bindings with a higher value
-priority, and bindings added more recently take precedence over other bindings with the same
-priority. Whenever the binding produces `AvaloniaProperty.UnsetValue` then the next binding in the
-priority order is selected.
-
-## Setting a binding in an object initializer
-
-It is often useful to set up bindings in object initializers. You can do this using the indexer:
-
-```csharp
-var source = new Subject<string>();
-var textBlock = new TextBlock
-{
-    Foreground = Brushes.Red,
-    MaxWidth = 200,
-    [!TextBlock.TextProperty] = source.ToBinding(),
-};
-```
-
-Using this method you can also easily bind a property on one control to a property on another:
-
-```csharp
-var textBlock1 = new TextBlock();
-var textBlock2 = new TextBlock
-{
-    Foreground = Brushes.Red,
-    MaxWidth = 200,
-    [!TextBlock.TextProperty] = textBlock1[!TextBlock.TextProperty],
-};
-```
-
-Of course the indexer can be used outside object initializers too:
-
-```csharp
-textBlock2[!TextBlock.TextProperty] = textBlock1[!TextBlock.TextProperty];
-```
-
-# Transforming binding values
-
-Because we're working with observables, we can easily transform the values we're binding!
-
-```csharp
-var source = new Subject<string>();
-var textBlock = new TextBlock
-{
-    Foreground = Brushes.Red,
-    MaxWidth = 200,
-    [!TextBlock.TextProperty] = source.Select(x => "Hello " + x).ToBinding(),
-};
-```
-
-# Using XAML bindings from code
-
-Sometimes when you want the additional features that XAML bindings provide, it's easier to use XAML bindings from code. For example, using only observables you could bind to a property on `DataContext` like this:
-
-```csharp
-var textBlock = new TextBlock();
-var viewModelProperty = textBlock.GetObservable(TextBlock.DataContext)
-    .OfType<MyViewModel>()
-    .Select(x => x?.Name);
-textBlock.Bind(TextBlock, viewModelProperty);
-```
-
-However, it might be preferable to use a XAML binding in this case:
-
-```csharp
-var textBlock = new TextBlock
-{
-    [!TextBlock.TextProperty] = new Binding("Name")
-};
-```
-
-By using XAML binding objects, you get access to binding to named controls and [all the other features that XAML bindings bring](binding-from.xaml.md):
-
-```csharp
-var textBlock = new TextBlock
-{
-    [!TextBlock.TextProperty] = new Binding("Text") { ElementName = "other" }
-};
-```
-

+ 0 - 99
docs/spec/binding-from-xaml.md

@@ -1,99 +0,0 @@
-# Binding from XAML
-
-Binding from XAML works on the whole the same as in other XAML frameworks: you use the `{Binding}`
-markup extension. Avalonia does have some extra syntacic niceties however. Here's an overview of
-what you can currently do in Avalonia:
-
-## Binding to a property on the DataContext
-
-By default a binding binds to a property on the `DataContext`, e.g.:
-
-```xml
-<!-- Binds to the tb.DataContext.Name property -->
-<TextBlock Name="tb" Text="{Binding Name}"/>
-<!-- Which is the same as ('Path' is optional) -->
-<TextBlock Name="tb" Text="{Binding Path=Name}"/>
-```
-
-An empty binding binds to DataContext itself
-
-```xml
-<!-- Binds to the tb.DataContext property -->
-<TextBlock Name="tb" Text="{Binding}"/>
-<!-- Which is the same as -->
-<TextBlock Name="tb" Text="{Binding .}"/>
-```
-
-This usage is identical to WPF/UWP etc.
-
-## Two way bindings and more
-
-You can also specify a binding `Mode`:
-
-```xml
-<!-- Bind two-way to the property (although this is actually the default binding mode for
-     TextBox.Text) so strictly speaking it's unnecessary here) -->
-<TextBox Name="tb" Text="{Binding Name, Mode=TwoWay}"/>
-```
-
-This usage is identical to WPF/UWP etc.
-
-## Binding to a property on the templated parent
-
-When you're creating a control template and you want to bind to the templated parent you can use:
-
-```xml
-<TextBlock Name="tb" Text="{TemplateBinding Caption}"/>
-<!-- Which is short for -->
-<TextBlock Name="tb" Text="{Binding Caption, RelativeSource={RelativeSource TemplatedParent}}"/>
-```
-
-This usage is identical to WPF/UWP etc.
-
-## Binding to a named control
-
-If you want to bind to a property on another (named) control, you can use `ElementName` as in
-WPF/UWP:
-
-```xml
-<!-- Binds to the Tag property of a control named "other" -->
-<TextBlock Text="{Binding Tag, ElementName=other}"/>
-```
-
-However Avalonia also introduces a shorthand syntax for this:
-
-```xml
-<TextBlock Text="{Binding #other.Tag}"/>
-```
-
-## Negating bindings
-
-You can also negate the value of a binding using the `!` operator:
-
-```xml
-<TextBox IsEnabled="{Binding !HasErrors}"/>
-```
-
-Here, the `TextBox` will only be enabled when the view model signals that it has no errors. Behind
-the scenes, Avalonia tries to convert the incoming value to a boolean, and if it can be converted
-it negates the value. If the incoming value cannot be converted to a boolean then no value will be
-pushed to the binding target.
-
-This syntax is specific to Avalonia.
-
-## Binding to tasks and observables
-
-You can subscribe to the result of a task or an observable by using the `^` stream binding operator.
-
-```xml
-<!-- If DataContext.Name is an IObservable<string> then this will bind to the length of each
-     string produced by the observable as each value is produced -->
-<TextBlock Text="{Binding Name^.Length}"/>
-```
-
-This syntax is specific to Avalonia.
-
-*Note: the stream operator is actually extensible, see
-[here](https://github.com/AvaloniaUI/Avalonia/blob/master/src/Markup/Avalonia.Markup/Data/Plugins/IStreamPlugin.cs)
-for the interface to implement and [here](https://github.com/AvaloniaUI/Avalonia/blob/master/src/Markup/Avalonia.Markup/Data/ExpressionObserver.cs#L47)
-for the registration.*

+ 0 - 199
docs/spec/defining-properties.md

@@ -1,199 +0,0 @@
-# Defining Properties
-
-If you are creating a control, you will want to define properties on your
-control. The process in Avalonia is broadly similar to other XAML languages
-with a few differences - the main one being that Perpsex's equivalent of
-`DependencyProperty` is called `StyledProperty`.
-
-## Registering Styled Properties
-
-A styled property is analogous to a `DependencyProperty` in other XAML
-frameworks.
-
-You register a styled property by calling `AvaloniaProperty.Register` and
-storing the result in a `static readonly` field. You then create a standard C#
-property to access it.
-
-Here's how the `Border` control defines its `Background` property:
-
-```c#
-    public static readonly StyledProperty<Brush> BackgroundProperty =
-        AvaloniaProperty.Register<Border, Brush>(nameof(Background));
-
-    public Brush Background
-    {
-        get { return GetValue(BackgroundProperty); }
-        set { SetValue(BackgroundProperty, value); }
-    }
-```
-
-The `AvaloniaProperty.Register` method also accepts a number of other parameters:
-
-- `defaultValue`: This gives the property a default value. Be sure to only pass
-value types and immutable types here as passing a reference type will cause the
-same object to be used on all instances on which the property is registered.
-- `inherits`: Specified that the property's default value should come from
-the parent control.
-- `defaultBindingMode`: The default binding mode for the property. Can be set to
-`OneWay`, `TwoWay`, `OneTime` or `OneWayToSource`.
-- `validate`: A validation/coercion function of type
-`Func<TOwner, TValue, TValue>`. The function accepts the instance of the class
-on which the property is being set and the value and returns the coerced value
-or throws an exception for an invalid value.
-
-## Using a StyledProperty on Another Class
-
-Sometimes the property you want to add to your control already exists on another
-control, `Background` being a good example. To register a property defined on
-another control, you call `StyledProperty.AddOwner`:
-
-
-```c#
-    public static readonly StyledProperty<Brush> BackgroundProperty =
-        Border.BackgroundProperty.AddOwner<Panel>();
-
-    public Brush Background
-    {
-        get { return GetValue(BackgroundProperty); }
-        set { SetValue(BackgroundProperty, value); }
-    }
-```
-
-*Note: Unlike WPF, a property must be registered on a class otherwise it cannot
-be set on an object of that class.*
-
-## Attached Properties
-
-Attached properties are defined almost identically to styled properties except
-that they are registered using the `RegisterAttached` method and their accessors
-are defined as static methods.
-
-Here's how `Grid` defines its `Grid.Column` attached property:
-
-```c#
-    public static readonly AttachedProperty<int> ColumnProperty =
-        AvaloniaProperty.RegisterAttached<Grid, Control, int>("Column");
-
-    public static int GetColumn(Control element)
-    {
-        return element.GetValue(ColumnProperty);
-    }
-
-    public static void SetColumn(Control element, int value)
-    {
-        element.SetValue(ColumnProperty, value);
-    }
-```
-
-## Readonly AvaloniaProperties
-
-To create a readonly property you use the `AvaloniaProperty.RegisterDirect`
-method. Here is how `Visual` registers the readonly `Bounds` property:
-
-```c#
-    public static readonly DirectProperty<Visual, Rect> BoundsProperty =
-        AvaloniaProperty.RegisterDirect<Visual, Rect>(
-            nameof(Bounds),
-            o => o.Bounds);
-
-    private Rect _bounds;
-
-    public Rect Bounds
-    {
-        get { return _bounds; }
-        private set { SetAndRaise(BoundsProperty, ref _bounds, value); }
-    }
-```
-
-As can be seen, readonly properties are stored as a field on the object. When
-registering the property, a getter is passed which is used to access the
-property value through `GetValue` and then `SetAndRaise` is used to notify
-listeners to changes to the property.
-
-## Direct AvaloniaProperties
-
-As its name suggests, `RegisterDirect` isn't just used for registering readonly
-properties. You can also pass a *setter* to `RegisterDirect` to expose a
-standard C# property as a Avalonia property.
-
-A `StyledProperty` which is registered using `AvaloniaProperty.Register`
-maintains a prioritized list of values and bindings that allow styles to work.
-However, this is overkill for many properties, such as `ItemsControl.Items` -
-this will never be styled and the overhead involved with styled properties is
-unnecessary.
-
-Here is how `ItemsControl.Items` is registered:
-
-```c#
-    public static readonly DirectProperty<ItemsControl, IEnumerable> ItemsProperty =
-        AvaloniaProperty.RegisterDirect<ItemsControl, IEnumerable>(
-            nameof(Items),
-            o => o.Items,
-            (o, v) => o.Items = v);
-
-    private IEnumerable _items = new AvaloniaList<object>();
-
-    public IEnumerable Items
-    {
-        get { return _items; }
-        set { SetAndRaise(ItemsProperty, ref _items, value); }
-    }
-```
-
-
-Direct properties are a lightweight version of styled properties that support
-the following:
-
-- AvaloniaObject.GetValue
-- AvaloniaObject.SetValue for non-readonly properties
-- PropertyChanged
-- Binding (only with LocalValue priority)
-- GetObservable
-- AddOwner
-- Metadata
-
-They don't support the following:
-
-- Validation/Coercion (although this could be done in the property setter)
-- Overriding default values.
-- Inherited values
-
-## Using a DirectProperty on Another Class
-
-In the same way that you can call `AddOwner` on a styled property, you can also
-add an owner to a direct property. Because direct properties reference fields
-on the control, you must also add a field for the property:
-
-```c#
-    public static readonly DirectProperty<MyControl, IEnumerable> ItemsProperty =
-        ItemsControl.ItemsProperty.AddOwner<MyControl>(
-            o => o.Items,
-            (o, v) => o.Items = v);
-
-    private IEnumerable _items = new AvaloniaList<object>();
-
-    public IEnumerable Items
-    {
-        get { return _items; }
-        set { SetAndRaise(ItemsProperty, ref _items, value); }
-    }
-```
-
-## When to use a Direct vs a Styled Property
-
-Direct properties have advantages and disadvantages:
-
-Pros:
-- No additional object is allocated per-instance for the property
-- Property getter is a standard C# property getter
-- Property setter is is a standard C# property setter that raises an event.
-
-Cons:
-- Cannot inherit value from parent control
-- Cannot take advantage of Avalonia's styling system
-- Property value is a field and as such is allocated whether the property is
-set on the object or not
-
-So use direct properties when you have the following requirements:
-- Property will not need to be styled
-- Property will usually or always have a value

+ 0 - 54
docs/spec/logging.md

@@ -1,54 +0,0 @@
-# Avalonia Logging
-
-Avalonia uses [Serilog](https://github.com/serilog/serilog) for logging via
-the Avalonia.Logging.Serilog assembly.
-
-The following method should be present in your App.xaml.cs file:
-
-```C#
-private void InitializeLogging()
-{
-#if DEBUG
-    SerilogLogger.Initialize(new LoggerConfiguration()
-        .MinimumLevel.Warning()
-        .WriteTo.Trace(outputTemplate: "{Area}: {Message}")
-        .CreateLogger());
-#endif
-}
-```
-
-By default, this logging setup will write log messages with a severity of
-`Warning` or higher to `System.Diagnostics.Trace`. See the [Serilog
-documentation](https://github.com/serilog/serilog/wiki/Configuration-Basics)
-for more information on the options here.
-
-## Areas
-
-Each Avalonia log message has an "Area" that can be used to filter the log to
-include only the type of events that you are interested in. These are currently:
-
-- Property
-- Binding
-- Visual
-- Layout
-- Control
-
-To limit the log output to a specific area you can add a filter; for example
-to enable verbose logging but only about layout:
-
-```C#
-SerilogLogger.Initialize(new LoggerConfiguration()
-    .Filter.ByIncludingOnly(Matching.WithProperty("Area", LogArea.Layout))
-    .MinimumLevel.Verbose()
-    .WriteTo.Trace(outputTemplate: "{Area}: {Message}")
-    .CreateLogger());
-```
-
-## Removing Serilog
-
-If you don't want a dependency on Serilog in your application, simply remove
-the reference to Avalonia.Logging.Serilog and the code that initializes it. If
-you do however still want some kinda of logging, there are two steps:
-
-- Implement `Avalonia.Logging.ILogSink`
-- Assign your implementation to `Logger.Sink`

+ 0 - 111
docs/spec/styles.md

@@ -1,111 +0,0 @@
-# Styling in Avalonia
-
-The main difference between Avalonia and existing XAML toolkits such as WPF and
-UWP is in styling. Styling in Avalonia uses a CSS-like system that aims to be
-more powerful and flexible than existing XAML styling systems. For convenience
-for the rest of this document we'll refer to existing XAML toolkit's styling as
-"WPF styling" as that's where it originated.
-
-## Basics
-
-- Styles are defined on the `Control.Styles` collection (as opposed to in
-`ResourceDictionaries` in WPF).
-- Styles have a `Selector` and a collection of `Setter`s
-- Selector works like a CSS selector.
-- Setters function like WPF's setters.
-- Styles are applied to a control and all its descendants, depending on whether
-  the selector matches.
-
-## Simple example
-
-Make all `Button`s in a `StackPanel` have a blue `Background`:
-
-```xaml
-<StackPanel>
-  <StackPanel.Styles>
-    <Style Selector="Button">
-      <Setter Property="Button.Background" Value="Blue"/>
-    </Style>
-  </StackPanel.Styles>
-  <Button>I will have a blue background.</Button>
-</StackPanel>
-```
-
-This is very similar to WPF, except `TargetType` is replaced by `Selector`.
-
-_Note that currently (as of Alpha 2) you **always** need to specify the fully
-qualified property name (i.e. `Button.Background` instead of simply
-`Background`). This restriction will be lifted in future._
-
-## Style Classes
-
-As in CSS, controls can be given *style classes* which can be used in selectors:
-
-```xaml
-<StackPanel>
-  <StackPanel.Styles>
-    <Style Selector="Button.blue">
-      <Setter Property="Button.Background" Value="Blue"/>
-    </Style>
-  </StackPanel.Styles>
-  <Button Classes="blue">I will have a blue background.</Button>
-  <Button>I will not.</Button>
-</StackPanel>
-```
-
-Each control can be given 0 or more style classes. This is different to WPF
-where only a single style can be applied to a control: in Avalonia any number
-of separate styles can be applied to a control. If more than one style affects
-a particular property, the style closest to the control will take precedence.
-
-Style classes can also be manipulated in code using the `Classes` collection:
-
-```csharp
-control.Classes.Add("blue");
-control.Classes.Remove("red");
-```
-
-## Pseudoclasses
-
-Also as in CSS, controls can have pseudoclasses; these are classes that are
-defined by the control itself rather than by the user. Pseudoclasses start
-with a `:` character.
-
-One example of a pseudoclass is the `:pointerover`
-pseudoclass (`:hover` in CSS - we may change to that in future).
-
-Pseudoclasses provide the functionality of `Triggers` in WPF and
-`VisualStateManager` in UWP:
-
-```xaml
-<StackPanel>
-  <StackPanel.Styles>
-    <Style Selector="Button:pointerover">
-      <Setter Property="Button.Foreground" Value="Red"/>
-    </Style>
-  </StackPanel.Styles>
-  <Button>I will have red text when hovered.</Button>
-</StackPanel>
-```
-
-Other pseudoclasses include `:focus`, `:disabled`, `:pressed` for buttons,
-`:checked` for checkboxes etc.
-
-## Named Controls
-
-Named controls can be selected using `#` as in CSS, e.g. `Button#Name`.
-
-## Children
-
-As with CSS, you can select children and descendants:
-
-- `StackPanel > Button#Foo` selects a `Button` named `"Foo"` that is the child
-  of a `StackPanel`.
-- `StackPanel Button.foo` selects all `Button`s with the `foo` class that are
-  descendants of a `StackPanel`.
-
-## Templates
-
-You can select controls in the template of a lookless control by using the
-`/template/` selector, so `Button /template/ Border#outline` selects `Border`
-controls named `"outline"` in the template of a `Button`.

+ 0 - 14
docs/spec/toc.yml

@@ -1,14 +0,0 @@
-- name: Avalonia Architecture
-  href: architecture.md
-- name: Styling in Avalonia
-  href: styles.md
-- name: Defining Properties
-  href: defining-properties.md
-- name: Working with Properties
-  href: working-with-properties.md
-- name: Logging
-  href: logging.md
-- name: Binding from XAML
-  href: binding-from-xaml.md
-- name: Binding from Code
-  href: binding-from-code.md

+ 0 - 102
docs/spec/working-with-properties.md

@@ -1,102 +0,0 @@
-# Working with Properties
-
-Avalonia controls expose their properties as standard CLR properties, so for
-reading and writing values there's no surprises:
-
-```c#
-    // Create a TextBlock and set its Text property.
-    var textBlock = new TextBlock();
-    textBlock.Text = "Hello World!";
-```
-
-
-However there's a lot more you can do with properties such as subscribing to
-changes on the property, and binding.
-
-# Subscribing to Changes to a Property
-
-You can subscribe to changes on a property by calling the `GetObservable`
-method. This returns an `IObservable<T>` which can be used to listen for changes
-to the property:
-
-```c#
-    var textBlock = new TextBlock();
-    var text = textBlock.GetObservable(TextBlock.TextProperty);
-```
-
-Each property that can be subscribed to has a static readonly field called
-`[PropertyName]Property` which is passed to `GetObservable` in order to
-subscribe to the property's changes.
-
-`IObservable` (part of Reactive Extensions, or rx for short) is out of scope
-for this guide, but here's an example which uses the returned observable to
-print a message with the changing property values to the console:
-
-```c#
-    var textBlock = new TextBlock();
-    var text = textBlock.GetObservable(TextBlock.TextProperty);
-    text.Subscribe(value => Console.WriteLine(value + " Changed"));
-```
-
-When the returned observable is subscribed, it will return the current value
-of the property immediately and then push a new value each time the property
-changes. If you don't want the current value, you can use the rx `Skip`
-operator:
-
-```c#
-    var text = textBlock.GetObservable(TextBlock.TextProperty).Skip(1);
-```
-
-# Binding a property
-
-Observables don't just go one way! You can also use them to bind properties.
-For example here we create two `TextBlock`s and bind the second's `Text`
-property to the first:
-
-```c#
-  var textBlock1 = new TextBlock();
-  var textBlock2 = new TextBlock();
-
-  // Get an observable for the first text block's Text property.
-  var source = textBlock1.GetObservable(TextBlock.TextProperty);
-
-  // And bind it to the second.
-  textBlock2.Bind(TextBlock.TextProperty, source);
-
-  // Changes to the first TextBlock's Text property will now be propagated
-  // to the second.
-  textBlock1.Text = "Goodbye Cruel World";
-
-  // Prints "Goodbye Cruel World"
-  Console.WriteLine(textBlock2.Text);
-```
-
-To read more about creating bindings from code, see [Binding from Code](binding-from-code.md).
-
-# Subscribing to a Property on Any Object
-
-The `GetObservable` method returns an observable that tracks changes to a
-property on a single instance. However, if you're writing a control you may
-want to implement an `OnPropertyChanged` method. In WPF this is done by passing
-a static `PropertyChangedCallback` to the `DependencyProperty` registration
-method, but in Avalonia it's slightly different (and hopefully easier!)
-
-The field which defines the property is derived from `AvaloniaProperty` and this
-has a `Changed` observable which is fired every time the property changes on
-*any* object. In addition there is an `AddClassHandler` extension method which
-can automatically route the event to a method on your control.
-
-For example if you want to listen to changes to your control's `Foo` property
-you'd do it like this:
-
-```c#
-    static MyControl()
-    {
-        FooProperty.Changed.AddClassHandler<MyControl>(x => x.FooChanged);
-    }
-
-    private void FooChanged(AvaloniaPropertyChangedEventArgs e)
-    {
-        // The 'e' parameter describes what's changed.
-    }
-```

+ 0 - 13
docs/template/partials/footer.tmpl.partial

@@ -1,13 +0,0 @@
-{{!Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license. See LICENSE file in the project root for full license information.}}
-
-<footer>
-  <div class="grad-bottom"></div>
-  <div class="footer">
-    <div class="container">
-      <span class="pull-right">
-        <a href="#top">Back to top</a>
-      </span>
-      <span>Copyright © 2016 The Avalonia Project<br>Generated by <strong>DocFX</strong></span>
-    </div>
-  </div>
-</footer>

+ 0 - 10
docs/toc.yml

@@ -1,10 +0,0 @@
-- name: Home
-  href: index.md
-- name: Getting Started
-  href: tutorial/
-- name: Guidelines
-  href: guidelines/
-- name: Specifications
-  href: spec/
-- name: API Documentation
-  href: api/

+ 0 - 161
docs/tutorial/from-wpf.md

@@ -1,161 +0,0 @@
-# Avalonia for WPF Developers
-
-Avalonia is in general very similar to WPF, but you will find differences. Here
-are the most common:
-
-## Styling
-
-The most obvious difference from other XAML frameworks is that Avalonia uses a
-[CSS-like styling system](../spec/styles.md). Styles aren't stored in a
-`Resources` collection as in WPF, they are stored in a separate `Styles`
-collection:
-
-    <UserControl>
-        <UserControl.Styles>
-            <!-- Make TextBlocks with the h1 style class have a font size of 24 points -->
-            <Style Selector="TextBlock.h1">
-                <Setter Property="FontSize" Value="24"/>
-            </Style>
-        </UserControl.Styles>
-        <TextBlock Classes="h1">Header</TextBlock>
-    <UserControl>    
-
-## DataTemplates
-
-As styles aren't stored  in `Resources`, neither are `DataTemplates` ([in fact
-there is no `Resources` collection](#resources)). Instead, `DataTemplates` are
-placed in a `DataTemplates` collection on each control (and on `Application`):
-
-    <UserControl xmlns:viewmodels="clr-namespace:MyApp.ViewModels;assembly=MyApp">
-        <UserControl.DataTemplates>
-            <DataTemplate DataType="viewmodels:FooViewModel">
-                <Border Background="Red" CornerRadius="8">
-                    <TextBox Text="{Binding Name}"/>
-                </Border>
-            </DataTemplate>
-        </UserControl.DataTemplates>
-        <!-- Assuming that DataContext.Foo is an object of type
-             MyApp.ViewModels.FooViewModel then a red border with a corner
-             radius of 8 containing a TextBox will be displayed here -->
-        <ContentControl Content="{Binding Foo}"/>
-    <UserControl>    
-
-Data templates in Avalonia can also target interfaces and derived classes (which
-cannot be done in WPF) and so the order of `DataTemplate`s can be important:
-`DataTemplate`s  within the same collection are evaluated in declaration order
-so you need to place them from most-specific to least-specific as you would in
-code.
-
-## HierachicalDataTemplate
-
-WPF's `HierarchicalDataTemplate` is called `TreeDataTemplate` in Avalonia (as the
-former is difficult to type!). The two are almost entirely equivalent except
-that the `ItemTemplate` property is not present in Avalonia.
-
-## UIElement, FrameworkElement and Control
-
-WPF's `UIElement` and `FrameworkElement` are non-templated control base classes,
-which roughly equate to the Avalonia `Control` class. WPF's `Control` class on
-the other hand is a templated control - Avalonia's equivalent of this is
-`TemplatedControl`.
-
-So to recap:
-
-- `UIElement`: `Control`
-- `FrameworkElement`: `Control`
-- `Control`: `TemplatedControl`
-
-## DependencyProperty
-
-The Avalonia equivalent of `DependencyProperty` is `StyledProperty`, however
-Avalonia [has a richer property system than WPF](../spec/defining-properties.md),
-and includes `DirectProperty` for turning standard CLR properties into Avalonia
-properties. The common base class of `StyledProperty` and `DirectProperty`
-is `AvaloniaProperty`.
-
-## Grid
-
-Column and row definitions can be specified in Avalonia using strings, avoiding
-the clunky syntax in WPF:
-
-    <Grid ColumnDefinitions="Auto,*,32" RowDefinitions="*,Auto">
-
-A common use of `Grid` in WPF is to stack two controls on top of each other.
-For this purpose in Avalonia you can just use a `Panel` which is more lightweight
-than `Grid`.
-
-We don't yet support `SharedSizeScope` in `Grid`.
-
-## ItemsControl
-
-In WPF, `ItemsControl` and derived classes such as `ListBox` have two separate
-items properties: `Items` and `ItemsSource`. Avalonia however just has a single
-one: `Items`.
-
-## Tunnelling Events
-
-Avalonia has tunnelling events (unlike UWP!) but they're not exposed via
-separate `Preview` CLR event handlers. To subscribe to a tunnelling event you
-must call `AddHandler` with `RoutingStrategies.Tunnel`:
-
-```
-target.AddHandler(InputElement.KeyDownEvent, OnPreviewKeyDown, RoutingStrategies.Tunnel);
-
-void OnPreviewKeyDown(object sender, KeyEventArgs e)
-{
-    // Handler code
-}
-```
-
-## Class Handlers
-
-In WPF, class handlers for events can be added by calling
-[EventManager.RegisterClassHandler](https://msdn.microsoft.com/en-us/library/ms597875.aspx).
-An example of registering a class handler in WPF might be:
-
-    static MyControl()
-    {
-      EventManager.RegisterClassHandler(typeof(MyControl), MyEvent, HandleMyEvent));
-    }
-
-    private static void HandleMyEvent(object sender, RoutedEventArgs e)
-    {
-    }
-
-The equivalent of this in Avalonia would be:
-
-    static MyControl()
-    {
-        MyEvent.AddClassHandler<MyControl>(x => x.HandleMyEvent);
-    }
-
-    private void HandleMyEvent(object sender, RoutedEventArgs e)
-    {
-    }
-
-Notice that in WPF you have to add the class handler as a static method, whereas
-in Avalonia the class handler is not static: the notification is automatically
-directed to the correct instance.
-
-## PropertyChangedCallback
-
-Listening to changes on DependencyProperties in WPF can be complex. When you
-register a `DependencyProperty` you can supply a static `PropertyChangedCallback`
-but if you want to listen to changes from elsewhere [things can get complicated
-and error-prone](http://stackoverflow.com/questions/23682232).
-
-In Avalonia, there is no `PropertyChangedCallback` at the time of registration,
-instead a class listener is [added to the control's static constructor in much
-the same way that event class listeners are added](../spec/working-with-properties.md#subscribing-to-a-property-on-any-object).
-
-## RenderTransforms and RenderTransformOrigin
-
-RenderTransformOrigins are different in WPF and Avalonia: If you apply a `RenderTransform`, keep in mind that our default value for the RenderTransformOrigin is `RelativePoint.Center`. In WPF the default value is `RelativePoint.TopLeft` (0, 0). In controls like Viewbox (currently being developed) the same code will lead to a different rendering behavior:
-
-In WPF:
-![WPF](https://files.gitter.im/AvaloniaUI/Avalonia/cDrM/image.png)
-
-In Avalonia:
-![Avalonia](https://files.gitter.im/AvaloniaUI/Avalonia/KGk7/image.png)
-
-In AvaloniaUI, to get the same scale transform we should indicate that the RenderTransformOrigin is the TopLeft part of the Visual. 

+ 0 - 10
docs/tutorial/gettingstarted.md

@@ -1,10 +0,0 @@
-# Getting Started
-
-## Windows
-
-![](images/add-dialogs.png)
-
-The easiest way to try out Avalonia is to install the [Visual Studio Extension](https://marketplace.visualstudio.com/items?itemName=AvaloniaTeam.AvaloniaforVisualStudio).
-
-This will add a Avalonia project template and a Window template to the standard Visual Studo “Add”
-dialog (yes, icons still to come :) ):

BIN
docs/tutorial/images/add-dialogs.png


+ 0 - 86
docs/tutorial/nuget.md

@@ -1,86 +0,0 @@
-# Avalonia NuGet Packages
-
-Avalonia is divided into several `NuGet` packages. 
-
-* The `Avalonia` package contains core portable class libraries.
-* The `Dekstop` and `Mobile` packages contain platform specific windowing and rendering back-ends.
-* The `Avalonia.Desktop` package is intended to be used by the end users targeting multiple desktop platforms (`Windows`, `Linux` and `OSX`).
-* The `Avalonia.iOS` and `Avalonia.Android` packages are intended to be used by the end users targeting specific mobile platforms. 
-* The `Avalonia.Mobile` package is intended to be used by the end users targeting multiple mobile platforms (`Android` and `iOS`).
-
-## Core
-
-* Avalonia (.nupkg)
-  - Avalonia.Animation (.dll)
-  - Avalonia.Base (.dll)
-  - Avalonia.Controls (.dll)
-  - Avalonia.DesignerSupport (.dll)
-  - Avalonia.Diagnostics (.dll)
-  - Avalonia.Input (.dll)
-  - Avalonia.Interactivity (.dll)
-  - Avalonia.Layout (.dll)
-  - Avalonia.Logging.Serilog (.dll)
-  - Avalonia.Visuals (.dll)
-  - Avalonia.Styling (.dll)
-  - Avalonia.ReactiveUI (.dll)
-  - Avalonia.Themes.Default (.dll)
-  - Avalonia.Markup (.dll)
-  - Avalonia.Markup.Xaml (.dll)
-  - Serilog (.nupkg)
-  - Splat (.nupkg)
-  - Sprache (.nupkg)
-  - System.Reactive (.nupkg)
-
-* Avalonia.HtmlRenderer (.nupkg)
-  - Avalonia (.nupkg)
-
-## Desktop
-
-* Avalonia.Win32 (.nupkg)
-  - Avalonia.Win32 (.dll)
-  - Avalonia (.nupkg)
-
-* Avalonia.Direct2D1 (.nupkg)
-  - Avalonia.Direct2D1 (.dll)
-  - Avalonia (.nupkg)
-  - SharpDX (.nupkg)
-  - SharpDX.Direct2D1 (.nupkg)
-  - SharpDX.DXGI (.nupkg)
-
-* Avalonia.Gtk (.nupkg)
-  - Avalonia.Gtk (.dll)
-  - Avalonia (.nupkg)
-
-* Avalonia.Cairo (.nupkg)
-  - Avalonia.Cairo (.dll)
-  - Avalonia (.nupkg)
-
-* Avalonia.Skia.Desktop (.nupkg)
-  - Avalonia.Skia.Desktop (.dll)
-  - Avalonia (.nupkg)
-  - SkiaSharp (.nupkg)
-
-* Avalonia.Desktop (.nupkg)
-  - Avalonia.Win32 (.nupkg)
-  - Avalonia.Direct2D1 (.nupkg)
-  - Avalonia.Gtk (.nupkg)
-  - Avalonia.Cairo (.nupkg)
-  - Avalonia.Skia.Desktop (.nupkg)
-
-## Mobile
-
-* Avalonia.Android (.nupkg)
-  - Avalonia.Android (.dll)
-  - Avalonia.Skia.Android (.dll)
-  - Avalonia (.nupkg)
-  - SkiaSharp (.nupkg)
-
-* Avalonia.iOS (.nupkg)
-  - Avalonia.iOS (.dll)
-  - Avalonia.Skia.iOS (.dll)
-  - Avalonia (.nupkg)
-  - SkiaSharp (.nupkg)
-
-* Avalonia.Mobile (.nupkg)
-  - Avalonia.Android (.nupkg)
-  - Avalonia.iOS (.nupkg)

+ 0 - 6
docs/tutorial/toc.yml

@@ -1,6 +0,0 @@
-- name: Getting Started
-  href: gettingstarted.md
-- name: Avalonia NuGet Packages
-  href: nuget.md
-- name: Avalonia for WPF Developers
-  href: from-wpf.md

+ 33 - 4
packages.cake

@@ -107,7 +107,8 @@ public class Packages
         context.Information("Setting NuGet package dependencies versions:");
 
         var SerilogVersion = packageVersions["Serilog"].FirstOrDefault().Item1;
-        var SplatVersion = packageVersions["Splat"].FirstOrDefault().Item1;
+        var SerilogSinksDebugVersion = packageVersions["Serilog.Sinks.Debug"].FirstOrDefault().Item1;
+        var SerilogSinksTraceVersion = packageVersions["Serilog.Sinks.Trace"].FirstOrDefault().Item1;
         var SpracheVersion = packageVersions["Sprache"].FirstOrDefault().Item1;
         var SystemReactiveVersion = packageVersions["System.Reactive"].FirstOrDefault().Item1;
         var ReactiveUIVersion = packageVersions["reactiveui"].FirstOrDefault().Item1;
@@ -121,7 +122,6 @@ public class Packages
         var SharpDXDXGIVersion = packageVersions["SharpDX.DXGI"].FirstOrDefault().Item1;
 
         context.Information("Package: Serilog, version: {0}", SerilogVersion);
-        context.Information("Package: Splat, version: {0}", SplatVersion);
         context.Information("Package: Sprache, version: {0}", SpracheVersion);
         context.Information("Package: System.Reactive, version: {0}", SystemReactiveVersion);
         context.Information("Package: reactiveui, version: {0}", ReactiveUIVersion);
@@ -223,6 +223,17 @@ public class Packages
             };
         });
 
+        var toolsContent = new[] {
+            new NuSpecContent{
+                Source = ((FilePath)context.File("./src/tools/Avalonia.Designer.HostApp/bin/" + parameters.DirSuffix + "/netcoreapp2.0/Avalonia.Designer.HostApp.dll")).FullPath, 
+                Target = "tools/netcoreapp2.0/previewer"
+            },
+            new NuSpecContent{
+                Source = ((FilePath)context.File("./src/tools/Avalonia.Designer.HostApp.NetFx/bin/" + parameters.DirSuffix + "/Avalonia.Designer.HostApp.exe")).FullPath, 
+                Target = "tools/net461/previewer"
+            }
+        };
+
         var nuspecNuGetSettingsCore = new []
         {
             ///////////////////////////////////////////////////////////////////////////////
@@ -234,17 +245,21 @@ public class Packages
                 Dependencies = new DependencyBuilder(this)
                 {
                     new NuSpecDependency() { Id = "Serilog", Version = SerilogVersion },
-                    new NuSpecDependency() { Id = "Splat", Version = SplatVersion },
+                    new NuSpecDependency() { Id = "Serilog.Sinks.Debug", Version = SerilogSinksDebugVersion },
+                    new NuSpecDependency() { Id = "Serilog.Sinks.Trace", Version = SerilogSinksTraceVersion },
                     new NuSpecDependency() { Id = "Sprache", Version = SpracheVersion },
                     new NuSpecDependency() { Id = "System.Reactive", Version = SystemReactiveVersion },
+                    new NuSpecDependency() { Id = "Avalonia.Remote.Protocol", Version = parameters.Version },
                     //.NET Core
                     new NuSpecDependency() { Id = "System.Threading.ThreadPool", TargetFramework = "netcoreapp2.0", Version = "4.3.0" },
                     new NuSpecDependency() { Id = "Microsoft.Extensions.DependencyModel", TargetFramework = "netcoreapp2.0", Version = "1.1.0" },
                     new NuSpecDependency() { Id = "NETStandard.Library", TargetFramework = "netcoreapp2.0", Version = "1.6.0" },
-                    new NuSpecDependency() { Id = "Splat", TargetFramework = "netcoreapp2.0", Version = SplatVersion },
                     new NuSpecDependency() { Id = "Serilog", TargetFramework = "netcoreapp2.0", Version = SerilogVersion },
+                    new NuSpecDependency() { Id = "Serilog.Sinks.Debug", TargetFramework = "netcoreapp2.0", Version = SerilogSinksDebugVersion },
+                    new NuSpecDependency() { Id = "Serilog.Sinks.Trace", TargetFramework = "netcoreapp2.0", Version = SerilogSinksTraceVersion },
                     new NuSpecDependency() { Id = "Sprache", TargetFramework = "netcoreapp2.0", Version = SpracheVersion },
                     new NuSpecDependency() { Id = "System.Reactive", TargetFramework = "netcoreapp2.0", Version = SystemReactiveVersion },
+                    new NuSpecDependency() { Id = "Avalonia.Remote.Protocol", TargetFramework = "netcoreapp2.0", Version = parameters.Version },
                 }
                 .Deps(new string[]{null, "netcoreapp2.0"},
                     "System.ValueTuple", "System.ComponentModel.TypeConverter", "System.ComponentModel.Primitives",
@@ -253,6 +268,7 @@ public class Packages
                 Files = coreLibrariesNuSpecContent
                     .Concat(win32CoreLibrariesNuSpecContent).Concat(net45RuntimePlatform)
                     .Concat(netcoreappCoreLibrariesNuSpecContent).Concat(netCoreRuntimePlatform)
+                    .Concat(toolsContent)
                     .ToList(),
                 BasePath = context.Directory("./"),
                 OutputDirectory = parameters.NugetRoot
@@ -291,6 +307,19 @@ public class Packages
                 BasePath = context.Directory("./src/Avalonia.ReactiveUI/bin/" + parameters.DirSuffix + "/netstandard2.0"),
                 OutputDirectory = parameters.NugetRoot
             },
+            ///////////////////////////////////////////////////////////////////////////////
+            // Avalonia.Remote.Protocol
+            ///////////////////////////////////////////////////////////////////////////////
+            new NuGetPackSettings()
+            {
+                Id = "Avalonia.Remote.Protocol",
+                Files = new []
+                {
+                    new NuSpecContent { Source = "Avalonia.Remote.Protocol.dll", Target = "lib/netstandard2.0" }
+                },
+                BasePath = context.Directory("./src/Avalonia.Remote.Protocol/bin/" + parameters.DirSuffix + "/netstandard2.0"),
+                OutputDirectory = parameters.NugetRoot
+            },
         };
 
         var nuspecNuGetSettingsMobile = new []

+ 21 - 41
readme.md

@@ -1,70 +1,50 @@
-# Avalonia
+<img src='https://avatars2.githubusercontent.com/u/14075148?s=200&v=4' width='100' />
 
+# Avalonia
 
 | Gitter Chat | Windows Build Status | Linux/Mac Build Status |
 |---|---|---|
 | [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/AvaloniaUI/Avalonia?utm_campaign=pr-badge&utm_content=badge&utm_medium=badge&utm_source=badge) | [![Build status](https://ci.appveyor.com/api/projects/status/hubk3k0w9idyibfg/branch/master?svg=true)](https://ci.appveyor.com/project/AvaloniaUI/Avalonia/branch/master) | [![Build Status](https://travis-ci.org/AvaloniaUI/Avalonia.svg?branch=master)](https://travis-ci.org/AvaloniaUI/Avalonia) |
 
-A multi-platform .NET UI framework. It can run on Windows, Linux, Mac OS X, iOS and Android.
-
-[![](docs/images/screen.png)](https://youtu.be/wHcB3sGLVYg)
+## About
 
-Desktop platforms:
+Avalonia is a WPF-inspired cross-platform XAML-based UI framework providing a flexible styling system and supporting a wide range of OSs: Windows (.NET Framework, .NET Core), Linux (GTK), MacOS, Android and iOS.
 
-<a href='https://www.youtube.com/watch?t=28&v=c_AB_XSILp0' target='_blank'>![](docs/images/avalonia-video.png)<a/>
+<b>Avalonia is now in alpha.</b> This means that framework is now at a stage where you can have a play and hopefully create simple applications. There's still a lot missing, and you *will* find bugs, and the API *will* change, but this represents the first time where we've made it somewhat easy to have a play and experiment with the framework.
 
-Mobile platforms:
+| Control catalog | Desktop platforms | Mobile platforms |
+|---|---|---|
+| <a href='https://youtu.be/wHcB3sGLVYg'><img width='300' src='http://avaloniaui.net/images/screen.png'></a> | <a href='https://www.youtube.com/watch?t=28&v=c_AB_XSILp0' target='_blank'><img width='300' src='http://avaloniaui.net/images/avalonia-video.png'></a> | <a href='https://www.youtube.com/watch?v=NJ9-hnmUbBM' target='_blank'><img width='300' src='https://i.ytimg.com/vi/NJ9-hnmUbBM/hqdefault.jpg'></a> |
 
-<a href='https://www.youtube.com/watch?v=NJ9-hnmUbBM' target='_blank'>![](https://i.ytimg.com/vi/NJ9-hnmUbBM/hqdefault.jpg)<a/>
+## Getting Started
 
-## NuGet
+Avalonia [Visual Studio Extension](https://marketplace.visualstudio.com/items?itemName=AvaloniaTeam.AvaloniaforVisualStudio) contains project and control templates that will help you get started. After installing it, open "New Project" dialog in Visual Studio, choose "Avalonia" in "Visual C#" section, select "Avalonia .NET Core Application" and press OK (<a href="http://avaloniaui.net/tutorial/images/add-dialogs.png">screenshot</a>). Now you can write code and markup that will work on multiple platforms!
 
-Avalonia is delivered as a NuGet package.
-You can find the packages here: ([stable(ish)](https://www.nuget.org/packages/Avalonia/), [nightly](https://github.com/AvaloniaUI/Avalonia/wiki/Using-nightly-build-feed))
+Avalonia is delivered via <b>NuGet</b> package manager. You can find the packages here: ([stable(ish)](https://www.nuget.org/packages/Avalonia/), [nightly](https://github.com/AvaloniaUI/Avalonia/wiki/Using-nightly-build-feed))
 
-You can install the package like this:
-`Install-Package Avalonia -Pre`
+Use these commands in Package Manager console to install Avalonia manually:
+```
+Install-Package Avalonia
+Install-Package Avalonia.Desktop
+```
 
 ## Bleeding Edge Builds
 
 Try out the latest build of Avalonia available for download here:
 https://ci.appveyor.com/project/AvaloniaUI/Avalonia/branch/master/artifacts
 
-Try out the ControlCatalog to give it a quick demo.
-
-## Background
-
-Avalonia is a multi-platform windowing toolkit - somewhat like WPF - that is intended to be multi-
-platform. It supports XAML, lookless controls and a flexible styling system, and runs on Windows
-using Direct2D and other operating systems using Skia and OS-specific windowing backend (GTK, Cocoa, etc).
-
-## Current Status
-
-Avalonia is now in alpha. What does "alpha" mean? Well, it means that it's now at a stage where you
-can have a play and hopefully create simple applications. There's now a [Visual
-Studio Extension](https://marketplace.visualstudio.com/items?itemName=AvaloniaTeam.AvaloniaforVisualStudio)
-containing project and item templates that will help you get started, and
-there's an initial complement of controls. There's still a lot missing, and you
-*will* find bugs, and the API *will* change, but this represents the first time
-where we've made it somewhat easy to have a play and experiment with the
-framework.
-
 ## Documentation
 
-As mentioned above, Avalonia is still in alpha and as such there's not much documentation yet. You can
-take a look at the [getting started page](docs/tutorial/gettingstarted.md) for an
-overview of how to get started but probably the best thing to do for now is to already know a little bit
-about WPF/Silverlight/UWP/XAML and ask questions in our [Gitter room](https://gitter.im/AvaloniaUI/Avalonia).
+As mentioned above, Avalonia is still in alpha and as such there's not much documentation yet. You can take a look at the [getting started page](http://avaloniaui.net/tutorial/gettingstarted) for an overview of how to get started but probably the best thing to do for now is to already know a little bit about WPF/Silverlight/UWP/XAML and ask questions in our [Gitter room](https://gitter.im/AvaloniaUI/Avalonia).
 
-There's also a high-level [architecture document](docs/spec/architecture.md) that is currently a little bit
-out of date, and I've also started writing blog posts on Avalonia at http://grokys.github.io/.
+There's also a high-level [architecture document](http://avaloniaui.net/spec/architecture) that is currently a little bit out of date, and I've also started writing blog posts on Avalonia at http://grokys.github.io/.
 
 Contributions are always welcome!
 
 ## Building and Using
 
-See the [build instructions here](docs/guidelines/build.md)
+See the [build instructions here](http://avaloniaui.net/guidelines/build).
 
-## Contributing ##
+## Contributing
 
-Please read the [contribution guidelines](docs/guidelines/contributing.md) before submitting a pull request.
+Please read the [contribution guidelines](http://avaloniaui.net/guidelines/contributing) before submitting a pull request.

+ 1 - 12
samples/BindingTest/App.xaml.cs

@@ -16,22 +16,11 @@ namespace BindingTest
 
         private static void Main()
         {
-            InitializeLogging();
-
             AppBuilder.Configure<App>()
                 .UsePlatformDetect()
                 .UseReactiveUI()
+                .LogToDebug()
                 .Start<MainWindow>();
         }
-
-        private static void InitializeLogging()
-        {
-#if DEBUG
-            SerilogLogger.Initialize(new LoggerConfiguration()
-                .MinimumLevel.Warning()
-                .WriteTo.Trace(outputTemplate: "{Area}: {Message}")
-                .CreateLogger());
-#endif
-        }
     }
 }

+ 0 - 2
samples/BindingTest/BindingTest.csproj

@@ -151,8 +151,6 @@
   </ItemGroup>
   <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
   <Import Project="..\..\build\Serilog.props" />
-  <Import Project="..\..\build\Serilog.Sinks.Trace.props" />
-  <Import Project="..\..\build\Splat.props" />
   <Import Project="..\..\build\Rx.props" />
   <Import Project="..\..\build\ReactiveUI.props" />
 </Project>

+ 0 - 1
samples/ControlCatalog.Desktop/ControlCatalog.Desktop.csproj

@@ -135,5 +135,4 @@
   <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
   <Import Project="..\..\build\Serilog.props" />
   <Import Project="..\..\build\SkiaSharp.props" />
-  <Import Project="..\..\build\Serilog.Sinks.Trace.props" />
 </Project>

+ 6 - 15
samples/ControlCatalog.Desktop/Program.cs

@@ -12,25 +12,16 @@ namespace ControlCatalog
     {
         static void Main(string[] args)
         {
-            InitializeLogging();
-
             // TODO: Make this work with GTK/Skia/Cairo depending on command-line args
             // again.
-            AppBuilder.Configure<App>()
-                .UsePlatformDetect()
-                .Start<MainWindow>();
+            BuildAvaloniaApp().Start<MainWindow>();
         }
 
-        // This will be made into a runtime configuration extension soon!
-        private static void InitializeLogging()
-        {
-#if DEBUG
-            SerilogLogger.Initialize(new LoggerConfiguration()
-                .MinimumLevel.Warning()
-                .WriteTo.Trace(outputTemplate: "{Area}: {Message}")
-                .CreateLogger());
-#endif
-        }
+        /// <summary>
+        /// This method is needed for IDE previewer infrastructure
+        /// </summary>
+        public static AppBuilder BuildAvaloniaApp()
+            => AppBuilder.Configure<App>().LogToDebug().UsePlatformDetect();
 
         private static void ConfigureAssetAssembly(AppBuilder builder)
         {

+ 6 - 11
samples/ControlCatalog.NetCore/Program.cs

@@ -28,19 +28,14 @@ namespace ControlCatalog.NetCore
                     System.Threading.ThreadPool.QueueUserWorkItem(_ => ConsoleSilencer());
                 });
             else
-                AppBuilder.Configure<App>()
-                    .CustomPlatformDetect()
-                    .UseReactiveUI()
-                    .Start<MainWindow>();
+                BuildAvaloniaApp().Start<MainWindow>();
         }
 
-        static AppBuilder CustomPlatformDetect(this AppBuilder builder)
-        {
-            //This is needed because we still aren't ready to have MonoMac backend as default one
-            if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
-                return builder.UseSkia().UseMonoMac();
-            return builder.UsePlatformDetect();
-        }
+        /// <summary>
+        /// This method is needed for IDE previewer infrastructure
+        /// </summary>
+        public static AppBuilder BuildAvaloniaApp()
+            => AppBuilder.Configure<App>().UsePlatformDetect().UseReactiveUI();
 
         static void ConsoleSilencer()
         {

+ 6 - 0
samples/ControlCatalog/ControlCatalog.csproj

@@ -44,6 +44,9 @@
     <EmbeddedResource Include="Pages\ButtonPage.xaml">
       <SubType>Designer</SubType>
     </EmbeddedResource>
+    <EmbeddedResource Include="Pages\CalendarPage.xaml">
+      <SubType>Designer</SubType>
+    </EmbeddedResource>
     <EmbeddedResource Include="Pages\CanvasPage.xaml">
       <SubType>Designer</SubType>
     </EmbeddedResource>
@@ -107,6 +110,9 @@
     <Compile Include="Pages\ButtonPage.xaml.cs">
       <DependentUpon>ButtonPage.xaml</DependentUpon>
     </Compile>
+    <Compile Include="Pages\CalendarPage.xaml.cs">
+      <DependentUpon>CalendarPage.xaml</DependentUpon>
+    </Compile>
     <Compile Include="Pages\CanvasPage.xaml.cs">
       <DependentUpon>CanvasPage.xaml</DependentUpon>
     </Compile>

+ 1 - 0
samples/ControlCatalog/MainView.xaml

@@ -7,6 +7,7 @@
     </TabControl.Transition>
     <TabItem Header="Border"><pages:BorderPage/></TabItem>
     <TabItem Header="Button"><pages:ButtonPage/></TabItem>
+    <TabItem Header="Calendar"><pages:CalendarPage/></TabItem>
     <TabItem Header="Canvas"><pages:CanvasPage/></TabItem>
     <TabItem Header="Carousel"><pages:CarouselPage/></TabItem>
     <TabItem Header="CheckBox"><pages:CheckBoxPage/></TabItem>

+ 47 - 0
samples/ControlCatalog/Pages/CalendarPage.xaml

@@ -0,0 +1,47 @@
+<UserControl xmlns="https://github.com/avaloniaui"
+             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
+  <StackPanel Orientation="Vertical" Gap="4">
+    <TextBlock Classes="h1">Calendar</TextBlock>
+    <TextBlock Classes="h2">A calendar control for selecting dates</TextBlock>
+        
+    <StackPanel Orientation="Horizontal"
+                Margin="0,16,0,0"
+                HorizontalAlignment="Center"
+                Gap="16">
+      <StackPanel Orientation="Vertical">
+        <TextBlock Text="SelectionMode: None"/>
+        <Calendar SelectionMode="None"
+                  Margin="0,0,0,8"/>
+
+        <TextBlock Text="SelectionMode: SingleDate"/>
+        <Calendar SelectionMode="SingleDate"
+                  Margin="0,0,0,8"/>
+
+        <TextBlock Text="Disabled"/>
+        <Calendar IsEnabled="False"
+                  SelectionMode="SingleDate"/>
+      </StackPanel>
+
+      <StackPanel Orientation="Vertical">
+        <TextBlock Text="SelectionMode: SingleRange"/>
+        <Calendar SelectionMode="SingleRange"
+                  Margin="0,0,0,8"/>
+
+        <TextBlock Text="SelectionMode: MultipleRange"/>
+        <Calendar SelectionMode="MultipleRange"/>
+      </StackPanel>
+
+      <StackPanel Orientation="Vertical">
+        <TextBlock Text="DisplayDates"/>
+        <Calendar Name="DisplayDatesCalendar"
+                  SelectionMode="SingleDate"
+                  Margin="0,0,0,8"/>
+
+        <TextBlock Text="BlackoutDates"/>
+        <Calendar Name="BlackoutDatesCalendar"
+                  SelectionMode="SingleDate" />
+      </StackPanel>
+      
+    </StackPanel> 
+  </StackPanel>
+</UserControl>

+ 28 - 0
samples/ControlCatalog/Pages/CalendarPage.xaml.cs

@@ -0,0 +1,28 @@
+using Avalonia.Controls;
+using Avalonia.Markup.Xaml;
+using System;
+
+namespace ControlCatalog.Pages
+{
+    public class CalendarPage : UserControl
+    {
+        public CalendarPage()
+        {
+            this.InitializeComponent();
+
+            var today = DateTime.Today; 
+            var cal1 = this.FindControl<Calendar>("DisplayDatesCalendar");
+            cal1.DisplayDateStart = today.AddDays(-25);
+            cal1.DisplayDateEnd = today.AddDays(25);
+
+            var cal2 = this.FindControl<Calendar>("BlackoutDatesCalendar");
+            cal2.BlackoutDates.AddDatesInPast();
+            cal2.BlackoutDates.Add(new CalendarDateRange(today.AddDays(6)));
+        }
+
+        private void InitializeComponent()
+        {
+            AvaloniaXamlLoader.Load(this);
+        }
+    }
+}

+ 9 - 1
samples/ControlCatalog/Pages/CarouselPage.xaml

@@ -9,7 +9,7 @@
       </Button>
       <Carousel Name="carousel">
         <Carousel.Transition>
-          <PageSlide Duration="0.25"/>
+          <PageSlide Duration="0.25" Orientation="Vertical" />
         </Carousel.Transition>
         <Image Source="resm:ControlCatalog.Assets.delicate-arch-896885_640.jpg"/>
         <Image Source="resm:ControlCatalog.Assets.hirsch-899118_640.jpg"/>
@@ -28,6 +28,14 @@
         <DropDownItem>Crossfade</DropDownItem>
       </DropDown>
     </StackPanel>
+
+    <StackPanel Orientation="Horizontal" Gap="4">
+      <TextBlock VerticalAlignment="Center">Orientation</TextBlock>
+      <DropDown Name="orientation" SelectedIndex="1" VerticalAlignment="Center">
+        <DropDownItem>Horizontal</DropDownItem>
+        <DropDownItem>Vertical</DropDownItem>
+      </DropDown>
+    </StackPanel>
     
   </StackPanel>
 </UserControl>

+ 4 - 1
samples/ControlCatalog/Pages/CarouselPage.xaml.cs

@@ -11,6 +11,7 @@ namespace ControlCatalog.Pages
         private Button _left;
         private Button _right;
         private DropDown _transition;
+        private DropDown _orientation;
 
         public CarouselPage()
         {
@@ -18,6 +19,7 @@ namespace ControlCatalog.Pages
             _left.Click += (s, e) => _carousel.Previous();
             _right.Click += (s, e) => _carousel.Next();
             _transition.SelectionChanged += TransitionChanged;
+            _orientation.SelectionChanged += TransitionChanged;
         }
 
         private void InitializeComponent()
@@ -27,6 +29,7 @@ namespace ControlCatalog.Pages
             _left = this.FindControl<Button>("left");
             _right = this.FindControl<Button>("right");
             _transition = this.FindControl<DropDown>("transition");
+            _orientation = this.FindControl<DropDown>("orientation");
         }
 
         private void TransitionChanged(object sender, SelectionChangedEventArgs e)
@@ -37,7 +40,7 @@ namespace ControlCatalog.Pages
                     _carousel.Transition = null;
                     break;
                 case 1:
-                    _carousel.Transition = new PageSlide(TimeSpan.FromSeconds(0.25));
+                    _carousel.Transition = new PageSlide(TimeSpan.FromSeconds(0.25), _orientation.SelectedIndex == 0 ? PageSlide.SlideAxis.Horizontal : PageSlide.SlideAxis.Vertical);
                     break;
                 case 2:
                     _carousel.Transition = new CrossFade(TimeSpan.FromSeconds(0.25));

+ 19 - 6
samples/ControlCatalog/Pages/CheckBoxPage.xaml

@@ -1,15 +1,28 @@
-<UserControl xmlns="https://github.com/avaloniaui">
+<UserControl xmlns="https://github.com/avaloniaui"
+             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
   <StackPanel Orientation="Vertical" Gap="4">
     <TextBlock Classes="h1">CheckBox</TextBlock>
     <TextBlock Classes="h2">A check box control</TextBlock>
 
-    <StackPanel Orientation="Vertical"
+    <StackPanel Orientation="Horizontal"
                 Margin="0,16,0,0"
                 HorizontalAlignment="Center"
                 Gap="16">
-      <CheckBox>Unchecked</CheckBox>
-      <CheckBox IsChecked="True">Checked</CheckBox>
-      <CheckBox IsChecked="True" IsEnabled="False">Disabled</CheckBox>
-    </StackPanel>    
+      <StackPanel Orientation="Vertical"
+                  Gap="16">
+        <CheckBox>Unchecked</CheckBox>
+        <CheckBox IsChecked="True">Checked</CheckBox>
+        <CheckBox IsChecked="{x:Null}">Indeterminate</CheckBox>
+        <CheckBox IsChecked="True" IsEnabled="False">Disabled</CheckBox>
+      </StackPanel>
+      <StackPanel Orientation="Vertical"
+                  HorizontalAlignment="Center"
+                  Gap="16">
+        <CheckBox IsChecked="False" IsThreeState="True">Three State: Unchecked</CheckBox>
+        <CheckBox IsChecked="True" IsThreeState="True">Three State: Checked</CheckBox>
+        <CheckBox IsChecked="{x:Null}" IsThreeState="True">Three State: Indeterminate</CheckBox>
+        <CheckBox IsChecked="{x:Null}" IsThreeState="True" IsEnabled="False">Three State: Disabled</CheckBox>
+      </StackPanel>
+    </StackPanel>
   </StackPanel>
 </UserControl>

+ 1 - 1
samples/ControlCatalog/Pages/DialogsPage.xaml.cs

@@ -36,7 +36,7 @@ namespace ControlCatalog.Pages
             };
         }
 
-        Window GetWindow() => this.FindControl<CheckBox>("IsModal").IsChecked ? (Window)this.VisualRoot : null;
+        Window GetWindow() => this.FindControl<CheckBox>("IsModal").IsChecked.Value ? (Window)this.VisualRoot : null;
 
         private void InitializeComponent()
         {

+ 18 - 6
samples/ControlCatalog/Pages/RadioButtonPage.xaml

@@ -1,15 +1,27 @@
-<UserControl xmlns="https://github.com/avaloniaui">
+<UserControl xmlns="https://github.com/avaloniaui"
+             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
   <StackPanel Orientation="Vertical" Gap="4">
     <TextBlock Classes="h1">RadioButton</TextBlock>
     <TextBlock Classes="h2">Allows the selection of a single option of many</TextBlock>
 
-    <StackPanel Orientation="Vertical"
+    <StackPanel Orientation="Horizontal"
                 Margin="0,16,0,0"
                 HorizontalAlignment="Center"
                 Gap="16">
-      <RadioButton IsChecked="True">Option 1</RadioButton>
-      <RadioButton>Option 2</RadioButton>
-      <RadioButton IsEnabled="False">Disabled</RadioButton>
-    </StackPanel>    
+      <StackPanel Orientation="Vertical"
+                  Gap="16">
+        <RadioButton IsChecked="True">Option 1</RadioButton>
+        <RadioButton>Option 2</RadioButton>
+        <RadioButton IsChecked="{x:Null}">Option 3</RadioButton>
+        <RadioButton IsEnabled="False">Disabled</RadioButton>
+      </StackPanel>
+      <StackPanel Orientation="Vertical"
+                  Gap="16">
+        <RadioButton IsChecked="True" IsThreeState="True">Three States: Option 1</RadioButton>
+        <RadioButton IsChecked="False" IsThreeState="True">Three States: Option 2</RadioButton>
+        <RadioButton IsChecked="{x:Null}" IsThreeState="True">Three States: Option 3</RadioButton>
+        <RadioButton IsChecked="{x:Null}" IsThreeState="True" IsEnabled="False">Disabled</RadioButton>
+      </StackPanel>
+    </StackPanel>
   </StackPanel>
 </UserControl>

+ 3 - 3
samples/ControlCatalog/SideBar.xaml

@@ -1,11 +1,11 @@
-<Styles xmlns="https://github.com/avaloniaui">
+<Styles xmlns="https://github.com/avaloniaui" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
   <Style Selector="TabControl.sidebar">
     <Setter Property="Template">
       <ControlTemplate>
         <DockPanel>
           <ScrollViewer MinWidth="190" Background="{DynamicResource ThemeAccentBrush}" DockPanel.Dock="Left">
             <TabStrip Name="PART_TabStrip"
-                      MemberSelector="{Static TabControl.HeaderSelector}"
+                      MemberSelector="{x:Static TabControl.HeaderSelector}"
                       Items="{TemplateBinding Items}"
                       SelectedIndex="{TemplateBinding Path=SelectedIndex, Mode=TwoWay}">
               <TabStrip.ItemsPanel>
@@ -17,7 +17,7 @@
           </ScrollViewer>
           <Carousel Name="PART_Content"
                     Margin="8 0 0 0"
-                    MemberSelector="{Static TabControl.ContentSelector}"
+                    MemberSelector="{x:Static TabControl.ContentSelector}"
                     Items="{TemplateBinding Items}"
                     SelectedIndex="{TemplateBinding Path=SelectedIndex}"
                     Transition="{TemplateBinding Transition}"

+ 6 - 0
samples/Previewer/App.xaml

@@ -0,0 +1,6 @@
+<Application xmlns="https://github.com/avaloniaui">
+    <Application.Styles>
+        <StyleInclude Source="resm:Avalonia.Themes.Default.DefaultTheme.xaml?assembly=Avalonia.Themes.Default"/>
+        <StyleInclude Source="resm:Avalonia.Themes.Default.Accents.BaseLight.xaml?assembly=Avalonia.Themes.Default"/>
+    </Application.Styles>
+</Application>

+ 14 - 0
samples/Previewer/App.xaml.cs

@@ -0,0 +1,14 @@
+using Avalonia;
+using Avalonia.Markup.Xaml;
+
+namespace Previewer
+{
+    public class App : Application
+    {
+        public override void Initialize()
+        {
+            AvaloniaXamlLoader.Load(this);
+        }
+    }
+
+}

+ 19 - 0
samples/Previewer/Center.cs

@@ -0,0 +1,19 @@
+using Avalonia;
+using Avalonia.Controls;
+
+namespace Previewer
+{
+    public class Center : Decorator
+    {
+        protected override Size ArrangeOverride(Size finalSize)
+        {
+            if (Child != null)
+            {
+                var desired = Child.DesiredSize;
+                Child.Arrange(new Rect((finalSize.Width - desired.Width) / 2, (finalSize.Height - desired.Height) / 2,
+                    desired.Width, desired.Height));
+            }
+            return finalSize;
+        }
+    }
+}

+ 12 - 0
samples/Previewer/MainWindow.xaml

@@ -0,0 +1,12 @@
+<Window xmlns="https://github.com/avaloniaui" Width="600" Height="500"
+        Title="Previewer">
+    <Grid RowDefinitions="0.5*,200">
+        <ScrollViewer Name="Remote"/>
+        
+        <ScrollViewer Name="ErrorsContainer" Background="#ffe0e0">
+            <TextBlock Name="Errors"/>
+        </ScrollViewer>
+        <TextBox Grid.Row="1" AcceptsReturn="True" Name="Xaml"/>
+    </Grid>
+    
+</Window>

+ 86 - 0
samples/Previewer/MainWindow.xaml.cs

@@ -0,0 +1,86 @@
+using System;
+using System.Net;
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Controls.Remote;
+using Avalonia.Markup.Xaml;
+using Avalonia.Remote.Protocol;
+using Avalonia.Remote.Protocol.Designer;
+using Avalonia.Remote.Protocol.Viewport;
+using Avalonia.Threading;
+
+namespace Previewer
+{
+    public class MainWindow : Window
+    {
+        private const string InitialXaml = @"<Window xmlns=""https://github.com/avaloniaui"" Width=""600"" Height=""500"">
+        <TextBlock>Hello world!</TextBlock>
+    
+        </Window>";
+        private IAvaloniaRemoteTransportConnection _connection;
+        private Control _errorsContainer;
+        private TextBlock _errors;
+        private RemoteWidget _remote;
+
+
+        public MainWindow()
+        {
+            this.InitializeComponent();
+            var tb = this.FindControl<TextBox>("Xaml");
+            tb.Text = InitialXaml;
+            var scroll = this.FindControl<ScrollViewer>("Remote");
+            var rem = new Center();
+            scroll.Content = rem;
+            _errorsContainer = this.FindControl<Control>("ErrorsContainer");
+            _errors = this.FindControl<TextBlock>("Errors");
+            tb.GetObservable(TextBox.TextProperty).Subscribe(text => _connection?.Send(new UpdateXamlMessage
+            {
+                Xaml = text
+            }));
+            new BsonTcpTransport().Listen(IPAddress.Loopback, 25000, t =>
+            {
+                Dispatcher.UIThread.InvokeAsync(() =>
+                {
+                    if (_connection != null)
+                    {
+                        _connection.Dispose();
+                        _connection.OnMessage -= OnMessage;
+                    }
+                    _connection = t;
+                    rem.Child = _remote = new RemoteWidget(t);
+                    t.Send(new UpdateXamlMessage
+                    {
+                        Xaml = tb.Text
+                    });
+                    
+                    t.OnMessage += OnMessage;
+                });
+            });
+            Title = "Listening on 127.0.0.1:25000";
+        }
+
+        private void OnMessage(IAvaloniaRemoteTransportConnection transport, object obj)
+        {
+            Dispatcher.UIThread.InvokeAsync(() =>
+            {
+                if (transport != _connection)
+                    return;
+                if (obj is UpdateXamlResultMessage result)
+                {
+                    _errorsContainer.IsVisible = result.Error != null;
+                    _errors.Text = result.Error ?? "";
+                }
+                if (obj is RequestViewportResizeMessage resize)
+                {
+                    _remote.Width = Math.Min(4096, Math.Max(resize.Width, 1));
+                    _remote.Height = Math.Min(4096, Math.Max(resize.Height, 1));
+                }
+            });
+        }
+
+        private void InitializeComponent()
+        {
+            AvaloniaXamlLoader.Load(this);
+        }
+    }
+}

+ 27 - 0
samples/Previewer/Previewer.csproj

@@ -0,0 +1,27 @@
+<Project Sdk="Microsoft.NET.Sdk">
+  <PropertyGroup>
+    <OutputType>Exe</OutputType>
+    <TargetFramework>netcoreapp2.0</TargetFramework>
+  </PropertyGroup>
+  <ItemGroup>
+    <Compile Update="**\*.xaml.cs">
+      <DependentUpon>%(Filename)</DependentUpon>
+    </Compile>
+    <EmbeddedResource Include="**\*.xaml" />
+    <ProjectReference Include="..\..\src\Avalonia.DotNetCoreRuntime\Avalonia.DotNetCoreRuntime.csproj" />
+    <ProjectReference Include="..\..\src\Markup\Avalonia.Markup.Xaml\Avalonia.Markup.Xaml.csproj" />
+    <ProjectReference Include="..\..\src\Markup\Avalonia.Markup\Avalonia.Markup.csproj" />
+    <ProjectReference Include="..\..\src\Avalonia.Animation\Avalonia.Animation.csproj" />
+    <ProjectReference Include="..\..\src\Avalonia.Base\Avalonia.Base.csproj" />
+    <ProjectReference Include="..\..\src\Avalonia.Controls\Avalonia.Controls.csproj" />
+    <ProjectReference Include="..\..\src\Avalonia.Diagnostics\Avalonia.Diagnostics.csproj" />
+    <ProjectReference Include="..\..\src\Avalonia.HtmlRenderer\Avalonia.HtmlRenderer.csproj" />
+    <ProjectReference Include="..\..\src\Avalonia.Input\Avalonia.Input.csproj" />
+    <ProjectReference Include="..\..\src\Avalonia.Interactivity\Avalonia.Interactivity.csproj" />
+    <ProjectReference Include="..\..\src\Avalonia.Layout\Avalonia.Layout.csproj" />
+    <ProjectReference Include="..\..\src\Avalonia.ReactiveUI\Avalonia.ReactiveUI.csproj" />
+    <ProjectReference Include="..\..\src\Avalonia.Visuals\Avalonia.Visuals.csproj" />
+    <ProjectReference Include="..\..\src\Avalonia.Styling\Avalonia.Styling.csproj" />
+    <ProjectReference Include="..\..\src\Avalonia.Themes.Default\Avalonia.Themes.Default.csproj" />
+  </ItemGroup>
+</Project>

+ 13 - 0
samples/Previewer/Program.cs

@@ -0,0 +1,13 @@
+using System;
+using Avalonia;
+
+namespace Previewer
+{
+    class Program
+    {
+        static void Main(string[] args)
+        {
+            AppBuilder.Configure<App>().UsePlatformDetect().Start<MainWindow>();
+        }
+    }
+}

+ 53 - 0
samples/RemoteTest/Program.cs

@@ -0,0 +1,53 @@
+using System;
+using System.Net;
+using System.Net.Sockets;
+using System.Threading;
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Controls.Remote;
+using Avalonia.Remote.Protocol;
+using Avalonia.Threading;
+using ControlCatalog;
+
+namespace RemoteTest
+{
+    class Program
+    {
+        static void Main(string[] args)
+        {
+            AppBuilder.Configure<App>().UsePlatformDetect().SetupWithoutStarting();
+
+            var l = new TcpListener(IPAddress.Loopback, 0);
+            l.Start();
+            var port = ((IPEndPoint) l.LocalEndpoint).Port;
+            l.Stop();
+            
+            var transport = new BsonTcpTransport();
+            transport.Listen(IPAddress.Loopback, port, sc =>
+            {
+                Dispatcher.UIThread.InvokeAsync(() =>
+                {
+                    new RemoteServer(sc).Content = new MainView();
+                });
+            });
+
+            var cts = new CancellationTokenSource();
+            transport.Connect(IPAddress.Loopback, port).ContinueWith(t =>
+            {
+                Dispatcher.UIThread.InvokeAsync(() =>
+                {
+                    var window = new Window()
+                    {
+                        Content = new RemoteWidget(t.Result)
+                    };
+                    window.Closed += delegate { cts.Cancel(); };
+                    window.Show();
+                });
+            });
+            Dispatcher.UIThread.MainLoop(cts.Token);
+
+
+
+        }
+    }
+}

+ 25 - 0
samples/RemoteTest/RemoteTest.csproj

@@ -0,0 +1,25 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+  <PropertyGroup>
+    <OutputType>Exe</OutputType>
+    <TargetFramework>netcoreapp2.0</TargetFramework>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <ProjectReference Include="..\..\src\Avalonia.Animation\Avalonia.Animation.csproj" />
+    <ProjectReference Include="..\..\src\Avalonia.Base\Avalonia.Base.csproj" />
+    <ProjectReference Include="..\..\src\Avalonia.Controls\Avalonia.Controls.csproj" />
+    <ProjectReference Include="..\..\src\Avalonia.DesignerSupport\Avalonia.DesignerSupport.csproj" />
+    <ProjectReference Include="..\..\src\Avalonia.DotNetCoreRuntime\Avalonia.DotNetCoreRuntime.csproj" />
+    <ProjectReference Include="..\..\src\Avalonia.Input\Avalonia.Input.csproj" />
+    <ProjectReference Include="..\..\src\Avalonia.Interactivity\Avalonia.Interactivity.csproj" />
+    <ProjectReference Include="..\..\src\Avalonia.Layout\Avalonia.Layout.csproj" />
+    <ProjectReference Include="..\..\src\Avalonia.Styling\Avalonia.Styling.csproj" />
+    <ProjectReference Include="..\..\src\Avalonia.Themes.Default\Avalonia.Themes.Default.csproj" />
+    <ProjectReference Include="..\..\src\Avalonia.Visuals\Avalonia.Visuals.csproj" />
+    <ProjectReference Include="..\..\src\Markup\Avalonia.Markup.Xaml\Avalonia.Markup.Xaml.csproj" />
+    <ProjectReference Include="..\..\src\Markup\Avalonia.Markup\Avalonia.Markup.csproj" />
+    <ProjectReference Include="..\ControlCatalog\ControlCatalog.csproj" />
+  </ItemGroup>
+
+</Project>

+ 1 - 13
samples/RenderTest/Program.cs

@@ -12,25 +12,13 @@ namespace RenderTest
     {
         static void Main(string[] args)
         {
-            InitializeLogging();
-
             // TODO: Make this work with GTK/Skia/Cairo depending on command-line args
             // again.
             AppBuilder.Configure<App>()
                 .UsePlatformDetect()
                 .UseReactiveUI()
+                .LogToDebug()
                 .Start<MainWindow>();
         }
-
-        // This will be made into a runtime configuration extension soon!
-        private static void InitializeLogging()
-        {
-#if DEBUG
-            SerilogLogger.Initialize(new LoggerConfiguration()
-                .MinimumLevel.Warning()
-                .WriteTo.Trace(outputTemplate: "{Area}: {Message}")
-                .CreateLogger());
-#endif
-        }
     }
 }

+ 0 - 2
samples/RenderTest/RenderTest.csproj

@@ -180,8 +180,6 @@
   </ItemGroup>
   <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
   <Import Project="..\..\build\Serilog.props" />
-  <Import Project="..\..\build\Serilog.Sinks.Trace.props" />
-  <Import Project="..\..\build\Splat.props" />
   <Import Project="..\..\build\Rx.props" />
   <Import Project="..\..\build\ReactiveUI.props" />
 </Project>

+ 3 - 3
samples/RenderTest/SideBar.xaml

@@ -1,11 +1,11 @@
-<Styles xmlns="https://github.com/avaloniaui">
+<Styles xmlns="https://github.com/avaloniaui" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
   <Style Selector="TabControl.sidebar">
     <Setter Property="Template">
       <ControlTemplate>
         <DockPanel>
           <ScrollViewer MinWidth="190" Background="{DynamicResource ThemeAccentBrush}" DockPanel.Dock="Left">
             <TabStrip Name="PART_TabStrip"
-                      MemberSelector="{Static TabControl.HeaderSelector}"
+                      MemberSelector="{x:Static TabControl.HeaderSelector}"
                       Items="{TemplateBinding Items}"
                       SelectedIndex="{TemplateBinding Path=SelectedIndex, Mode=TwoWay}">
               <TabStrip.ItemsPanel>
@@ -17,7 +17,7 @@
           </ScrollViewer>
           <Carousel Name="PART_Content"
                     Margin="8 0 0 0"
-                    MemberSelector="{Static TabControl.ContentSelector}"
+                    MemberSelector="{x:Static TabControl.ContentSelector}"
                     Items="{TemplateBinding Items}"
                     SelectedIndex="{TemplateBinding Path=SelectedIndex}"
                     Transition="{TemplateBinding Transition}"

+ 1 - 12
samples/VirtualizationTest/Program.cs

@@ -13,22 +13,11 @@ namespace VirtualizationTest
     {
         static void Main(string[] args)
         {
-            InitializeLogging();
-
             AppBuilder.Configure<App>()
                .UsePlatformDetect()
                .UseReactiveUI()
+               .LogToDebug()
                .Start<MainWindow>();
         }
-
-        private static void InitializeLogging()
-        {
-#if DEBUG
-            SerilogLogger.Initialize(new LoggerConfiguration()
-                .MinimumLevel.Warning()
-                .WriteTo.Trace(outputTemplate: "{Area}: {Message}")
-                .CreateLogger());
-#endif
-        }
     }
 }

+ 1 - 1
samples/VirtualizationTest/ViewModels/MainWindowViewModel.cs

@@ -17,7 +17,7 @@ namespace VirtualizationTest.ViewModels
         private int _newItemIndex;
         private IReactiveList<ItemViewModel> _items;
         private string _prefix = "Item";
-        private Orientation _orientation;
+        private Orientation _orientation = Orientation.Vertical;
         private ItemVirtualizationMode _virtualizationMode = ItemVirtualizationMode.Simple;
 
         public MainWindowViewModel()

+ 0 - 2
samples/VirtualizationTest/VirtualizationTest.csproj

@@ -147,8 +147,6 @@
   </ItemGroup>
   <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
   <Import Project="..\..\build\Serilog.props" />
-  <Import Project="..\..\build\Serilog.Sinks.Trace.props" />
-  <Import Project="..\..\build\Splat.props" />
   <Import Project="..\..\build\Rx.props" />
   <Import Project="..\..\build\ReactiveUI.props" />
 </Project>

+ 0 - 2
samples/interop/Direct3DInteropSample/Direct3DInteropSample.csproj

@@ -28,7 +28,5 @@
         <ProjectReference Include="..\..\..\src\Windows\Avalonia.Win32\Avalonia.Win32.csproj" />
     </ItemGroup>
     <Import Project="..\..\..\build\Serilog.props" />
-    <Import Project="..\..\..\build\Serilog.Sinks.Trace.props" />
-    <Import Project="..\..\..\build\Splat.props" />
     <Import Project="..\..\..\build\Rx.props" />
 </Project>

+ 3 - 2
samples/interop/Direct3DInteropSample/MainWindow.cs

@@ -253,8 +253,9 @@ namespace Direct3DInteropSample
 
             public IDrawingContextImpl CreateDrawingContext(IVisualBrushRenderer visualBrushRenderer)
             {
-                return new DrawingContextImpl(visualBrushRenderer, _window._d2dRenderTarget,
-                    AvaloniaLocator.Current.GetService<SharpDX.DirectWrite.Factory>());
+                return new DrawingContextImpl(visualBrushRenderer, null, _window._d2dRenderTarget,
+                    AvaloniaLocator.Current.GetService<SharpDX.DirectWrite.Factory>(),
+                    AvaloniaLocator.Current.GetService<ImagingFactory>());
             }
         }
 

+ 4 - 5
scripts/ReplaceNugetCache.ps1

@@ -1,5 +1,4 @@
-copy ..\samples\ControlCatalog.NetCore\bin\Debug\netcoreapp1.1\Avalonia**.dll ~\.nuget\packages\avalonia\$args\lib\netcoreapp1.0\
-copy ..\samples\ControlCatalog.NetCore.\bin\Debug\netcoreapp1.1\Avalonia**.dll ~\.nuget\packages\avalonia\$args\lib\netstandard1.3\
-copy ..\samples\ControlCatalog.NetCore.\bin\Debug\netcoreapp1.1\Avalonia**.dll ~\.nuget\packages\avalonia.gtk3\$args\lib\netstandard1.3\
-copy ..\samples\ControlCatalog.NetCore.\bin\Debug\netcoreapp1.1\Avalonia**.dll ~\.nuget\packages\avalonia.skia.desktop\$args\lib\netstandard1.3\
-copy ..\samples\ControlCatalog.NetCore.\bin\Debug\netcoreapp1.1\Avalonia**.dll ~\.nuget\packages\avalonia.win32\$args\lib\netstandard1.3\
+copy ..\samples\ControlCatalog.NetCore\bin\Debug\netcoreapp2.0\Avalonia**.dll ~\.nuget\packages\avalonia\$args\lib\netcoreapp2.0\
+copy ..\samples\ControlCatalog.NetCore.\bin\Debug\netcoreapp2.0\Avalonia**.dll ~\.nuget\packages\avalonia\$args\lib\netstandard2.0\
+copy ..\samples\ControlCatalog.NetCore.\bin\Debug\netcoreapp2.0\Avalonia**.dll ~\.nuget\packages\avalonia.gtk3\$args\lib\netstandard2.0\
+copy ..\samples\ControlCatalog.NetCore.\bin\Debug\netcoreapp2.0\Avalonia**.dll ~\.nuget\packages\avalonia.win32\$args\lib\netstandard2.0\

+ 3 - 4
scripts/ReplaceNugetCache.sh

@@ -1,7 +1,6 @@
  #!/usr/bin/env bash
  
- cp ../samples/ControlCatalog.NetCore/bin/Debug/netcoreapp1.1/Avalonia**.dll ~/.nuget/packages/avalonia/$1/lib/netcoreapp1.0/
- cp ../samples/ControlCatalog.NetCore/bin/Debug/netcoreapp1.1/Avalonia**.dll ~/.nuget/packages/avalonia/$1/lib/netstandard1.1/
- cp ../samples/ControlCatalog.NetCore/bin/Debug/netcoreapp1.1/Avalonia**.dll ~/.nuget/packages/avalonia.gtk3/$1/lib/netstandard1.1/
- cp ../samples/ControlCatalog.NetCore/bin/Debug/netcoreapp1.1/Avalonia**.dll ~/.nuget/packages/avalonia.skia.desktop/$1/lib/netstandard1.3/
+ cp ../samples/ControlCatalog.NetCore/bin/Debug/netcoreapp2.0/Avalonia**.dll ~/.nuget/packages/avalonia/$1/lib/netcoreapp2.0/
+ cp ../samples/ControlCatalog.NetCore/bin/Debug/netcoreapp2.0/Avalonia**.dll ~/.nuget/packages/avalonia/$1/lib/netstandard2.0/
+ cp ../samples/ControlCatalog.NetCore/bin/Debug/netcoreapp2.0/Avalonia**.dll ~/.nuget/packages/avalonia.gtk3/$1/lib/netstandard2.0/
  

+ 4 - 5
scripts/ReplaceNugetCacheRelease.ps1

@@ -1,5 +1,4 @@
-copy ..\samples\ControlCatalog.NetCore\bin\Release\netcoreapp1.1\Avalonia**.dll ~\.nuget\packages\avalonia\$args\lib\netcoreapp1.0\
-copy ..\samples\ControlCatalog.NetCore.\bin\Release\netcoreapp1.1\Avalonia**.dll ~\.nuget\packages\avalonia\$args\lib\netstandard1.3\
-copy ..\samples\ControlCatalog.NetCore.\bin\Release\netcoreapp1.1\Avalonia**.dll ~\.nuget\packages\avalonia.gtk3\$args\lib\netstandard1.3\
-copy ..\samples\ControlCatalog.NetCore.\bin\Release\netcoreapp1.1\Avalonia**.dll ~\.nuget\packages\avalonia.skia.desktop\$args\lib\netstandard1.3\
-copy ..\samples\ControlCatalog.NetCore.\bin\Release\netcoreapp1.1\Avalonia**.dll ~\.nuget\packages\avalonia.win32\$args\lib\netstandard1.3\
+copy ..\samples\ControlCatalog.NetCore\bin\Release\netcoreapp2.0\Avalonia**.dll ~\.nuget\packages\avalonia\$args\lib\netcoreapp2.0\
+copy ..\samples\ControlCatalog.NetCore.\bin\Release\netcoreapp2.0\Avalonia**.dll ~\.nuget\packages\avalonia\$args\lib\netstandard2.0\
+copy ..\samples\ControlCatalog.NetCore.\bin\Release\netcoreapp2.0\Avalonia**.dll ~\.nuget\packages\avalonia.gtk3\$args\lib\netstandard2.0\
+copy ..\samples\ControlCatalog.NetCore.\bin\Release\netcoreapp2.0\Avalonia**.dll ~\.nuget\packages\avalonia.win32\$args\lib\netstandard2.0\

+ 1 - 1
src/Android/Avalonia.Android/AndroidThreadingInterface.cs

@@ -30,7 +30,7 @@ namespace Avalonia.Android
             return;
         }
 
-        public IDisposable StartTimer(TimeSpan interval, Action tick)
+        public IDisposable StartTimer(DispatcherPriority priority, TimeSpan interval, Action tick)
         {
             if (interval.TotalMilliseconds < 10)
                 interval = TimeSpan.FromMilliseconds(10);

+ 0 - 1
src/Android/Avalonia.AndroidTestApplication/Avalonia.AndroidTestApplication.csproj

@@ -154,7 +154,6 @@
   </ItemGroup>
   <Import Project="$(MSBuildExtensionsPath)\Xamarin\Android\Xamarin.Android.CSharp.targets" />
   <Import Project="..\..\..\build\Serilog.props" />
-  <Import Project="..\..\..\build\Splat.props" />
   <Import Project="..\..\..\build\Sprache.props" />
   <Import Project="..\..\..\build\Rx.props" />
 </Project>

+ 2 - 1
src/Avalonia.Base/AvaloniaObject.cs

@@ -56,6 +56,7 @@ namespace Avalonia
         /// </summary>
         public AvaloniaObject()
         {
+            VerifyAccess();
             foreach (var property in AvaloniaPropertyRegistry.Instance.GetRegistered(this))
             {
                 object value = property.IsDirect ?
@@ -817,4 +818,4 @@ namespace Avalonia
             throw new ArgumentException($"Property '{p.Name} not registered on '{this.GetType()}");
         }
     }
-}
+}

+ 1 - 1
src/Avalonia.Base/AvaloniaObjectExtensions.cs

@@ -326,7 +326,7 @@ namespace Avalonia
                 object anchor = null,
                 bool enableDataValidation = false)
             {
-                return new InstancedBinding(_source);
+                return InstancedBinding.OneWay(_source);
             }
         }
     }

+ 11 - 6
src/Avalonia.Base/Data/IndexerBinding.cs

@@ -31,13 +31,18 @@ namespace Avalonia.Data
                 targetProperty.GetMetadata(target.GetType()).DefaultBindingMode :
                 Mode;
 
-            if (mode == BindingMode.TwoWay)
+            switch (mode)
             {
-                return new InstancedBinding(Source.GetSubject(Property), mode);
-            }
-            else
-            {
-                return new InstancedBinding(Source.GetObservable(Property), mode);
+                case BindingMode.OneTime:
+                    return InstancedBinding.OneTime(Source.GetObservable(Property));
+                case BindingMode.OneWay:
+                    return InstancedBinding.OneWay(Source.GetObservable(Property));
+                case BindingMode.OneWayToSource:
+                    return InstancedBinding.OneWayToSource(Source.GetSubject(Property));
+                case BindingMode.TwoWay:
+                    return InstancedBinding.TwoWay(Source.GetSubject(Property));
+                default:
+                    throw new NotSupportedException("Unsupported BindingMode.");
             }
         }
     }

+ 101 - 54
src/Avalonia.Base/Data/InstancedBinding.cs

@@ -14,71 +14,35 @@ namespace Avalonia.Data
     /// property on a control's DataContext"; this class represents a binding that has been 
     /// *instanced* by calling <see cref="IBinding.Initiate(IAvaloniaObject, AvaloniaProperty, object, bool)"/>
     /// on a target object.
-    /// 
-    /// When a binding is initiated, it can return one of 3 possible sources for the binding:
-    /// - An <see cref="ISubject{Object}"/> which can be used for any type of binding.
-    /// - An <see cref="IObservable{Object}"/> which can be used for all types of bindings except
-    ///  <see cref="BindingMode.OneWayToSource"/> and <see cref="BindingMode.TwoWay"/>.
-    /// - A plain object, which can only represent a <see cref="BindingMode.OneTime"/> binding.
     /// </remarks>
     public class InstancedBinding
     {
         /// <summary>
         /// Initializes a new instance of the <see cref="InstancedBinding"/> class.
         /// </summary>
-        /// <param name="value">
-        /// The value used for the <see cref="BindingMode.OneTime"/> binding.
-        /// </param>
-        /// <param name="priority">The binding priority.</param>
-        public InstancedBinding(object value,
-            BindingPriority priority = BindingPriority.LocalValue)
-        {
-            Mode = BindingMode.OneTime;
-            Priority = priority;
-            Value = value;
-        }
-
-        /// <summary>
-        /// Initializes a new instance of the <see cref="InstancedBinding"/> class.
-        /// </summary>
-        /// <param name="observable">The observable for a one-way binding.</param>
+        /// <param name="subject">The binding source.</param>
         /// <param name="mode">The binding mode.</param>
-        /// <param name="priority">The binding priority.</param>
-        public InstancedBinding(
-            IObservable<object> observable, 
-            BindingMode mode = BindingMode.OneWay,
-            BindingPriority priority = BindingPriority.LocalValue)
+        /// <param name="priority">The priority of the binding.</param>
+        /// <remarks>
+        /// This constructor can be used to create any type of binding and as such requires an
+        /// <see cref="ISubject{Object}"/> as the binding source because this is the only binding
+        /// source which can be used for all binding modes. If you wish to create an instance with
+        /// something other than a subject, use one of the static creation methods on this class.
+        /// </remarks>
+        public InstancedBinding(ISubject<object> subject, BindingMode mode, BindingPriority priority)
         {
-            Contract.Requires<ArgumentNullException>(observable != null);
-
-            if (mode == BindingMode.OneWayToSource || mode == BindingMode.TwoWay)
-            {
-                throw new ArgumentException(
-                    "Invalid BindingResult mode: OneWayToSource and TwoWay bindings" +
-                    "require a Subject.");
-            }
+            Contract.Requires<ArgumentNullException>(subject != null);
 
             Mode = mode;
             Priority = priority;
-            Observable = observable;
+            Value = subject;
         }
 
-        /// <summary>
-        /// Initializes a new instance of the <see cref="InstancedBinding"/> class.
-        /// </summary>
-        /// <param name="subject">The subject for a two-way binding.</param>
-        /// <param name="mode">The binding mode.</param>
-        /// <param name="priority">The binding priority.</param>
-        public InstancedBinding(
-            ISubject<object> subject,
-            BindingMode mode = BindingMode.OneWay,
-            BindingPriority priority = BindingPriority.LocalValue)
+        private InstancedBinding(object value, BindingMode mode, BindingPriority priority)
         {
-            Contract.Requires<ArgumentNullException>(subject != null);
-
             Mode = mode;
             Priority = priority;
-            Subject = subject;
+            Value = value;
         }
 
         /// <summary>
@@ -92,18 +56,101 @@ namespace Avalonia.Data
         public BindingPriority Priority { get; }
 
         /// <summary>
-        /// Gets the value used for a <see cref="BindingMode.OneTime"/> binding.
+        /// Gets the value or source of the binding.
         /// </summary>
         public object Value { get; }
 
         /// <summary>
-        /// Gets the observable for a one-way binding.
+        /// Gets the <see cref="Value"/> as an observable.
         /// </summary>
-        public IObservable<object> Observable { get; }
+        public IObservable<object> Observable => Value as IObservable<object>;
 
         /// <summary>
-        /// Gets the subject for a two-way binding.
+        /// Gets the <see cref="Value"/> as a subject.
         /// </summary>
-        public ISubject<object> Subject { get; }
+        public ISubject<object> Subject => Value as ISubject<object>;
+
+        /// <summary>
+        /// Creates a new one-time binding with a fixed value.
+        /// </summary>
+        /// <param name="value">The value.</param>
+        /// <param name="priority">The priority of the binding.</param>
+        /// <returns>An <see cref="InstancedBinding"/> instance.</returns>
+        public static InstancedBinding OneTime(
+            object value,
+            BindingPriority priority = BindingPriority.LocalValue)
+        {
+            return new InstancedBinding(value, BindingMode.OneTime, priority);
+        }
+
+        /// <summary>
+        /// Creates a new one-time binding.
+        /// </summary>
+        /// <param name="observable">The source observable.</param>
+        /// <param name="priority">The priority of the binding.</param>
+        /// <returns>An <see cref="InstancedBinding"/> instance.</returns>
+        public static InstancedBinding OneTime(
+            IObservable<object> observable,
+            BindingPriority priority = BindingPriority.LocalValue)
+        {
+            Contract.Requires<ArgumentNullException>(observable != null);
+
+            return new InstancedBinding(observable, BindingMode.OneTime, priority);
+        }
+
+        /// <summary>
+        /// Creates a new one-way binding.
+        /// </summary>
+        /// <param name="observable">The source observable.</param>
+        /// <param name="priority">The priority of the binding.</param>
+        /// <returns>An <see cref="InstancedBinding"/> instance.</returns>
+        public static InstancedBinding OneWay(
+            IObservable<object> observable,
+            BindingPriority priority = BindingPriority.LocalValue)
+        {
+            Contract.Requires<ArgumentNullException>(observable != null);
+
+            return new InstancedBinding(observable, BindingMode.OneWay, priority);
+        }
+
+        /// <summary>
+        /// Creates a new one-way to source binding.
+        /// </summary>
+        /// <param name="subject">The binding source.</param>
+        /// <param name="priority">The priority of the binding.</param>
+        /// <returns>An <see cref="InstancedBinding"/> instance.</returns>
+        public static InstancedBinding OneWayToSource(
+            ISubject<object> subject,
+            BindingPriority priority = BindingPriority.LocalValue)
+        {
+            Contract.Requires<ArgumentNullException>(subject != null);
+
+            return new InstancedBinding(subject, BindingMode.OneWayToSource, priority);
+        }
+
+        /// <summary>
+        /// Creates a new two-way binding.
+        /// </summary>
+        /// <param name="subject">The binding source.</param>
+        /// <param name="priority">The priority of the binding.</param>
+        /// <returns>An <see cref="InstancedBinding"/> instance.</returns>
+        public static InstancedBinding TwoWay(
+            ISubject<object> subject,
+            BindingPriority priority = BindingPriority.LocalValue)
+        {
+            Contract.Requires<ArgumentNullException>(subject != null);
+
+            return new InstancedBinding(subject, BindingMode.TwoWay, priority);
+        }
+
+        /// <summary>
+        /// Creates a copy of the <see cref="InstancedBinding"/> with a different priority.
+        /// </summary>
+        /// <param name="priority">The priority of the binding.</param>
+        /// <returns>An <see cref="InstancedBinding"/> instance.</returns>
+        public InstancedBinding WithPriority(BindingPriority priority)
+        {
+            return new InstancedBinding(Value, Mode, priority);
+        }
     }
 }

+ 2 - 1
src/Avalonia.Base/Platform/IPlatformThreadingInterface.cs

@@ -17,10 +17,11 @@ namespace Avalonia.Platform
         /// <summary>
         /// Starts a timer.
         /// </summary>
+        /// <param name="priority"></param>
         /// <param name="interval">The interval.</param>
         /// <param name="tick">The action to call on each tick.</param>
         /// <returns>An <see cref="IDisposable"/> used to stop the timer.</returns>
-        IDisposable StartTimer(TimeSpan interval, Action tick);
+        IDisposable StartTimer(DispatcherPriority priority, TimeSpan interval, Action tick);
 
         void Signal(DispatcherPriority priority);
 

+ 19 - 0
src/Avalonia.Base/Threading/Dispatcher.cs

@@ -72,6 +72,12 @@ namespace Avalonia.Threading
             _jobRunner?.RunJobs(null);
         }
 
+        /// <summary>
+        /// Use this method to ensure that more prioritized tasks are executed
+        /// </summary>
+        /// <param name="minimumPriority"></param>
+        public void RunJobs(DispatcherPriority minimumPriority) => _jobRunner.RunJobs(minimumPriority);
+
         /// <inheritdoc/>
         public Task InvokeTaskAsync(Action action, DispatcherPriority priority = DispatcherPriority.Normal)
         {
@@ -84,6 +90,19 @@ namespace Avalonia.Threading
             _jobRunner?.Post(action, priority);
         }
 
+        /// <summary>
+        /// This is needed for platform backends that don't have internal priority system (e. g. win32)
+        /// To ensure that there are no jobs with higher priority
+        /// </summary>
+        /// <param name="currentPriority"></param>
+        internal void EnsurePriority(DispatcherPriority currentPriority)
+        {
+            if (currentPriority == DispatcherPriority.MaxValue)
+                return;
+            currentPriority += 1;
+            _jobRunner.RunJobs(currentPriority);
+        }
+
         /// <summary>
         /// Allows unit tests to change the platform threading interface.
         /// </summary>

+ 4 - 14
src/Avalonia.Base/Threading/DispatcherTimer.cs

@@ -17,13 +17,11 @@ namespace Avalonia.Threading
         private readonly DispatcherPriority _priority;
 
         private TimeSpan _interval;
-
-        private readonly Action _raiseTickAction;
-
+        
         /// <summary>
         /// Initializes a new instance of the <see cref="DispatcherTimer"/> class.
         /// </summary>
-        public DispatcherTimer() : this(DispatcherPriority.Normal)
+        public DispatcherTimer() : this(DispatcherPriority.Background)
         {
         }
 
@@ -34,7 +32,6 @@ namespace Avalonia.Threading
         public DispatcherTimer(DispatcherPriority priority)
         {
             _priority = priority;
-            _raiseTickAction = RaiseTick;
         }
 
         /// <summary>
@@ -187,7 +184,7 @@ namespace Avalonia.Threading
                     throw new Exception("Could not start timer: IPlatformThreadingInterface is not registered.");
                 }
 
-                _timer = threading.StartTimer(Interval, InternalTick);
+                _timer = threading.StartTimer(_priority, Interval, InternalTick);
             }
         }
 
@@ -210,14 +207,7 @@ namespace Avalonia.Threading
         /// </summary>
         private void InternalTick()
         {
-            Dispatcher.UIThread.InvokeAsync(_raiseTickAction, _priority);
-        }
-
-        /// <summary>
-        /// Raises the <see cref="Tick"/> event.
-        /// </summary>
-        private void RaiseTick()
-        {
+            Dispatcher.UIThread.EnsurePriority(_priority);
             Tick?.Invoke(this, EventArgs.Empty);
         }
     }

+ 2 - 1
src/Avalonia.Controls/Avalonia.Controls.csproj

@@ -12,7 +12,7 @@
     <ErrorReport>prompt</ErrorReport>
     <WarningLevel>4</WarningLevel>
     <DocumentationFile>bin\Debug\Avalonia.Controls.XML</DocumentationFile>
-    <NoWarn>CS1591</NoWarn>
+    <NoWarn>CS1591;CS0067</NoWarn>
   </PropertyGroup>
   <PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
     <DebugType>pdbonly</DebugType>
@@ -36,6 +36,7 @@
     <ProjectReference Include="..\Avalonia.Input\Avalonia.Input.csproj" />
     <ProjectReference Include="..\Avalonia.Interactivity\Avalonia.Interactivity.csproj" />
     <ProjectReference Include="..\Avalonia.Layout\Avalonia.Layout.csproj" />
+    <ProjectReference Include="..\Avalonia.Remote.Protocol\Avalonia.Remote.Protocol.csproj" />
     <ProjectReference Include="..\Avalonia.Visuals\Avalonia.Visuals.csproj" />
     <ProjectReference Include="..\Avalonia.Styling\Avalonia.Styling.csproj" />
   </ItemGroup>

+ 2132 - 0
src/Avalonia.Controls/Calendar/Calendar.cs

@@ -0,0 +1,2132 @@
+// (c) Copyright Microsoft Corporation.
+// This source is subject to the Microsoft Public License (Ms-PL).
+// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
+// All other rights reserved.
+
+using Avalonia.Controls.Primitives;
+using Avalonia.Data;
+using Avalonia.Input;
+using Avalonia.Interactivity;
+using Avalonia.Media;
+using System;
+using System.Collections.ObjectModel;
+using System.Diagnostics;
+
+namespace Avalonia.Controls
+{
+    /// <summary>
+    /// Specifies values for the different modes of operation of a
+    /// <see cref="T:Avalonia.Controls.Calendar" />.
+    /// </summary>
+    public enum CalendarMode
+    {
+        /// <summary>
+        /// The <see cref="T:Avalonia.Controls.Calendar" /> displays a
+        /// month at a time.
+        /// </summary>
+        Month = 0,
+
+        /// <summary>
+        /// The <see cref="T:Avalonia.Controls.Calendar" /> displays a
+        /// year at a time.
+        /// </summary>
+        Year = 1,
+
+        /// <summary>
+        /// The <see cref="T:Avalonia.Controls.Calendar" /> displays a
+        /// decade at a time.
+        /// </summary>
+        Decade = 2,
+    }
+
+    /// <summary>
+    /// Specifies values that describe the available selection modes for a
+    /// <see cref="T:Avalonia.Controls.Calendar" />.
+    /// </summary>
+    /// <remarks>
+    /// This enumeration provides the values that are used by the SelectionMode
+    /// property.
+    /// </remarks>
+    public enum CalendarSelectionMode
+    {
+        /// <summary>
+        /// Only a single date can be selected. Use the
+        /// <see cref="P:Avalonia.Controls.Calendar.SelectedDate" />
+        /// property to retrieve the selected date.
+        /// </summary>
+        SingleDate = 0,
+
+        /// <summary>
+        /// A single range of dates can be selected. Use 
+        /// <see cref="P:Avalonia.Controls.Calendar.SelectedDates" />
+        /// property to retrieve the selected dates.
+        /// </summary>
+        SingleRange = 1,
+
+        /// <summary>
+        /// Multiple non-contiguous ranges of dates can be selected. Use the
+        /// <see cref="P:Avalonia.Controls.Calendar.SelectedDates" />
+        /// property to retrieve the selected dates.
+        /// </summary>
+        MultipleRange = 2,
+
+        /// <summary>
+        /// No selections are allowed.
+        /// </summary>
+        None = 3,
+    }
+
+    /// <summary>
+    /// Provides data for the
+    /// <see cref="E:Avalonia.Controls.Calendar.DisplayDateChanged" />
+    /// event.
+    /// </summary>
+    public class CalendarDateChangedEventArgs : RoutedEventArgs
+    {
+        /// <summary>
+        /// Gets the date that was previously displayed.
+        /// </summary>
+        /// <value>
+        /// The date previously displayed.
+        /// </value>
+        public DateTime? RemovedDate { get; private set; }
+
+        /// <summary>
+        /// Gets the date to be newly displayed.
+        /// </summary>
+        /// <value>The new date to display.</value>
+        public DateTime? AddedDate { get; private set; }
+
+        /// <summary>
+        /// Initializes a new instance of the CalendarDateChangedEventArgs
+        /// class.
+        /// </summary>
+        /// <param name="removedDate">
+        /// The date that was previously displayed.
+        /// </param>
+        /// <param name="addedDate">The date to be newly displayed.</param>
+        internal CalendarDateChangedEventArgs(DateTime? removedDate, DateTime? addedDate)
+        {
+            RemovedDate = removedDate;
+            AddedDate = addedDate;
+        }
+    }
+
+    /// <summary>
+    /// Provides data for the
+    /// <see cref="E:Avalonia.Controls.Calendar.DisplayModeChanged" />
+    /// event.
+    /// </summary>
+    /// <QualityBand>Mature</QualityBand>
+    public class CalendarModeChangedEventArgs : RoutedEventArgs
+    {
+        /// <summary>
+        /// Gets the previous mode of the
+        /// <see cref="T:Avalonia.Controls.Calendar" />.
+        /// </summary>
+        /// <value>
+        /// A <see cref="T:Avalonia.Controls.CalendarMode" /> representing
+        /// the previous mode.
+        /// </value>
+        public CalendarMode OldMode { get; private set; }
+
+        /// <summary>
+        /// Gets the new mode of the
+        /// <see cref="T:Avalonia.Controls.Calendar" />.
+        /// </summary>
+        /// <value>
+        /// A <see cref="T:Avalonia.Controls.CalendarMode" /> 
+        /// the new mode.
+        /// </value>
+        public CalendarMode NewMode { get; private set; }
+
+        /// <summary>
+        /// Initializes a new instance of the
+        /// <see cref="T:Avalonia.Controls.CalendarModeChangedEventArgs" />
+        /// class.
+        /// </summary>
+        /// <param name="oldMode">The previous mode.</param>
+        /// <param name="newMode">The new mode.</param>
+        public CalendarModeChangedEventArgs(CalendarMode oldMode, CalendarMode newMode)
+        {
+            OldMode = oldMode;
+            NewMode = newMode;
+        }
+    }
+
+    /// <summary>
+    /// Represents a control that enables a user to select a date by using a
+    /// visual calendar display.
+    /// </summary>
+    /// <remarks>
+    /// <para>
+    /// A Calendar control can be used on its own, or as a drop-down part of a
+    /// DatePicker control. For more information, see DatePicker.  A Calendar
+    /// displays either the days of a month, the months of a year, or the years
+    /// of a decade, depending on the value of the DisplayMode property.  When
+    /// displaying the days of a month, the user can select a date, a range of
+    /// dates, or multiple ranges of dates.  The kinds of selections that are
+    /// allowed are controlled by the SelectionMode property.
+    /// </para>
+    /// <para>
+    /// The range of dates displayed is governed by the DisplayDateStart and
+    /// DisplayDateEnd properties.  If DisplayMode is Year or Decade, only
+    /// months or years that contain displayable dates will be displayed.
+    /// Setting the displayable range to a range that does not include the
+    /// current DisplayDate will throw an ArgumentOutOfRangeException.
+    /// </para>
+    /// <para>
+    /// The BlackoutDates property can be used to specify dates that cannot be
+    /// selected. These dates will be displayed as dimmed and disabled.
+    /// </para>
+    /// <para>
+    /// By default, Today is highlighted.  This can be disabled by setting
+    /// IsTodayHighlighted to false.
+    /// </para>
+    /// <para>
+    /// The Calendar control provides basic navigation using either the mouse or
+    /// keyboard. The following table summarizes keyboard navigation.
+    /// 
+    ///     Key Combination     DisplayMode     Action
+    ///     ARROW               Any             Change focused date, unselect
+    ///                                         all selected dates, and select
+    ///                                         new focused date.
+    ///                                         
+    ///     SHIFT+ARROW         Any             If SelectionMode is not set to
+    ///                                         SingleDate or None begin
+    ///                                         selecting a range of dates.
+    ///                                         
+    ///     CTRL+UP ARROW       Any             Switch to the next larger
+    ///                                         DisplayMode.  If DisplayMode is
+    ///                                         already Decade, no action.
+    ///                                         
+    ///     CTRL+DOWN ARROW     Any             Switch to the next smaller
+    ///                                         DisplayMode.  If DisplayMode is
+    ///                                         already Month, no action.
+    ///                                         
+    ///     SPACEBAR            Month           Select focused date.
+    ///     
+    ///     SPACEBAR            Year or Decade  Switch DisplayMode to the Month
+    ///                                         or Year represented by focused
+    ///                                         item.
+    /// </para>
+    /// <para>
+    /// XAML Usage for Classes Derived from Calendar
+    /// If you define a class that derives from Calendar, the class can be used
+    /// as an object element in XAML, and all of the inherited properties and
+    /// events that show a XAML usage in the reference for the Calendar members
+    /// can have the same XAML usage for the derived class. However, the object
+    /// element itself must have a different prefix mapping than the controls:
+    /// mapping shown in the usages, because the derived class comes from an
+    /// assembly and namespace that you create and define.  You must define your
+    /// own prefix mapping to an XML namespace to use the class as an object
+    /// element in XAML.
+    /// </para>
+    /// </remarks>
+    public class Calendar : TemplatedControl
+    {
+        internal const int RowsPerMonth = 7;
+        internal const int ColumnsPerMonth = 7;
+        internal const int RowsPerYear = 3;
+        internal const int ColumnsPerYear = 4;
+
+        private DateTime? _selectedDate;
+        private DateTime _selectedMonth;
+        private DateTime _selectedYear;
+
+        private DateTime _displayDate = DateTime.Today;
+        private DateTime? _displayDateStart = null;
+        private DateTime? _displayDateEnd = null;
+
+        private bool _isShiftPressed;
+        private bool _displayDateIsChanging = false;
+
+        internal CalendarDayButton FocusButton { get; set; }
+        internal CalendarButton FocusCalendarButton { get; set; }
+
+        internal Panel Root { get; set; }
+        internal CalendarItem MonthControl
+        {
+            get
+            {
+
+                if (Root != null && Root.Children.Count > 0)
+                {
+                    return Root.Children[0] as CalendarItem;
+                }
+                return null;
+            }
+        }
+
+        public static readonly StyledProperty<DayOfWeek> FirstDayOfWeekProperty =
+            AvaloniaProperty.Register<Calendar, DayOfWeek>(
+                    nameof(FirstDayOfWeek),
+                    defaultValue: DateTimeHelper.GetCurrentDateFormat().FirstDayOfWeek);
+        /// <summary>
+        /// Gets or sets the day that is considered the beginning of the week.
+        /// </summary>
+        /// <value>
+        /// A <see cref="T:System.DayOfWeek" /> representing the beginning of
+        /// the week. The default is <see cref="F:System.DayOfWeek.Sunday" />.
+        /// </value>
+        public DayOfWeek FirstDayOfWeek
+        {
+            get { return GetValue(FirstDayOfWeekProperty); }
+            set { SetValue(FirstDayOfWeekProperty, value); }
+        }
+        /// <summary>
+        /// FirstDayOfWeekProperty property changed handler.
+        /// </summary>
+        /// <param name="e">The DependencyPropertyChangedEventArgs.</param>
+        private void OnFirstDayOfWeekChanged(AvaloniaPropertyChangedEventArgs e)
+        {
+
+            if (IsValidFirstDayOfWeek(e.NewValue))
+            {
+                UpdateMonths();
+            }
+            else
+            {
+                throw new ArgumentOutOfRangeException("d", "Invalid DayOfWeek");
+            }
+        }
+        /// <summary>
+        /// Inherited code: Requires comment.
+        /// </summary>
+        /// <param name="value">Inherited code: Requires comment 1.</param>
+        /// <returns>Inherited code: Requires comment 2.</returns>
+        private static bool IsValidFirstDayOfWeek(object value)
+        {
+            DayOfWeek day = (DayOfWeek)value;
+
+            return day == DayOfWeek.Sunday
+                || day == DayOfWeek.Monday
+                || day == DayOfWeek.Tuesday
+                || day == DayOfWeek.Wednesday
+                || day == DayOfWeek.Thursday
+                || day == DayOfWeek.Friday
+                || day == DayOfWeek.Saturday;
+        }
+
+        public static readonly StyledProperty<bool> IsTodayHighlightedProperty =
+            AvaloniaProperty.Register<Calendar, bool>(
+                nameof(IsTodayHighlighted),
+                defaultValue: true);
+        /// <summary>
+        /// Gets or sets a value indicating whether the current date is
+        /// highlighted.
+        /// </summary>
+        /// <value>
+        /// True if the current date is highlighted; otherwise, false. The
+        /// default is true.
+        /// </value>
+        public bool IsTodayHighlighted
+        {
+            get { return GetValue(IsTodayHighlightedProperty); }
+            set { SetValue(IsTodayHighlightedProperty, value); }
+        }
+        /// <summary>
+        /// IsTodayHighlightedProperty property changed handler.
+        /// </summary>
+        /// <param name="e">The DependencyPropertyChangedEventArgs.</param>
+        private void OnIsTodayHighlightedChanged(AvaloniaPropertyChangedEventArgs e)
+        {
+            if (DisplayDate != null)
+            {
+                int i = DateTimeHelper.CompareYearMonth(DisplayDateInternal, DateTime.Today);
+
+                if (i > -2 && i < 2)
+                {
+                    UpdateMonths();
+                }
+            }
+        }
+
+        public static readonly StyledProperty<IBrush> HeaderBackgroundProperty =
+            AvaloniaProperty.Register<Calendar, IBrush>(nameof(HeaderBackground));
+        public IBrush HeaderBackground
+        {
+            get { return GetValue(HeaderBackgroundProperty); }
+            set { SetValue(HeaderBackgroundProperty, value); }
+        }
+
+        public static readonly StyledProperty<CalendarMode> DisplayModeProperty =
+            AvaloniaProperty.Register<Calendar, CalendarMode>(
+                nameof(DisplayMode),
+                validate: ValidateDisplayMode);
+        /// <summary>
+        /// Gets or sets a value indicating whether the calendar is displayed in
+        /// months, years, or decades.
+        /// </summary>
+        /// <value>
+        /// A value indicating what length of time the
+        /// <see cref="T:System.Windows.Controls.Calendar" /> should display.
+        /// </value>
+        public CalendarMode DisplayMode
+        {
+            get { return GetValue(DisplayModeProperty); }
+            set { SetValue(DisplayModeProperty, value); }
+        }
+        /// <summary>
+        /// DisplayModeProperty property changed handler.
+        /// </summary>
+        /// <param name="e">The DependencyPropertyChangedEventArgs.</param>
+        private void OnDisplayModePropertyChanged(AvaloniaPropertyChangedEventArgs e)
+        {
+            CalendarMode mode = (CalendarMode)e.NewValue;
+            CalendarMode oldMode = (CalendarMode)e.OldValue;
+            CalendarItem monthControl = MonthControl;
+
+            if (monthControl != null)
+            {
+                switch (oldMode)
+                {
+                    case CalendarMode.Month:
+                        {
+                            SelectedYear = DisplayDateInternal;
+                            SelectedMonth = DisplayDateInternal;
+                            break;
+                        }
+                    case CalendarMode.Year:
+                        {
+                            DisplayDate = SelectedMonth;
+                            SelectedYear = SelectedMonth;
+                            break;
+                        }
+                    case CalendarMode.Decade:
+                        {
+                            DisplayDate = SelectedYear;
+                            SelectedMonth = SelectedYear;
+                            break;
+                        }
+                }
+
+                switch (mode)
+                {
+                    case CalendarMode.Month:
+                        {
+                            OnMonthClick();
+                            break;
+                        }
+                    case CalendarMode.Year:
+                    case CalendarMode.Decade:
+                        {
+                            OnHeaderClick();
+                            break;
+                        }
+                }
+            }
+            OnDisplayModeChanged(new CalendarModeChangedEventArgs((CalendarMode)e.OldValue, mode));
+        }
+        private static CalendarMode ValidateDisplayMode(Calendar o, CalendarMode mode)
+        {
+            if(IsValidDisplayMode(mode))
+            {
+                return mode;
+            }
+            else
+            {
+                throw new ArgumentOutOfRangeException(nameof(mode), "Invalid DisplayMode");
+            }
+        }
+        private static bool IsValidDisplayMode(CalendarMode mode)
+        {
+            return mode == CalendarMode.Month
+                || mode == CalendarMode.Year
+                || mode == CalendarMode.Decade;
+        }
+        private void OnDisplayModeChanged(CalendarModeChangedEventArgs args)
+        {
+            DisplayModeChanged?.Invoke(this, args);
+        }
+
+        public static readonly StyledProperty<CalendarSelectionMode> SelectionModeProperty =
+            AvaloniaProperty.Register<Calendar, CalendarSelectionMode>(
+                nameof(SelectionMode),
+                defaultValue: CalendarSelectionMode.SingleDate);
+        /// <summary>
+        /// Gets or sets a value that indicates what kind of selections are
+        /// allowed.
+        /// </summary>
+        /// <value>
+        /// A value that indicates the current selection mode. The default is
+        /// <see cref="F:System.Windows.Controls.CalendarSelectionMode.SingleDate" />.
+        /// </value>
+        /// <remarks>
+        /// <para>
+        /// This property determines whether the Calendar allows no selection,
+        /// selection of a single date, or selection of multiple dates.  The
+        /// selection mode is specified with the CalendarSelectionMode
+        /// enumeration.
+        /// </para>
+        /// <para>
+        /// When this property is changed, all selected dates will be cleared.
+        /// </para>
+        /// </remarks>
+        public CalendarSelectionMode SelectionMode
+        {
+            get { return GetValue(SelectionModeProperty); }
+            set { SetValue(SelectionModeProperty, value); }
+        }
+        private void OnSelectionModeChanged(AvaloniaPropertyChangedEventArgs e)
+        {
+            if (IsValidSelectionMode(e.NewValue))
+            {
+                _displayDateIsChanging = true;
+                SelectedDate = null;
+                _displayDateIsChanging = false;
+                SelectedDates.Clear();
+            }
+            else
+            {
+                throw new ArgumentOutOfRangeException("d", "Invalid SelectionMode");
+            }
+        }
+        /// <summary>
+        /// Inherited code: Requires comment.
+        /// </summary>
+        /// <param name="value">Inherited code: Requires comment 1.</param>
+        /// <returns>Inherited code: Requires comment 2.</returns>
+        private static bool IsValidSelectionMode(object value)
+        {
+            CalendarSelectionMode mode = (CalendarSelectionMode)value;
+
+            return mode == CalendarSelectionMode.SingleDate
+                || mode == CalendarSelectionMode.SingleRange
+                || mode == CalendarSelectionMode.MultipleRange
+                || mode == CalendarSelectionMode.None;
+        }
+
+        public static readonly DirectProperty<Calendar, DateTime?> SelectedDateProperty =
+            AvaloniaProperty.RegisterDirect<Calendar, DateTime?>(
+                nameof(SelectedDate),
+                o => o.SelectedDate,
+                (o, v) => o.SelectedDate = v,
+                defaultBindingMode: BindingMode.TwoWay);
+        /// <summary>
+        /// Gets or sets the currently selected date.
+        /// </summary>
+        /// <value>The date currently selected. The default is null.</value>
+        /// <exception cref="T:System.ArgumentOutOfRangeException">
+        /// The given date is outside the range specified by
+        /// <see cref="P:System.Windows.Controls.Calendar.DisplayDateStart" />
+        /// and <see cref="P:System.Windows.Controls.Calendar.DisplayDateEnd" />
+        /// -or-
+        /// The given date is in the
+        /// <see cref="P:System.Windows.Controls.Calendar.BlackoutDates" />
+        /// collection.
+        /// </exception>
+        /// <exception cref="T:System.InvalidOperationException">
+        /// If set to anything other than null when
+        /// <see cref="P:System.Windows.Controls.Calendar.SelectionMode" /> is
+        /// set to
+        /// <see cref="F:System.Windows.Controls.CalendarSelectionMode.None" />.
+        /// </exception>
+        /// <remarks>
+        /// Use this property when SelectionMode is set to SingleDate.  In other
+        /// modes, this property will always be the first date in SelectedDates.
+        /// </remarks>
+        public DateTime? SelectedDate
+        {
+            get { return _selectedDate; }
+            set { SetAndRaise(SelectedDateProperty, ref _selectedDate, value); }
+        }
+        private void OnSelectedDateChanged(AvaloniaPropertyChangedEventArgs e)
+        {
+            if (!_displayDateIsChanging)
+            {
+                if (SelectionMode != CalendarSelectionMode.None)
+                {
+                    DateTime? addedDate;
+
+                    addedDate = (DateTime?)e.NewValue;
+
+                    if (IsValidDateSelection(this, addedDate))
+                    {
+                        if (addedDate == null)
+                        {
+                            SelectedDates.Clear();
+                        }
+                        else
+                        {
+                            if (addedDate.HasValue && !(SelectedDates.Count > 0 && SelectedDates[0] == addedDate.Value))
+                            {
+                                foreach (DateTime item in SelectedDates)
+                                {
+                                    RemovedItems.Add(item);
+                                }
+                                SelectedDates.ClearInternal();
+                                // the value is added as a range so that the
+                                // SelectedDatesChanged event can be thrown with
+                                // all the removed items
+                                SelectedDates.AddRange(addedDate.Value, addedDate.Value);
+                            }
+                        }
+
+                        // We update the LastSelectedDate for only the Single
+                        // mode.  For the other modes it automatically gets
+                        // updated when the HoverEnd is updated.
+                        if (SelectionMode == CalendarSelectionMode.SingleDate)
+                        {
+                            LastSelectedDate = addedDate;
+                        }
+                    }
+                    else
+                    {
+                        throw new ArgumentOutOfRangeException("d", "SelectedDate value is not valid.");
+                    }
+                }
+                else
+                {
+                    throw new InvalidOperationException("The SelectedDate property cannot be set when the selection mode is None.");
+                }
+            }
+        }
+
+        /// <summary>
+        /// Gets a collection of selected dates.
+        /// </summary>
+        /// <value>
+        /// A <see cref="T:System.Windows.Controls.SelectedDatesCollection" />
+        /// object that contains the currently selected dates. The default is an
+        /// empty collection.
+        /// </value>
+        /// <remarks>
+        /// Dates can be added to the collection either individually or in a
+        /// range using the AddRange method.  Depending on the value of the
+        /// SelectionMode property, adding a date or range to the collection may
+        /// cause it to be cleared.  The following table lists how
+        /// CalendarSelectionMode affects the SelectedDates property.
+        /// 
+        ///     CalendarSelectionMode   Description
+        ///     None                    No selections are allowed.  SelectedDate
+        ///                             cannot be set and no values can be added
+        ///                             to SelectedDates.
+        ///                             
+        ///     SingleDate              Only a single date can be selected,
+        ///                             either by setting SelectedDate or the
+        ///                             first value in SelectedDates.  AddRange
+        ///                             cannot be used.
+        ///                             
+        ///     SingleRange             A single range of dates can be selected.
+        ///                             Setting SelectedDate, adding a date
+        ///                             individually to SelectedDates, or using
+        ///                             AddRange will clear all previous values
+        ///                             from SelectedDates.
+        ///     MultipleRange           Multiple non-contiguous ranges of dates
+        ///                             can be selected. Adding a date
+        ///                             individually to SelectedDates or using
+        ///                             AddRange will not clear SelectedDates.
+        ///                             Setting SelectedDate will still clear
+        ///                             SelectedDates, but additional dates or
+        ///                             range can then be added.  Adding a range
+        ///                             that includes some dates that are
+        ///                             already selected or overlaps with
+        ///                             another range results in the union of
+        ///                             the ranges and does not cause an
+        ///                             exception.
+        /// </remarks>
+        public SelectedDatesCollection SelectedDates { get; private set; }
+        private static bool IsSelectionChanged(SelectionChangedEventArgs e)
+        {
+            if (e.AddedItems.Count != e.RemovedItems.Count)
+            {
+                return true;
+            }
+            foreach (DateTime addedDate in e.AddedItems)
+            {
+                if (!e.RemovedItems.Contains(addedDate))
+                {
+                    return true;
+                }
+            }
+            return false;
+        }
+        internal void OnSelectedDatesCollectionChanged(SelectionChangedEventArgs e)
+        {
+            if (IsSelectionChanged(e))
+            {
+                e.RoutedEvent = SelectingItemsControl.SelectionChangedEvent;
+                e.Source = this;
+                SelectedDatesChanged?.Invoke(this, e);
+            }
+        }
+        
+        internal Collection<DateTime> RemovedItems { get; set; }
+        internal DateTime? LastSelectedDateInternal { get; set; }
+        internal DateTime? LastSelectedDate
+        {
+            get { return LastSelectedDateInternal; }
+            set
+            {
+                LastSelectedDateInternal = value;
+
+                if (SelectionMode == CalendarSelectionMode.None)
+                {
+                    if (FocusButton != null)
+                    {
+                        FocusButton.IsCurrent = false;
+                    }
+                    FocusButton = FindDayButtonFromDay(LastSelectedDate.Value);
+                    if (FocusButton != null)
+                    {
+                        FocusButton.IsCurrent = HasFocusInternal;
+                    }
+                }
+            }
+        }
+
+        internal DateTime SelectedMonth
+        {
+            get { return _selectedMonth; }
+            set
+            {
+                int monthDifferenceStart = DateTimeHelper.CompareYearMonth(value, DisplayDateRangeStart);
+                int monthDifferenceEnd = DateTimeHelper.CompareYearMonth(value, DisplayDateRangeEnd);
+
+                if (monthDifferenceStart >= 0 && monthDifferenceEnd <= 0)
+                {
+                    _selectedMonth = DateTimeHelper.DiscardDayTime(value);
+                }
+                else
+                {
+                    if (monthDifferenceStart < 0)
+                    {
+                        _selectedMonth = DateTimeHelper.DiscardDayTime(DisplayDateRangeStart);
+                    }
+                    else
+                    {
+                        Debug.Assert(monthDifferenceEnd > 0, "monthDifferenceEnd should be greater than 0!");
+                        _selectedMonth = DateTimeHelper.DiscardDayTime(DisplayDateRangeEnd);
+                    }
+                }
+            }
+        }
+        internal DateTime SelectedYear
+        {
+            get { return _selectedYear; }
+            set
+            {
+                if (value.Year < DisplayDateRangeStart.Year)
+                {
+                    _selectedYear = DisplayDateRangeStart;
+                }
+                else
+                {
+                    if (value.Year > DisplayDateRangeEnd.Year)
+                    {
+                        _selectedYear = DisplayDateRangeEnd;
+                    }
+                    else
+                    {
+                        _selectedYear = value;
+                    }
+                }
+            }
+        }
+
+        public static readonly DirectProperty<Calendar, DateTime> DisplayDateProperty =
+            AvaloniaProperty.RegisterDirect<Calendar, DateTime>(
+                nameof(DisplayDate),
+                o => o.DisplayDate,
+                (o, v) => o.DisplayDate = v,
+                defaultBindingMode: BindingMode.TwoWay);
+        /// <summary>
+        /// Gets or sets the date to display.
+        /// </summary>
+        /// <value>The date to display.</value>
+        /// <exception cref="T:System.ArgumentOutOfRangeException">
+        /// The given date is not in the range specified by
+        /// <see cref="P:System.Windows.Controls.Calendar.DisplayDateStart" />
+        /// and
+        /// <see cref="P:System.Windows.Controls.Calendar.DisplayDateEnd" />.
+        /// </exception>
+        /// <remarks>
+        /// <para>
+        /// This property allows the developer to specify a date to display.  If
+        /// this property is a null reference (Nothing in Visual Basic),
+        /// SelectedDate is displayed.  If SelectedDate is also a null reference
+        /// (Nothing in Visual Basic), Today is displayed.  The default is
+        /// Today.
+        /// </para>
+        /// <para>
+        /// To set this property in XAML, use a date specified in the format
+        /// yyyy/mm/dd.  The mm and dd components must always consist of two
+        /// characters, with a leading zero if necessary.  For instance, the
+        /// month of May should be specified as 05.
+        /// </para>
+        /// </remarks>
+        public DateTime DisplayDate
+        {
+            get { return _displayDate; }
+            set { SetAndRaise(DisplayDateProperty, ref _displayDate, value); }
+        }
+        internal DateTime DisplayDateInternal { get; private set; }
+
+        private void OnDisplayDateChanged(AvaloniaPropertyChangedEventArgs e)
+        {
+            UpdateDisplayDate(this, (DateTime)e.NewValue, (DateTime)e.OldValue);
+        }
+        private static void UpdateDisplayDate(Calendar c, DateTime addedDate, DateTime removedDate)
+        {
+            Debug.Assert(c != null, "c should not be null!");
+
+            // If DisplayDate < DisplayDateStart, DisplayDate = DisplayDateStart
+            if (DateTime.Compare(addedDate, c.DisplayDateRangeStart) < 0)
+            {
+                c.DisplayDate = c.DisplayDateRangeStart;
+                return;
+            }
+
+            // If DisplayDate > DisplayDateEnd, DisplayDate = DisplayDateEnd
+            if (DateTime.Compare(addedDate, c.DisplayDateRangeEnd) > 0)
+            {
+                c.DisplayDate = c.DisplayDateRangeEnd;
+                return;
+            }
+
+            c.DisplayDateInternal = DateTimeHelper.DiscardDayTime(addedDate);
+            c.UpdateMonths();
+            c.OnDisplayDate(new CalendarDateChangedEventArgs(removedDate, addedDate));
+        }
+        private void OnDisplayDate(CalendarDateChangedEventArgs e)
+        {
+            DisplayDateChanged?.Invoke(this, e);
+        }
+
+        public static readonly DirectProperty<Calendar, DateTime?> DisplayDateStartProperty =
+            AvaloniaProperty.RegisterDirect<Calendar, DateTime?>(
+                nameof(DisplayDateStart),
+                o => o.DisplayDateStart,
+                (o, v) => o.DisplayDateStart = v,
+                defaultBindingMode: BindingMode.TwoWay);
+        /// <summary>
+        /// Gets or sets the first date to be displayed.
+        /// </summary>
+        /// <value>The first date to display.</value>
+        /// <remarks>
+        /// To set this property in XAML, use a date specified in the format
+        /// yyyy/mm/dd.  The mm and dd components must always consist of two
+        /// characters, with a leading zero if necessary.  For instance, the
+        /// month of May should be specified as 05.
+        /// </remarks>
+        public DateTime? DisplayDateStart
+        {
+            get { return _displayDateStart; }
+            set { SetAndRaise(DisplayDateStartProperty, ref _displayDateStart, value); }
+        }
+        private void OnDisplayDateStartChanged(AvaloniaPropertyChangedEventArgs e)
+        {
+            if (!_displayDateIsChanging)
+            {
+                DateTime? newValue = e.NewValue as DateTime?;
+
+                if (newValue.HasValue)
+                {
+                    // DisplayDateStart coerces to the value of the
+                    // SelectedDateMin if SelectedDateMin < DisplayDateStart
+                    DateTime? selectedDateMin = SelectedDateMin(this);
+
+                    if (selectedDateMin.HasValue && DateTime.Compare(selectedDateMin.Value, newValue.Value) < 0)
+                    {
+                        DisplayDateStart = selectedDateMin.Value;
+                        return;
+                    }
+
+                    // if DisplayDateStart > DisplayDateEnd,
+                    // DisplayDateEnd = DisplayDateStart
+                    if (DateTime.Compare(newValue.Value, DisplayDateRangeEnd) > 0)
+                    {
+                        DisplayDateEnd = DisplayDateStart;
+                    }
+
+                    // If DisplayDate < DisplayDateStart,
+                    // DisplayDate = DisplayDateStart
+                    if (DateTimeHelper.CompareYearMonth(newValue.Value, DisplayDateInternal) > 0)
+                    {
+                        DisplayDate = newValue.Value;
+                    }
+                }
+                UpdateMonths();
+            }
+        }
+
+        /// <summary>
+        /// Gets a collection of dates that are marked as not selectable.
+        /// </summary>
+        /// <value>
+        /// A collection of dates that cannot be selected. The default value is
+        /// an empty collection.
+        /// </value>
+        /// <exception cref="System.ArgumentOutOfRangeException">
+        /// Adding a date to this collection when it is already selected or
+        /// adding a date outside the range specified by DisplayDateStart and
+        /// DisplayDateEnd.
+        /// </exception>
+        /// <remarks>
+        /// <para>
+        /// Dates in this collection will appear as disabled on the calendar.
+        /// </para>
+        /// <para>
+        /// To make all past dates not selectable, you can use the
+        /// AddDatesInPast method provided by the collection returned by this
+        /// property.
+        /// </para>
+        /// </remarks>
+        public CalendarBlackoutDatesCollection BlackoutDates { get; private set; }
+
+        private static DateTime? SelectedDateMin(Calendar cal)
+        {
+            DateTime selectedDateMin;
+
+            if (cal.SelectedDates.Count > 0)
+            {
+                selectedDateMin = cal.SelectedDates[0];
+                Debug.Assert(DateTime.Compare(cal.SelectedDate.Value, selectedDateMin) == 0, "The SelectedDate should be the minimum selected date!");
+            }
+            else
+            {
+                return null;
+            }
+
+            foreach (DateTime selectedDate in cal.SelectedDates)
+            {
+                if (DateTime.Compare(selectedDate, selectedDateMin) < 0)
+                {
+                    selectedDateMin = selectedDate;
+                }
+            }
+            return selectedDateMin;
+        }
+        internal DateTime DisplayDateRangeStart
+        {
+            get { return DisplayDateStart.GetValueOrDefault(DateTime.MinValue); }
+        }
+
+        public static readonly DirectProperty<Calendar, DateTime?> DisplayDateEndProperty =
+            AvaloniaProperty.RegisterDirect<Calendar, DateTime?>(
+                nameof(DisplayDateEnd),
+                o => o.DisplayDateEnd,
+                (o, v) => o.DisplayDateEnd = v,
+                defaultBindingMode: BindingMode.TwoWay);
+        
+        /// <summary>
+        /// Gets or sets the last date to be displayed.
+        /// </summary>
+        /// <value>The last date to display.</value>
+        /// <remarks>
+        /// To set this property in XAML, use a date specified in the format
+        /// yyyy/mm/dd.  The mm and dd components must always consist of two
+        /// characters, with a leading zero if necessary.  For instance, the
+        /// month of May should be specified as 05.
+        /// </remarks>
+        public DateTime? DisplayDateEnd
+        {
+            get { return _displayDateEnd; }
+            set { SetAndRaise(DisplayDateEndProperty, ref _displayDateEnd, value); }
+        }
+
+        private void OnDisplayDateEndChanged(AvaloniaPropertyChangedEventArgs e)
+        {
+            if (!_displayDateIsChanging)
+            {
+                DateTime? newValue = e.NewValue as DateTime?;
+
+                if (newValue.HasValue)
+                {
+                    // DisplayDateEnd coerces to the value of the
+                    // SelectedDateMax if SelectedDateMax > DisplayDateEnd
+                    DateTime? selectedDateMax = SelectedDateMax(this);
+
+                    if (selectedDateMax.HasValue && DateTime.Compare(selectedDateMax.Value, newValue.Value) > 0)
+                    {
+                        DisplayDateEnd = selectedDateMax.Value;
+                        return;
+                    }
+
+                    // if DisplayDateEnd < DisplayDateStart,
+                    // DisplayDateEnd = DisplayDateStart
+                    if (DateTime.Compare(newValue.Value, DisplayDateRangeStart) < 0)
+                    {
+                        DisplayDateEnd = DisplayDateStart;
+                        return;
+                    }
+
+                    // If DisplayDate > DisplayDateEnd,
+                    // DisplayDate = DisplayDateEnd
+                    if (DateTimeHelper.CompareYearMonth(newValue.Value, DisplayDateInternal) < 0)
+                    {
+                        DisplayDate = newValue.Value;
+                    }
+                }
+                UpdateMonths();
+            }
+        }
+
+        private static DateTime? SelectedDateMax(Calendar cal)
+        {
+            DateTime selectedDateMax;
+
+            if (cal.SelectedDates.Count > 0)
+            {
+                selectedDateMax = cal.SelectedDates[0];
+                Debug.Assert(DateTime.Compare(cal.SelectedDate.Value, selectedDateMax) == 0, "The SelectedDate should be the maximum SelectedDate!");
+            }
+            else
+            {
+                return null;
+            }
+
+            foreach (DateTime selectedDate in cal.SelectedDates)
+            {
+                if (DateTime.Compare(selectedDate, selectedDateMax) > 0)
+                {
+                    selectedDateMax = selectedDate;
+                }
+            }
+            return selectedDateMax;
+        }
+        internal DateTime DisplayDateRangeEnd
+        {
+            get { return DisplayDateEnd.GetValueOrDefault(DateTime.MaxValue); }
+        }
+
+        internal DateTime? HoverStart { get; set; }
+        internal int? HoverStartIndex { get; set; }
+        internal DateTime? HoverEndInternal { get; set; }
+        internal DateTime? HoverEnd
+        {
+            get { return HoverEndInternal; }
+            set
+            {
+                HoverEndInternal = value;
+                LastSelectedDate = value;
+            }
+        }
+        internal int? HoverEndIndex { get; set; }
+        internal bool HasFocusInternal { get; set; }
+        internal bool IsMouseSelection { get; set; }
+
+
+        /// <summary>
+        /// Gets or sets a value indicating whether DatePicker should change its 
+        /// DisplayDate because of a SelectedDate change on its Calendar.
+        /// </summary>
+        internal bool DatePickerDisplayDateFlag { get; set; }
+
+        internal CalendarDayButton FindDayButtonFromDay(DateTime day)
+        {
+            CalendarDayButton b;
+            DateTime? d;
+            CalendarItem monthControl = MonthControl;
+
+            // REMOVE_RTM: should be updated if we support MultiCalendar
+            int count = RowsPerMonth * ColumnsPerMonth;
+            if (monthControl != null)
+            {
+                if (monthControl.MonthView != null)
+                {
+                    for (int childIndex = ColumnsPerMonth; childIndex < count; childIndex++)
+                    {
+                        b = monthControl.MonthView.Children[childIndex] as CalendarDayButton;
+                        d = b.DataContext as DateTime?;
+
+                        if (d.HasValue)
+                        {
+                            if (DateTimeHelper.CompareDays(d.Value, day) == 0)
+                            {
+                                return b;
+                            }
+                        }
+                    }
+                }
+            }
+            return null;
+        }
+
+        private void Calendar_SizeChanged(object sender, EventArgs e)
+        {
+            Debug.Assert(sender is Calendar, "The sender should be a Calendar!");
+
+            var size = Bounds.Size;
+            RectangleGeometry rg = new RectangleGeometry();
+            rg.Rect = new Rect(0, 0, size.Width, size.Height);
+
+            if (Root != null)
+            {
+                Root.Clip = rg;
+            }
+        }
+
+        private void OnSelectedMonthChanged(DateTime? selectedMonth)
+        {
+            if (selectedMonth.HasValue)
+            {
+                Debug.Assert(DisplayMode == CalendarMode.Year, "DisplayMode should be Year!");
+                SelectedMonth = selectedMonth.Value;
+                UpdateMonths();
+            }
+        }
+        private void OnSelectedYearChanged(DateTime? selectedYear)
+        {
+            if (selectedYear.HasValue)
+            {
+                Debug.Assert(DisplayMode == CalendarMode.Decade, "DisplayMode should be Decade!");
+                SelectedYear = selectedYear.Value;
+                UpdateMonths();
+            }
+        }
+        internal void OnHeaderClick()
+        {
+            Debug.Assert(DisplayMode == CalendarMode.Year || DisplayMode == CalendarMode.Decade, "The DisplayMode should be Year or Decade");
+            CalendarItem monthControl = MonthControl;
+            if (monthControl != null && monthControl.MonthView != null && monthControl.YearView != null)
+            {
+                monthControl.MonthView.IsVisible = false;
+                monthControl.YearView.IsVisible = true;
+                UpdateMonths();
+            }
+        }
+
+        internal void ResetStates()
+        {
+            CalendarDayButton d;
+            CalendarItem monthControl = MonthControl;
+            int count = RowsPerMonth * ColumnsPerMonth;
+            if (monthControl != null)
+            {
+                if (monthControl.MonthView != null)
+                {
+                    for (int childIndex = ColumnsPerMonth; childIndex < count; childIndex++)
+                    {
+                        d = monthControl.MonthView.Children[childIndex] as CalendarDayButton;
+                        d.IgnoreMouseOverState();
+                    }
+                }
+            }
+
+        }
+
+        internal void UpdateMonths()
+        {
+            CalendarItem monthControl = MonthControl;
+            if (monthControl != null)
+            {
+                switch (DisplayMode)
+                {
+                    case CalendarMode.Month:
+                        {
+                            monthControl.UpdateMonthMode();
+                            break;
+                        }
+                    case CalendarMode.Year:
+                        {
+                            monthControl.UpdateYearMode();
+                            break;
+                        }
+                    case CalendarMode.Decade:
+                        {
+                            monthControl.UpdateDecadeMode();
+                            break;
+                        }
+                }
+            }
+        }
+
+        internal static bool IsValidDateSelection(Calendar cal, DateTime? value)
+        {
+            if (!value.HasValue)
+            {
+                return true;
+            }
+            else
+            {
+                if (cal.BlackoutDates.Contains(value.Value))
+                {
+                    return false;
+                }
+                else
+                {
+                    cal._displayDateIsChanging = true;
+                    if (DateTime.Compare(value.Value, cal.DisplayDateRangeStart) < 0)
+                    {
+                        cal.DisplayDateStart = value;
+                    }
+                    else if (DateTime.Compare(value.Value, cal.DisplayDateRangeEnd) > 0)
+                    {
+                        cal.DisplayDateEnd = value;
+                    }
+                    cal._displayDateIsChanging = false;
+
+                    return true;
+                }
+            }
+        }
+        private static bool IsValidKeyboardSelection(Calendar cal, DateTime? value)
+        {
+            if (!value.HasValue)
+            {
+                return true;
+            }
+            else
+            {
+                if (cal.BlackoutDates.Contains(value.Value))
+                {
+                    return false;
+                }
+                else
+                {
+                    return (DateTime.Compare(value.Value, cal.DisplayDateRangeStart) >= 0 && DateTime.Compare(value.Value, cal.DisplayDateRangeEnd) <= 0);
+                }
+            }
+        }
+
+        /// <summary>
+        /// This method highlights the days in MultiSelection mode without
+        /// adding them to the SelectedDates collection.
+        /// </summary>
+        internal void HighlightDays()
+        {
+            if (HoverEnd != null && HoverStart != null)
+            {
+                int startIndex, endIndex, i;
+                CalendarDayButton b;
+                DateTime? d;
+                CalendarItem monthControl = MonthControl;
+
+                // This assumes a contiguous set of dates:
+                if (HoverEndIndex != null && HoverStartIndex != null)
+                {
+                    SortHoverIndexes(out startIndex, out endIndex);
+
+                    for (i = startIndex; i <= endIndex; i++)
+                    {
+                        b = monthControl.MonthView.Children[i] as CalendarDayButton;
+                        b.IsSelected = true;
+                        d = b.DataContext as DateTime?;
+
+                        if (d.HasValue && DateTimeHelper.CompareDays(HoverEnd.Value, d.Value) == 0)
+                        {
+                            if (FocusButton != null)
+                            {
+                                FocusButton.IsCurrent = false;
+                            }
+                            b.IsCurrent = HasFocusInternal;
+                            FocusButton = b;
+                        }
+                    }
+                }
+            }
+        }
+        /// <summary>
+        /// This method un-highlights the days that were hovered over but not
+        /// added to the SelectedDates collection or un-highlighted the
+        /// previously selected days in SingleRange Mode.
+        /// </summary>
+        internal void UnHighlightDays()
+        {
+            if (HoverEnd != null && HoverStart != null)
+            {
+                CalendarItem monthControl = MonthControl;
+                CalendarDayButton b;
+                DateTime? d;
+
+                if (HoverEndIndex != null && HoverStartIndex != null)
+                {
+                    int i;
+                    SortHoverIndexes(out int startIndex, out int endIndex);
+
+                    if (SelectionMode == CalendarSelectionMode.MultipleRange)
+                    {
+                        for (i = startIndex; i <= endIndex; i++)
+                        {
+                            b = monthControl.MonthView.Children[i] as CalendarDayButton;
+                            d = b.DataContext as DateTime?;
+
+                            if (d.HasValue)
+                            {
+                                if (!SelectedDates.Contains(d.Value))
+                                {
+                                    b.IsSelected = false;
+                                }
+                            }
+                        }
+                    }
+                    else
+                    {
+                        // It is SingleRange
+                        for (i = startIndex; i <= endIndex; i++)
+                        {
+                            (monthControl.MonthView.Children[i] as CalendarDayButton).IsSelected = false;
+                        }
+                    }
+                }
+            }
+        }
+        internal void SortHoverIndexes(out int startIndex, out int endIndex)
+        {
+            if (DateTimeHelper.CompareDays(HoverEnd.Value, HoverStart.Value) > 0)
+            {
+                startIndex = HoverStartIndex.Value;
+                endIndex = HoverEndIndex.Value;
+            }
+            else
+            {
+                startIndex = HoverEndIndex.Value;
+                endIndex = HoverStartIndex.Value;
+            }
+        }
+
+        internal void OnPreviousClick()
+        {
+            if (DisplayMode == CalendarMode.Month && DisplayDate != null)
+            {
+                DateTime? d = DateTimeHelper.AddMonths(DateTimeHelper.DiscardDayTime(DisplayDate), -1);
+                if (d.HasValue)
+                {
+                    if (!LastSelectedDate.HasValue || DateTimeHelper.CompareYearMonth(LastSelectedDate.Value, d.Value) != 0)
+                    {
+                        LastSelectedDate = d.Value;
+                    }
+                    DisplayDate = d.Value;
+                }
+            }
+            else
+            {
+                if (DisplayMode == CalendarMode.Year)
+                {
+                    DateTime? d = DateTimeHelper.AddYears(new DateTime(SelectedMonth.Year, 1, 1), -1);
+
+                    if (d.HasValue)
+                    {
+                        SelectedMonth = d.Value;
+                    }
+                    else
+                    {
+                        SelectedMonth = DateTimeHelper.DiscardDayTime(DisplayDateRangeStart);
+                    }
+                }
+                else
+                {
+                    Debug.Assert(DisplayMode == CalendarMode.Decade, "DisplayMode should be Decade!");
+
+                    DateTime? d = DateTimeHelper.AddYears(new DateTime(SelectedYear.Year, 1, 1), -10);
+
+                    if (d.HasValue)
+                    {
+                        int decade = Math.Max(1, DateTimeHelper.DecadeOfDate(d.Value));
+                        SelectedYear = new DateTime(decade, 1, 1);
+                    }
+                    else
+                    {
+                        SelectedYear = DateTimeHelper.DiscardDayTime(DisplayDateRangeStart);
+                    }
+                }
+                UpdateMonths();
+            }
+        }
+        internal void OnNextClick()
+        {
+            if (DisplayMode == CalendarMode.Month && DisplayDate != null)
+            {
+                DateTime? d = DateTimeHelper.AddMonths(DateTimeHelper.DiscardDayTime(DisplayDate), 1);
+                if (d.HasValue)
+                {
+                    if (!LastSelectedDate.HasValue || DateTimeHelper.CompareYearMonth(LastSelectedDate.Value, d.Value) != 0)
+                    {
+                        LastSelectedDate = d.Value;
+                    }
+                    DisplayDate = d.Value;
+                }
+            }
+            else
+            {
+                if (DisplayMode == CalendarMode.Year)
+                {
+                    DateTime? d = DateTimeHelper.AddYears(new DateTime(SelectedMonth.Year, 1, 1), 1);
+
+                    if (d.HasValue)
+                    {
+                        SelectedMonth = d.Value;
+                    }
+                    else
+                    {
+                        SelectedMonth = DateTimeHelper.DiscardDayTime(DisplayDateRangeEnd);
+                    }
+                }
+                else
+                {
+                    Debug.Assert(DisplayMode == CalendarMode.Decade, "DisplayMode should be Decade");
+
+                    DateTime? d = DateTimeHelper.AddYears(new DateTime(SelectedYear.Year, 1, 1), 10);
+
+                    if (d.HasValue)
+                    {
+                        int decade = Math.Max(1, DateTimeHelper.DecadeOfDate(d.Value));
+                        SelectedYear = new DateTime(decade, 1, 1);
+                    }
+                    else
+                    {
+                        SelectedYear = DateTimeHelper.DiscardDayTime(DisplayDateRangeEnd);
+                    }
+                }
+                UpdateMonths();
+            }
+        }
+
+        /// <summary>
+        /// If the day is a trailing day, Update the DisplayDate.
+        /// </summary>
+        /// <param name="selectedDate">Inherited code: Requires comment.</param>
+        internal void OnDayClick(DateTime selectedDate)
+        {
+            Debug.Assert(DisplayMode == CalendarMode.Month, "DisplayMode should be Month!");
+            int i = DateTimeHelper.CompareYearMonth(selectedDate, DisplayDateInternal);
+
+            if (SelectionMode == CalendarSelectionMode.None)
+            {
+                LastSelectedDate = selectedDate;
+            }
+
+            if (i > 0)
+            {
+                OnNextClick();
+            }
+            else if (i < 0)
+            {
+                OnPreviousClick();
+            }
+        }
+        private void OnMonthClick()
+        {
+            CalendarItem monthControl = MonthControl;
+            if (monthControl != null && monthControl.YearView != null && monthControl.MonthView != null)
+            {
+                monthControl.YearView.IsVisible = false;
+                monthControl.MonthView.IsVisible = true;
+
+                if (!LastSelectedDate.HasValue || DateTimeHelper.CompareYearMonth(LastSelectedDate.Value, DisplayDate) != 0)
+                {
+                    LastSelectedDate = DisplayDate;
+                }
+
+                UpdateMonths();
+            }
+        }
+
+        public override string ToString()
+        {
+            if (SelectedDate != null)
+            {
+                return SelectedDate.Value.ToString(DateTimeHelper.GetCurrentDateFormat());
+            }
+            else
+            {
+                return string.Empty;
+            }
+        }
+
+        public event EventHandler<SelectionChangedEventArgs> SelectedDatesChanged;
+
+        /// <summary>
+        /// Occurs when the
+        /// <see cref="P:System.Windows.Controls.Calendar.DisplayDate" />
+        /// property is changed.
+        /// </summary>
+        /// <remarks>
+        /// This event occurs after DisplayDate is assigned its new value.
+        /// </remarks>
+        public event EventHandler<CalendarDateChangedEventArgs> DisplayDateChanged;
+
+        /// <summary>
+        /// Occurs when the
+        /// <see cref="P:System.Windows.Controls.Calendar.DisplayMode" />
+        /// property is changed.
+        /// </summary>
+        public event EventHandler<CalendarModeChangedEventArgs> DisplayModeChanged;
+
+        /// <summary>
+        /// Inherited code: Requires comment.
+        /// </summary>
+        internal event EventHandler<PointerReleasedEventArgs> DayButtonMouseUp;
+
+        /// <summary>
+        /// This method adds the days that were selected by Keyboard to the
+        /// SelectedDays Collection.
+        /// </summary>
+        private void AddSelection()
+        {
+            if (HoverEnd != null && HoverStart != null)
+            {
+                foreach (DateTime item in SelectedDates)
+                {
+                    RemovedItems.Add(item);
+                }
+
+                SelectedDates.ClearInternal();
+                // In keyboard selection, we are sure that the collection does
+                // not include any blackout days
+                SelectedDates.AddRange(HoverStart.Value, HoverEnd.Value);
+            }
+        }
+        private void ProcessSelection(bool shift, DateTime? lastSelectedDate, int? index)
+        {
+            if (SelectionMode == CalendarSelectionMode.None && lastSelectedDate != null)
+            {
+                OnDayClick(lastSelectedDate.Value);
+                return;
+            }
+            if (lastSelectedDate != null && IsValidKeyboardSelection(this, lastSelectedDate.Value))
+            {
+                if (SelectionMode == CalendarSelectionMode.SingleRange || SelectionMode == CalendarSelectionMode.MultipleRange)
+                {
+                    foreach (DateTime item in SelectedDates)
+                    {
+                        RemovedItems.Add(item);
+                    }
+                    SelectedDates.ClearInternal();
+                    if (shift)
+                    {
+                        CalendarDayButton b;
+                        _isShiftPressed = true;
+                        if (HoverStart == null)
+                        {
+                            if (LastSelectedDate != null)
+                            {
+                                HoverStart = LastSelectedDate;
+                            }
+                            else
+                            {
+                                if (DateTimeHelper.CompareYearMonth(DisplayDateInternal, DateTime.Today) == 0)
+                                {
+                                    HoverStart = DateTime.Today;
+                                }
+                                else
+                                {
+                                    HoverStart = DisplayDateInternal;
+                                }
+                            }
+
+                            b = FindDayButtonFromDay(HoverStart.Value);
+                            if (b != null)
+                            {
+                                HoverStartIndex = b.Index;
+                            }
+                        }
+                        // the index of the SelectedDate is always the last
+                        // selectedDate's index
+                        UnHighlightDays();
+                        // If we hit a BlackOutDay with keyboard we do not
+                        // update the HoverEnd
+                        CalendarDateRange range;
+
+                        if (DateTime.Compare(HoverStart.Value, lastSelectedDate.Value) < 0)
+                        {
+                            range = new CalendarDateRange(HoverStart.Value, lastSelectedDate.Value);
+                        }
+                        else
+                        {
+                            range = new CalendarDateRange(lastSelectedDate.Value, HoverStart.Value);
+                        }
+
+                        if (!BlackoutDates.ContainsAny(range))
+                        {
+                            HoverEnd = lastSelectedDate;
+
+                            if (index.HasValue)
+                            {
+                                HoverEndIndex += index;
+                            }
+                            else
+                            {
+                                // For Home, End, PageUp and PageDown Keys there
+                                // is no easy way to predict the index value
+                                b = FindDayButtonFromDay(HoverEndInternal.Value);
+
+                                if (b != null)
+                                {
+                                    HoverEndIndex = b.Index;
+                                }
+                            }
+                        }
+
+                        OnDayClick(HoverEnd.Value);
+                        HighlightDays();
+                    }
+                    else
+                    {
+                        HoverStart = lastSelectedDate;
+                        HoverEnd = lastSelectedDate;
+                        AddSelection();
+                        OnDayClick(lastSelectedDate.Value);
+                    }
+                }
+                else
+                {
+                    // ON CLEAR 
+                    LastSelectedDate = lastSelectedDate.Value;
+                    if (SelectedDates.Count > 0)
+                    {
+                        SelectedDates[0] = lastSelectedDate.Value;
+                    }
+                    else
+                    {
+                        SelectedDates.Add(lastSelectedDate.Value);
+                    }
+                    OnDayClick(lastSelectedDate.Value);
+                }
+            }
+        }
+
+        protected override void OnPointerReleased(PointerReleasedEventArgs e)
+        {
+            base.OnPointerReleased(e);
+            if (!HasFocusInternal && e.MouseButton == MouseButton.Left)
+            {
+                FocusManager.Instance.Focus(this);
+            }
+        }
+
+        internal void OnDayButtonMouseUp(PointerReleasedEventArgs e)
+        {
+            DayButtonMouseUp?.Invoke(this, e);
+        }
+
+        /// <summary>
+        /// Default mouse wheel handler for the calendar control.
+        /// </summary>
+        /// <param name="e">Mouse wheel event args.</param>
+        protected override void OnPointerWheelChanged(PointerWheelEventArgs e)
+        {
+            base.OnPointerWheelChanged(e);
+            if (!e.Handled)
+            {
+                CalendarExtensions.GetMetaKeyState(e.InputModifiers, out bool ctrl, out bool shift);
+
+                if (!ctrl)
+                {
+                    if (e.Delta.Y > 0)
+                    {
+                        ProcessPageUpKey(false);
+                    }
+                    else
+                    {
+                        ProcessPageDownKey(false);
+                    }
+                }
+                else
+                {
+                    if (e.Delta.Y > 0)
+                    {
+                        ProcessDownKey(ctrl, shift);
+                    }
+                    else
+                    {
+                        ProcessUpKey(ctrl, shift);
+                    }
+                }
+                e.Handled = true;
+            }
+        }
+        internal void Calendar_KeyDown(object sender, KeyEventArgs e)
+        {
+            Calendar c = sender as Calendar;
+            Debug.Assert(c != null, "c should not be null!");
+
+            if (!e.Handled && c.IsEnabled)
+            {
+                e.Handled = ProcessCalendarKey(e);
+            }
+        }
+        internal bool ProcessCalendarKey(KeyEventArgs e)
+        {
+            if (DisplayMode == CalendarMode.Month)
+            {
+                if (LastSelectedDate.HasValue && DisplayDateInternal != null)
+                {
+                    // If a blackout day is inactive, when clicked on it, the
+                    // previous inactive day which is not a blackout day can get
+                    // the focus.  In this case we should allow keyboard
+                    // functions on that inactive day
+                    if (DateTimeHelper.CompareYearMonth(LastSelectedDate.Value, DisplayDateInternal) != 0 && FocusButton != null && !FocusButton.IsInactive)
+                    {
+                        return true;
+                    }
+                }
+            }
+
+            // Some keys (e.g. Left/Right) need to be translated in RightToLeft mode
+            Key invariantKey = e.Key;  //InteractionHelper.GetLogicalKey(FlowDirection, e.Key);
+
+            CalendarExtensions.GetMetaKeyState(e.Modifiers, out bool ctrl, out bool shift);
+
+            switch (invariantKey)
+            {
+                case Key.Up:
+                    {
+                        ProcessUpKey(ctrl, shift);
+                        return true;
+                    }
+                case Key.Down:
+                    {
+                        ProcessDownKey(ctrl, shift);
+                        return true;
+                    }
+                case Key.Left:
+                    {
+                        ProcessLeftKey(shift);
+                        return true;
+                    }
+                case Key.Right:
+                    {
+                        ProcessRightKey(shift);
+                        return true;
+                    }
+                case Key.PageDown:
+                    {
+                        ProcessPageDownKey(shift);
+                        return true;
+                    }
+                case Key.PageUp:
+                    {
+                        ProcessPageUpKey(shift);
+                        return true;
+                    }
+                case Key.Home:
+                    {
+                        ProcessHomeKey(shift);
+                        return true;
+                    }
+                case Key.End:
+                    {
+                        ProcessEndKey(shift);
+                        return true;
+                    }
+                case Key.Enter:
+                case Key.Space:
+                    {
+                        return ProcessEnterKey();
+                    }
+            }
+            return false;
+        }
+        internal void ProcessUpKey(bool ctrl, bool shift)
+        {
+            switch (DisplayMode)
+            {
+                case CalendarMode.Month:
+                    {
+                        if (ctrl)
+                        {
+                            SelectedMonth = DisplayDateInternal;
+                            DisplayMode = CalendarMode.Year;
+                        }
+                        else
+                        {
+                            DateTime? selectedDate = DateTimeHelper.AddDays(LastSelectedDate.GetValueOrDefault(DateTime.Today), -ColumnsPerMonth);
+                            ProcessSelection(shift, selectedDate, -ColumnsPerMonth);
+                        }
+                        break;
+                    }
+                case CalendarMode.Year:
+                    {
+                        if (ctrl)
+                        {
+                            SelectedYear = SelectedMonth;
+                            DisplayMode = CalendarMode.Decade;
+                        }
+                        else
+                        {
+                            DateTime? selectedMonth = DateTimeHelper.AddMonths(_selectedMonth, -ColumnsPerYear);
+                            OnSelectedMonthChanged(selectedMonth);
+                        }
+                        break;
+                    }
+                case CalendarMode.Decade:
+                    {
+                        if (!ctrl)
+                        {
+                            DateTime? selectedYear = DateTimeHelper.AddYears(SelectedYear, -ColumnsPerYear);
+                            OnSelectedYearChanged(selectedYear);
+                        }
+                        break;
+                    }
+            }
+        }
+        internal void ProcessDownKey(bool ctrl, bool shift)
+        {
+            switch (DisplayMode)
+            {
+                case CalendarMode.Month:
+                    {
+                        if (!ctrl || shift)
+                        {
+                            DateTime? selectedDate = DateTimeHelper.AddDays(LastSelectedDate.GetValueOrDefault(DateTime.Today), ColumnsPerMonth);
+                            ProcessSelection(shift, selectedDate, ColumnsPerMonth);
+                        }
+                        break;
+                    }
+                case CalendarMode.Year:
+                    {
+                        if (ctrl)
+                        {
+                            DisplayDate = SelectedMonth;
+                            DisplayMode = CalendarMode.Month;
+                        }
+                        else
+                        {
+                            DateTime? selectedMonth = DateTimeHelper.AddMonths(_selectedMonth, ColumnsPerYear);
+                            OnSelectedMonthChanged(selectedMonth);
+                        }
+                        break;
+                    }
+                case CalendarMode.Decade:
+                    {
+                        if (ctrl)
+                        {
+                            SelectedMonth = SelectedYear;
+                            DisplayMode = CalendarMode.Year;
+                        }
+                        else
+                        {
+                            DateTime? selectedYear = DateTimeHelper.AddYears(SelectedYear, ColumnsPerYear);
+                            OnSelectedYearChanged(selectedYear);
+                        }
+                        break;
+                    }
+            }
+        }
+        internal void ProcessLeftKey(bool shift)
+        {
+            switch (DisplayMode)
+            {
+                case CalendarMode.Month:
+                    {
+                        DateTime? selectedDate = DateTimeHelper.AddDays(LastSelectedDate.GetValueOrDefault(DateTime.Today), -1);
+                        ProcessSelection(shift, selectedDate, -1);
+                        break;
+                    }
+                case CalendarMode.Year:
+                    {
+                        DateTime? selectedMonth = DateTimeHelper.AddMonths(_selectedMonth, -1);
+                        OnSelectedMonthChanged(selectedMonth);
+                        break;
+                    }
+                case CalendarMode.Decade:
+                    {
+                        DateTime? selectedYear = DateTimeHelper.AddYears(SelectedYear, -1);
+                        OnSelectedYearChanged(selectedYear);
+                        break;
+                    }
+            }
+        }
+        internal void ProcessRightKey(bool shift)
+        {
+            switch (DisplayMode)
+            {
+                case CalendarMode.Month:
+                    {
+                        DateTime? selectedDate = DateTimeHelper.AddDays(LastSelectedDate.GetValueOrDefault(DateTime.Today), 1);
+                        ProcessSelection(shift, selectedDate, 1);
+                        break;
+                    }
+                case CalendarMode.Year:
+                    {
+                        DateTime? selectedMonth = DateTimeHelper.AddMonths(_selectedMonth, 1);
+                        OnSelectedMonthChanged(selectedMonth);
+                        break;
+                    }
+                case CalendarMode.Decade:
+                    {
+                        DateTime? selectedYear = DateTimeHelper.AddYears(SelectedYear, 1);
+                        OnSelectedYearChanged(selectedYear);
+                        break;
+                    }
+            }
+        }
+        private bool ProcessEnterKey()
+        {
+            switch (DisplayMode)
+            {
+                case CalendarMode.Year:
+                    {
+                        DisplayDate = SelectedMonth;
+                        DisplayMode = CalendarMode.Month;
+                        return true;
+                    }
+                case CalendarMode.Decade:
+                    {
+                        SelectedMonth = SelectedYear;
+                        DisplayMode = CalendarMode.Year;
+                        return true;
+                    }
+            }
+            return false;
+        }
+        internal void ProcessHomeKey(bool shift)
+        {
+            switch (DisplayMode)
+            {
+                case CalendarMode.Month:
+                    {
+                        // REMOVE_RTM: Not all types of calendars start with Day1. If Non-Gregorian is supported check this:
+                        DateTime? selectedDate = new DateTime(DisplayDateInternal.Year, DisplayDateInternal.Month, 1);
+                        ProcessSelection(shift, selectedDate, null);
+                        break;
+                    }
+                case CalendarMode.Year:
+                    {
+                        DateTime selectedMonth = new DateTime(_selectedMonth.Year, 1, 1);
+                        OnSelectedMonthChanged(selectedMonth);
+                        break;
+                    }
+                case CalendarMode.Decade:
+                    {
+                        DateTime? selectedYear = new DateTime(DateTimeHelper.DecadeOfDate(SelectedYear), 1, 1);
+                        OnSelectedYearChanged(selectedYear);
+                        break;
+                    }
+            }
+        }
+        internal void ProcessEndKey(bool shift)
+        {
+            switch (DisplayMode)
+            {
+                case CalendarMode.Month:
+                    {
+                        if (DisplayDate != null)
+                        {
+                            DateTime? selectedDate = new DateTime(DisplayDateInternal.Year, DisplayDateInternal.Month, 1);
+
+                            if (DateTimeHelper.CompareYearMonth(DateTime.MaxValue, selectedDate.Value) > 0)
+                            {
+                                // since DisplayDate is not equal to
+                                // DateTime.MaxValue we are sure selectedDate is\
+                                // not null
+                                selectedDate = DateTimeHelper.AddMonths(selectedDate.Value, 1).Value;
+                                selectedDate = DateTimeHelper.AddDays(selectedDate.Value, -1).Value;
+                            }
+                            else
+                            {
+                                selectedDate = DateTime.MaxValue;
+                            }
+                            ProcessSelection(shift, selectedDate, null);
+                        }
+                        break;
+                    }
+                case CalendarMode.Year:
+                    {
+                        DateTime selectedMonth = new DateTime(_selectedMonth.Year, 12, 1);
+                        OnSelectedMonthChanged(selectedMonth);
+                        break;
+                    }
+                case CalendarMode.Decade:
+                    {
+                        DateTime? selectedYear = new DateTime(DateTimeHelper.EndOfDecade(SelectedYear), 1, 1);
+                        OnSelectedYearChanged(selectedYear);
+                        break;
+                    }
+            }
+        }
+        internal void ProcessPageDownKey(bool shift)
+        {
+            switch (DisplayMode)
+            {
+                case CalendarMode.Month:
+                    {
+                        DateTime? selectedDate = DateTimeHelper.AddMonths(LastSelectedDate.GetValueOrDefault(DateTime.Today), 1);
+                        ProcessSelection(shift, selectedDate, null);
+                        break;
+                    }
+                case CalendarMode.Year:
+                    {
+                        DateTime? selectedMonth = DateTimeHelper.AddYears(_selectedMonth, 1);
+                        OnSelectedMonthChanged(selectedMonth);
+                        break;
+                    }
+                case CalendarMode.Decade:
+                    {
+                        DateTime? selectedYear = DateTimeHelper.AddYears(SelectedYear, 10);
+                        OnSelectedYearChanged(selectedYear);
+                        break;
+                    }
+            }
+        }
+        internal void ProcessPageUpKey(bool shift)
+        {
+            switch (DisplayMode)
+            {
+                case CalendarMode.Month:
+                    {
+                        DateTime? selectedDate = DateTimeHelper.AddMonths(LastSelectedDate.GetValueOrDefault(DateTime.Today), -1);
+                        ProcessSelection(shift, selectedDate, null);
+                        break;
+                    }
+                case CalendarMode.Year:
+                    {
+                        DateTime? selectedMonth = DateTimeHelper.AddYears(_selectedMonth, -1);
+                        OnSelectedMonthChanged(selectedMonth);
+                        break;
+                    }
+                case CalendarMode.Decade:
+                    {
+                        DateTime? selectedYear = DateTimeHelper.AddYears(SelectedYear, -10);
+                        OnSelectedYearChanged(selectedYear);
+                        break;
+                    }
+            }
+        }
+        private void Calendar_KeyUp(object sender, KeyEventArgs e)
+        {
+            if (!e.Handled && (e.Key == Key.LeftShift || e.Key == Key.RightShift))
+            {
+                ProcessShiftKeyUp();
+            }
+        }
+        internal void ProcessShiftKeyUp()
+        {
+            if (_isShiftPressed && (SelectionMode == CalendarSelectionMode.SingleRange || SelectionMode == CalendarSelectionMode.MultipleRange))
+            {
+                AddSelection();
+                _isShiftPressed = false;
+            }
+        }
+
+        protected override void OnGotFocus(GotFocusEventArgs e)
+        {
+            base.OnGotFocus(e);
+            HasFocusInternal = true;
+
+            switch (DisplayMode)
+            {
+                case CalendarMode.Month:
+                    {
+                        DateTime focusDate;
+                        if (LastSelectedDate.HasValue && DateTimeHelper.CompareYearMonth(DisplayDateInternal, LastSelectedDate.Value) == 0)
+                        {
+                            focusDate = LastSelectedDate.Value;
+                        }
+                        else
+                        {
+                            focusDate = DisplayDate;
+                            LastSelectedDate = DisplayDate;
+                        }
+                        Debug.Assert(focusDate != null, "focusDate should not be null!");
+                        FocusButton = FindDayButtonFromDay(focusDate);
+
+                        if (FocusButton != null)
+                        {
+                            FocusButton.IsCurrent = true;
+                        }
+                        break;
+                    }
+                case CalendarMode.Year:
+                case CalendarMode.Decade:
+                    {
+                        if (this.FocusCalendarButton != null)
+                        {
+                            FocusCalendarButton.IsCalendarButtonFocused = true;
+                        }
+                        break;
+                    }
+            }
+        }
+        protected override void OnLostFocus(RoutedEventArgs e)
+        {
+            base.OnLostFocus(e);
+            HasFocusInternal = false;
+
+            switch (DisplayMode)
+            {
+                case CalendarMode.Month:
+                    {
+                        if (FocusButton != null)
+                        {
+                            FocusButton.IsCurrent = false;
+                        }
+                        break;
+                    }
+                case CalendarMode.Year:
+                case CalendarMode.Decade:
+                    {
+                        if (FocusCalendarButton != null)
+                        {
+                            FocusCalendarButton.IsCalendarButtonFocused = false;
+                        }
+                        break;
+                    }
+            }
+        }
+        /// <summary>
+        ///  Called when the IsEnabled property changes.
+        /// </summary>
+        /// <param name="e">Property changed args.</param>
+        private void OnIsEnabledChanged(AvaloniaPropertyChangedEventArgs e)
+        {
+            Debug.Assert(e.NewValue is bool, "NewValue should be a boolean!");
+            bool isEnabled = (bool)e.NewValue;
+
+            if (MonthControl != null)
+            {
+                MonthControl.UpdateDisabled(isEnabled);
+            }
+        }
+
+        static Calendar()
+        {
+            IsEnabledProperty.Changed.AddClassHandler<Calendar>(x => x.OnIsEnabledChanged);
+            FirstDayOfWeekProperty.Changed.AddClassHandler<Calendar>(x => x.OnFirstDayOfWeekChanged);
+            IsTodayHighlightedProperty.Changed.AddClassHandler<Calendar>(x => x.OnIsTodayHighlightedChanged);
+            DisplayModeProperty.Changed.AddClassHandler<Calendar>(x => x.OnDisplayModePropertyChanged);
+            SelectionModeProperty.Changed.AddClassHandler<Calendar>(x => x.OnSelectionModeChanged);
+            SelectedDateProperty.Changed.AddClassHandler<Calendar>(x => x.OnSelectedDateChanged);
+            DisplayDateProperty.Changed.AddClassHandler<Calendar>(x => x.OnDisplayDateChanged);
+            DisplayDateStartProperty.Changed.AddClassHandler<Calendar>(x => x.OnDisplayDateStartChanged);
+            DisplayDateEndProperty.Changed.AddClassHandler<Calendar>(x => x.OnDisplayDateEndChanged);
+        }
+
+        /// <summary>
+        /// Initializes a new instance of the
+        /// <see cref="T:System.Windows.Controls.Calendar" /> class.
+        /// </summary>
+        public Calendar()
+        {
+            UpdateDisplayDate(this, this.DisplayDate, DateTime.MinValue);
+            BlackoutDates = new CalendarBlackoutDatesCollection(this);
+            SelectedDates = new SelectedDatesCollection(this);
+            RemovedItems = new Collection<DateTime>();
+        }
+
+        private const string PART_ElementRoot = "Root";
+        private const string PART_ElementMonth = "CalendarItem";
+        /// <summary>
+        /// Builds the visual tree for the
+        /// <see cref="T:System.Windows.Controls.Calendar" /> when a new
+        /// template is applied.
+        /// </summary>
+        protected override void OnTemplateApplied(TemplateAppliedEventArgs e)
+        {
+            base.OnTemplateApplied(e);
+
+            Root = e.NameScope.Find<Panel>(PART_ElementRoot);
+
+            SelectedMonth = DisplayDate;
+            SelectedYear = DisplayDate;
+
+            if (Root != null)
+            {
+                CalendarItem month = e.NameScope.Find<CalendarItem>(PART_ElementMonth);
+
+                if (month != null)
+                {
+                    month.Owner = this;
+                }
+            }
+
+            LayoutUpdated += Calendar_SizeChanged;
+            KeyDown += Calendar_KeyDown;
+            KeyUp += Calendar_KeyUp;
+        }
+
+    }
+}

+ 215 - 0
src/Avalonia.Controls/Calendar/CalendarBlackoutDatesCollection.cs

@@ -0,0 +1,215 @@
+// (c) Copyright Microsoft Corporation.
+// This source is subject to the Microsoft Public License (Ms-PL).
+// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
+// All other rights reserved.
+
+using Avalonia.Threading;
+using System;
+using System.Collections.ObjectModel;
+using System.Linq;
+using System.Threading;
+
+namespace Avalonia.Controls.Primitives
+{
+    public sealed class CalendarBlackoutDatesCollection : ObservableCollection<CalendarDateRange>
+    {
+        /// <summary>
+        /// The Calendar whose dates this object represents.
+        /// </summary>
+        private Calendar _owner;
+        
+        /// <summary>
+        /// Initializes a new instance of the
+        /// <see cref="T:Avalonia.Controls.Primitives.CalendarBlackoutDatesCollection" />
+        /// class.
+        /// </summary>
+        /// <param name="owner">
+        /// The <see cref="T:Avalonia.Controls.Calendar" /> whose dates
+        /// this object represents.
+        /// </param>
+        public CalendarBlackoutDatesCollection(Calendar owner)
+        {
+            _owner = owner ?? throw new ArgumentNullException(nameof(owner));
+        }
+
+        /// <summary>
+        /// Adds all dates before <see cref="P:System.DateTime.Today" /> to the
+        /// collection.
+        /// </summary>
+        public void AddDatesInPast()
+        {
+            Add(new CalendarDateRange(DateTime.MinValue, DateTime.Today.AddDays(-1)));
+        }
+
+        /// <summary>
+        /// Returns a value that represents whether this collection contains the
+        /// specified date.
+        /// </summary>
+        /// <param name="date">The date to search for.</param>
+        /// <returns>
+        /// True if the collection contains the specified date; otherwise,
+        /// false.
+        /// </returns>
+        public bool Contains(DateTime date)
+        {
+            int count = Count;
+            for (int i = 0; i < count; i++)
+            {
+                if (DateTimeHelper.InRange(date, this[i]))
+                {
+                    return true;
+                }
+            }
+            return false;
+        }
+
+        /// <summary>
+        /// Returns a value that represents whether this collection contains the
+        /// specified range of dates.
+        /// </summary>
+        /// <param name="start">The start of the date range.</param>
+        /// <param name="end">The end of the date range.</param>
+        /// <returns>
+        /// True if all dates in the range are contained in the collection;
+        /// otherwise, false.
+        /// </returns>
+        public bool Contains(DateTime start, DateTime end)
+        {
+            DateTime rangeStart;
+            DateTime rangeEnd;
+
+            if (DateTime.Compare(end, start) > -1)
+            {
+                rangeStart = DateTimeHelper.DiscardTime(start).Value;
+                rangeEnd = DateTimeHelper.DiscardTime(end).Value;
+            }
+            else
+            {
+                rangeStart = DateTimeHelper.DiscardTime(end).Value;
+                rangeEnd = DateTimeHelper.DiscardTime(start).Value;
+            }
+
+            int count = Count;
+            for (int i = 0; i < count; i++)
+            {
+                CalendarDateRange range = this[i];
+                if (DateTime.Compare(range.Start, rangeStart) == 0 && DateTime.Compare(range.End, rangeEnd) == 0)
+                {
+                    return true;
+                }
+            }
+            return false;
+        }
+
+        /// <summary>
+        /// Returns a value that represents whether this collection contains any
+        /// date in the specified range.
+        /// </summary>
+        /// <param name="range">The range of dates to search for.</param>
+        /// <returns>
+        /// True if any date in the range is contained in the collection;
+        /// otherwise, false.
+        /// </returns>
+        public bool ContainsAny(CalendarDateRange range)
+        {
+            return this.Any(r => r.ContainsAny(range));
+        }
+
+        /// <summary>
+        /// Removes all items from the collection.
+        /// </summary>
+        /// <remarks>
+        /// This implementation raises the CollectionChanged event.
+        /// </remarks>
+        protected override void ClearItems()
+        {
+            EnsureValidThread();
+
+            base.ClearItems();
+            _owner.UpdateMonths();
+        }
+
+        /// <summary>
+        /// Inserts an item into the collection at the specified index.
+        /// </summary>
+        /// <param name="index">
+        /// The zero-based index at which item should be inserted.
+        /// </param>
+        /// <param name="item">The object to insert.</param>
+        /// <remarks>
+        /// This implementation raises the CollectionChanged event.
+        /// </remarks>
+        protected override void InsertItem(int index, CalendarDateRange item)
+        {
+            EnsureValidThread();
+
+            if (!IsValid(item))
+            {
+                throw new ArgumentOutOfRangeException("Value is not valid.");
+            }
+
+            base.InsertItem(index, item);
+            _owner.UpdateMonths();
+        }
+
+        /// <summary>
+        /// Removes the item at the specified index of the collection.
+        /// </summary>
+        /// <param name="index">
+        /// The zero-based index of the element to remove.
+        /// </param>
+        /// <remarks>
+        /// This implementation raises the CollectionChanged event.
+        /// </remarks>
+        protected override void RemoveItem(int index)
+        {
+            EnsureValidThread();
+
+            base.RemoveItem(index);
+            _owner.UpdateMonths();
+        }
+
+        /// <summary>
+        /// Replaces the element at the specified index.
+        /// </summary>
+        /// <param name="index">
+        /// The zero-based index of the element to replace.
+        /// </param>
+        /// <param name="item">
+        /// The new value for the element at the specified index.
+        /// </param>
+        /// <remarks>
+        /// This implementation raises the CollectionChanged event.
+        /// </remarks>
+        protected override void SetItem(int index, CalendarDateRange item)
+        {
+            EnsureValidThread();
+
+            if (!IsValid(item))
+            {
+                throw new ArgumentOutOfRangeException("Value is not valid.");
+            }
+
+            base.SetItem(index, item);
+            _owner.UpdateMonths();
+        }
+        
+        private bool IsValid(CalendarDateRange item)
+        {
+            foreach (DateTime day in _owner.SelectedDates)
+            {
+                if (DateTimeHelper.InRange(day, item))
+                {
+                    return false;
+                }
+            }
+
+            return true;
+        }
+        
+        private void EnsureValidThread()
+        {
+            Dispatcher.UIThread.VerifyAccess();
+        }
+    }
+}

+ 193 - 0
src/Avalonia.Controls/Calendar/CalendarButton.cs

@@ -0,0 +1,193 @@
+// (c) Copyright Microsoft Corporation.
+// This source is subject to the Microsoft Public License (Ms-PL).
+// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
+// All other rights reserved.
+
+using Avalonia.Input;
+using System;
+
+namespace Avalonia.Controls.Primitives
+{
+    /// <summary>
+    /// Represents a button on a
+    /// <see cref="T:Avalonia.Controls.Calendar" />.
+    /// </summary>
+    public sealed class CalendarButton : Button
+    {
+        /// <summary>
+        /// A value indicating whether the button is focused.
+        /// </summary>
+        private bool _isCalendarButtonFocused;
+
+        /// <summary>
+        /// A value indicating whether the button is inactive.
+        /// </summary>
+        private bool _isInactive;
+
+        /// <summary>
+        /// A value indicating whether the button is selected.
+        /// </summary>
+        private bool _isSelected;
+
+        /// <summary>
+        /// Initializes a new instance of the
+        /// <see cref="T:Avalonia.Controls.Primitives.CalendarButton" />
+        /// class.
+        /// </summary>
+        public CalendarButton()
+            : base()
+        {
+            Content = DateTimeHelper.GetCurrentDateFormat().AbbreviatedMonthNames[0];
+        }
+
+        /// <summary>
+        /// Gets or sets the Calendar associated with this button.
+        /// </summary>
+        internal Calendar Owner { get; set; }
+
+        /// <summary>
+        /// Gets or sets a value indicating whether the button is focused.
+        /// </summary>
+        internal bool IsCalendarButtonFocused
+        {
+            get { return _isCalendarButtonFocused; }
+            set
+            {
+                if (_isCalendarButtonFocused != value)
+                {
+                    _isCalendarButtonFocused = value;
+                    SetPseudoClasses();
+                }
+            }
+        }
+
+        /// <summary>
+        /// Gets or sets a value indicating whether the button is inactive.
+        /// </summary>
+        internal bool IsInactive
+        {
+            get { return _isInactive; }
+            set
+            {
+                if (_isInactive != value)
+                {
+                    _isInactive = value;
+                    SetPseudoClasses();
+                }
+            }
+        }
+
+        /// <summary>
+        /// Gets or sets a value indicating whether the button is selected.
+        /// </summary>
+        internal bool IsSelected
+        {
+            get { return _isSelected; }
+            set
+            {
+                if (_isSelected != value)
+                {
+                    _isSelected = value;
+                    SetPseudoClasses();
+                }
+            }
+        }
+
+        /// <summary>
+        /// Builds the visual tree for the
+        /// <see cref="T:System.Windows.Controls.Primitives.CalendarButton" />
+        /// when a new template is applied.
+        /// </summary>
+        protected override void OnTemplateApplied(TemplateAppliedEventArgs e)
+        {
+            base.OnTemplateApplied(e);
+            SetPseudoClasses();
+        }
+        
+        /// <summary>
+        /// Sets PseudoClasses based on current state.
+        /// </summary>
+        private void SetPseudoClasses()
+        {
+            PseudoClasses.Set(":selected", IsSelected);
+            PseudoClasses.Set(":inactive", IsInactive);
+            PseudoClasses.Set(":btnfocused", IsCalendarButtonFocused && IsEnabled);
+        }
+
+        /// <summary>
+        /// Occurs when the left mouse button is pressed (or when the tip of the
+        /// stylus touches the tablet PC) while the mouse pointer is over a
+        /// UIElement.
+        /// </summary>
+        public event EventHandler<PointerPressedEventArgs> CalendarLeftMouseButtonDown;
+
+        /// <summary>
+        /// Occurs when the left mouse button is released (or the tip of the
+        /// stylus is removed from the tablet PC) while the mouse (or the
+        /// stylus) is over a UIElement (or while a UIElement holds mouse
+        /// capture).
+        /// </summary>
+        public event EventHandler<PointerReleasedEventArgs> CalendarLeftMouseButtonUp;
+
+        /// <summary>
+        /// Provides class handling for the MouseLeftButtonDown event that
+        /// occurs when the left mouse button is pressed while the mouse pointer
+        /// is over this control.
+        /// </summary>
+        /// <param name="e">The event data. </param>
+        /// <exception cref="System.ArgumentNullException">
+        /// e is a null reference (Nothing in Visual Basic).
+        /// </exception>
+        /// <remarks>
+        /// This method marks the MouseLeftButtonDown event as handled by
+        /// setting the MouseButtonEventArgs.Handled property of the event data
+        /// to true when the button is enabled and its ClickMode is not set to
+        /// Hover.  Since this method marks the MouseLeftButtonDown event as
+        /// handled in some situations, you should use the Click event instead
+        /// to detect a button click.
+        /// </remarks>
+        protected override void OnPointerPressed(PointerPressedEventArgs e)
+        {
+            base.OnPointerPressed(e);
+            if (e.MouseButton == MouseButton.Left)
+                CalendarLeftMouseButtonDown?.Invoke(this, e);
+        }
+
+        /// <summary>
+        /// Provides handling for the MouseLeftButtonUp event that occurs when
+        /// the left mouse button is released while the mouse pointer is over
+        /// this control. 
+        /// </summary>
+        /// <param name="e">The event data.</param>
+        /// <exception cref="System.ArgumentNullException">
+        /// e is a null reference (Nothing in Visual Basic).
+        /// </exception>
+        /// <remarks>
+        /// This method marks the MouseLeftButtonUp event as handled by setting
+        /// the MouseButtonEventArgs.Handled property of the event data to true
+        /// when the button is enabled and its ClickMode is not set to Hover.
+        /// Since this method marks the MouseLeftButtonUp event as handled in
+        /// some situations, you should use the Click event instead to detect a
+        /// button click.
+        /// </remarks>
+        protected override void OnPointerReleased(PointerReleasedEventArgs e)
+        {
+            base.OnPointerReleased(e);
+            if (e.MouseButton == MouseButton.Left)
+                CalendarLeftMouseButtonUp?.Invoke(this, e);
+        }
+        
+        /// <summary>
+        /// We need to simulate the MouseLeftButtonUp event for the
+        /// CalendarButton that stays in Pressed state after MouseCapture is
+        /// released since there is no actual MouseLeftButtonUp event for the
+        /// release.
+        /// </summary>
+        /// <param name="e">Event arguments.</param>
+        internal void SendMouseLeftButtonUp(PointerReleasedEventArgs e)
+        {
+            e.Handled = false;
+            base.OnPointerReleased(e);
+        }
+    }
+}

+ 79 - 0
src/Avalonia.Controls/Calendar/CalendarDateRange.cs

@@ -0,0 +1,79 @@
+// (c) Copyright Microsoft Corporation.
+// This source is subject to the Microsoft Public License (Ms-PL).
+// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
+// All other rights reserved.
+
+using System;
+using System.Diagnostics;
+
+namespace Avalonia.Controls
+{
+    public sealed class CalendarDateRange
+    {
+        /// <summary>
+        /// Gets the first date in the represented range.
+        /// </summary>
+        /// <value>The first date in the represented range.</value>
+        public DateTime Start { get; private set; }
+
+        /// <summary>
+        /// Gets the last date in the represented range.
+        /// </summary>
+        /// <value>The last date in the represented range.</value>
+        public DateTime End { get; private set; }
+
+        /// <summary>
+        /// Initializes a new instance of the
+        /// <see cref="T:System.Windows.Controls.CalendarDateRange" /> class
+        /// with a single date.
+        /// </summary>
+        /// <param name="day">The date to be represented by the range.</param>
+        public CalendarDateRange(DateTime day)
+        {
+            Start = day;
+            End = day;
+        }
+
+        /// <summary>
+        /// Initializes a new instance of the
+        /// <see cref="T:System.Windows.Controls.CalendarDateRange" /> class
+        /// with a range of dates.
+        /// </summary>
+        /// <param name="start">
+        /// The start of the range to be represented.
+        /// </param>
+        /// <param name="end">The end of the range to be represented.</param>
+        public CalendarDateRange(DateTime start, DateTime end)
+        {
+            if (DateTime.Compare(end, start) >= 0)
+            {
+                Start = start;
+                End = end;
+            }
+            else
+            {
+                // Always use the start for ranges on the same day
+                Start = start;
+                End = start;
+            }
+        }
+
+        /// <summary>
+        /// Returns true if any day in the given DateTime range is contained in
+        /// the current CalendarDateRange.
+        /// </summary>
+        /// <param name="range">Inherited code: Requires comment 1.</param>
+        /// <returns>Inherited code: Requires comment 2.</returns>
+        internal bool ContainsAny(CalendarDateRange range)
+        {
+            Debug.Assert(range != null, "range should not be null!");
+
+            int start = DateTime.Compare(Start, range.Start);
+
+            // Check if any part of the supplied range is contained by this
+            // range or if the supplied range completely covers this range.
+            return (start <= 0 && DateTime.Compare(End, range.Start) >= 0) ||
+                (start >= 0 && DateTime.Compare(Start, range.End) <= 0);
+        }
+    }
+}

+ 253 - 0
src/Avalonia.Controls/Calendar/CalendarDayButton.cs

@@ -0,0 +1,253 @@
+// (c) Copyright Microsoft Corporation.
+// This source is subject to the Microsoft Public License (Ms-PL).
+// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
+// All other rights reserved.
+
+using Avalonia.Input;
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Text;
+
+namespace Avalonia.Controls.Primitives
+{
+    public sealed class CalendarDayButton : Button
+    {
+        /// <summary>
+        /// Default content for the CalendarDayButton.
+        /// </summary>
+        private const int DefaultContent = 1;
+
+        private bool _isCurrent;
+        private bool _ignoringMouseOverState;
+        private bool _isBlackout;
+        private bool _isToday;
+        private bool _isInactive;
+        private bool _isSelected;
+
+        /// <summary>
+        /// Initializes a new instance of the
+        /// <see cref="T:Avalonia.Controls.Primitives.CalendarDayButton" />
+        /// class.
+        /// </summary>
+        public CalendarDayButton()
+            : base()
+        {
+            //Focusable = false;
+            Content = DefaultContent.ToString(CultureInfo.CurrentCulture);
+        }
+
+        /// <summary>
+        /// Gets or sets the Calendar associated with this button.
+        /// </summary>
+        internal Calendar Owner { get; set; }
+        internal int Index { get; set; }
+
+        /// <summary>
+        /// Gets or sets a value indicating whether the button is the focused
+        /// element on the Calendar control.
+        /// </summary>
+        internal bool IsCurrent
+        {
+            get { return _isCurrent; }
+            set
+            {
+                if (_isCurrent != value)
+                {
+                    _isCurrent = value;
+                    SetPseudoClasses();
+                }
+            }
+        }
+
+        /// <summary>
+        /// Ensure the button is not in the MouseOver state.
+        /// </summary>
+        /// <remarks>
+        /// If a button is in the MouseOver state when a Popup is closed (as is
+        /// the case when you select a date in the DatePicker control), it will
+        /// continue to think it's in the mouse over state even when the Popup
+        /// opens again and it's not.  This method is used to forcibly clear the
+        /// state by changing the CommonStates state group.
+        /// </remarks>
+        internal void IgnoreMouseOverState()
+        {
+            // TODO: Investigate whether this needs to be done by changing the
+            // state everytime we change any state, or if it can be done once
+            // to properly reset the control.
+
+            _ignoringMouseOverState = false;
+
+            // If the button thinks it's in the MouseOver state (which can
+            // happen when a Popup is closed before the button can change state)
+            // we will override the state so it shows up as normal.
+            if (IsPointerOver)
+            {
+                _ignoringMouseOverState = true;
+                SetPseudoClasses();
+            }
+        }
+
+        /// <summary>
+        /// Gets or sets a value indicating whether this is a blackout date.
+        /// </summary>
+        internal bool IsBlackout
+        {
+            get { return _isBlackout; }
+            set
+            {
+                if (_isBlackout != value)
+                {
+                    _isBlackout = value;
+                    SetPseudoClasses();
+                }
+            }
+        }
+
+        /// <summary>
+        /// Gets or sets a value indicating whether this button represents
+        /// today.
+        /// </summary>
+        internal bool IsToday
+        {
+            get { return _isToday; }
+            set
+            {
+                if (_isToday != value)
+                {
+                    _isToday = value;
+                    SetPseudoClasses();
+                }
+            }
+        }
+        /// <summary>
+        /// Gets or sets a value indicating whether the button is inactive.
+        /// </summary>
+        internal bool IsInactive
+        {
+            get { return _isInactive; }
+            set
+            {
+                if (_isInactive != value)
+                {
+                    _isInactive = value;
+                    SetPseudoClasses();
+                }
+            }
+        }
+
+        /// <summary>
+        /// Gets or sets a value indicating whether the button is selected.
+        /// </summary>
+        internal bool IsSelected
+        {
+            get { return _isSelected; }
+            set
+            {
+                if (_isSelected != value)
+                {
+                    _isSelected = value;
+                    SetPseudoClasses();
+                }
+            }
+        }
+
+        protected override void OnTemplateApplied(TemplateAppliedEventArgs e)
+        {
+            base.OnTemplateApplied(e);
+            SetPseudoClasses();
+        }
+        private void SetPseudoClasses()
+        {
+            if (_ignoringMouseOverState)
+            {
+                PseudoClasses.Set(":pressed", IsPressed);
+                PseudoClasses.Set(":disabled", !IsEnabled);
+            }
+
+            PseudoClasses.Set(":selected", IsSelected);
+            PseudoClasses.Set(":inactive", IsInactive);
+            PseudoClasses.Set(":today", IsToday);
+            PseudoClasses.Set(":blackout", IsBlackout);
+            PseudoClasses.Set(":dayfocused", IsCurrent && IsEnabled);
+        }
+
+        /// <summary>
+        /// Occurs when the left mouse button is pressed (or when the tip of the
+        /// stylus touches the tablet PC) while the mouse pointer is over a
+        /// UIElement.
+        /// </summary>
+        public event EventHandler<PointerPressedEventArgs> CalendarDayButtonMouseDown;
+
+        /// <summary>
+        /// Occurs when the left mouse button is released (or the tip of the
+        /// stylus is removed from the tablet PC) while the mouse (or the
+        /// stylus) is over a UIElement (or while a UIElement holds mouse
+        /// capture).
+        /// </summary>
+        public event EventHandler<PointerReleasedEventArgs> CalendarDayButtonMouseUp;
+
+        /// <summary>
+        /// Provides class handling for the MouseLeftButtonDown event that
+        /// occurs when the left mouse button is pressed while the mouse pointer
+        /// is over this control.
+        /// </summary>
+        /// <param name="e">The event data. </param>
+        /// <exception cref="System.ArgumentNullException">
+        /// e is a null reference (Nothing in Visual Basic).
+        /// </exception>
+        /// <remarks>
+        /// This method marks the MouseLeftButtonDown event as handled by
+        /// setting the MouseButtonEventArgs.Handled property of the event data
+        /// to true when the button is enabled and its ClickMode is not set to
+        /// Hover.  Since this method marks the MouseLeftButtonDown event as
+        /// handled in some situations, you should use the Click event instead
+        /// to detect a button click.
+        /// </remarks>
+        protected override void OnPointerPressed(PointerPressedEventArgs e)
+        {
+            base.OnPointerPressed(e);
+
+            if (e.MouseButton == MouseButton.Left)
+                CalendarDayButtonMouseDown?.Invoke(this, e);
+        }
+
+        /// <summary>
+        /// Provides handling for the MouseLeftButtonUp event that occurs when
+        /// the left mouse button is released while the mouse pointer is over
+        /// this control. 
+        /// </summary>
+        /// <param name="e">The event data.</param>
+        /// <exception cref="System.ArgumentNullException">
+        /// e is a null reference (Nothing in Visual Basic).
+        /// </exception>
+        /// <remarks>
+        /// This method marks the MouseLeftButtonUp event as handled by setting
+        /// the MouseButtonEventArgs.Handled property of the event data to true
+        /// when the button is enabled and its ClickMode is not set to Hover.
+        /// Since this method marks the MouseLeftButtonUp event as handled in
+        /// some situations, you should use the Click event instead to detect a
+        /// button click.
+        /// </remarks>
+        protected override void OnPointerReleased(PointerReleasedEventArgs e)
+        {
+            base.OnPointerReleased(e);
+
+            if (e.MouseButton == MouseButton.Left)
+                CalendarDayButtonMouseUp?.Invoke(this, e);
+        }
+
+        /// <summary>
+        /// We need to simulate the MouseLeftButtonUp event for the
+        /// CalendarDayButton that stays in Pressed state after MouseCapture is
+        /// released since there is no actual MouseLeftButtonUp event for the
+        /// release.
+        /// </summary>
+        /// <param name="e">Event arguments.</param>
+        internal void SendMouseLeftButtonUp(PointerReleasedEventArgs e)
+        {
+            e.Handled = false;
+            base.OnPointerReleased(e);
+        }
+    }
+}

+ 21 - 0
src/Avalonia.Controls/Calendar/CalendarExtensions.cs

@@ -0,0 +1,21 @@
+// (c) Copyright Microsoft Corporation.
+// This source is subject to the Microsoft Public License (Ms-PL).
+// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
+// All other rights reserved.
+
+using System;
+using System.Collections.Generic;
+using Avalonia.Input;
+using System.Diagnostics;
+
+namespace Avalonia.Controls.Primitives
+{
+    internal static class CalendarExtensions
+    {
+        public static void GetMetaKeyState(InputModifiers modifiers, out bool ctrl, out bool shift)
+        {
+            ctrl = (modifiers & InputModifiers.Control) == InputModifiers.Control;
+            shift = (modifiers & InputModifiers.Shift) == InputModifiers.Shift;
+        }
+    }
+}

+ 1264 - 0
src/Avalonia.Controls/Calendar/CalendarItem.cs

@@ -0,0 +1,1264 @@
+// (c) Copyright Microsoft Corporation.
+// This source is subject to the Microsoft Public License (Ms-PL).
+// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
+// All other rights reserved.
+
+using Avalonia.Data;
+using Avalonia.Input;
+using Avalonia.Interactivity;
+using Avalonia.Media;
+using System;
+using System.Diagnostics;
+using System.Globalization;
+
+namespace Avalonia.Controls.Primitives
+{
+    /// <summary>
+    /// Represents the currently displayed month or year on a
+    /// <see cref="T:Avalonia.Controls.Calendar" />.
+    /// </summary>
+    public sealed class CalendarItem : TemplatedControl
+    {
+        /// <summary>
+        /// The number of days per week.
+        /// </summary>
+        private const int NumberOfDaysPerWeek = 7;
+
+        private const string PART_ElementHeaderButton = "HeaderButton";
+        private const string PART_ElementPreviousButton = "PreviousButton";
+        private const string PART_ElementNextButton = "NextButton";
+        private const string PART_ElementMonthView = "MonthView";
+        private const string PART_ElementYearView = "YearView";
+
+        private Button _headerButton;
+        private Button _nextButton;
+        private Button _previousButton;
+        private Grid _monthView;
+        private Grid _yearView;
+        private ITemplate<IControl> _dayTitleTemplate;
+        private CalendarButton _lastCalendarButton;
+        private CalendarDayButton _lastCalendarDayButton;
+        
+        private DateTime _currentMonth;
+        private bool _isMouseLeftButtonDown = false;
+        private bool _isMouseLeftButtonDownYearView = false;
+        private bool _isControlPressed = false;
+
+        private System.Globalization.Calendar _calendar = new System.Globalization.GregorianCalendar();
+
+        private PointerPressedEventArgs _downEventArg;
+        private PointerPressedEventArgs _downEventArgYearView;
+
+        internal Calendar Owner { get; set; }
+        internal CalendarDayButton CurrentButton { get; set; }
+
+        public static StyledProperty<IBrush> HeaderBackgroundProperty = Calendar.HeaderBackgroundProperty.AddOwner<CalendarItem>();
+        public IBrush HeaderBackground
+        {
+            get { return GetValue(HeaderBackgroundProperty); }
+            set { SetValue(HeaderBackgroundProperty, value); }
+        }
+        public static readonly DirectProperty<CalendarItem, ITemplate<IControl>> DayTitleTemplateProperty =
+                AvaloniaProperty.RegisterDirect<CalendarItem, ITemplate<IControl>>(
+                    nameof(DayTitleTemplate),
+                    o => o.DayTitleTemplate,
+                    (o,v) => o.DayTitleTemplate = v,
+                    defaultBindingMode: BindingMode.OneTime);
+        public ITemplate<IControl> DayTitleTemplate
+        {
+            get { return _dayTitleTemplate; }
+            set { SetAndRaise(DayTitleTemplateProperty, ref _dayTitleTemplate, value); }
+        }
+
+        /// <summary>
+        /// Gets the button that allows switching between month mode, year mode,
+        /// and decade mode. 
+        /// </summary>
+        internal Button HeaderButton
+        {
+            get { return _headerButton; }
+            private set
+            {
+                if (_headerButton != null)
+                    _headerButton.Click -= HeaderButton_Click;
+
+                _headerButton = value;
+
+                if (_headerButton != null)
+                {
+                    _headerButton.Click += HeaderButton_Click;
+                    _headerButton.Focusable = false;
+                }
+            }
+        }
+        /// <summary>
+        /// Gets the button that displays the next page of the calendar when it
+        /// is clicked.
+        /// </summary>
+        internal Button NextButton
+        {
+            get { return _nextButton; }
+            private set
+            {
+                if (_nextButton != null)
+                    _nextButton.Click -= NextButton_Click;
+
+                _nextButton = value;
+
+                if (_nextButton != null)
+                {
+                    // If the user does not provide a Content value in template,
+                    // we provide a helper text that can be used in
+                    // Accessibility this text is not shown on the UI, just used
+                    // for Accessibility purposes
+                    if (_nextButton.Content == null)
+                    {
+                        _nextButton.Content = "next button";
+                    }
+
+                    _nextButton.IsVisible = true;
+                    _nextButton.Click += NextButton_Click;
+                    _nextButton.Focusable = false;
+                }
+            }
+        }
+        /// <summary>
+        /// Gets the button that displays the previous page of the calendar when
+        /// it is clicked.
+        /// </summary>
+        internal Button PreviousButton
+        {
+            get { return _previousButton; }
+            private set
+            {
+                if (_previousButton != null)
+                    _previousButton.Click -= PreviousButton_Click;
+
+                _previousButton = value;
+
+                if (_previousButton != null)
+                {
+                    // If the user does not provide a Content value in template,
+                    // we provide a helper text that can be used in
+                    // Accessibility this text is not shown on the UI, just used
+                    // for Accessibility purposes
+                    if (_previousButton.Content == null)
+                    {
+                        _previousButton.Content = "previous button";
+                    }
+
+                    _previousButton.IsVisible = true;
+                    _previousButton.Click += PreviousButton_Click;
+                    _previousButton.Focusable = false;
+                }
+            }
+        }
+
+        /// <summary>
+        /// Gets the Grid that hosts the content when in month mode.
+        /// </summary>
+        internal Grid MonthView
+        {
+            get { return _monthView; }
+            private set
+            {
+                if (_monthView != null)
+                    _monthView.PointerLeave -= MonthView_MouseLeave;
+
+                _monthView = value;
+
+                if (_monthView != null)
+                    _monthView.PointerLeave += MonthView_MouseLeave;
+            }
+        }
+        /// <summary>
+        /// Gets the Grid that hosts the content when in year or decade mode.
+        /// </summary>
+        internal Grid YearView
+        {
+            get { return _yearView; }
+            private set
+            {
+                if (_yearView != null)
+                    _yearView.PointerLeave -= YearView_MouseLeave;
+
+                _yearView = value;
+
+                if (_yearView != null)
+                    _yearView.PointerLeave += YearView_MouseLeave;
+            }
+        }
+
+        private void PopulateGrids()
+        {
+            if (MonthView != null)
+            {
+                for (int i = 0; i < Calendar.RowsPerMonth; i++)
+                {
+                    if (_dayTitleTemplate != null)
+                    {
+                        var cell = _dayTitleTemplate.Build();
+                        cell.DataContext = string.Empty;
+                        cell.SetValue(Grid.RowProperty, 0);
+                        cell.SetValue(Grid.ColumnProperty, i);
+                        MonthView.Children.Add(cell);
+                    }
+                }
+
+                for (int i = 1; i < Calendar.RowsPerMonth; i++)
+                {
+                    for (int j = 0; j < Calendar.ColumnsPerMonth; j++)
+                    {
+                        CalendarDayButton cell = new CalendarDayButton();
+
+                        if (Owner != null)
+                        {
+                            cell.Owner = Owner;
+                        }
+                        cell.SetValue(Grid.RowProperty, i);
+                        cell.SetValue(Grid.ColumnProperty, j);
+                        cell.CalendarDayButtonMouseDown += Cell_MouseLeftButtonDown;
+                        cell.CalendarDayButtonMouseUp += Cell_MouseLeftButtonUp;
+                        cell.PointerEnter += Cell_MouseEnter;
+                        cell.PointerLeave += Cell_MouseLeave;
+                        cell.Click += Cell_Click;
+                        MonthView.Children.Add(cell);
+                    }
+                }
+            }
+
+            if (YearView != null)
+            {
+                CalendarButton month;
+                for (int i = 0; i < Calendar.RowsPerYear; i++)
+                {
+                    for (int j = 0; j < Calendar.ColumnsPerYear; j++)
+                    {
+                        month = new CalendarButton();
+
+                        if (Owner != null)
+                        {
+                            month.Owner = Owner;
+                        }
+                        month.SetValue(Grid.RowProperty, i);
+                        month.SetValue(Grid.ColumnProperty, j);
+                        month.CalendarLeftMouseButtonDown += Month_CalendarButtonMouseDown;
+                        month.CalendarLeftMouseButtonUp += Month_CalendarButtonMouseUp;
+                        month.PointerEnter += Month_MouseEnter;
+                        month.PointerLeave += Month_MouseLeave;
+                        YearView.Children.Add(month);
+                    }
+                }
+            }
+        }
+
+        /// <summary>
+        /// Builds the visual tree for the
+        /// <see cref="T:System.Windows.Controls.Primitives.CalendarItem" />
+        /// when a new template is applied.
+        /// </summary>
+        protected override void OnTemplateApplied(TemplateAppliedEventArgs e)
+        {
+            base.OnTemplateApplied(e);
+            
+            HeaderButton = e.NameScope.Find<Button>(PART_ElementHeaderButton);
+            PreviousButton = e.NameScope.Find<Button>(PART_ElementPreviousButton);
+            NextButton = e.NameScope.Find<Button>(PART_ElementNextButton);
+            MonthView = e.NameScope.Find<Grid>(PART_ElementMonthView);
+            YearView = e.NameScope.Find<Grid>(PART_ElementYearView);
+            
+            if (Owner != null)
+            {
+                UpdateDisabled(Owner.IsEnabled);
+            }
+
+            PopulateGrids();
+
+            if (MonthView != null && YearView != null)
+            {
+                if (Owner != null)
+                {
+                    Owner.SelectedMonth = Owner.DisplayDateInternal;
+                    Owner.SelectedYear = Owner.DisplayDateInternal;
+
+                    if (Owner.DisplayMode == CalendarMode.Year)
+                    {
+                        UpdateYearMode();
+                    }
+                    else if (Owner.DisplayMode == CalendarMode.Decade)
+                    {
+                        UpdateDecadeMode();
+                    }
+
+                    if (Owner.DisplayMode == CalendarMode.Month)
+                    {
+                        UpdateMonthMode();
+                        MonthView.IsVisible = true;
+                        YearView.IsVisible = false;
+                    }
+                    else
+                    {
+                        YearView.IsVisible = true;
+                        MonthView.IsVisible = false;
+                    }
+                }
+                else
+                {
+                    UpdateMonthMode();
+                    MonthView.IsVisible = true;
+                    YearView.IsVisible = false;
+                }
+            }
+        }
+
+        private void SetDayTitles()
+        {
+            for (int childIndex = 0; childIndex < Calendar.ColumnsPerMonth; childIndex++)
+            {
+                var daytitle = MonthView.Children[childIndex];
+                if (daytitle != null)
+                {
+                    if (Owner != null)
+                    {
+                        daytitle.DataContext = DateTimeHelper.GetCurrentDateFormat().ShortestDayNames[(childIndex + (int)Owner.FirstDayOfWeek) % NumberOfDaysPerWeek];
+                    }
+                    else
+                    {
+                        daytitle.DataContext = DateTimeHelper.GetCurrentDateFormat().ShortestDayNames[(childIndex + (int)DateTimeHelper.GetCurrentDateFormat().FirstDayOfWeek) % NumberOfDaysPerWeek];
+                    }
+                }
+            }
+        }
+        /// <summary>
+        /// How many days of the previous month need to be displayed.
+        /// </summary>
+        private int PreviousMonthDays(DateTime firstOfMonth)
+        {
+            DayOfWeek day = _calendar.GetDayOfWeek(firstOfMonth);
+            int i;
+
+            if (Owner != null)
+            {
+                i = ((day - Owner.FirstDayOfWeek + NumberOfDaysPerWeek) % NumberOfDaysPerWeek);
+            }
+            else
+            {
+                i = ((day - DateTimeHelper.GetCurrentDateFormat().FirstDayOfWeek + NumberOfDaysPerWeek) % NumberOfDaysPerWeek);
+            }
+
+            if (i == 0)
+            {
+                return NumberOfDaysPerWeek;
+            }
+            else
+            {
+                return i;
+            }
+        }
+
+        internal void UpdateMonthMode()
+        {
+            if (Owner != null)
+            {
+                Debug.Assert(Owner.DisplayDate != null, "The Owner Calendar's DisplayDate should not be null!");
+                _currentMonth = Owner.DisplayDateInternal;
+            }
+            else
+            {
+                _currentMonth = DateTime.Today;
+            }
+
+            if (_currentMonth != null)
+            {
+                SetMonthModeHeaderButton();
+                SetMonthModePreviousButton(_currentMonth);
+                SetMonthModeNextButton(_currentMonth);
+
+                if (MonthView != null)
+                {
+                    SetDayTitles();
+                    SetCalendarDayButtons(_currentMonth);
+                }
+            }
+        }
+        private void SetMonthModeHeaderButton()
+        {
+            if (HeaderButton != null)
+            {
+                if (Owner != null)
+                {
+                    HeaderButton.Content = Owner.DisplayDateInternal.ToString("Y", DateTimeHelper.GetCurrentDateFormat());
+                    HeaderButton.IsEnabled = true;
+                }
+                else
+                {
+                    HeaderButton.Content = DateTime.Today.ToString("Y", DateTimeHelper.GetCurrentDateFormat());
+                }
+            }
+        }
+        private void SetMonthModeNextButton(DateTime firstDayOfMonth)
+        {
+            if (Owner != null && NextButton != null)
+            {
+                // DisplayDate is equal to DateTime.MaxValue
+                if (DateTimeHelper.CompareYearMonth(firstDayOfMonth, DateTime.MaxValue) == 0)
+                {
+                    NextButton.IsEnabled = false;
+                }
+                else
+                {
+                    // Since we are sure DisplayDate is not equal to
+                    // DateTime.MaxValue, it is safe to use AddMonths  
+                    DateTime firstDayOfNextMonth = _calendar.AddMonths(firstDayOfMonth, 1);
+                    NextButton.IsEnabled = (DateTimeHelper.CompareDays(Owner.DisplayDateRangeEnd, firstDayOfNextMonth) > -1);
+                }
+            }
+        }
+        private void SetMonthModePreviousButton(DateTime firstDayOfMonth)
+        {
+            if (Owner != null && PreviousButton != null)
+            {
+                PreviousButton.IsEnabled = (DateTimeHelper.CompareDays(Owner.DisplayDateRangeStart, firstDayOfMonth) < 0);
+            }
+        }
+
+        private void SetButtonState(CalendarDayButton childButton, DateTime dateToAdd)
+        {
+            if (Owner != null)
+            {
+                childButton.Opacity = 1;
+
+                // If the day is outside the DisplayDateStart/End boundary, do
+                // not show it
+                if (DateTimeHelper.CompareDays(dateToAdd, Owner.DisplayDateRangeStart) < 0 || DateTimeHelper.CompareDays(dateToAdd, Owner.DisplayDateRangeEnd) > 0)
+                {
+                    childButton.IsEnabled = false;
+                    childButton.IsToday = false;
+                    childButton.IsSelected = false;
+                    childButton.Opacity = 0;
+                }
+                else
+                {
+                    // SET IF THE DAY IS SELECTABLE OR NOT
+                    if (Owner.BlackoutDates.Contains(dateToAdd))
+                    {
+                        childButton.IsBlackout = true;
+                    }
+                    else
+                    {
+                        childButton.IsBlackout = false;
+                    }
+                    childButton.IsEnabled = true;
+
+                    // SET IF THE DAY IS INACTIVE OR NOT: set if the day is a
+                    // trailing day or not
+                    childButton.IsInactive = (DateTimeHelper.CompareYearMonth(dateToAdd, Owner.DisplayDateInternal) != 0);
+
+                    // SET IF THE DAY IS TODAY OR NOT
+                    childButton.IsToday = (Owner.IsTodayHighlighted && dateToAdd == DateTime.Today);
+
+                    // SET IF THE DAY IS SELECTED OR NOT
+                    childButton.IsSelected = false;
+                    foreach (DateTime item in Owner.SelectedDates)
+                    {
+                        // Since we should be comparing the Date values not
+                        // DateTime values, we can't use
+                        // Owner.SelectedDates.Contains(dateToAdd) directly
+                        childButton.IsSelected |= (DateTimeHelper.CompareDays(dateToAdd, item) == 0);
+                    }
+
+                    // SET THE FOCUS ELEMENT
+                    if (Owner.LastSelectedDate != null)
+                    {
+                        if (DateTimeHelper.CompareDays(Owner.LastSelectedDate.Value, dateToAdd) == 0)
+                        {
+                            if (Owner.FocusButton != null)
+                            {
+                                Owner.FocusButton.IsCurrent = false;
+                            }
+                            Owner.FocusButton = childButton;
+                            if (Owner.HasFocusInternal)
+                            {
+                                Owner.FocusButton.IsCurrent = true;
+                            }
+                        }
+                        else
+                        {
+                            childButton.IsCurrent = false;
+                        }
+                    }
+                }
+            }
+        }
+        private void SetCalendarDayButtons(DateTime firstDayOfMonth)
+        {
+            int lastMonthToDisplay = PreviousMonthDays(firstDayOfMonth);
+            DateTime dateToAdd;
+
+            if (DateTimeHelper.CompareYearMonth(firstDayOfMonth, DateTime.MinValue) > 0)
+            {
+                // DisplayDate is not equal to DateTime.MinValue we can subtract
+                // days from the DisplayDate
+                dateToAdd = _calendar.AddDays(firstDayOfMonth, -lastMonthToDisplay);
+            }
+            else
+            {
+                dateToAdd = firstDayOfMonth;
+            }
+
+            if (Owner != null && Owner.HoverEnd != null && Owner.HoverStart != null)
+            {
+                Owner.HoverEndIndex = null;
+                Owner.HoverStartIndex = null;
+            }
+
+            int count = Calendar.RowsPerMonth * Calendar.ColumnsPerMonth;
+
+            for (int childIndex = Calendar.ColumnsPerMonth; childIndex < count; childIndex++)
+            {
+                CalendarDayButton childButton = MonthView.Children[childIndex] as CalendarDayButton;
+                Debug.Assert(childButton != null, "childButton should not be null!");
+
+                childButton.Index = childIndex;
+                SetButtonState(childButton, dateToAdd);
+
+                // Update the indexes of hoverStart and hoverEnd
+                if (Owner != null && Owner.HoverEnd != null && Owner.HoverStart != null)
+                {
+                    if (DateTimeHelper.CompareDays(dateToAdd, Owner.HoverEnd.Value) == 0)
+                    {
+                        Owner.HoverEndIndex = childIndex;
+                    }
+
+                    if (DateTimeHelper.CompareDays(dateToAdd, Owner.HoverStart.Value) == 0)
+                    {
+                        Owner.HoverStartIndex = childIndex;
+                    }
+                }
+
+                //childButton.Focusable = false;
+                childButton.Content = dateToAdd.Day.ToString(DateTimeHelper.GetCurrentDateFormat());
+                childButton.DataContext = dateToAdd;
+
+                if (DateTime.Compare((DateTime)DateTimeHelper.DiscardTime(DateTime.MaxValue), dateToAdd) > 0)
+                {
+                    // Since we are sure DisplayDate is not equal to
+                    // DateTime.MaxValue, it is safe to use AddDays 
+                    dateToAdd = _calendar.AddDays(dateToAdd, 1);
+                }
+                else
+                {
+                    // DisplayDate is equal to the DateTime.MaxValue, so there
+                    // are no trailing days
+                    childIndex++;
+                    for (int i = childIndex; i < count; i++)
+                    {
+                        childButton = MonthView.Children[i] as CalendarDayButton;
+                        Debug.Assert(childButton != null, "childButton should not be null!");
+                        // button needs a content to occupy the necessary space
+                        // for the content presenter
+                        childButton.Content = i.ToString(DateTimeHelper.GetCurrentDateFormat());
+                        childButton.IsEnabled = false;
+                        childButton.Opacity = 0;
+                    }
+                    return;
+                }
+            }
+
+            // If the HoverStart or HoverEndInternal could not be found on the
+            // DisplayMonth set the values of the HoverStartIndex or
+            // HoverEndIndex to be the first or last day indexes on the current
+            // month
+            if (Owner != null && Owner.HoverStart.HasValue && Owner.HoverEndInternal.HasValue)
+            {
+                if (!Owner.HoverEndIndex.HasValue)
+                {
+                    if (DateTimeHelper.CompareDays(Owner.HoverEndInternal.Value, Owner.HoverStart.Value) > 0)
+                    {
+                        Owner.HoverEndIndex = Calendar.ColumnsPerMonth * Calendar.RowsPerMonth - 1;
+                    }
+                    else
+                    {
+                        Owner.HoverEndIndex = Calendar.ColumnsPerMonth;
+                    }
+                }
+
+                if (!Owner.HoverStartIndex.HasValue)
+                {
+                    if (DateTimeHelper.CompareDays(Owner.HoverEndInternal.Value, Owner.HoverStart.Value) > 0)
+                    {
+                        Owner.HoverStartIndex = Calendar.ColumnsPerMonth;
+                    }
+                    else
+                    {
+                        Owner.HoverStartIndex = Calendar.ColumnsPerMonth * Calendar.RowsPerMonth - 1;
+                    }
+                }
+            }
+        }
+
+        internal void UpdateYearMode()
+        {
+            if (Owner != null)
+            {
+                Debug.Assert(Owner.SelectedMonth != null, "The Owner Calendar's SelectedMonth should not be null!");
+                _currentMonth = (DateTime)Owner.SelectedMonth;
+            }
+            else
+            {
+                _currentMonth = DateTime.Today;
+            }
+
+            if (_currentMonth != null)
+            {
+                SetYearModeHeaderButton();
+                SetYearModePreviousButton();
+                SetYearModeNextButton();
+
+                if (YearView != null)
+                {
+                    SetMonthButtonsForYearMode();
+                }
+            }
+        }
+        private void SetYearModeHeaderButton()
+        {
+            if (HeaderButton != null)
+            {
+                HeaderButton.IsEnabled = true;
+                HeaderButton.Content = _currentMonth.Year.ToString(DateTimeHelper.GetCurrentDateFormat());
+            }
+        }
+        private void SetYearModePreviousButton()
+        {
+            if (Owner != null && PreviousButton != null)
+            {
+                PreviousButton.IsEnabled = (Owner.DisplayDateRangeStart.Year != _currentMonth.Year);
+            }
+        }
+        private void SetYearModeNextButton()
+        {
+            if (Owner != null && NextButton != null)
+            {
+                NextButton.IsEnabled = (Owner.DisplayDateRangeEnd.Year != _currentMonth.Year);
+            }
+        }
+
+        private void SetMonthButtonsForYearMode()
+        {
+            int count = 0;
+            foreach (object child in YearView.Children)
+            {
+                CalendarButton childButton = child as CalendarButton;
+                Debug.Assert(childButton != null, "childButton should not be null!");
+                // There should be no time component. Time is 12:00 AM
+                DateTime day = new DateTime(_currentMonth.Year, count + 1, 1);
+                childButton.DataContext = day;
+
+                childButton.Content = DateTimeHelper.GetCurrentDateFormat().AbbreviatedMonthNames[count];
+                childButton.IsVisible = true;
+
+                if (Owner != null)
+                {
+                    if (day.Year == _currentMonth.Year && day.Month == _currentMonth.Month && day.Day == _currentMonth.Day)
+                    {
+                        Owner.FocusCalendarButton = childButton;
+                        childButton.IsCalendarButtonFocused = Owner.HasFocusInternal;
+                    }
+                    else
+                    {
+                        childButton.IsCalendarButtonFocused = false;
+                    }
+
+                    Debug.Assert(Owner.DisplayDateInternal != null, "The Owner Calendar's DisplayDateInternal should not be null!");
+                    childButton.IsSelected = (DateTimeHelper.CompareYearMonth(day, Owner.DisplayDateInternal) == 0);
+
+                    if (DateTimeHelper.CompareYearMonth(day, Owner.DisplayDateRangeStart) < 0 || DateTimeHelper.CompareYearMonth(day, Owner.DisplayDateRangeEnd) > 0)
+                    {
+                        childButton.IsEnabled = false;
+                        childButton.Opacity = 0;
+                    }
+                    else
+                    {
+                        childButton.IsEnabled = true;
+                        childButton.Opacity = 1;
+                    }
+                }
+
+                childButton.IsInactive = false;
+                count++;
+            }
+        }
+        internal void UpdateDecadeMode()
+        {
+            DateTime selectedYear;
+
+            if (Owner != null)
+            {
+                Debug.Assert(Owner.SelectedYear != null, "The owning Calendar's selected year should not be null!");
+                selectedYear = Owner.SelectedYear;
+                _currentMonth = (DateTime)Owner.SelectedMonth;
+            }
+            else
+            {
+                _currentMonth = DateTime.Today;
+                selectedYear = DateTime.Today;
+            }
+
+            if (_currentMonth != null)
+            {
+                int decade = DateTimeHelper.DecadeOfDate(selectedYear);
+                int decadeEnd = DateTimeHelper.EndOfDecade(selectedYear);
+
+                SetDecadeModeHeaderButton(decade, decadeEnd);
+                SetDecadeModePreviousButton(decade);
+                SetDecadeModeNextButton(decadeEnd);
+
+                if (YearView != null)
+                {
+                    SetYearButtons(decade, decadeEnd);
+                }
+            }
+        }
+        internal void UpdateYearViewSelection(CalendarButton calendarButton)
+        {
+            if (Owner != null && calendarButton != null && calendarButton.DataContext != null)
+            {
+                Owner.FocusCalendarButton.IsCalendarButtonFocused = false;
+                Owner.FocusCalendarButton = calendarButton;
+                calendarButton.IsCalendarButtonFocused = Owner.HasFocusInternal;
+
+                if (Owner.DisplayMode == CalendarMode.Year)
+                {
+                    Owner.SelectedMonth = (DateTime)calendarButton.DataContext;
+                }
+                else
+                {
+                    Owner.SelectedYear = (DateTime)calendarButton.DataContext;
+                }
+            }
+        }
+
+        private void SetYearButtons(int decade, int decadeEnd)
+        {
+            int year;
+            int count = -1;
+            foreach (object child in YearView.Children)
+            {
+                CalendarButton childButton = child as CalendarButton;
+                Debug.Assert(childButton != null, "childButton should not be null!");
+                year = decade + count;
+
+                if (year <= DateTime.MaxValue.Year && year >= DateTime.MinValue.Year)
+                {
+                    // There should be no time component. Time is 12:00 AM
+                    DateTime day = new DateTime(year, 1, 1);
+                    childButton.DataContext = day;
+                    childButton.Content = year.ToString(DateTimeHelper.GetCurrentDateFormat());
+                    childButton.IsVisible = true;
+
+                    if (Owner != null)
+                    {
+                        if (year == Owner.SelectedYear.Year)
+                        {
+                            Owner.FocusCalendarButton = childButton;
+                            childButton.IsCalendarButtonFocused = Owner.HasFocusInternal;
+                        }
+                        else
+                        {
+                            childButton.IsCalendarButtonFocused = false;
+                        }
+                        childButton.IsSelected = (Owner.DisplayDate.Year == year);
+
+                        if (year < Owner.DisplayDateRangeStart.Year || year > Owner.DisplayDateRangeEnd.Year)
+                        {
+                            childButton.IsEnabled = false;
+                            childButton.Opacity = 0;
+                        }
+                        else
+                        {
+                            childButton.IsEnabled = true;
+                            childButton.Opacity = 1;
+                        }
+                    }
+
+                    // SET IF THE YEAR IS INACTIVE OR NOT: set if the year is a
+                    // trailing year or not
+                    childButton.IsInactive = (year < decade || year > decadeEnd);
+                }
+                else
+                {
+                    childButton.IsEnabled = false;
+                    childButton.Opacity = 0;
+                }
+
+                count++;
+            }
+        }
+        private void SetDecadeModeHeaderButton(int decade, int decadeEnd)
+        {
+            if (HeaderButton != null)
+            {
+                HeaderButton.Content = decade.ToString(CultureInfo.CurrentCulture) + "-" + decadeEnd.ToString(CultureInfo.CurrentCulture);
+                HeaderButton.IsEnabled = false;
+            }
+        }
+        private void SetDecadeModeNextButton(int decadeEnd)
+        {
+            if (Owner != null && NextButton != null)
+            {
+                NextButton.IsEnabled = (Owner.DisplayDateRangeEnd.Year > decadeEnd);
+            }
+        }
+        private void SetDecadeModePreviousButton(int decade)
+        {
+            if (Owner != null && PreviousButton != null)
+            {
+                PreviousButton.IsEnabled = (decade > Owner.DisplayDateRangeStart.Year);
+            }
+        }
+
+        internal void HeaderButton_Click(object sender, RoutedEventArgs e)
+        {
+            if (Owner != null)
+            {
+                if (!Owner.HasFocusInternal)
+                {
+                    Owner.Focus();
+                }
+                Button b = sender as Button;
+                DateTime d;
+
+                if (b.IsEnabled)
+                {
+                    if (Owner.DisplayMode == CalendarMode.Month)
+                    {
+                        if (Owner.DisplayDate != null)
+                        {
+                            d = Owner.DisplayDateInternal;
+                            Owner.SelectedMonth = new DateTime(d.Year, d.Month, 1);
+                        }
+                        Owner.DisplayMode = CalendarMode.Year;
+                    }
+                    else
+                    {
+                        Debug.Assert(Owner.DisplayMode == CalendarMode.Year, "The Owner Calendar's DisplayMode should be Year!");
+
+                        if (Owner.SelectedMonth != null)
+                        {
+                            d = Owner.SelectedMonth;
+                            Owner.SelectedYear = new DateTime(d.Year, d.Month, 1);
+                        }
+                        Owner.DisplayMode = CalendarMode.Decade;
+                    }
+                }
+            }
+        }
+        internal void PreviousButton_Click(object sender, RoutedEventArgs e)
+        {
+            if (Owner != null)
+            {
+                if (!Owner.HasFocusInternal)
+                {
+                    Owner.Focus();
+                }
+
+                Button b = sender as Button;
+                if (b.IsEnabled)
+                {
+                    Owner.OnPreviousClick();
+                }
+            }
+        }
+        internal void NextButton_Click(object sender, RoutedEventArgs e)
+        {
+            if (Owner != null)
+            {
+                if (!Owner.HasFocusInternal)
+                {
+                    Owner.Focus();
+                }
+                Button b = sender as Button;
+
+                if (b.IsEnabled)
+                {
+                    Owner.OnNextClick();
+                }
+            }
+        }
+
+        internal void Cell_MouseEnter(object sender, PointerEventArgs e)
+        {
+            if (Owner != null)
+            {
+                CalendarDayButton b = sender as CalendarDayButton;
+                if (_isMouseLeftButtonDown && b != null && b.IsEnabled && !b.IsBlackout)
+                {
+                    // Update the states of all buttons to be selected starting
+                    // from HoverStart to b
+                    switch (Owner.SelectionMode)
+                    {
+                        case CalendarSelectionMode.SingleDate:
+                            {
+                                DateTime selectedDate = (DateTime)b.DataContext;
+                                Owner.DatePickerDisplayDateFlag = true;
+                                if (Owner.SelectedDates.Count == 0)
+                                {
+                                    Owner.SelectedDates.Add(selectedDate);
+                                }
+                                else
+                                {
+                                    Owner.SelectedDates[0] = selectedDate;
+                                }
+                                return;
+                            }
+                        case CalendarSelectionMode.SingleRange:
+                        case CalendarSelectionMode.MultipleRange:
+                            {
+                                Debug.Assert(b.DataContext != null, "The DataContext should not be null!");
+                                Owner.UnHighlightDays();
+                                Owner.HoverEndIndex = b.Index;
+                                Owner.HoverEnd = (DateTime)b.DataContext;
+                                // Update the States of the buttons
+                                Owner.HighlightDays();
+                                return;
+                            }
+                    }
+                }
+            }
+        }
+        internal void Cell_MouseLeave(object sender, PointerEventArgs e)
+        {
+            if (_isMouseLeftButtonDown)
+            {
+                CalendarDayButton b = sender as CalendarDayButton;
+                // The button is in Pressed state. Change the state to normal.
+                if (e.Device.Captured == b)
+                    e.Device.Capture(null);
+                // null check is added for unit tests
+                if (_downEventArg != null)
+                {
+                    var arg =
+                        new PointerReleasedEventArgs()
+                        {
+                            Device = _downEventArg.Device,
+                            MouseButton = _downEventArg.MouseButton,
+                            Handled = _downEventArg.Handled,
+                            InputModifiers = _downEventArg.InputModifiers,
+                            Route = _downEventArg.Route,
+                            Source = _downEventArg.Source
+                        };
+
+                    b.SendMouseLeftButtonUp(arg);
+                }
+                _lastCalendarDayButton = b;
+            }
+        }
+        internal void Cell_MouseLeftButtonDown(object sender, PointerPressedEventArgs e)
+        {
+            if (Owner != null)
+            {
+                if (!Owner.HasFocusInternal)
+                {
+                    Owner.Focus();
+                }
+
+                bool ctrl, shift;
+                CalendarExtensions.GetMetaKeyState(e.InputModifiers, out ctrl, out shift);
+                CalendarDayButton b = sender as CalendarDayButton;
+
+                if (b != null)
+                {
+                    _isControlPressed = ctrl;
+                    if (b.IsEnabled && !b.IsBlackout)
+                    {
+                        DateTime selectedDate = (DateTime)b.DataContext;
+                        Debug.Assert(selectedDate != null, "selectedDate should not be null!");
+                        _isMouseLeftButtonDown = true;
+                        // null check is added for unit tests
+                        if (e != null)
+                        {
+                            _downEventArg = e;
+                        }
+
+                        switch (Owner.SelectionMode)
+                        {
+                            case CalendarSelectionMode.None:
+                                {
+                                    return;
+                                }
+                            case CalendarSelectionMode.SingleDate:
+                                {
+                                    Owner.DatePickerDisplayDateFlag = true;
+                                    if (Owner.SelectedDates.Count == 0)
+                                    {
+                                        Owner.SelectedDates.Add(selectedDate);
+                                    }
+                                    else
+                                    {
+                                        Owner.SelectedDates[0] = selectedDate;
+                                    }
+                                    return;
+                                }
+                            case CalendarSelectionMode.SingleRange:
+                                {
+                                    // Set the start or end of the selection
+                                    // range
+                                    if (shift)
+                                    {
+                                        Owner.UnHighlightDays();
+                                        Owner.HoverEnd = selectedDate;
+                                        Owner.HoverEndIndex = b.Index;
+                                        Owner.HighlightDays();
+                                    }
+                                    else
+                                    {
+                                        Owner.UnHighlightDays();
+                                        Owner.HoverStart = selectedDate;
+                                        Owner.HoverStartIndex = b.Index;
+                                    }
+                                    return;
+                                }
+                            case CalendarSelectionMode.MultipleRange:
+                                {
+                                    if (shift)
+                                    {
+                                        if (!ctrl)
+                                        {
+                                            // clear the list, set the states to
+                                            // default
+                                            foreach (DateTime item in Owner.SelectedDates)
+                                            {
+                                                Owner.RemovedItems.Add(item);
+                                            }
+                                            Owner.SelectedDates.ClearInternal();
+                                        }
+                                        Owner.HoverEnd = selectedDate;
+                                        Owner.HoverEndIndex = b.Index;
+                                        Owner.HighlightDays();
+                                    }
+                                    else
+                                    {
+                                        if (!ctrl)
+                                        {
+                                            // clear the list, set the states to
+                                            // default
+                                            foreach (DateTime item in Owner.SelectedDates)
+                                            {
+                                                Owner.RemovedItems.Add(item);
+                                            }
+                                            Owner.SelectedDates.ClearInternal();
+                                            Owner.UnHighlightDays();
+                                        }
+                                        Owner.HoverStart = selectedDate;
+                                        Owner.HoverStartIndex = b.Index;
+                                    }
+                                    return;
+                                }
+                        }
+                    }
+                    else
+                    {
+                        // If a click occurs on a BlackOutDay we set the
+                        // HoverStart to be null
+                        Owner.HoverStart = null;
+                    }
+                }
+                else
+                {
+                    _isControlPressed = false;
+                }
+            }
+        }
+        private void AddSelection(CalendarDayButton b)
+        {
+            if (Owner != null)
+            {
+                Owner.HoverEndIndex = b.Index;
+                Owner.HoverEnd = (DateTime)b.DataContext;
+
+                if (Owner.HoverEnd != null && Owner.HoverStart != null)
+                {
+                    // this is selection with Mouse, we do not guarantee the
+                    // range does not include BlackOutDates.  AddRange method
+                    // will throw away the BlackOutDates based on the
+                    // SelectionMode
+                    Owner.IsMouseSelection = true;
+                    Owner.SelectedDates.AddRange(Owner.HoverStart.Value, Owner.HoverEnd.Value);
+                    Owner.OnDayClick((DateTime)b.DataContext);
+                }
+            }
+        }
+        internal void Cell_MouseLeftButtonUp(object sender, PointerReleasedEventArgs e)
+        {
+            if (Owner != null)
+            {
+                CalendarDayButton b = sender as CalendarDayButton;
+                if (b != null && !b.IsBlackout)
+                {
+                    Owner.OnDayButtonMouseUp(e);
+                }
+                _isMouseLeftButtonDown = false;
+                if (b != null && b.DataContext != null)
+                {
+                    if (Owner.SelectionMode == CalendarSelectionMode.None || Owner.SelectionMode == CalendarSelectionMode.SingleDate)
+                    {
+                        Owner.OnDayClick((DateTime)b.DataContext);
+                        return;
+                    }
+                    if (Owner.HoverStart.HasValue)
+                    {
+                        switch (Owner.SelectionMode)
+                        {
+                            case CalendarSelectionMode.SingleRange:
+                                {
+                                    // Update SelectedDates
+                                    foreach (DateTime item in Owner.SelectedDates)
+                                    {
+                                        Owner.RemovedItems.Add(item);
+                                    }
+                                    Owner.SelectedDates.ClearInternal();
+                                    AddSelection(b);
+                                    return;
+                                }
+                            case CalendarSelectionMode.MultipleRange:
+                                {
+                                    // add the selection (either single day or
+                                    // SingleRange day)
+                                    AddSelection(b);
+                                    return;
+                                }
+                        }
+                    }
+                    else
+                    {
+                        // If the day is Disabled but a trailing day we should
+                        // be able to switch months
+                        if (b.IsInactive && b.IsBlackout)
+                        {
+                            Owner.OnDayClick((DateTime)b.DataContext);
+                        }
+                    }
+                }
+            }
+        }
+        private void Cell_Click(object sender, RoutedEventArgs e)
+        {
+            if (Owner != null)
+            {
+                if (_isControlPressed && Owner.SelectionMode == CalendarSelectionMode.MultipleRange)
+                {
+                    CalendarDayButton b = sender as CalendarDayButton;
+                    Debug.Assert(b != null, "The sender should be a non-null CalendarDayButton!");
+
+                    if (b.IsSelected)
+                    {
+                        Owner.HoverStart = null;
+                        _isMouseLeftButtonDown = false;
+                        b.IsSelected = false;
+                        if (b.DataContext != null)
+                        {
+                            Owner.SelectedDates.Remove((DateTime)b.DataContext);
+                        }
+                    }
+                }
+            }
+            _isControlPressed = false;
+        }
+
+        private void Month_CalendarButtonMouseDown(object sender, PointerPressedEventArgs e)
+        {
+            CalendarButton b = sender as CalendarButton;
+            Debug.Assert(b != null, "The sender should be a non-null CalendarDayButton!");
+
+            _isMouseLeftButtonDownYearView = true;
+
+            if (e != null)
+            {
+                _downEventArgYearView = e;
+            }
+
+            UpdateYearViewSelection(b);
+        }
+
+        internal void Month_CalendarButtonMouseUp(object sender, PointerReleasedEventArgs e)
+        {
+            _isMouseLeftButtonDownYearView = false;
+
+            if (Owner != null)
+            {
+                DateTime newmonth = (DateTime)((CalendarButton)sender).DataContext;
+
+                if (Owner.DisplayMode == CalendarMode.Year)
+                {
+                    Owner.DisplayDate = newmonth;
+                    Owner.DisplayMode = CalendarMode.Month;
+                }
+                else
+                {
+                    Debug.Assert(Owner.DisplayMode == CalendarMode.Decade, "The owning Calendar should be in decade mode!");
+                    Owner.SelectedMonth = newmonth;
+                    Owner.DisplayMode = CalendarMode.Year;
+                }
+            }
+        }
+
+        private void Month_MouseEnter(object sender, PointerEventArgs e)
+        {
+            if (_isMouseLeftButtonDownYearView)
+            {
+                CalendarButton b = sender as CalendarButton;
+                Debug.Assert(b != null, "The sender should be a non-null CalendarDayButton!");
+                UpdateYearViewSelection(b);
+            }
+        }
+
+        private void Month_MouseLeave(object sender, PointerEventArgs e)
+        {
+            if (_isMouseLeftButtonDownYearView)
+            {
+                CalendarButton b = sender as CalendarButton;
+                // The button is in Pressed state. Change the state to normal.
+                if (e.Device.Captured == b)
+                    e.Device.Capture(null);
+                //b.ReleaseMouseCapture();
+                if (_downEventArgYearView != null)
+                {
+                    var args =
+                        new PointerReleasedEventArgs()
+                        {
+                            Device = _downEventArgYearView.Device,
+                            MouseButton = _downEventArgYearView.MouseButton,
+                            Handled = _downEventArgYearView.Handled,
+                            InputModifiers = _downEventArgYearView.InputModifiers,
+                            Route = _downEventArgYearView.Route,
+                            Source = _downEventArgYearView.Source
+                        };
+
+                    b.SendMouseLeftButtonUp(args);
+                }
+                _lastCalendarButton = b;
+            }
+        }
+        private void MonthView_MouseLeave(object sender, PointerEventArgs e)
+        {
+            if (_lastCalendarDayButton != null)
+            {
+                e.Device.Capture(_lastCalendarDayButton);
+            }
+        }
+
+        private void YearView_MouseLeave(object sender, PointerEventArgs e)
+        {
+            if (_lastCalendarButton != null)
+            {
+                e.Device.Capture(_lastCalendarButton);
+            }
+        }
+        
+        internal void UpdateDisabled(bool isEnabled)
+        {
+            PseudoClasses.Set(":calendardisabled", !isEnabled);
+        }
+    }
+}

+ 155 - 0
src/Avalonia.Controls/Calendar/DateTimeHelper.cs

@@ -0,0 +1,155 @@
+// (c) Copyright Microsoft Corporation.
+// This source is subject to the Microsoft Public License (Ms-PL).
+// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
+// All other rights reserved.
+
+using System;
+using System.Diagnostics;
+using System.Globalization;
+
+namespace Avalonia.Controls
+{
+    internal static class DateTimeHelper
+    {
+        public static DateTime? AddDays(DateTime time, int days)
+        {
+            System.Globalization.Calendar cal = new GregorianCalendar();
+            try
+            {
+                return cal.AddDays(time, days);
+            }
+            catch (ArgumentException)
+            {
+                return null;
+            }
+        }
+
+        public static DateTime? AddMonths(DateTime time, int months)
+        {
+            System.Globalization.Calendar cal = new GregorianCalendar();
+            try
+            {
+                return cal.AddMonths(time, months);
+            }
+            catch (ArgumentException)
+            {
+                return null;
+            }
+        }
+
+        public static DateTime? AddYears(DateTime time, int years)
+        {
+            System.Globalization.Calendar cal = new GregorianCalendar();
+            try
+            {
+                return cal.AddYears(time, years);
+            }
+            catch (ArgumentException)
+            {
+                return null;
+            }
+        }
+
+        public static int CompareDays(DateTime dt1, DateTime dt2)
+        {
+            return DateTime.Compare(DiscardTime(dt1).Value, DiscardTime(dt2).Value);
+        }
+
+        public static int CompareYearMonth(DateTime dt1, DateTime dt2)
+        {
+            return (dt1.Year - dt2.Year) * 12 + (dt1.Month - dt2.Month);
+        }
+
+        public static int DecadeOfDate(DateTime date)
+        {
+            return date.Year - (date.Year % 10);
+        }
+
+        public static DateTime DiscardDayTime(DateTime d)
+        {
+            int year = d.Year;
+            int month = d.Month;
+            DateTime newD = new DateTime(year, month, 1, 0, 0, 0);
+            return newD;
+        }
+
+        public static DateTime? DiscardTime(DateTime? d)
+        {
+            if (d == null)
+            {
+                return null;
+            }
+            return d.Value.Date;
+        }
+
+        public static int EndOfDecade(DateTime date)
+        {
+            return DecadeOfDate(date) + 9;
+        }
+
+        public static DateTimeFormatInfo GetCurrentDateFormat()
+        {
+            if (CultureInfo.CurrentCulture.Calendar is GregorianCalendar)
+            {
+                return CultureInfo.CurrentCulture.DateTimeFormat;
+            }
+            else
+            {
+                foreach (System.Globalization.Calendar cal in CultureInfo.CurrentCulture.OptionalCalendars)
+                {
+                    if (cal is GregorianCalendar)
+                    {
+                        // if the default calendar is not Gregorian, return the
+                        // first supported GregorianCalendar dtfi
+                        DateTimeFormatInfo dtfi = new CultureInfo(CultureInfo.CurrentCulture.Name).DateTimeFormat;
+                        dtfi.Calendar = cal;
+                        return dtfi;
+                    }
+                }
+
+                // if there are no GregorianCalendars in the OptionalCalendars
+                // list, use the invariant dtfi
+                DateTimeFormatInfo dt = new CultureInfo(CultureInfo.InvariantCulture.Name).DateTimeFormat;
+                dt.Calendar = new GregorianCalendar();
+                return dt;
+            }
+        }
+        public static bool InRange(DateTime date, CalendarDateRange range)
+        {
+            Debug.Assert(DateTime.Compare(range.Start, range.End) < 1, "The range should start before it ends!");
+
+            if (CompareDays(date, range.Start) > -1 && CompareDays(date, range.End) < 1)
+            {
+                return true;
+            }
+
+            return false;
+        }
+
+        public static string ToYearMonthPatternString(DateTime date)
+        {
+            string result = string.Empty;
+            DateTimeFormatInfo format = GetCurrentDateFormat();
+
+            if (format != null)
+            {
+                result = date.ToString(format.YearMonthPattern, format);
+            }
+
+            return result;
+        }
+
+        public static string ToYearString(DateTime date)
+        {
+            string result = string.Empty;
+            DateTimeFormatInfo format = GetCurrentDateFormat();
+
+            if (format != null)
+            {
+                result = date.Year.ToString(format);
+            }
+
+            return result;
+        }
+    }
+}

+ 361 - 0
src/Avalonia.Controls/Calendar/SelectedDatesCollection.cs

@@ -0,0 +1,361 @@
+// (c) Copyright Microsoft Corporation.
+// This source is subject to the Microsoft Public License (Ms-PL).
+// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
+// All other rights reserved.
+
+using Avalonia.Threading;
+using System;
+using System.Collections.ObjectModel;
+using System.Threading;
+
+namespace Avalonia.Controls.Primitives
+{
+    public sealed class SelectedDatesCollection : ObservableCollection<DateTime>
+    {
+        /// <summary>
+        /// Inherited code: Requires comment.
+        /// </summary>
+        private Collection<DateTime> _addedItems;
+        
+        /// <summary>
+        /// Inherited code: Requires comment.
+        /// </summary>
+        private bool _isCleared;
+
+        /// <summary>
+        /// Inherited code: Requires comment.
+        /// </summary>
+        private bool _isRangeAdded;
+
+        /// <summary>
+        /// Inherited code: Requires comment.
+        /// </summary>
+        private Calendar _owner;
+
+        /// <summary>
+        /// Initializes a new instance of the
+        /// <see cref="T:Avalonia.Controls.Primitives.SelectedDatesCollection" />
+        /// class.
+        /// </summary>
+        /// <param name="owner">
+        /// The <see cref="T:Avalonia.Controls.Calendar" /> associated
+        /// with this object.
+        /// </param>
+        public SelectedDatesCollection(Calendar owner)
+        {
+            _owner = owner;
+            _addedItems = new Collection<DateTime>();
+        }
+
+        private void InvokeCollectionChanged(System.Collections.IList removedItems, System.Collections.IList addedItems)
+        {
+            _owner.OnSelectedDatesCollectionChanged(new SelectionChangedEventArgs(null, addedItems, removedItems));
+        }
+
+        /// <summary>
+        /// Adds all the dates in the specified range, which includes the first
+        /// and last dates, to the collection.
+        /// </summary>
+        /// <param name="start">The first date to add to the collection.</param>
+        /// <param name="end">The last date to add to the collection.</param>
+        public void AddRange(DateTime start, DateTime end)
+        {
+            DateTime? rangeStart;
+
+            // increment parameter specifies if the Days were selected in
+            // Descending order or Ascending order based on this value, we add 
+            // the days in the range either in Ascending order or in Descending
+            // order
+            int increment = (DateTime.Compare(end, start) >= 0) ? 1 : -1;
+
+            _addedItems.Clear();
+
+            rangeStart = start;
+            _isRangeAdded = true;
+
+            if (_owner.IsMouseSelection)
+            {
+                // In Mouse Selection we allow the user to be able to add
+                // multiple ranges in one action in MultipleRange Mode.  In
+                // SingleRange Mode, we only add the first selected range.
+                while (rangeStart.HasValue && DateTime.Compare(end, rangeStart.Value) != -increment)
+                {
+                    if (Calendar.IsValidDateSelection(_owner, rangeStart))
+                    {
+                        Add(rangeStart.Value);
+                    }
+                    else
+                    {
+                        if (_owner.SelectionMode == CalendarSelectionMode.SingleRange)
+                        {
+                            _owner.HoverEnd = rangeStart.Value.AddDays(-increment);
+                            break;
+                        }
+                    }
+
+                    rangeStart = DateTimeHelper.AddDays(rangeStart.Value, increment);
+                }
+            }
+            else
+            {
+                // If CalendarSelectionMode.SingleRange and a user
+                // programmatically tries to add multiple ranges, we will throw
+                // away the old range and replace it with the new one.  In order
+                // to provide the removed items without an additional event, we
+                // are calling ClearInternal
+                if (_owner.SelectionMode == CalendarSelectionMode.SingleRange && Count > 0)
+                {
+                    foreach (DateTime item in this)
+                    {
+                        _owner.RemovedItems.Add(item);
+                    }
+                    ClearInternal();
+                }
+
+                while (rangeStart.HasValue && DateTime.Compare(end, rangeStart.Value) != -increment)
+                {
+                    Add(rangeStart.Value);
+                    rangeStart = DateTimeHelper.AddDays(rangeStart.Value, increment);
+                }
+            }
+
+            _owner.OnSelectedDatesCollectionChanged(new SelectionChangedEventArgs(null, _addedItems, _owner.RemovedItems));
+            _owner.RemovedItems.Clear();
+            _owner.UpdateMonths();
+            _isRangeAdded = false;
+        }
+
+        /// <summary>
+        /// Removes all items from the collection.
+        /// </summary>
+        /// <remarks>
+        /// This implementation raises the CollectionChanged event.
+        /// </remarks>
+        protected override void ClearItems()
+        {
+            EnsureValidThread();
+
+            Collection<DateTime> addedItems = new Collection<DateTime>();
+            Collection<DateTime> removedItems = new Collection<DateTime>();
+
+            foreach (DateTime item in this)
+            {
+                removedItems.Add(item);
+            }
+
+            base.ClearItems();
+
+            // The event fires after SelectedDate changes
+            if (_owner.SelectionMode != CalendarSelectionMode.None && _owner.SelectedDate != null)
+            {
+                _owner.SelectedDate = null;
+            }
+
+            if (removedItems.Count != 0)
+            {
+                InvokeCollectionChanged(removedItems, addedItems);
+            }
+            _owner.UpdateMonths();
+        }
+
+        /// <summary>
+        /// Inserts an item into the collection at the specified index.
+        /// </summary>
+        /// <param name="index">
+        /// The zero-based index at which item should be inserted.
+        /// </param>
+        /// <param name="item">The object to insert.</param>
+        /// <remarks>
+        /// This implementation raises the CollectionChanged event.
+        /// </remarks>
+        protected override void InsertItem(int index, DateTime item)
+        {
+            EnsureValidThread();
+
+            if (!Contains(item))
+            {
+                Collection<DateTime> addedItems = new Collection<DateTime>();
+
+                if (CheckSelectionMode())
+                {
+                    if (Calendar.IsValidDateSelection(_owner, item))
+                    {
+                        // If the Collection is cleared since it is SingleRange
+                        // and it had another range set the index to 0
+                        if (_isCleared)
+                        {
+                            index = 0;
+                            _isCleared = false;
+                        }
+
+                        base.InsertItem(index, item);
+
+                        // The event fires after SelectedDate changes
+                        if (index == 0 && !(_owner.SelectedDate.HasValue && DateTime.Compare(_owner.SelectedDate.Value, item) == 0))
+                        {
+                            _owner.SelectedDate = item;
+                        }
+
+                        if (!_isRangeAdded)
+                        {
+                            addedItems.Add(item);
+
+                            InvokeCollectionChanged(_owner.RemovedItems, addedItems);
+                            _owner.RemovedItems.Clear();
+                            int monthDifference = DateTimeHelper.CompareYearMonth(item, _owner.DisplayDateInternal);
+
+                            if (monthDifference < 2 && monthDifference > -2)
+                            {
+                                _owner.UpdateMonths();
+                            }
+                        }
+                        else
+                        {
+                            _addedItems.Add(item);
+                        }
+                    }
+                    else
+                    {
+                        throw new ArgumentOutOfRangeException("SelectedDate value is not valid.");
+                    }
+                }
+            }
+        }
+
+        /// <summary>
+        /// Removes the item at the specified index of the collection.
+        /// </summary>
+        /// <param name="index">
+        /// The zero-based index of the element to remove.
+        /// </param>
+        /// <remarks>
+        /// This implementation raises the CollectionChanged event.
+        /// </remarks>
+        protected override void RemoveItem(int index)
+        {
+            EnsureValidThread();
+
+            if (index >= Count)
+            {
+                base.RemoveItem(index);
+            }
+            else
+            {
+                Collection<DateTime> addedItems = new Collection<DateTime>();
+                Collection<DateTime> removedItems = new Collection<DateTime>();
+                int monthDifference = DateTimeHelper.CompareYearMonth(this[index], _owner.DisplayDateInternal);
+
+                removedItems.Add(this[index]);
+                base.RemoveItem(index);
+
+                // The event fires after SelectedDate changes
+                if (index == 0)
+                {
+                    if (Count > 0)
+                    {
+                        _owner.SelectedDate = this[0];
+                    }
+                    else
+                    {
+                        _owner.SelectedDate = null;
+                    }
+                }
+
+                InvokeCollectionChanged(removedItems, addedItems);
+
+                if (monthDifference < 2 && monthDifference > -2)
+                {
+                    _owner.UpdateMonths();
+                }
+            }
+        }
+
+        /// <summary>
+        /// Replaces the element at the specified index.
+        /// </summary>
+        /// <param name="index">
+        /// The zero-based index of the element to replace.
+        /// </param>
+        /// <param name="item">
+        /// The new value for the element at the specified index.
+        /// </param>
+        /// <remarks>
+        /// This implementation raises the CollectionChanged event.
+        /// </remarks>
+        protected override void SetItem(int index, DateTime item)
+        {
+            EnsureValidThread();
+
+            if (!Contains(item))
+            {
+                Collection<DateTime> addedItems = new Collection<DateTime>();
+                Collection<DateTime> removedItems = new Collection<DateTime>();
+
+                if (index >= Count)
+                {
+                    base.SetItem(index, item);
+                }
+                else
+                {
+                    if (item != null && DateTime.Compare(this[index], item) != 0 && Calendar.IsValidDateSelection(_owner, item))
+                    {
+                        removedItems.Add(this[index]);
+                        base.SetItem(index, item);
+                        addedItems.Add(item);
+
+                        // The event fires after SelectedDate changes
+                        if (index == 0 && !(_owner.SelectedDate.HasValue && DateTime.Compare(_owner.SelectedDate.Value, item) == 0))
+                        {
+                            _owner.SelectedDate = item;
+                        }
+                        InvokeCollectionChanged(removedItems, addedItems);
+
+                        int monthDifference = DateTimeHelper.CompareYearMonth(item, _owner.DisplayDateInternal);
+
+                        if (monthDifference < 2 && monthDifference > -2)
+                        {
+                            _owner.UpdateMonths();
+                        }
+                    }
+                }
+            }
+        }
+        
+        internal void ClearInternal()
+        {
+            base.ClearItems();
+        }
+        
+        private bool CheckSelectionMode()
+        {
+            if (_owner.SelectionMode == CalendarSelectionMode.None)
+            {
+                throw new InvalidOperationException("The SelectedDate property cannot be set when the selection mode is None.");
+            }
+            if (_owner.SelectionMode == CalendarSelectionMode.SingleDate && Count > 0)
+            {
+                throw new InvalidOperationException("The SelectedDates collection can be changed only in a multiple selection mode. Use the SelectedDate in a single selection mode.");
+            }
+
+            // if user tries to add an item into the SelectedDates in
+            // SingleRange mode, we throw away the old range and replace it with
+            // the new one in order to provide the removed items without an
+            // additional event, we are calling ClearInternal
+            if (_owner.SelectionMode == CalendarSelectionMode.SingleRange && !_isRangeAdded && Count > 0)
+            {
+                foreach (DateTime item in this)
+                {
+                    _owner.RemovedItems.Add(item);
+                }
+                ClearInternal();
+                _isCleared = true;
+            }
+            return true;
+        }
+        
+        private void EnsureValidThread()
+        {
+            Dispatcher.UIThread.VerifyAccess();
+        }
+    }
+}

+ 1 - 1
src/Avalonia.Controls/Classes.cs

@@ -179,7 +179,7 @@ namespace Avalonia.Controls
             {
                 ThrowIfPseudoclass(name, "removed");
 
-                if (!Contains(name))
+                if (Contains(name))
                 {
                     c.Add(name);
                 }

+ 53 - 24
src/Avalonia.Controls/Control.cs

@@ -101,6 +101,7 @@ namespace Avalonia.Controls
         private Styles _styles;
         private bool _styled;
         private Subject<IStyleable> _styleDetach = new Subject<IStyleable>();
+        private bool _dataContextUpdating;
 
         /// <summary>
         /// Initializes static members of the <see cref="Control"/> class.
@@ -111,6 +112,7 @@ namespace Avalonia.Controls
             PseudoClass(IsEnabledCoreProperty, x => !x, ":disabled");
             PseudoClass(IsFocusedProperty, ":focus");
             PseudoClass(IsPointerOverProperty, ":pointerover");
+            DataContextProperty.Changed.AddClassHandler<Control>(x => x.OnDataContextChangedCore);
         }
 
         /// <summary>
@@ -175,9 +177,9 @@ namespace Avalonia.Controls
 
             set
             {
-                if (value.Trim() == string.Empty)
+                if (String.IsNullOrWhiteSpace(value))
                 {
-                    throw new InvalidOperationException("Cannot set Name to empty string.");
+                    throw new InvalidOperationException("Cannot set Name to null or empty string.");
                 }
 
                 if (_styled)
@@ -656,7 +658,6 @@ namespace Avalonia.Controls
         /// <param name="e">The event args.</param>
         protected virtual void OnAttachedToLogicalTree(LogicalTreeAttachmentEventArgs e)
         {
-            AttachedToLogicalTree?.Invoke(this, e);
         }
 
         /// <summary>
@@ -665,7 +666,6 @@ namespace Avalonia.Controls
         /// <param name="e">The event args.</param>
         protected virtual void OnDetachedFromLogicalTree(LogicalTreeAttachmentEventArgs e)
         {
-            DetachedFromLogicalTree?.Invoke(this, e);
         }
 
         /// <inheritdoc/>
@@ -683,18 +683,26 @@ namespace Avalonia.Controls
         }
 
         /// <summary>
-        /// Called before the <see cref="DataContext"/> property changes.
+        /// Called when the <see cref="DataContext"/> property changes.
         /// </summary>
-        protected virtual void OnDataContextChanging()
+        /// <param name="e">The event args.</param>
+        protected virtual void OnDataContextChanged(EventArgs e)
         {
+            DataContextChanged?.Invoke(this, EventArgs.Empty);
         }
 
         /// <summary>
-        /// Called after the <see cref="DataContext"/> property changes.
+        /// Called when the <see cref="DataContext"/> begins updating.
         /// </summary>
-        protected virtual void OnDataContextChanged()
+        protected virtual void OnDataContextBeginUpdate()
+        {
+        }
+
+        /// <summary>
+        /// Called when the <see cref="DataContext"/> finishes updating.
+        /// </summary>
+        protected virtual void OnDataContextEndUpdate()
         {
-            DataContextChanged?.Invoke(this, EventArgs.Empty);
         }
 
         /// <inheritdoc/>
@@ -747,24 +755,40 @@ namespace Avalonia.Controls
             }
         }
 
-        /// <summary>
-        /// Called when the <see cref="DataContext"/> property begins and ends being notified.
-        /// </summary>
-        /// <param name="o">The object on which the DataContext is changing.</param>
-        /// <param name="notifying">Whether the notifcation is beginning or ending.</param>
         private static void DataContextNotifying(IAvaloniaObject o, bool notifying)
         {
-            var control = o as Control;
+            if (o is Control control)
+            {
+                DataContextNotifying(control, notifying);
+            }
+        }
 
-            if (control != null)
+        private static void DataContextNotifying(Control control, bool notifying)
+        {
+            if (notifying)
             {
-                if (notifying)
+                if (!control._dataContextUpdating)
                 {
-                    control.OnDataContextChanging();
+                    control._dataContextUpdating = true;
+                    control.OnDataContextBeginUpdate();
+
+                    foreach (var child in control.LogicalChildren)
+                    {
+                        if (child is Control c && 
+                            c.InheritanceParent == control &&
+                            !c.IsSet(DataContextProperty))
+                        {
+                            DataContextNotifying(c, notifying);
+                        }
+                    }
                 }
-                else
+            }
+            else
+            {
+                if (control._dataContextUpdating)
                 {
-                    control.OnDataContextChanged();
+                    control.OnDataContextEndUpdate();
+                    control._dataContextUpdating = false;
                 }
             }
         }
@@ -773,11 +797,9 @@ namespace Avalonia.Controls
         {
             while (e != null)
             {
-                var root = e as IStyleRoot;
-
-                if (root != null && root.StylingParent == null)
+                if (e is IRenderRoot root)
                 {
-                    return root;
+                    return root as IStyleRoot;
                 }
 
                 e = e.StylingParent;
@@ -844,6 +866,7 @@ namespace Avalonia.Controls
                 InitializeStylesIfNeeded(true);
 
                 OnAttachedToLogicalTree(e);
+                AttachedToLogicalTree?.Invoke(this, e);
             }
 
             foreach (var child in LogicalChildren.OfType<Control>())
@@ -864,6 +887,7 @@ namespace Avalonia.Controls
                 _isAttachedToLogicalTree = false;
                 _styleDetach.OnNext(this);
                 OnDetachedFromLogicalTree(e);
+                DetachedFromLogicalTree?.Invoke(this, e);
 
                 foreach (var child in LogicalChildren.OfType<Control>())
                 {
@@ -883,6 +907,11 @@ namespace Avalonia.Controls
             }
         }
 
+        private void OnDataContextChangedCore(AvaloniaPropertyChangedEventArgs e)
+        {
+            OnDataContextChanged(EventArgs.Empty);
+        }
+
         private void LogicalChildrenCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
         {
             switch (e.Action)

+ 1 - 1
src/Avalonia.Controls/Design.cs

@@ -64,7 +64,7 @@ namespace Avalonia.Controls
             return rv;
         }
 
-        internal static void ApplyDesignerProperties(Control target, Control source)
+        public static void ApplyDesignModeProperties(Control target, Control source)
         {
             if (source.IsSet(WidthProperty))
                 target.Width = source.GetValue(WidthProperty);

Alguns ficheiros não foram mostrados porque muitos ficheiros mudaram neste diff