Browse Source

Merge remote-tracking branch 'Perspex/master' into DocFX

Wiesław Šoltés 9 years ago
parent
commit
d420e46902
100 changed files with 1141 additions and 994 deletions
  1. 31 3
      Perspex.sln
  2. 1 1
      docs/spec/architecture.md
  3. 3 3
      nuget/template/Perspex.Android.nuspec
  4. 6 6
      nuget/template/Perspex.Desktop.nuspec
  5. 1 1
      nuget/template/Perspex.Skia.Desktop.nuspec
  6. 3 3
      nuget/template/Perspex.iOS.nuspec
  7. 2 2
      nuget/template/Perspex.nuspec
  8. 10 2
      samples/TestApplication/App.config
  9. 9 1
      samples/XamlTestApplication/App.config
  10. 0 2
      src/Gtk/Perspex.Cairo/Media/DrawingContext.cs
  11. 0 5
      src/Gtk/Perspex.Gtk/WindowImpl.cs
  12. 0 15
      src/Markup/Perspex.Markup.Xaml/Data/Binding.cs
  13. 6 6
      src/Markup/Perspex.Markup/ControlLocator.cs
  14. 7 8
      src/Perspex.Application/Application.cs
  15. 0 10
      src/Perspex.Base/PerspexObject.cs
  16. 6 1
      src/Perspex.Base/Threading/DispatcherTimer.cs
  17. 1 1
      src/Perspex.Controls/Button.cs
  18. 1 1
      src/Perspex.Controls/Carousel.cs
  19. 0 11
      src/Perspex.Controls/ContextMenu.cs
  20. 17 4
      src/Perspex.Controls/Control.cs
  21. 1 1
      src/Perspex.Controls/DropDown.cs
  22. 0 8
      src/Perspex.Controls/Grid.cs
  23. 11 0
      src/Perspex.Controls/IControl.cs
  24. 1 1
      src/Perspex.Controls/ListBox.cs
  25. 1 1
      src/Perspex.Controls/Menu.cs
  26. 1 8
      src/Perspex.Controls/MenuItem.cs
  27. 0 7
      src/Perspex.Controls/Mixins/ContentControlMixin.cs
  28. 4 1
      src/Perspex.Controls/Mixins/SelectableMixin.cs
  29. 8 2
      src/Perspex.Controls/Platform/PlatformManager.cs
  30. 0 8
      src/Perspex.Controls/Presenters/ScrollContentPresenter.cs
  31. 1 1
      src/Perspex.Controls/Primitives/Popup.cs
  32. 17 0
      src/Perspex.Controls/Primitives/SelectingItemsControl.cs
  33. 1 1
      src/Perspex.Controls/Primitives/TabStrip.cs
  34. 48 0
      src/Perspex.Controls/Primitives/TemplatedControl.cs
  35. 1 1
      src/Perspex.Controls/Primitives/Thumb.cs
  36. 20 11
      src/Perspex.Controls/Templates/TemplateExtensions.cs
  37. 41 22
      src/Perspex.Controls/TextBox.cs
  38. 2 4
      src/Perspex.Controls/TreeView.cs
  39. 1 1
      src/Perspex.HtmlRenderer/HtmlControl.cs
  40. 13 3
      src/Perspex.Input/Cursors.cs
  41. 1 1
      src/Perspex.Input/FocusManager.cs
  42. 1 1
      src/Perspex.Input/IInputElement.cs
  43. 4 4
      src/Perspex.Input/InputElement.cs
  44. 9 2
      src/Perspex.Input/KeyboardDevice.cs
  45. 33 19
      src/Perspex.Input/MouseDevice.cs
  46. 1 1
      src/Perspex.Input/PointerEventArgs.cs
  47. 1 1
      src/Perspex.Input/Raw/RawInputEventArgs.cs
  48. 5 0
      src/Perspex.SceneGraph/Media/FormattedText.cs
  49. 1 7
      src/Perspex.Themes.Default/TreeViewItem.paml
  50. 1 1
      src/Skia/Perspex.Skia/DrawingContextImpl.cs
  51. 10 2
      src/Windows/Perspex.Direct2D1/app.config
  52. 4 4
      src/Windows/Perspex.Win32/WindowImpl.cs
  53. 0 3
      tests/Perspex.Base.UnitTests/PerspexObjectTests_Binding.cs
  54. 0 12
      tests/Perspex.Base.UnitTests/PerspexObjectTests_Metadata.cs
  55. 0 2
      tests/Perspex.Base.UnitTests/StyledPropertyTests.cs
  56. 28 47
      tests/Perspex.Controls.UnitTests/ContentControlTests.cs
  57. 1 41
      tests/Perspex.Controls.UnitTests/ControlTests.cs
  58. 9 67
      tests/Perspex.Controls.UnitTests/ControlTests_NameScope.cs
  59. 0 10
      tests/Perspex.Controls.UnitTests/DropDownTests.cs
  60. 1 1
      tests/Perspex.Controls.UnitTests/GridSplitterTests.cs
  61. 1 1
      tests/Perspex.Controls.UnitTests/ListBoxTests.cs
  62. 6 16
      tests/Perspex.Controls.UnitTests/ListBoxTests_Single.cs
  63. 0 5
      tests/Perspex.Controls.UnitTests/PanelTests.cs
  64. 11 1
      tests/Perspex.Controls.UnitTests/Perspex.Controls.UnitTests.csproj
  65. 1 0
      tests/Perspex.Controls.UnitTests/Presenters/ContentPresenterTests.cs
  66. 1 0
      tests/Perspex.Controls.UnitTests/Primitives/PopupTests.cs
  67. 0 5
      tests/Perspex.Controls.UnitTests/Primitives/SelectingItemsControlTests_AutoSelect.cs
  68. 3 33
      tests/Perspex.Controls.UnitTests/Primitives/TemplatedControlTests.cs
  69. 0 12
      tests/Perspex.Controls.UnitTests/ScrollViewerTests.cs
  70. 2 3
      tests/Perspex.Controls.UnitTests/TabControlTests.cs
  71. 0 38
      tests/Perspex.Controls.UnitTests/TestRoot.cs
  72. 21 70
      tests/Perspex.Controls.UnitTests/TopLevelTests.cs
  73. 2 1
      tests/Perspex.Controls.UnitTests/TreeViewTests.cs
  74. 198 174
      tests/Perspex.LeakTests/ControlTests.cs
  75. 4 1
      tests/Perspex.LeakTests/Perspex.LeakTests.csproj
  76. 0 47
      tests/Perspex.LeakTests/TestApp.cs
  77. 1 0
      tests/Perspex.Markup.UnitTests/ControlLocatorTests.cs
  78. 0 5
      tests/Perspex.Markup.UnitTests/Data/ExpressionSubjectTests.cs
  79. 4 1
      tests/Perspex.Markup.UnitTests/Perspex.Markup.UnitTests.csproj
  80. 1 0
      tests/Perspex.Markup.Xaml.UnitTests/Data/BindingTests_ElementName.cs
  81. 0 5
      tests/Perspex.Markup.Xaml.UnitTests/Data/BindingTests_TemplatedParent.cs
  82. 9 1
      tests/Perspex.Markup.Xaml.UnitTests/Perspex.Markup.Xaml.UnitTests.csproj
  83. 4 20
      tests/Perspex.Markup.Xaml.UnitTests/StyleTests.cs
  84. 2 7
      tests/Perspex.Markup.Xaml.UnitTests/Templates/TreeDataTemplateTests.cs
  85. 0 67
      tests/Perspex.Markup.Xaml.UnitTests/TestRoot.cs
  86. 11 0
      tests/Perspex.Markup.Xaml.UnitTests/app.config
  87. 10 2
      tests/Perspex.RenderTests/app.config
  88. 0 12
      tests/Perspex.Styling.UnitTests/ActivatedSubjectTests.cs
  89. 4 1
      tests/Perspex.Styling.UnitTests/Perspex.Styling.UnitTests.csproj
  90. 0 13
      tests/Perspex.Styling.UnitTests/StyleActivatorTests.cs
  91. 1 0
      tests/Perspex.Styling.UnitTests/StyleTests.cs
  92. 0 39
      tests/Perspex.Styling.UnitTests/TestRoot.cs
  93. 30 0
      tests/Perspex.UnitTests/MockWindowingPlatform.cs
  94. 143 0
      tests/Perspex.UnitTests/Perspex.UnitTests.csproj
  95. 36 0
      tests/Perspex.UnitTests/Properties/AssemblyInfo.cs
  96. 16 21
      tests/Perspex.UnitTests/TestRoot.cs
  97. 110 0
      tests/Perspex.UnitTests/TestServices.cs
  98. 65 0
      tests/Perspex.UnitTests/TestTemplatedRoot.cs
  99. 47 0
      tests/Perspex.UnitTests/UnitTestApplication.cs
  100. 11 0
      tests/Perspex.UnitTests/app.config

+ 31 - 3
Perspex.sln

@@ -140,6 +140,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Perspex.LeakTests", "tests\
 EndProject
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ControlCatalog", "samples\ControlCatalog\ControlCatalog.csproj", "{61BEC86C-F307-4295-B5B8-9428610D7D55}"
 EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Perspex.UnitTests", "tests\Perspex.UnitTests\Perspex.UnitTests.csproj", "{88060192-33D5-4932-B0F9-8BD2763E857D}"
+EndProject
 Global
 	GlobalSection(SharedMSBuildProjectFiles) = preSolution
 		src\Shared\RenderHelpers\RenderHelpers.projitems*{fb05ac90-89ba-4f2f-a924-f37875fb547c}*SharedItemsImports = 4
@@ -148,19 +150,20 @@ Global
 		src\Shared\PlatformSupport\PlatformSupport.projitems*{e4d9629c-f168-4224-3f51-a5e482ffbc42}*SharedItemsImports = 13
 		src\Skia\Perspex.Skia\Perspex.Skia.projitems*{2f59f3d0-748d-4652-b01e-e0d954756308}*SharedItemsImports = 13
 		src\Shared\PlatformSupport\PlatformSupport.projitems*{db070a10-bf39-4752-8456-86e9d5928478}*SharedItemsImports = 4
-		src\Shared\RenderHelpers\RenderHelpers.projitems*{925dd807-b651-475f-9f7c-cbeb974ce43d}*SharedItemsImports = 4
 		src\Skia\Perspex.Skia\Perspex.Skia.projitems*{925dd807-b651-475f-9f7c-cbeb974ce43d}*SharedItemsImports = 4
+		src\Shared\RenderHelpers\RenderHelpers.projitems*{925dd807-b651-475f-9f7c-cbeb974ce43d}*SharedItemsImports = 4
 		samples\TestApplicationShared\TestApplicationShared.projitems*{78345174-5b52-4a14-b9fd-d5f2428137f0}*SharedItemsImports = 13
 		src\Shared\PlatformSupport\PlatformSupport.projitems*{54f237d5-a70a-4752-9656-0c70b1a7b047}*SharedItemsImports = 4
 		samples\TestApplicationShared\TestApplicationShared.projitems*{ff69b927-c545-49ae-8e16-3d14d621aa12}*SharedItemsImports = 4
 		src\Shared\RenderHelpers\RenderHelpers.projitems*{3c4c0cb4-0c0f-4450-a37b-148c84ff905f}*SharedItemsImports = 13
 		src\Shared\PlatformSupport\PlatformSupport.projitems*{811a76cf-1cf6-440f-963b-bbe31bd72a82}*SharedItemsImports = 4
-		src\Shared\RenderHelpers\RenderHelpers.projitems*{47be08a7-5985-410b-9ffc-2264b8ea595f}*SharedItemsImports = 4
+		src\Shared\PlatformSupport\PlatformSupport.projitems*{88060192-33d5-4932-b0f9-8bd2763e857d}*SharedItemsImports = 4
 		src\Skia\Perspex.Skia\Perspex.Skia.projitems*{47be08a7-5985-410b-9ffc-2264b8ea595f}*SharedItemsImports = 4
+		src\Shared\RenderHelpers\RenderHelpers.projitems*{47be08a7-5985-410b-9ffc-2264b8ea595f}*SharedItemsImports = 4
 		samples\TestApplicationShared\TestApplicationShared.projitems*{8c923867-8a8f-4f6b-8b80-47d9e8436166}*SharedItemsImports = 4
 		samples\TestApplicationShared\TestApplicationShared.projitems*{e3a1060b-50d0-44e8-88b6-f44ef2e5bd72}*SharedItemsImports = 4
-		src\Shared\RenderHelpers\RenderHelpers.projitems*{bd43f7c0-396b-4aa1-bad9-dfde54d51298}*SharedItemsImports = 4
 		src\Skia\Perspex.Skia\Perspex.Skia.projitems*{bd43f7c0-396b-4aa1-bad9-dfde54d51298}*SharedItemsImports = 4
+		src\Shared\RenderHelpers\RenderHelpers.projitems*{bd43f7c0-396b-4aa1-bad9-dfde54d51298}*SharedItemsImports = 4
 		src\Shared\RenderHelpers\RenderHelpers.projitems*{3e908f67-5543-4879-a1dc-08eace79b3cd}*SharedItemsImports = 4
 		src\Shared\PlatformSupport\PlatformSupport.projitems*{e1aa3dbf-9056-4530-9376-18119a7a3ffe}*SharedItemsImports = 4
 	EndGlobalSection
@@ -1294,6 +1297,30 @@ Global
 		{61BEC86C-F307-4295-B5B8-9428610D7D55}.Release|iPhone.Build.0 = Release|Any CPU
 		{61BEC86C-F307-4295-B5B8-9428610D7D55}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
 		{61BEC86C-F307-4295-B5B8-9428610D7D55}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
+		{88060192-33D5-4932-B0F9-8BD2763E857D}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU
+		{88060192-33D5-4932-B0F9-8BD2763E857D}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU
+		{88060192-33D5-4932-B0F9-8BD2763E857D}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU
+		{88060192-33D5-4932-B0F9-8BD2763E857D}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU
+		{88060192-33D5-4932-B0F9-8BD2763E857D}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU
+		{88060192-33D5-4932-B0F9-8BD2763E857D}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU
+		{88060192-33D5-4932-B0F9-8BD2763E857D}.AppStore|Any CPU.ActiveCfg = Release|Any CPU
+		{88060192-33D5-4932-B0F9-8BD2763E857D}.AppStore|Any CPU.Build.0 = Release|Any CPU
+		{88060192-33D5-4932-B0F9-8BD2763E857D}.AppStore|iPhone.ActiveCfg = Release|Any CPU
+		{88060192-33D5-4932-B0F9-8BD2763E857D}.AppStore|iPhone.Build.0 = Release|Any CPU
+		{88060192-33D5-4932-B0F9-8BD2763E857D}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU
+		{88060192-33D5-4932-B0F9-8BD2763E857D}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU
+		{88060192-33D5-4932-B0F9-8BD2763E857D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{88060192-33D5-4932-B0F9-8BD2763E857D}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{88060192-33D5-4932-B0F9-8BD2763E857D}.Debug|iPhone.ActiveCfg = Debug|Any CPU
+		{88060192-33D5-4932-B0F9-8BD2763E857D}.Debug|iPhone.Build.0 = Debug|Any CPU
+		{88060192-33D5-4932-B0F9-8BD2763E857D}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
+		{88060192-33D5-4932-B0F9-8BD2763E857D}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
+		{88060192-33D5-4932-B0F9-8BD2763E857D}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{88060192-33D5-4932-B0F9-8BD2763E857D}.Release|Any CPU.Build.0 = Release|Any CPU
+		{88060192-33D5-4932-B0F9-8BD2763E857D}.Release|iPhone.ActiveCfg = Release|Any CPU
+		{88060192-33D5-4932-B0F9-8BD2763E857D}.Release|iPhone.Build.0 = Release|Any CPU
+		{88060192-33D5-4932-B0F9-8BD2763E857D}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
+		{88060192-33D5-4932-B0F9-8BD2763E857D}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE
@@ -1338,5 +1365,6 @@ Global
 		{8C923867-8A8F-4F6B-8B80-47D9E8436166} = {0CB0B92E-6CFF-4240-80A5-CCAFE75D91E1}
 		{E1AA3DBF-9056-4530-9376-18119A7A3FFE} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
 		{61BEC86C-F307-4295-B5B8-9428610D7D55} = {9B9E3891-2366-4253-A952-D08BCEB71098}
+		{88060192-33D5-4932-B0F9-8BD2763E857D} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
 	EndGlobalSection
 EndGlobal

+ 1 - 1
docs/spec/architecture.md

@@ -31,7 +31,7 @@ The assemblies are as follows, from lowest to highest level:
 
 The main classes in this assembly are `PerspexObject` and `PerspexProperty`.
 
-These are Perspex's versions of XAML's `DependencyObject` and `DepenendencyProperty`. It also 
+These are Perspex's versions of XAML's `DependencyObject` and `DependencyProperty`. It also 
 defines a `PerspexDispatcher` which is - surprise - our version of XAML's `Dispatcher`.
 
 ### Perspex.Animation

+ 3 - 3
nuget/template/Perspex.Android.nuspec

@@ -13,15 +13,15 @@
     <copyright>Copyright 2015</copyright>
     <tags>Perspex</tags>
     <dependencies>
-      <dependency id="Serilog" version="1.5.9" />
+      <dependency id="Serilog" version="1.5.14" />
       <dependency id="Splat" version="1.6.2" />
-      <dependency id="Sprache" version="2.0.0.47" />
+      <dependency id="Sprache" version="2.0.0.50" />
       <dependency id="Rx-Core" version="2.2.5" />
       <dependency id="Rx-Interfaces" version="2.2.5" />
       <dependency id="Rx-Linq" version="2.2.5" />
       <dependency id="Rx-Main" version="2.2.5" />
       <dependency id="Rx-PlatformServices" version="2.2.5" />
-	  <dependency id="Perspex" version="#VERSION#" />
+      <dependency id="Perspex" version="#VERSION#" />
     </dependencies>
   </metadata>
 </package>

+ 6 - 6
nuget/template/Perspex.Desktop.nuspec

@@ -13,18 +13,18 @@
     <copyright>Copyright 2015</copyright>
     <tags>Perspex</tags>
     <dependencies>
-      <dependency id="Serilog" version="1.5.9" />
+      <dependency id="Serilog" version="1.5.14" />
       <dependency id="Splat" version="1.6.2" />
-      <dependency id="Sprache" version="2.0.0.47" />
+      <dependency id="Sprache" version="2.0.0.50" />
       <dependency id="Rx-Core" version="2.2.5" />
       <dependency id="Rx-Interfaces" version="2.2.5" />
       <dependency id="Rx-Linq" version="2.2.5" />
       <dependency id="Rx-Main" version="2.2.5" />
       <dependency id="Rx-PlatformServices" version="2.2.5" />
-      <dependency id="SharpDX" version="3.0.0"/>
-      <dependency id="SharpDX.Direct2D1" version="3.0.0"/>
-      <dependency id="SharpDX.DXGI" version="3.0.0"/>
-	  <dependency id="Perspex" version="#VERSION#" />
+      <dependency id="SharpDX" version="3.0.1"/>
+      <dependency id="SharpDX.Direct2D1" version="3.0.1"/>
+      <dependency id="SharpDX.DXGI" version="3.0.1"/>
+      <dependency id="Perspex" version="#VERSION#" />
     </dependencies>
   </metadata>
 </package>

+ 1 - 1
nuget/template/Perspex.Skia.Desktop.nuspec

@@ -13,7 +13,7 @@
     <copyright>Copyright 2015</copyright>
     <tags>Perspex</tags>
     <dependencies>
-	  <dependency id="Perspex" version="#VERSION#" />
+      <dependency id="Perspex" version="#VERSION#" />
     </dependencies>
   </metadata>
 </package>

+ 3 - 3
nuget/template/Perspex.iOS.nuspec

@@ -13,15 +13,15 @@
     <copyright>Copyright 2015</copyright>
     <tags>Perspex</tags>
     <dependencies>
-      <dependency id="Serilog" version="1.5.9" />
+      <dependency id="Serilog" version="1.5.14" />
       <dependency id="Splat" version="1.6.2" />
-      <dependency id="Sprache" version="2.0.0.47" />
+      <dependency id="Sprache" version="2.0.0.50" />
       <dependency id="Rx-Core" version="2.2.5" />
       <dependency id="Rx-Interfaces" version="2.2.5" />
       <dependency id="Rx-Linq" version="2.2.5" />
       <dependency id="Rx-Main" version="2.2.5" />
       <dependency id="Rx-PlatformServices" version="2.2.5" />
-	  <dependency id="Perspex" version="#VERSION#" />
+      <dependency id="Perspex" version="#VERSION#" />
     </dependencies>
   </metadata>
 </package>

+ 2 - 2
nuget/template/Perspex.nuspec

@@ -13,9 +13,9 @@
     <copyright>Copyright 2015</copyright>
     <tags>Perspex</tags>
     <dependencies>
-      <dependency id="Serilog" version="1.5.9" />
+      <dependency id="Serilog" version="1.5.14" />
       <dependency id="Splat" version="1.6.2" />
-      <dependency id="Sprache" version="2.0.0.47" />
+      <dependency id="Sprache" version="2.0.0.50" />
       <dependency id="Rx-Core" version="2.2.5" />
       <dependency id="Rx-Interfaces" version="2.2.5" />
       <dependency id="Rx-Linq" version="2.2.5" />

+ 10 - 2
samples/TestApplication/App.config

@@ -18,8 +18,16 @@
         <bindingRedirect oldVersion="0.0.0.0-2.2.5.0" newVersion="2.2.5.0" />
       </dependentAssembly>
       <dependentAssembly>
-        <assemblyIdentity name="SharpDX" publicKeyToken="b4dcf0f35e5521f1" culture="neutral" />
-        <bindingRedirect oldVersion="0.0.0.0-2.6.3.0" newVersion="2.6.3.0" />
+        <assemblyIdentity name="SharpDX.Direct2D1" publicKeyToken="b4dcf0f35e5521f1" culture="neutral"/>
+        <bindingRedirect oldVersion="0.0.0.0-3.0.1.0" newVersion="3.0.1.0"/>
+      </dependentAssembly>
+      <dependentAssembly>
+        <assemblyIdentity name="SharpDX" publicKeyToken="b4dcf0f35e5521f1" culture="neutral"/>
+        <bindingRedirect oldVersion="0.0.0.0-3.0.1.0" newVersion="3.0.1.0"/>
+      </dependentAssembly>
+      <dependentAssembly>
+        <assemblyIdentity name="SharpDX.DXGI" publicKeyToken="b4dcf0f35e5521f1" culture="neutral"/>
+        <bindingRedirect oldVersion="0.0.0.0-3.0.1.0" newVersion="3.0.1.0"/>
       </dependentAssembly>
       <dependentAssembly>
         <assemblyIdentity name="Mono.Cairo" publicKeyToken="0738eb9f132ed756" culture="neutral" />

+ 9 - 1
samples/XamlTestApplication/App.config

@@ -17,9 +17,17 @@
         <assemblyIdentity name="System.Reactive.Linq" publicKeyToken="31bf3856ad364e35" culture="neutral"/>
         <bindingRedirect oldVersion="0.0.0.0-2.2.5.0" newVersion="2.2.5.0"/>
       </dependentAssembly>
+      <dependentAssembly>
+        <assemblyIdentity name="SharpDX.Direct2D1" publicKeyToken="b4dcf0f35e5521f1" culture="neutral"/>
+        <bindingRedirect oldVersion="0.0.0.0-3.0.1.0" newVersion="3.0.1.0"/>
+      </dependentAssembly>
       <dependentAssembly>
         <assemblyIdentity name="SharpDX" publicKeyToken="b4dcf0f35e5521f1" culture="neutral"/>
-        <bindingRedirect oldVersion="0.0.0.0-2.6.3.0" newVersion="2.6.3.0"/>
+        <bindingRedirect oldVersion="0.0.0.0-3.0.1.0" newVersion="3.0.1.0"/>
+      </dependentAssembly>
+      <dependentAssembly>
+        <assemblyIdentity name="SharpDX.DXGI" publicKeyToken="b4dcf0f35e5521f1" culture="neutral"/>
+        <bindingRedirect oldVersion="0.0.0.0-3.0.1.0" newVersion="3.0.1.0"/>
       </dependentAssembly>
     </assemblyBinding>
   </runtime>

+ 0 - 2
src/Gtk/Perspex.Cairo/Media/DrawingContext.cs

@@ -215,8 +215,6 @@ namespace Perspex.Cairo.Media
 			}
         }
 
-        private static Random Random = new Random();
-
         /// <summary>
         /// Pushes a clip rectange.
         /// </summary>

+ 0 - 5
src/Gtk/Perspex.Gtk/WindowImpl.cs

@@ -379,10 +379,5 @@ namespace Perspex.Gtk
             Input(e);
             return true;
         }
-
-        private IPlatformHandle GetHandle(Gdk.Window window)
-        {
-            return new PlatformHandle(window.Handle, "GdkWindow");
-        }
     }
 }

+ 0 - 15
src/Markup/Perspex.Markup.Xaml/Data/Binding.cs

@@ -226,21 +226,6 @@ namespace Perspex.Markup.Xaml.Data
             return result;
         }
 
-        private IControl LookupNamedControl(IControl target)
-        {
-            Contract.Requires<ArgumentNullException>(target != null);
-
-            var nameScope = target.FindNameScope();
-
-            if (nameScope == null)
-            {
-                throw new InvalidOperationException(
-                    "Could not find name scope for ElementName binding.");
-            }
-
-            return nameScope.Find<IControl>(ElementName);
-        }
-
         private class PathInfo
         {
             public string Path { get; set; }

+ 6 - 6
src/Markup/Perspex.Markup/ControlLocator.cs

@@ -22,15 +22,15 @@ namespace Perspex.Markup
         /// <param name="name">The name of the control to find.</param>
         public static IObservable<IControl> Track(IControl relativeTo, string name)
         {
-            var attached = Observable.FromEventPattern<VisualTreeAttachmentEventArgs>(
-                x => relativeTo.AttachedToVisualTree += x,
-                x => relativeTo.DetachedFromVisualTree += x)
+            var attached = Observable.FromEventPattern<LogicalTreeAttachmentEventArgs>(
+                x => relativeTo.AttachedToLogicalTree += x,
+                x => relativeTo.DetachedFromLogicalTree += x)
                 .Select(x => ((IControl)x.Sender).FindNameScope())
                 .StartWith(relativeTo.FindNameScope());
 
-            var detached = Observable.FromEventPattern<VisualTreeAttachmentEventArgs>(
-                x => relativeTo.DetachedFromVisualTree += x,
-                x => relativeTo.DetachedFromVisualTree += x)
+            var detached = Observable.FromEventPattern<LogicalTreeAttachmentEventArgs>(
+                x => relativeTo.DetachedFromLogicalTree += x,
+                x => relativeTo.DetachedFromLogicalTree += x)
                 .Select(x => (INameScope)null);
 
             return attached.Merge(detached).Select(nameScope =>

+ 7 - 8
src/Perspex.Application/Application.cs

@@ -59,12 +59,7 @@ namespace Perspex
                 throw new InvalidOperationException("Cannot create more than one Application instance.");
             }
 
-            Current = this;
-        }
-
-        public static void RegisterPlatformCallback(Action cb)
-        {
-            _platformInitializationCallback = cb;
+            PerspexLocator.CurrentMutable.BindToSelf(this);
         }
 
         /// <summary>
@@ -75,8 +70,7 @@ namespace Perspex
         /// </value>
         public static Application Current
         {
-            get;
-            private set;
+            get { return PerspexLocator.Current.GetService<Application>(); }
         }
 
         /// <summary>
@@ -140,6 +134,11 @@ namespace Perspex
         /// </summary>
         IStyleHost IStyleHost.StylingParent => null;
 
+        public static void RegisterPlatformCallback(Action cb)
+        {
+            _platformInitializationCallback = cb;
+        }
+
         /// <summary>
         /// Runs the application's main loop until the <see cref="ICloseable"/> is closed.
         /// </summary>

+ 0 - 10
src/Perspex.Base/PerspexObject.cs

@@ -678,16 +678,6 @@ namespace Perspex
             }
         }
 
-        /// <summary>
-        /// Gets a description of a property that van be used in observables.
-        /// </summary>
-        /// <param name="property">The property</param>
-        /// <returns>The description.</returns>
-        private string GetDescription(PerspexProperty property)
-        {
-            return $"{GetType().Name}.{property.Name}";
-        }
-
         /// <summary>
         /// Gets a description of an observable that van be used in logs.
         /// </summary>

+ 6 - 1
src/Perspex.Base/Threading/DispatcherTimer.cs

@@ -181,6 +181,12 @@ namespace Perspex.Threading
             if (!IsEnabled)
             {
                 IPlatformThreadingInterface threading = PerspexLocator.Current.GetService<IPlatformThreadingInterface>();
+
+                if (threading == null)
+                {
+                    throw new Exception("Could not start timer: IPlatformThreadingInterface is not registered.");
+                }
+
                 _timer = threading.StartTimer(Interval, InternalTick);
             }
         }
@@ -192,7 +198,6 @@ namespace Perspex.Threading
         {
             if (IsEnabled)
             {
-                IPlatformThreadingInterface threading = PerspexLocator.Current.GetService<IPlatformThreadingInterface>();
                 _timer.Dispose();
                 _timer = null;
             }

+ 1 - 1
src/Perspex.Controls/Button.cs

@@ -211,7 +211,7 @@ namespace Perspex.Controls
         }
 
         /// <inheritdoc/>
-        protected override void OnPointerPressed(PointerPressEventArgs e)
+        protected override void OnPointerPressed(PointerPressedEventArgs e)
         {
             base.OnPointerPressed(e);
 

+ 1 - 1
src/Perspex.Controls/Carousel.cs

@@ -74,7 +74,7 @@ namespace Perspex.Controls
         }
 
         /// <inheritdoc/>
-        protected override void OnPointerPressed(PointerPressEventArgs e)
+        protected override void OnPointerPressed(PointerPressedEventArgs e)
         {
             // Ignore pointer presses.
         }

+ 0 - 11
src/Perspex.Controls/ContextMenu.cs

@@ -119,17 +119,6 @@
             }
         }
 
-        private void PopupOpened(object sender, EventArgs e)
-        {
-            var selectedIndex = SelectedIndex;
-
-            if (selectedIndex != -1)
-            {
-                var container = ItemContainerGenerator.ContainerFromIndex(selectedIndex);
-                container?.Focus();
-            }
-        }
-
         private static void ControlPointerReleased(object sender, PointerReleasedEventArgs e)
         {
             var control = (Control)sender;

+ 17 - 4
src/Perspex.Controls/Control.cs

@@ -69,8 +69,7 @@ namespace Perspex.Controls
         /// Defines the <see cref="ContextMenu"/> property.
         /// </summary>
         public static readonly StyledProperty<ContextMenu> ContextMenuProperty =
-            PerspexProperty.Register<Control, ContextMenu>(nameof(ContextMenu));        
-
+            PerspexProperty.Register<Control, ContextMenu>(nameof(ContextMenu));
 
         /// <summary>
         /// Event raised when an element wishes to be scrolled into view.
@@ -393,6 +392,15 @@ namespace Perspex.Controls
                 });
         }
 
+        /// <summary>
+        /// Gets the element that recieves the focus adorner.
+        /// </summary>
+        /// <returns>The control that recieves the focus adorner.</returns>
+        protected virtual IControl GetTemplateFocusTarget()
+        {
+            return this;
+        }
+
         /// <summary>
         /// Called when the control is added to a logical tree.
         /// </summary>
@@ -488,8 +496,13 @@ namespace Perspex.Controls
 
                     if (_focusAdorner != null)
                     {
-                        AdornerLayer.SetAdornedElement((Visual)_focusAdorner, this);
-                        adornerLayer.Children.Add(_focusAdorner);
+                        var target = (Visual)GetTemplateFocusTarget();
+
+                        if (target != null)
+                        {
+                            AdornerLayer.SetAdornedElement((Visual)_focusAdorner, target);
+                            adornerLayer.Children.Add(_focusAdorner);
+                        }
                     }
                 }
             }

+ 1 - 1
src/Perspex.Controls/DropDown.cs

@@ -99,7 +99,7 @@ namespace Perspex.Controls
         }
 
         /// <inheritdoc/>
-        protected override void OnPointerPressed(PointerPressEventArgs e)
+        protected override void OnPointerPressed(PointerPressedEventArgs e)
         {
             if (!IsDropDownOpen && ((IVisual)e.Source).GetVisualRoot() != typeof(PopupRoot))
             {

+ 0 - 8
src/Perspex.Controls/Grid.cs

@@ -876,14 +876,6 @@ namespace Perspex.Controls
                 Stars = 0;
                 Type = type;
             }
-
-            public void Init(double offeredSize, double min, double max, GridUnitType type)
-            {
-                OfferedSize = offeredSize;
-                Min = min;
-                Max = max;
-                Type = type;
-            }
         }
 
         private struct GridNode

+ 11 - 0
src/Perspex.Controls/IControl.cs

@@ -1,6 +1,7 @@
 // Copyright (c) The Perspex Project. All rights reserved.
 // Licensed under the MIT license. See licence.md file in the project root for full license information.
 
+using System;
 using Perspex.Controls.Templates;
 using Perspex.Input;
 using Perspex.Layout;
@@ -13,6 +14,16 @@ namespace Perspex.Controls
     /// </summary>
     public interface IControl : IVisual, ILogical, ILayoutable, IInputElement, INamed, IStyleable, IStyleHost
     {
+        /// <summary>
+        /// Raised when the control is attached to a rooted logical tree.
+        /// </summary>
+        event EventHandler<LogicalTreeAttachmentEventArgs> AttachedToLogicalTree;
+
+        /// <summary>
+        /// Raised when the control is detached from a rooted logical tree.
+        /// </summary>
+        event EventHandler<LogicalTreeAttachmentEventArgs> DetachedFromLogicalTree;
+
         /// <summary>
         /// Gets or sets the control's styling classes.
         /// </summary>

+ 1 - 1
src/Perspex.Controls/ListBox.cs

@@ -59,7 +59,7 @@ namespace Perspex.Controls
         }
 
         /// <inheritdoc/>
-        protected override void OnPointerPressed(PointerPressEventArgs e)
+        protected override void OnPointerPressed(PointerPressedEventArgs e)
         {
             base.OnPointerPressed(e);
 

+ 1 - 1
src/Perspex.Controls/Menu.cs

@@ -211,7 +211,7 @@ namespace Perspex.Controls
         /// </summary>
         /// <param name="sender">The sender.</param>
         /// <param name="e">The event args.</param>
-        private void TopLevelPreviewPointerPress(object sender, PointerPressEventArgs e)
+        private void TopLevelPreviewPointerPress(object sender, PointerPressedEventArgs e)
         {
             if (IsOpen)
             {

+ 1 - 8
src/Perspex.Controls/MenuItem.cs

@@ -310,7 +310,7 @@ namespace Perspex.Controls
         /// Called when the pointer is pressed over the <see cref="MenuItem"/>.
         /// </summary>
         /// <param name="e">The event args.</param>
-        protected override void OnPointerPressed(PointerPressEventArgs e)
+        protected override void OnPointerPressed(PointerPressedEventArgs e)
         {
             base.OnPointerPressed(e);
 
@@ -447,13 +447,6 @@ namespace Perspex.Controls
             /// </summary>
             public static readonly DependencyResolver Instance = new DependencyResolver();
 
-            /// <summary>
-            /// Disposes of all managed resources.
-            /// </summary>
-            public void Dispose()
-            {
-            }
-
             /// <summary>
             /// Gets a service of the specified type.
             /// </summary>

+ 0 - 7
src/Perspex.Controls/Mixins/ContentControlMixin.cs

@@ -118,13 +118,6 @@ namespace Perspex.Controls.Mixins
             });
         }
 
-        private static event EventHandler<TemplateAppliedEventArgs> TemplateApplied;
-
-        private static void OnTemplateApplied(object sender, RoutedEventArgs e)
-        {
-            TemplateApplied?.Invoke(sender, (TemplateAppliedEventArgs)e);
-        }
-
         private static void UpdateLogicalChild(
             IControl control,
             IPerspexList<ILogical> logicalChildren,

+ 4 - 1
src/Perspex.Controls/Mixins/SelectableMixin.cs

@@ -50,11 +50,14 @@ namespace Perspex.Controls.Mixins
 
                 if (sender != null)
                 {
+                    var itemsControl = sender.Parent as SelectingItemsControl;
+
                     if ((bool)x.NewValue)
                     {
                         ((IPseudoClasses)sender.Classes).Add(":selected");
 
-                        if (((IVisual)sender).IsAttachedToVisualTree)
+                        if (((IVisual)sender).IsAttachedToVisualTree && 
+                            itemsControl?.AutoScrollToSelectedItem == true)
                         {
                             sender.BringIntoView();
                         }

+ 8 - 2
src/Perspex.Controls/Platform/PlatformManager.cs

@@ -192,8 +192,14 @@ namespace Perspex.Controls.Platform
         public static IWindowImpl CreateWindow()
         {
             var platform = PerspexLocator.Current.GetService<IWindowingPlatform>();
-            return
-                new WindowDecorator(s_designerMode ? platform.CreateEmbeddableWindow() : platform.CreateWindow());
+            
+            if (platform == null)
+            {
+                throw new Exception("Could not CreateWindow(): IWindowingPlatform is not registered.");
+            }
+
+            var window = s_designerMode ? platform.CreateEmbeddableWindow() : platform.CreateWindow();
+            return new WindowDecorator(window);
         }
 
         public static IPopupImpl CreatePopup()

+ 0 - 8
src/Perspex.Controls/Presenters/ScrollContentPresenter.cs

@@ -256,13 +256,5 @@ namespace Perspex.Controls.Presenters
             Extent = scrollable.Extent;
             Offset = scrollable.Offset;
         }
-
-        private static Vector ValidateOffset(ScrollContentPresenter o, Vector value)
-        {
-            return ScrollViewer.CoerceOffset(
-                o.GetValue(ExtentProperty),
-                o.GetValue(ViewportProperty),
-                value);
-        }
     }
 }

+ 1 - 1
src/Perspex.Controls/Primitives/Popup.cs

@@ -288,7 +288,7 @@ namespace Perspex.Controls.Primitives
             }
         }
 
-        private void PointerPressedOutside(object sender, PointerPressEventArgs e)
+        private void PointerPressedOutside(object sender, PointerPressedEventArgs e)
         {
             if (!StaysOpen)
             {

+ 17 - 0
src/Perspex.Controls/Primitives/SelectingItemsControl.cs

@@ -38,6 +38,14 @@ namespace Perspex.Controls.Primitives
     /// </remarks>
     public class SelectingItemsControl : ItemsControl
     {
+        /// <summary>
+        /// Defines the <see cref="AutoScrollToSelectedItem"/> property.
+        /// </summary>
+        public static readonly StyledProperty<bool> AutoScrollToSelectedItemProperty =
+            PerspexProperty.Register<SelectingItemsControl, bool>(
+                nameof(AutoScrollToSelectedItem),
+                defaultValue: true);
+
         /// <summary>
         /// Defines the <see cref="SelectedIndex"/> property.
         /// </summary>
@@ -123,6 +131,15 @@ namespace Perspex.Controls.Primitives
             remove { RemoveHandler(SelectionChangedEvent, value); }
         }
 
+        /// <summary>
+        /// Gets or sets a value indicating whether to automatically scroll to newly selected items.
+        /// </summary>
+        public bool AutoScrollToSelectedItem
+        {
+            get { return GetValue(AutoScrollToSelectedItemProperty); }
+            set { SetValue(AutoScrollToSelectedItemProperty, value); }
+        }
+
         /// <summary>
         /// Gets or sets the index of the selected item.
         /// </summary>

+ 1 - 1
src/Perspex.Controls/Primitives/TabStrip.cs

@@ -35,7 +35,7 @@ namespace Perspex.Controls.Primitives
         }
 
         /// <inheritdoc/>
-        protected override void OnPointerPressed(PointerPressEventArgs e)
+        protected override void OnPointerPressed(PointerPressedEventArgs e)
         {
             base.OnPointerPressed(e);
 

+ 48 - 0
src/Perspex.Controls/Primitives/TemplatedControl.cs

@@ -74,6 +74,12 @@ namespace Perspex.Controls.Primitives
         public static readonly StyledProperty<IControlTemplate> TemplateProperty =
             PerspexProperty.Register<TemplatedControl, IControlTemplate>("Template");
 
+        /// <summary>
+        /// Defines the IsTemplateFocusTarget attached property.
+        /// </summary>
+        public static readonly AttachedProperty<bool> IsTemplateFocusTargetProperty =
+            PerspexProperty.RegisterAttached<TemplatedControl, Control, bool>("IsTemplateFocusTarget");
+
         /// <summary>
         /// Defines the <see cref="TemplateApplied"/> routed event.
         /// </summary>
@@ -198,6 +204,33 @@ namespace Perspex.Controls.Primitives
             set { SetValue(TemplateProperty, value); }
         }
 
+        /// <summary>
+        /// Gets the value of the IsTemplateFocusTargetProperty attached property on a control.
+        /// </summary>
+        /// <param name="control">The control.</param>
+        /// <returns>The property value.</returns>
+        /// <see cref="SetIsTemplateFocusTarget(Control, bool)"/>
+        public bool GetIsTemplateFocusTarget(Control control)
+        {
+            return control.GetValue(IsTemplateFocusTargetProperty);
+        }
+
+        /// <summary>
+        /// Sets the value of the IsTemplateFocusTargetProperty attached property on a control.
+        /// </summary>
+        /// <param name="control">The control.</param>
+        /// <param name="value">The property value.</param>
+        /// <remarks>
+        /// When a control is navigated to using the keyboard, a focus adorner is shown - usually
+        /// around the control itself. However if the TemplatedControl.IsTemplateFocusTarget 
+        /// attached property is set to true on an element in the control template, then the focus
+        /// adorner will be shown around that control instead.
+        /// </remarks>
+        public void SetIsTemplateFocusTarget(Control control, bool value)
+        {
+            control.SetValue(IsTemplateFocusTargetProperty, value);
+        }
+
         /// <inheritdoc/>
         public sealed override void ApplyTemplate()
         {
@@ -224,6 +257,7 @@ namespace Perspex.Controls.Primitives
             }
         }
 
+        /// <inheritdoc/>
         protected sealed override IndexerDescriptor CreateBindingDescriptor(IndexerDescriptor source)
         {
             var result = base.CreateBindingDescriptor(source);
@@ -240,6 +274,20 @@ namespace Perspex.Controls.Primitives
             return result;
         }
 
+        /// <inheritdoc/>
+        protected override IControl GetTemplateFocusTarget()
+        {
+            foreach (Control child in this.GetTemplateChildren())
+            {
+                if (GetIsTemplateFocusTarget(child))
+                {
+                    return child;
+                }
+            }
+
+            return this;
+        }
+
         /// <summary>
         /// Called when the control's template is applied.
         /// </summary>

+ 1 - 1
src/Perspex.Controls/Primitives/Thumb.cs

@@ -72,7 +72,7 @@ namespace Perspex.Controls.Primitives
             }
         }
 
-        protected override void OnPointerPressed(PointerPressEventArgs e)
+        protected override void OnPointerPressed(PointerPressedEventArgs e)
         {
             e.Device.Capture(this);
             _lastPoint = e.GetPosition(this);

+ 20 - 11
src/Perspex.Controls/Templates/TemplateExtensions.cs

@@ -12,21 +12,30 @@ namespace Perspex.Controls.Templates
 {
     public static class TemplateExtensions
     {
-        public static IEnumerable<Control> GetTemplateChildren(this ITemplatedControl control)
+        public static IEnumerable<IControl> GetTemplateChildren(this ITemplatedControl control)
         {
-            var visual = control as IVisual;
-
-            if (visual != null)
+            foreach (IControl child in GetTemplateChildren((IControl)control, control))
             {
-                // TODO: This searches the whole descendent tree - it can stop when it exits the
-                // template.
-                return visual.GetVisualDescendents()
-                    .OfType<Control>()
-                    .Where(x => x.TemplatedParent == control);
+                yield return child;
             }
-            else
+        }
+
+        private static IEnumerable<IControl> GetTemplateChildren(IControl control, ITemplatedControl templatedParent)
+        {
+            foreach (IControl child in control.GetVisualChildren())
             {
-                return Enumerable.Empty<Control>();
+                if (child.TemplatedParent == templatedParent)
+                {
+                    yield return child;
+                }
+
+                if (child.TemplatedParent != null)
+                {
+                    foreach (var descendent in GetTemplateChildren(child, templatedParent))
+                    {
+                        yield return descendent;
+                    }
+                }
             }
         }
     }

+ 41 - 22
src/Perspex.Controls/TextBox.cs

@@ -51,6 +51,9 @@ namespace Perspex.Controls
         public static readonly StyledProperty<bool> UseFloatingWatermarkProperty =
             PerspexProperty.Register<TextBox, bool>("UseFloatingWatermark");
 
+        public static readonly StyledProperty<bool> IsReadOnlyProperty =
+            PerspexProperty.Register<TextBox, bool>(nameof(IsReadOnly));
+
         struct UndoRedoState : IEquatable<UndoRedoState>
         {
             public string Text { get; }
@@ -154,6 +157,12 @@ namespace Perspex.Controls
             set { SetValue(UseFloatingWatermarkProperty, value); }
         }
 
+        public bool IsReadOnly
+        {
+            get { return GetValue(IsReadOnlyProperty); }
+            set { SetValue(IsReadOnlyProperty, value); }
+        }
+
         public TextWrapping TextWrapping
         {
             get { return GetValue(TextWrappingProperty); }
@@ -187,17 +196,20 @@ namespace Perspex.Controls
 
         private void HandleTextInput(string input)
         {
-            string text = Text ?? string.Empty;
-            int caretIndex = CaretIndex;
-            if (!string.IsNullOrEmpty(input))
+            if (!IsReadOnly)
             {
-                DeleteSelection();
-                caretIndex = CaretIndex;
-                text = Text ?? string.Empty;
-                Text = text.Substring(0, caretIndex) + input + text.Substring(caretIndex);
-                CaretIndex += input.Length;
-                SelectionStart = SelectionEnd = CaretIndex;
-                _undoRedoHelper.DiscardRedo();
+                string text = Text ?? string.Empty;
+                int caretIndex = CaretIndex;
+                if (!string.IsNullOrEmpty(input))
+                {
+                    DeleteSelection();
+                    caretIndex = CaretIndex;
+                    text = Text ?? string.Empty;
+                    Text = text.Substring(0, caretIndex) + input + text.Substring(caretIndex);
+                    CaretIndex += input.Length;
+                    SelectionStart = SelectionEnd = CaretIndex;
+                    _undoRedoHelper.DiscardRedo();
+                }
             }
         }
 
@@ -343,7 +355,7 @@ namespace Perspex.Controls
             }
         }
 
-        protected override void OnPointerPressed(PointerPressEventArgs e)
+        protected override void OnPointerPressed(PointerPressedEventArgs e)
         {
             if (e.Source == _presenter)
             {
@@ -510,21 +522,28 @@ namespace Perspex.Controls
 
         private bool DeleteSelection()
         {
-            var selectionStart = SelectionStart;
-            var selectionEnd = SelectionEnd;
-
-            if (selectionStart != selectionEnd)
+            if (!IsReadOnly)
             {
-                var start = Math.Min(selectionStart, selectionEnd);
-                var end = Math.Max(selectionStart, selectionEnd);
-                var text = Text;
-                Text = text.Substring(0, start) + text.Substring(end);
-                SelectionStart = SelectionEnd = CaretIndex = start;
-                return true;
+                var selectionStart = SelectionStart;
+                var selectionEnd = SelectionEnd;
+
+                if (selectionStart != selectionEnd)
+                {
+                    var start = Math.Min(selectionStart, selectionEnd);
+                    var end = Math.Max(selectionStart, selectionEnd);
+                    var text = Text;
+                    Text = text.Substring(0, start) + text.Substring(end);
+                    SelectionStart = SelectionEnd = CaretIndex = start;
+                    return true;
+                }
+                else
+                {
+                    return false;
+                }
             }
             else
             {
-                return false;
+                return true;
             }
         }
 

+ 2 - 4
src/Perspex.Controls/TreeView.cs

@@ -22,9 +22,7 @@ namespace Perspex.Controls
         /// Defines the <see cref="AutoScrollToSelectedItem"/> property.
         /// </summary>
         public static readonly StyledProperty<bool> AutoScrollToSelectedItemProperty =
-            PerspexProperty.Register<TreeView, bool>(
-                nameof(AutoScrollToSelectedItem),
-                defaultValue: true);
+            SelectingItemsControl.AutoScrollToSelectedItemProperty.AddOwner<TreeView>();
 
         /// <summary>
         /// Defines the <see cref="SelectedItem"/> property.
@@ -118,7 +116,7 @@ namespace Perspex.Controls
         }
 
         /// <inheritdoc/>
-        protected override void OnPointerPressed(PointerPressEventArgs e)
+        protected override void OnPointerPressed(PointerPressedEventArgs e)
         {
             base.OnPointerPressed(e);
 

+ 1 - 1
src/Perspex.HtmlRenderer/HtmlControl.cs

@@ -401,7 +401,7 @@ namespace Perspex.Controls.Html
         /// <summary>
         /// Handle mouse down to handle selection. 
         /// </summary>
-        protected override void OnPointerPressed(PointerPressEventArgs e)
+        protected override void OnPointerPressed(PointerPressedEventArgs e)
         {
             base.OnPointerPressed(e);
             LeftMouseButton = true;

+ 13 - 3
src/Perspex.Input/Cursors.cs

@@ -56,12 +56,22 @@ namespace Perspex.Input
         }
 
         public Cursor(StandardCursorType cursorType)
-            : this(
-                ((IStandardCursorFactory)PerspexLocator.Current.GetService(typeof(IStandardCursorFactory))).GetCursor(
-                    cursorType))
+            : this(GetCursor(cursorType))
         {
         }
 
         public IPlatformHandle PlatformCursor { get; }
+
+        private static IPlatformHandle GetCursor(StandardCursorType type)
+        {
+            var platform = PerspexLocator.Current.GetService<IStandardCursorFactory>();
+
+            if (platform == null)
+            {
+                throw new Exception("Could not create Cursor: IStandardCursorFactory not registered.");
+            }
+
+            return platform.GetCursor(type);
+        }
     }
 }

+ 1 - 1
src/Perspex.Input/FocusManager.cs

@@ -175,7 +175,7 @@ namespace Perspex.Input
         {
             if (sender == e.Source)
             {
-                var ev = (PointerPressEventArgs)e;
+                var ev = (PointerPressedEventArgs)e;
                 var element = (ev.Device.Captured as IInputElement) ?? (e.Source as IInputElement);
 
                 if (element == null || !CanFocus(element))

+ 1 - 1
src/Perspex.Input/IInputElement.cs

@@ -51,7 +51,7 @@ namespace Perspex.Input
         /// <summary>
         /// Occurs when the pointer is pressed over the control.
         /// </summary>
-        event EventHandler<PointerPressEventArgs> PointerPressed;
+        event EventHandler<PointerPressedEventArgs> PointerPressed;
 
         /// <summary>
         /// Occurs when the pointer moves over the control.

+ 4 - 4
src/Perspex.Input/InputElement.cs

@@ -116,8 +116,8 @@ namespace Perspex.Input
         /// <summary>
         /// Defines the <see cref="PointerPressed"/> event.
         /// </summary>
-        public static readonly RoutedEvent<PointerPressEventArgs> PointerPressedEvent =
-            RoutedEvent.Register<InputElement, PointerPressEventArgs>(
+        public static readonly RoutedEvent<PointerPressedEventArgs> PointerPressedEvent =
+            RoutedEvent.Register<InputElement, PointerPressedEventArgs>(
                 "PointerPressed",
                 RoutingStrategies.Tunnel | RoutingStrategies.Bubble);
 
@@ -235,7 +235,7 @@ namespace Perspex.Input
         /// <summary>
         /// Occurs when the pointer is pressed over the control.
         /// </summary>
-        public event EventHandler<PointerPressEventArgs> PointerPressed
+        public event EventHandler<PointerPressedEventArgs> PointerPressed
         {
             add { AddHandler(PointerPressedEvent, value); }
             remove { RemoveHandler(PointerPressedEvent, value); }
@@ -437,7 +437,7 @@ namespace Perspex.Input
         /// Called before the <see cref="PointerPressed"/> event occurs.
         /// </summary>
         /// <param name="e">The event args.</param>
-        protected virtual void OnPointerPressed(PointerPressEventArgs e)
+        protected virtual void OnPointerPressed(PointerPressedEventArgs e)
         {
         }
 

+ 9 - 2
src/Perspex.Input/KeyboardDevice.cs

@@ -83,6 +83,7 @@ namespace Perspex.Input
             if (element != null)
             {
                 var keyInput = e as RawKeyEventArgs;
+
                 if (keyInput != null)
                 {
                     switch (keyInput.Type)
@@ -117,19 +118,25 @@ namespace Perspex.Input
                             }
 
                             element.RaiseEvent(ev);
+                            e.Handled = ev.Handled;
                             break;
                     }
                 }
+
                 var text = e as RawTextInputEventArgs;
+
                 if (text != null)
                 {
-                    element.RaiseEvent(new TextInputEventArgs()
+                    var ev = new TextInputEventArgs()
                     {
                         Device = this,
                         Text = text.Text,
                         Source = element,
                         RoutedEvent = InputElement.TextInputEvent
-                    });
+                    };
+
+                    element.RaiseEvent(ev);
+                    e.Handled = ev.Handled;
                 }
             }
         }

+ 33 - 19
src/Perspex.Input/MouseDevice.cs

@@ -115,7 +115,7 @@ namespace Perspex.Input
                 case RawMouseEventType.LeftButtonDown:
                 case RawMouseEventType.RightButtonDown:
                 case RawMouseEventType.MiddleButtonDown:
-                    MouseDown(mouse, e.Timestamp, e.Root, e.Position,
+                    e.Handled = MouseDown(mouse, e.Timestamp, e.Root, e.Position,
                          e.Type == RawMouseEventType.LeftButtonDown
                             ? MouseButton.Left
                             : e.Type == RawMouseEventType.RightButtonDown ? MouseButton.Right : MouseButton.Middle,
@@ -124,17 +124,17 @@ namespace Perspex.Input
                 case RawMouseEventType.LeftButtonUp:
                 case RawMouseEventType.RightButtonUp:
                 case RawMouseEventType.MiddleButtonUp:
-                    MouseUp(mouse, e.Root, e.Position,
+                    e.Handled = MouseUp(mouse, e.Root, e.Position,
                         e.Type == RawMouseEventType.LeftButtonUp
                             ? MouseButton.Left
                             : e.Type == RawMouseEventType.RightButtonUp ? MouseButton.Right : MouseButton.Middle,
                         e.InputModifiers);
                     break;
                 case RawMouseEventType.Move:
-                    MouseMove(mouse, e.Root, e.Position, e.InputModifiers);
+                    e.Handled = MouseMove(mouse, e.Root, e.Position, e.InputModifiers);
                     break;
                 case RawMouseEventType.Wheel:
-                    MouseWheel(mouse, e.Root, e.Position, ((RawMouseWheelEventArgs)e).Delta, e.InputModifiers);
+                    e.Handled = MouseWheel(mouse, e.Root, e.Position, ((RawMouseWheelEventArgs)e).Delta, e.InputModifiers);
                     break;
             }
         }
@@ -144,7 +144,7 @@ namespace Perspex.Input
             ClearPointerOver(this, root);
         }
 
-        private void MouseDown(IMouseDevice device, uint timestamp, IInputElement root, Point p, MouseButton button, InputModifiers inputModifiers)
+        private bool MouseDown(IMouseDevice device, uint timestamp, IInputElement root, Point p, MouseButton button, InputModifiers inputModifiers)
         {
             var hit = HitTest(root, p);
 
@@ -167,7 +167,7 @@ namespace Perspex.Input
                     _lastClickRect = new Rect(p, new Size())
                         .Inflate(new Thickness(settings.DoubleClickSize.Width / 2, settings.DoubleClickSize.Height / 2));
 
-                    var e = new PointerPressEventArgs
+                    var e = new PointerPressedEventArgs
                     {
                         Device = this,
                         RoutedEvent = InputElement.PointerPressedEvent,
@@ -178,11 +178,14 @@ namespace Perspex.Input
                     };
 
                     source.RaiseEvent(e);
+                    return e.Handled;
                 }
             }
+
+            return false;
         }
 
-        private void MouseMove(IMouseDevice device, IInputRoot root, Point p, InputModifiers inputModifiers)
+        private bool MouseMove(IMouseDevice device, IInputRoot root, Point p, InputModifiers inputModifiers)
         {
             IInputElement source;
 
@@ -197,51 +200,62 @@ namespace Perspex.Input
                 source = Captured;
             }
 
-            source.RaiseEvent(new PointerEventArgs
+            var e = new PointerEventArgs
             {
                 Device = this,
                 RoutedEvent = InputElement.PointerMovedEvent,
                 Source = source,
                 InputModifiers = inputModifiers
-            });
+            };
+
+            source.RaiseEvent(e);
+            return e.Handled;
         }
 
-        private void MouseUp(IMouseDevice device, IInputRoot root, Point p, MouseButton button, InputModifiers inputModifiers)
+        private bool MouseUp(IMouseDevice device, IInputRoot root, Point p, MouseButton button, InputModifiers inputModifiers)
         {
             var hit = HitTest(root, p);
 
             if (hit != null)
             {
-                IInteractive source = GetSource(hit);
-
-                source?.RaiseEvent(new PointerReleasedEventArgs
+                var source = GetSource(hit);
+                var e = new PointerReleasedEventArgs
                 {
                     Device = this,
                     RoutedEvent = InputElement.PointerReleasedEvent,
                     Source = source,
                     MouseButton = button,
                     InputModifiers = inputModifiers
-                });
+                };
+
+                source?.RaiseEvent(e);
+                return e.Handled;
             }
+
+            return false;
         }
 
-        private void MouseWheel(IMouseDevice device, IInputRoot root, Point p, Vector delta, InputModifiers inputModifiers)
+        private bool MouseWheel(IMouseDevice device, IInputRoot root, Point p, Vector delta, InputModifiers inputModifiers)
         {
             var hit = HitTest(root, p);
 
             if (hit != null)
             {
-                IInteractive source = GetSource(hit);
-
-                source?.RaiseEvent(new PointerWheelEventArgs
+                var source = GetSource(hit);
+                var e = new PointerWheelEventArgs
                 {
                     Device = this,
                     RoutedEvent = InputElement.PointerWheelChangedEvent,
                     Source = source,
                     Delta = delta,
                     InputModifiers = inputModifiers
-                });
+                };
+
+                source?.RaiseEvent(e);
+                return e.Handled;
             }
+
+            return false;
         }
 
         private IInteractive GetSource(IVisual hit)

+ 1 - 1
src/Perspex.Input/PointerEventArgs.cs

@@ -26,7 +26,7 @@ namespace Perspex.Input
         Middle
     }
 
-    public class PointerPressEventArgs : PointerEventArgs
+    public class PointerPressedEventArgs : PointerEventArgs
     {
         public int ClickCount { get; set; }
         public MouseButton MouseButton { get; set; }

+ 1 - 1
src/Perspex.Input/Raw/RawInputEventArgs.cs

@@ -16,7 +16,7 @@ namespace Perspex.Input.Raw
         }
 
         public IInputDevice Device { get; private set; }
-
+        public bool Handled { get; set; }
         public uint Timestamp { get; private set; }
     }
 }

+ 5 - 0
src/Perspex.SceneGraph/Media/FormattedText.cs

@@ -42,6 +42,11 @@ namespace Perspex.Media
 
             var platform = PerspexLocator.Current.GetService<IPlatformRenderInterface>();
 
+            if (platform == null)
+            {
+                throw new Exception("Could not create FormattedText: IPlatformRenderInterface not registered.");
+            }
+
             PlatformImpl = platform.CreateFormattedText(
                 text,
                 fontFamilyName,

+ 1 - 7
src/Perspex.Themes.Default/TreeViewItem.paml

@@ -11,18 +11,12 @@
                     Background="{TemplateBinding Background}"
                     BorderBrush="{TemplateBinding BorderBrush}"
                     BorderThickness="{TemplateBinding BorderThickness}"
+                    TemplatedControl.IsTemplateFocusTarget="True"
                     Grid.Column="1">
               <ContentPresenter Name="PART_HeaderPresenter"
                                 Content="{TemplateBinding Header}"
                                 Margin="{TemplateBinding Padding}"/>
             </Border>
-            <Rectangle Name="focus"
-                       IsHitTestVisible="False"
-                       IsVisible="{TemplateBinding IsFocused}"
-                       Stroke="Black"
-                       StrokeThickness="1"
-                       StrokeDashArray="1,2"
-                       Grid.Column="1"/>
           </Grid>
           <ItemsPresenter Name="PART_ItemsPresenter"
                           IsVisible="{TemplateBinding IsExpanded}"

+ 1 - 1
src/Skia/Perspex.Skia/DrawingContextImpl.cs

@@ -180,7 +180,7 @@ namespace Perspex.Skia
         public void PopOpacity() => _settings->Opacity = _opacityStack.Pop();
 
         private Matrix _currentTransform = Matrix.Identity;
-        private readonly float[] _fmatrix = new float[6];
+
         public Matrix Transform
         {
             get { return _currentTransform; }

+ 10 - 2
src/Windows/Perspex.Direct2D1/app.config

@@ -15,8 +15,16 @@
         <bindingRedirect oldVersion="0.0.0.0-2.1.30214.0" newVersion="2.1.30214.0" />
       </dependentAssembly>
       <dependentAssembly>
-        <assemblyIdentity name="SharpDX" publicKeyToken="b4dcf0f35e5521f1" culture="neutral" />
-        <bindingRedirect oldVersion="0.0.0.0-2.6.3.0" newVersion="2.6.3.0" />
+        <assemblyIdentity name="SharpDX.Direct2D1" publicKeyToken="b4dcf0f35e5521f1" culture="neutral"/>
+        <bindingRedirect oldVersion="0.0.0.0-3.0.1.0" newVersion="3.0.1.0"/>
+      </dependentAssembly>
+      <dependentAssembly>
+        <assemblyIdentity name="SharpDX" publicKeyToken="b4dcf0f35e5521f1" culture="neutral"/>
+        <bindingRedirect oldVersion="0.0.0.0-3.0.1.0" newVersion="3.0.1.0"/>
+      </dependentAssembly>
+      <dependentAssembly>
+        <assemblyIdentity name="SharpDX.DXGI" publicKeyToken="b4dcf0f35e5521f1" culture="neutral"/>
+        <bindingRedirect oldVersion="0.0.0.0-3.0.1.0" newVersion="3.0.1.0"/>
       </dependentAssembly>
     </assemblyBinding>
   </runtime>

+ 4 - 4
src/Windows/Perspex.Win32/WindowImpl.cs

@@ -553,10 +553,10 @@ namespace Perspex.Win32
             {
                 Input(e);
 
-                if (msg >= 161 && msg <= 173)
-                    return UnmanagedMethods.DefWindowProc(hWnd, msg, wParam, lParam);
-
-                return IntPtr.Zero;
+                if (e.Handled)
+                {
+                    return IntPtr.Zero;
+                }
             }
 
             return UnmanagedMethods.DefWindowProc(hWnd, msg, wParam, lParam);

+ 0 - 3
tests/Perspex.Base.UnitTests/PerspexObjectTests_Binding.cs

@@ -262,9 +262,6 @@ namespace Perspex.Base.UnitTests
             public static readonly StyledProperty<string> FooProperty =
                 PerspexProperty.Register<Class1, string>("Foo", "foodefault");
 
-            public static readonly StyledProperty<string> BazProperty =
-                PerspexProperty.Register<Class1, string>("Baz", "bazdefault", true);
-
             public static readonly StyledProperty<double> QuxProperty =
                 PerspexProperty.Register<Class1, double>("Qux", 5.6);
         }

+ 0 - 12
tests/Perspex.Base.UnitTests/PerspexObjectTests_Metadata.cs

@@ -51,24 +51,12 @@ namespace Perspex.Base.UnitTests
         {
             public static readonly StyledProperty<string> FooProperty =
                 PerspexProperty.Register<Class1, string>("Foo");
-
-            public static readonly StyledProperty<string> BazProperty =
-                PerspexProperty.Register<Class1, string>("Baz");
-
-            public static readonly StyledProperty<int> QuxProperty =
-                PerspexProperty.Register<Class1, int>("Qux");
         }
 
         private class Class2 : Class1
         {
             public static readonly StyledProperty<string> BarProperty =
                 PerspexProperty.Register<Class2, string>("Bar");
-
-            public static readonly StyledProperty<double> FlobProperty =
-                PerspexProperty.Register<Class2, double>("Flob");
-
-            public static readonly StyledProperty<double?> FredProperty =
-                PerspexProperty.Register<Class2, double?>("Fred");
         }
 
         private class AttachedOwner

+ 0 - 2
tests/Perspex.Base.UnitTests/StyledPropertyTests.cs

@@ -35,8 +35,6 @@ namespace Perspex.Base.UnitTests
 
         private class Class1 : PerspexObject
         {
-            public static readonly StyledProperty<string> FooProperty =
-                PerspexProperty.Register<Class1, string>("Foo", "default");
         }
 
         private class Class2 : PerspexObject

+ 28 - 47
tests/Perspex.Controls.UnitTests/ContentControlTests.cs

@@ -1,18 +1,15 @@
 // Copyright (c) The Perspex Project. All rights reserved.
 // Licensed under the MIT license. See licence.md file in the project root for full license information.
 
-using System;
 using System.Collections.Specialized;
 using System.Linq;
 using Moq;
 using Perspex.Controls.Presenters;
 using Perspex.Controls.Templates;
 using Perspex.LogicalTree;
-using Perspex.Platform;
 using Perspex.Styling;
+using Perspex.UnitTests;
 using Perspex.VisualTree;
-using Ploeh.AutoFixture;
-using Ploeh.AutoFixture.AutoMoq;
 using Xunit;
 
 namespace Perspex.Controls.UnitTests
@@ -22,44 +19,38 @@ namespace Perspex.Controls.UnitTests
         [Fact]
         public void Template_Should_Be_Instantiated()
         {
-            using (var ctx = RegisterServices())
-            {
-                var target = new ContentControl();
-                target.Content = "Foo";
-                target.Template = GetTemplate();
-                target.ApplyTemplate();
-                ((ContentPresenter)target.Presenter).UpdateChild();
-
-                var child = ((IVisual)target).VisualChildren.Single();
-                Assert.IsType<Border>(child);
-                child = child.VisualChildren.Single();
-                Assert.IsType<ContentPresenter>(child);
-                child = child.VisualChildren.Single();
-                Assert.IsType<TextBlock>(child);
-            }
+            var target = new ContentControl();
+            target.Content = "Foo";
+            target.Template = GetTemplate();
+            target.ApplyTemplate();
+            ((ContentPresenter)target.Presenter).UpdateChild();
+
+            var child = ((IVisual)target).VisualChildren.Single();
+            Assert.IsType<Border>(child);
+            child = child.VisualChildren.Single();
+            Assert.IsType<ContentPresenter>(child);
+            child = child.VisualChildren.Single();
+            Assert.IsType<TextBlock>(child);
         }
 
         [Fact]
         public void Templated_Children_Should_Be_Styled()
         {
-            using (var ctx = RegisterServices())
-            {
-                var root = new TestRoot();
-                var target = new ContentControl();
-                var styler = new Mock<IStyler>();
-
-                PerspexLocator.CurrentMutable.Bind<IStyler>().ToConstant(styler.Object);
-                target.Content = "Foo";
-                target.Template = GetTemplate();
-                root.Child = target;
-
-                target.ApplyTemplate();
-
-                styler.Verify(x => x.ApplyStyles(It.IsAny<ContentControl>()), Times.Once());
-                styler.Verify(x => x.ApplyStyles(It.IsAny<Border>()), Times.Once());
-                styler.Verify(x => x.ApplyStyles(It.IsAny<ContentPresenter>()), Times.Once());
-                styler.Verify(x => x.ApplyStyles(It.IsAny<TextBlock>()), Times.Once());
-            }
+            var root = new TestRoot();
+            var target = new ContentControl();
+            var styler = new Mock<IStyler>();
+
+            PerspexLocator.CurrentMutable.Bind<IStyler>().ToConstant(styler.Object);
+            target.Content = "Foo";
+            target.Template = GetTemplate();
+            root.Child = target;
+
+            target.ApplyTemplate();
+
+            styler.Verify(x => x.ApplyStyles(It.IsAny<ContentControl>()), Times.Once());
+            styler.Verify(x => x.ApplyStyles(It.IsAny<Border>()), Times.Once());
+            styler.Verify(x => x.ApplyStyles(It.IsAny<ContentPresenter>()), Times.Once());
+            styler.Verify(x => x.ApplyStyles(It.IsAny<TextBlock>()), Times.Once());
         }
 
         [Fact]
@@ -261,15 +252,5 @@ namespace Perspex.Controls.UnitTests
                 };
             });
         }
-
-        private IDisposable RegisterServices()
-        {
-            var result = PerspexLocator.EnterScope();
-            var fixture = new Fixture().Customize(new AutoMoqCustomization());
-            var renderInterface = fixture.Create<IPlatformRenderInterface>();
-            PerspexLocator.CurrentMutable
-                .Bind<IPlatformRenderInterface>().ToConstant(renderInterface);
-            return result;
-        }
     }
 }

+ 1 - 41
tests/Perspex.Controls.UnitTests/ControlTests.cs

@@ -5,10 +5,8 @@ using System;
 using System.Collections.Generic;
 using System.Reactive.Linq;
 using Moq;
-using Perspex.Layout;
-using Perspex.Platform;
-using Perspex.Rendering;
 using Perspex.Styling;
+using Perspex.UnitTests;
 using Xunit;
 
 namespace Perspex.Controls.UnitTests
@@ -147,44 +145,6 @@ namespace Perspex.Controls.UnitTests
             }
         }
 
-        private class TestRoot : Decorator, ILayoutRoot, IRenderRoot, IStyleRoot
-        {
-            public Size ClientSize
-            {
-                get { throw new NotImplementedException(); }
-            }
-
-            public Size MaxClientSize
-            {
-                get { throw new NotImplementedException(); }
-            }
-
-            public ILayoutManager LayoutManager
-            {
-                get { throw new NotImplementedException(); }
-            }
-
-            public IRenderTarget RenderTarget
-            {
-                get { throw new NotImplementedException(); }
-            }
-
-            public IRenderQueueManager RenderQueueManager
-            {
-                get { throw new NotImplementedException(); }
-            }
-
-            public Point PointToClient(Point p)
-            {
-                throw new NotImplementedException();
-            }
-
-            public Point PointToScreen(Point p)
-            {
-                throw new NotImplementedException();
-            }
-        }
-
         private class TestControl : Control
         {
             public new PerspexObject InheritanceParent => base.InheritanceParent;

+ 9 - 67
tests/Perspex.Controls.UnitTests/ControlTests_NameScope.cs

@@ -6,6 +6,7 @@ using Perspex.Controls.Presenters;
 using Perspex.Controls.Templates;
 using Perspex.Rendering;
 using Perspex.Styling;
+using Perspex.UnitTests;
 using Xunit;
 
 namespace Perspex.Controls.UnitTests
@@ -17,7 +18,7 @@ namespace Perspex.Controls.UnitTests
         {
             var root = new TestRoot
             {
-                Content = new Border
+                Child = new Border
                 {
                     Name = "foo",
                     Child = new Border
@@ -28,10 +29,9 @@ namespace Perspex.Controls.UnitTests
             };
 
             root.ApplyTemplate();
-            ((ContentPresenter)root.Presenter).UpdateChild();
 
-            Assert.Same(root.Find("foo"), root.Content);
-            Assert.Same(root.Find("bar"), ((Border)root.Content).Child);
+            Assert.Same(root.FindControl<Border>("foo"), root.Child);
+            Assert.Same(root.FindControl<Border>("bar"), ((Border)root.Child).Child);
         }
 
         [Fact]
@@ -39,7 +39,7 @@ namespace Perspex.Controls.UnitTests
         {
             var root = new TestRoot
             {
-                Content = new Border
+                Child = new Border
                 {
                     Name = "foo",
                     Child = new Border
@@ -49,18 +49,16 @@ namespace Perspex.Controls.UnitTests
                 }
             };
 
-            root.ApplyTemplate();
-            root.Content = null;
-            root.Presenter.ApplyTemplate();
+            root.Child = null;
 
-            Assert.Null(root.Find("foo"));
-            Assert.Null(root.Find("bar"));
+            Assert.Null(root.FindControl<Border>("foo"));
+            Assert.Null(root.FindControl<Border>("bar"));
         }
 
         [Fact]
         public void Control_Should_Not_Register_With_Template_NameScope()
         {
-            var root = new TestRoot
+            var root = new TestTemplatedRoot
             {
                 Content = new Border
                 {
@@ -72,61 +70,5 @@ namespace Perspex.Controls.UnitTests
 
             Assert.Null(NameScope.GetNameScope((Control)root.Presenter).Find("foo"));
         }
-
-        private class TestRoot : ContentControl, IRenderRoot, INameScope, IStyleRoot
-        {
-            private readonly NameScope _nameScope = new NameScope();
-
-            public TestRoot()
-            {
-                Template = new FuncControlTemplate<TestRoot>(x => new ContentPresenter
-                {
-                    Name = "PART_ContentPresenter",
-                    [!ContentPresenter.ContentProperty] = x[!ContentControl.ContentProperty],
-                });
-            }
-
-            public event EventHandler<NameScopeEventArgs> Registered
-            {
-                add { _nameScope.Registered += value; }
-                remove { _nameScope.Registered -= value; }
-            }
-
-            public event EventHandler<NameScopeEventArgs> Unregistered
-            {
-                add { _nameScope.Unregistered += value; }
-                remove { _nameScope.Unregistered -= value; }
-            }
-
-            public IRenderQueueManager RenderQueueManager
-            {
-                get { throw new NotImplementedException(); }
-            }
-
-            public Point PointToClient(Point p)
-            {
-                throw new NotImplementedException();
-            }
-
-            public Point PointToScreen(Point p)
-            {
-                throw new NotImplementedException();
-            }
-
-            public void Register(string name, object element)
-            {
-                _nameScope.Register(name, element);
-            }
-
-            public object Find(string name)
-            {
-                return _nameScope.Find(name);
-            }
-
-            public void Unregister(string name)
-            {
-                _nameScope.Unregister(name);
-            }
-        }
     }
 }

+ 0 - 10
tests/Perspex.Controls.UnitTests/DropDownTests.cs

@@ -41,15 +41,5 @@ namespace Perspex.Controls.UnitTests
                 };
             });
         }
-
-        private IDisposable RegisterServices()
-        {
-            var result = PerspexLocator.EnterScope();
-            var fixture = new Fixture().Customize(new AutoMoqCustomization());
-            var renderInterface = fixture.Create<IPlatformRenderInterface>();
-            PerspexLocator.CurrentMutable.Bind<IPlatformRenderInterface>().ToConstant(renderInterface);
-
-            return result;
-        }
     }
 }

+ 1 - 1
tests/Perspex.Controls.UnitTests/GridSplitterTests.cs

@@ -3,7 +3,7 @@
 using Perspex.Controls.Primitives;
 using Perspex.Input;
 using Perspex.Platform;
-
+using Perspex.UnitTests;
 using Xunit;
 
 namespace Perspex.Controls.UnitTests

+ 1 - 1
tests/Perspex.Controls.UnitTests/ListBoxTests.cs

@@ -115,7 +115,7 @@ namespace Perspex.Controls.UnitTests
 
             ApplyTemplate(target);
 
-            target.Presenter.Panel.Children[1].RaiseEvent(new PointerPressEventArgs
+            target.Presenter.Panel.Children[1].RaiseEvent(new PointerPressedEventArgs
             {
                 RoutedEvent = InputElement.PointerPressedEvent,
                 MouseButton = MouseButton.Left,

+ 6 - 16
tests/Perspex.Controls.UnitTests/ListBoxTests_Single.cs

@@ -65,7 +65,7 @@ namespace Perspex.Controls.UnitTests
 
             ApplyTemplate(target);
 
-            target.Presenter.Panel.Children[0].RaiseEvent(new PointerPressEventArgs
+            target.Presenter.Panel.Children[0].RaiseEvent(new PointerPressedEventArgs
             {
                 RoutedEvent = InputElement.PointerPressedEvent,
                 MouseButton = MouseButton.Left,
@@ -86,7 +86,7 @@ namespace Perspex.Controls.UnitTests
             ApplyTemplate(target);
             target.SelectedIndex = 0;
 
-            target.Presenter.Panel.Children[0].RaiseEvent(new PointerPressEventArgs
+            target.Presenter.Panel.Children[0].RaiseEvent(new PointerPressedEventArgs
             {
                 RoutedEvent = InputElement.PointerPressedEvent,
                 MouseButton = MouseButton.Left,
@@ -107,7 +107,7 @@ namespace Perspex.Controls.UnitTests
 
             ApplyTemplate(target);
 
-            target.Presenter.Panel.Children[0].RaiseEvent(new PointerPressEventArgs
+            target.Presenter.Panel.Children[0].RaiseEvent(new PointerPressedEventArgs
             {
                 RoutedEvent = InputElement.PointerPressedEvent,
                 MouseButton = MouseButton.Left,
@@ -129,7 +129,7 @@ namespace Perspex.Controls.UnitTests
             ApplyTemplate(target);
             target.SelectedIndex = 0;
 
-            target.Presenter.Panel.Children[0].RaiseEvent(new PointerPressEventArgs
+            target.Presenter.Panel.Children[0].RaiseEvent(new PointerPressedEventArgs
             {
                 RoutedEvent = InputElement.PointerPressedEvent,
                 MouseButton = MouseButton.Left,
@@ -151,7 +151,7 @@ namespace Perspex.Controls.UnitTests
             ApplyTemplate(target);
             target.SelectedIndex = 0;
 
-            target.Presenter.Panel.Children[0].RaiseEvent(new PointerPressEventArgs
+            target.Presenter.Panel.Children[0].RaiseEvent(new PointerPressedEventArgs
             {
                 RoutedEvent = InputElement.PointerPressedEvent,
                 MouseButton = MouseButton.Left,
@@ -173,7 +173,7 @@ namespace Perspex.Controls.UnitTests
             ApplyTemplate(target);
             target.SelectedIndex = 1;
 
-            target.Presenter.Panel.Children[0].RaiseEvent(new PointerPressEventArgs
+            target.Presenter.Panel.Children[0].RaiseEvent(new PointerPressedEventArgs
             {
                 RoutedEvent = InputElement.PointerPressedEvent,
                 MouseButton = MouseButton.Left,
@@ -236,15 +236,5 @@ namespace Perspex.Controls.UnitTests
             // Now the ItemsPresenter should be reigstered, so apply its template.
             target.Presenter.ApplyTemplate();
         }
-
-        private class Item
-        {
-            public Item(string value)
-            {
-                Value = value;
-            }
-
-            public string Value { get; }
-        }
     }
 }

+ 0 - 5
tests/Perspex.Controls.UnitTests/PanelTests.cs

@@ -117,10 +117,5 @@ namespace Perspex.Controls.UnitTests
             Assert.Equal(new Control[0], panel.Children);
             Assert.Equal(new ILogical[0], panel.GetLogicalChildren());
         }
-
-        private class TestReparent : Panel
-        {
-            public new IPerspexList<ILogical> LogicalChildren => base.LogicalChildren;
-        }
     }
 }

+ 11 - 1
tests/Perspex.Controls.UnitTests/Perspex.Controls.UnitTests.csproj

@@ -137,7 +137,6 @@
     <Compile Include="Properties\AssemblyInfo.cs" />
     <Compile Include="Templates\TemplateExtensionsTests.cs" />
     <Compile Include="TestTemplatedControl.cs" />
-    <Compile Include="TestRoot.cs" />
     <Compile Include="Utils\AncestorFinderTests.cs" />
     <Compile Include="Utils\HotKeyManagerTests.cs" />
     <Compile Include="WrapPanelTests.cs" />
@@ -151,6 +150,10 @@
       <Project>{D211E587-D8BC-45B9-95A4-F297C8FA5200}</Project>
       <Name>Perspex.Animation</Name>
     </ProjectReference>
+    <ProjectReference Include="..\..\src\Perspex.Application\Perspex.Application.csproj">
+      <Project>{799a7bb5-3c2c-48b6-85a7-406a12c420da}</Project>
+      <Name>Perspex.Application</Name>
+    </ProjectReference>
     <ProjectReference Include="..\..\src\Perspex.Base\Perspex.Base.csproj">
       <Project>{B09B78D8-9B26-48B0-9149-D64A2F120F3F}</Project>
       <Name>Perspex.Base</Name>
@@ -179,6 +182,10 @@
       <Project>{F1BAA01A-F176-4C6A-B39D-5B40BB1B148F}</Project>
       <Name>Perspex.Styling</Name>
     </ProjectReference>
+    <ProjectReference Include="..\Perspex.UnitTests\Perspex.UnitTests.csproj">
+      <Project>{88060192-33d5-4932-b0f9-8bd2763e857d}</Project>
+      <Name>Perspex.UnitTests</Name>
+    </ProjectReference>
   </ItemGroup>
   <ItemGroup>
     <None Include="app.config" />
@@ -187,6 +194,9 @@
   <ItemGroup>
     <Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
   </ItemGroup>
+  <ItemGroup>
+    <WCFMetadata Include="Service References\" />
+  </ItemGroup>
   <Choose>
     <When Condition="'$(VisualStudioVersion)' == '10.0' And '$(IsCodedUITest)' == 'True'">
       <ItemGroup>

+ 1 - 0
tests/Perspex.Controls.UnitTests/Presenters/ContentPresenterTests.cs

@@ -6,6 +6,7 @@ using Moq;
 using Perspex.Controls.Presenters;
 using Perspex.Controls.Primitives;
 using Perspex.Controls.Templates;
+using Perspex.UnitTests;
 using Perspex.VisualTree;
 using Xunit;
 

+ 1 - 0
tests/Perspex.Controls.UnitTests/Primitives/PopupTests.cs

@@ -12,6 +12,7 @@ using Perspex.Layout;
 using Perspex.LogicalTree;
 using Perspex.Platform;
 using Perspex.Styling;
+using Perspex.UnitTests;
 using Perspex.VisualTree;
 using Xunit;
 

+ 0 - 5
tests/Perspex.Controls.UnitTests/Primitives/SelectingItemsControlTests_AutoSelect.cs

@@ -100,10 +100,5 @@ namespace Perspex.Controls.UnitTests.Primitives
                 SelectionModeProperty.OverrideDefaultValue<TestSelector>(SelectionMode.AlwaysSelected);
             }
         }
-
-        private class Item : Control, ISelectable
-        {
-            public bool IsSelected { get; set; }
-        }
     }
 }

+ 3 - 33
tests/Perspex.Controls.UnitTests/Primitives/TemplatedControlTests.cs

@@ -10,6 +10,7 @@ using Perspex.Controls.Primitives;
 using Perspex.Controls.Templates;
 using Perspex.LogicalTree;
 using Perspex.Styling;
+using Perspex.UnitTests;
 using Perspex.VisualTree;
 using Xunit;
 
@@ -176,12 +177,8 @@ namespace Perspex.Controls.UnitTests.Primitives
         [Fact]
         public void Templated_Children_Should_Be_Styled()
         {
-            using (PerspexLocator.EnterScope())
+            using (UnitTestApplication.Start(TestServices.MockStyler))
             {
-                var styler = new Mock<IStyler>();
-
-                PerspexLocator.CurrentMutable.Bind<IStyler>().ToConstant(styler.Object);
-
                 TestTemplatedControl target;
 
                 var root = new TestRoot
@@ -205,6 +202,7 @@ namespace Perspex.Controls.UnitTests.Primitives
 
                 target.ApplyTemplate();
 
+                var styler = Mock.Get(UnitTestApplication.Current.Services.Styler);
                 styler.Verify(x => x.ApplyStyles(It.IsAny<TestTemplatedControl>()), Times.Once());
                 styler.Verify(x => x.ApplyStyles(It.IsAny<StackPanel>()), Times.Once());
                 styler.Verify(x => x.ApplyStyles(It.IsAny<TextBlock>()), Times.Once());
@@ -330,23 +328,6 @@ namespace Perspex.Controls.UnitTests.Primitives
             };
         }
 
-        private static IControl ItemsControlTemplate(ItemsControl control)
-        {
-            return new Border
-            {
-                Child = new ScrollViewer
-                {
-                    Template = new FuncControlTemplate<ScrollViewer>(ScrollViewerTemplate),
-                    Content = new ItemsPresenter
-                    {
-                        Name = "PART_ItemsPresenter",
-                        [!ItemsPresenter.ItemsProperty] = control[!ItemsControl.ItemsProperty],
-                        [!ItemsPresenter.ItemsPanelProperty] = control[!ItemsControl.ItemsPanelProperty],
-                    }
-                }
-            };
-        }
-
         private static Control ScrollViewerTemplate(ScrollViewer control)
         {
             var result = new ScrollContentPresenter
@@ -357,16 +338,5 @@ namespace Perspex.Controls.UnitTests.Primitives
 
             return result;
         }
-
-        private class ApplyTemplateTracker : Control
-        {
-            public List<Tuple<IVisual, ILogical>> Invocations { get; } = new List<Tuple<IVisual, ILogical>>();
-
-            public override void ApplyTemplate()
-            {
-                base.ApplyTemplate();
-                Invocations.Add(Tuple.Create(this.GetVisualParent(), this.GetLogicalParent()));
-            }
-        }
     }
 }

+ 0 - 12
tests/Perspex.Controls.UnitTests/ScrollViewerTests.cs

@@ -84,17 +84,5 @@ namespace Perspex.Controls.UnitTests
                 },
             };
         }
-
-        private Control CreateNestedTemplate(ContentControl control)
-        {
-            return new ScrollViewer
-            {
-                Template = new FuncControlTemplate<ScrollViewer>(CreateTemplate),
-                Content = new ContentPresenter
-                {
-                    Name = "PART_ContentPresenter",
-                }
-            };
-        }
     }
 }

+ 2 - 3
tests/Perspex.Controls.UnitTests/TabControlTests.cs

@@ -9,6 +9,7 @@ using Perspex.Controls.Primitives;
 using Perspex.Controls.Templates;
 using Perspex.LogicalTree;
 using Perspex.Styling;
+using Perspex.UnitTests;
 using Xunit;
 
 namespace Perspex.Controls.UnitTests
@@ -130,10 +131,8 @@ namespace Perspex.Controls.UnitTests
 
             var template = new FuncControlTemplate<TabItem>(x => new Decorator());
 
-            using (PerspexLocator.EnterScope())
+            using (UnitTestApplication.Start(TestServices.RealStyler))
             {
-                PerspexLocator.CurrentMutable.Bind<IStyler>().ToConstant(new Styler());
-
                 var root = new TestRoot
                 {
                     Styles = new Styles

+ 0 - 38
tests/Perspex.Controls.UnitTests/TestRoot.cs

@@ -1,38 +0,0 @@
-// Copyright (c) The Perspex Project. All rights reserved.
-// Licensed under the MIT license. See licence.md file in the project root for full license information.
-
-using System;
-using Moq;
-using Perspex.Layout;
-using Perspex.Platform;
-using Perspex.Rendering;
-using Perspex.Styling;
-
-namespace Perspex.Controls.UnitTests
-{
-    internal class TestRoot : Decorator, ILayoutRoot, IRenderRoot, IStyleRoot
-    {
-        public Size ClientSize => new Size(100, 100);
-
-        public Size MaxClientSize => Size.Infinity;
-
-        public ILayoutManager LayoutManager => new Mock<ILayoutManager>().Object;
-
-        public IRenderTarget RenderTarget
-        {
-            get { throw new NotImplementedException(); }
-        }
-
-        public IRenderQueueManager RenderQueueManager => null;
-
-        public Point PointToClient(Point p)
-        {
-            throw new NotImplementedException();
-        }
-
-        public Point PointToScreen(Point p)
-        {
-            return new Point();
-        }
-    }
-}

+ 21 - 70
tests/Perspex.Controls.UnitTests/TopLevelTests.cs

@@ -13,6 +13,7 @@ using Perspex.Layout;
 using Perspex.Platform;
 using Perspex.Rendering;
 using Perspex.Styling;
+using Perspex.UnitTests;
 using Ploeh.AutoFixture;
 using Ploeh.AutoFixture.AutoMoq;
 using Xunit;
@@ -24,10 +25,8 @@ namespace Perspex.Controls.UnitTests
         [Fact]
         public void ClientSize_Should_Be_Set_On_Construction()
         {
-            using (PerspexLocator.EnterScope())
+            using (UnitTestApplication.Start(TestServices.StyledWindow))
             {
-                RegisterServices();
-
                 var impl = new Mock<ITopLevelImpl>();
                 impl.Setup(x => x.ClientSize).Returns(new Size(123, 456));
 
@@ -40,10 +39,8 @@ namespace Perspex.Controls.UnitTests
         [Fact]
         public void Width_Should_Not_Be_Set_On_Construction()
         {
-            using (PerspexLocator.EnterScope())
+            using (UnitTestApplication.Start(TestServices.StyledWindow))
             {
-                RegisterServices();
-
                 var impl = new Mock<ITopLevelImpl>();
                 impl.Setup(x => x.ClientSize).Returns(new Size(123, 456));
 
@@ -56,10 +53,8 @@ namespace Perspex.Controls.UnitTests
         [Fact]
         public void Height_Should_Not_Be_Set_On_Construction()
         {
-            using (PerspexLocator.EnterScope())
+            using (UnitTestApplication.Start(TestServices.StyledWindow))
             {
-                RegisterServices();
-
                 var impl = new Mock<ITopLevelImpl>();
                 impl.Setup(x => x.ClientSize).Returns(new Size(123, 456));
 
@@ -72,10 +67,10 @@ namespace Perspex.Controls.UnitTests
         [Fact]
         public void Layout_Pass_Should_Not_Be_Automatically_Scheduled()
         {
-            using (PerspexLocator.EnterScope())
-            {
-                RegisterServices();
+            var services = TestServices.StyledWindow.With(layoutManager: Mock.Of<ILayoutManager>());
 
+            using (UnitTestApplication.Start(services))
+            {
                 var impl = new Mock<ITopLevelImpl>();
                 var target = new TestTopLevel(impl.Object);
 
@@ -88,11 +83,8 @@ namespace Perspex.Controls.UnitTests
         [Fact]
         public void Bounds_Should_Be_Set_After_Layout_Pass()
         {
-            using (PerspexLocator.EnterScope())
+            using (UnitTestApplication.Start(TestServices.StyledWindow))
             {
-                RegisterServices();
-                PerspexLocator.CurrentMutable.Bind<ILayoutManager>().ToConstant(new LayoutManager());
-
                 var impl = new Mock<ITopLevelImpl>();
                 impl.SetupProperty(x => x.ClientSize);
                 impl.SetupProperty(x => x.Resized);
@@ -116,11 +108,8 @@ namespace Perspex.Controls.UnitTests
         [Fact]
         public void Impl_ClientSize_Should_Be_Set_After_Layout_Pass()
         {
-            using (PerspexLocator.EnterScope())
+            using (UnitTestApplication.Start(TestServices.StyledWindow))
             {
-                RegisterServices();
-                PerspexLocator.CurrentMutable.Bind<ILayoutManager>().ToConstant(new LayoutManager());
-
                 var impl = new Mock<ITopLevelImpl>();
 
                 var target = new TestTopLevel(impl.Object)
@@ -142,10 +131,8 @@ namespace Perspex.Controls.UnitTests
         [Fact]
         public void Width_And_Height_Should_Not_Be_Set_After_Layout_Pass()
         {
-            using (PerspexLocator.EnterScope())
+            using (UnitTestApplication.Start(TestServices.StyledWindow))
             {
-                RegisterServices();
-
                 var impl = new Mock<ITopLevelImpl>();
                 impl.Setup(x => x.ClientSize).Returns(new Size(123, 456));
 
@@ -160,10 +147,8 @@ namespace Perspex.Controls.UnitTests
         [Fact]
         public void Width_And_Height_Should_Be_Set_After_Window_Resize_Notification()
         {
-            using (PerspexLocator.EnterScope())
+            using (UnitTestApplication.Start(TestServices.StyledWindow))
             {
-                RegisterServices();
-
                 var impl = new Mock<ITopLevelImpl>();
                 impl.SetupAllProperties();
                 impl.Setup(x => x.ClientSize).Returns(new Size(123, 456));
@@ -180,10 +165,8 @@ namespace Perspex.Controls.UnitTests
         [Fact]
         public void Activate_Should_Call_Impl_Activate()
         {
-            using (PerspexLocator.EnterScope())
+            using (UnitTestApplication.Start(TestServices.StyledWindow))
             {
-                RegisterServices();
-
                 var impl = new Mock<ITopLevelImpl>();
                 var target = new TestTopLevel(impl.Object);
 
@@ -196,10 +179,8 @@ namespace Perspex.Controls.UnitTests
         [Fact]
         public void Impl_Activate_Should_Call_Raise_Activated_Event()
         {
-            using (PerspexLocator.EnterScope())
+            using (UnitTestApplication.Start(TestServices.StyledWindow))
             {
-                RegisterServices();
-
                 var impl = new Mock<ITopLevelImpl>();
                 impl.SetupAllProperties();
 
@@ -216,10 +197,8 @@ namespace Perspex.Controls.UnitTests
         [Fact]
         public void Impl_Close_Should_Call_Raise_Closed_Event()
         {
-            using (PerspexLocator.EnterScope())
+            using (UnitTestApplication.Start(TestServices.StyledWindow))
             {
-                RegisterServices();
-
                 var impl = new Mock<ITopLevelImpl>();
                 impl.SetupAllProperties();
 
@@ -236,10 +215,8 @@ namespace Perspex.Controls.UnitTests
         [Fact]
         public void Impl_Deactivate_Should_Call_Raise_Activated_Event()
         {
-            using (PerspexLocator.EnterScope())
+            using (UnitTestApplication.Start(TestServices.StyledWindow))
             {
-                RegisterServices();
-
                 var impl = new Mock<ITopLevelImpl>();
                 impl.SetupAllProperties();
 
@@ -256,12 +233,14 @@ namespace Perspex.Controls.UnitTests
         [Fact]
         public void Impl_Input_Should_Pass_Input_To_InputManager()
         {
-            using (PerspexLocator.EnterScope())
-            {
-                RegisterServices();
+            var inputManagerMock = new Mock<IInputManager>();
+            var services = TestServices.StyledWindow.With(inputManager: inputManagerMock.Object);
 
+            using (UnitTestApplication.Start(services))
+            {
                 var impl = new Mock<ITopLevelImpl>();
                 impl.SetupAllProperties();
+
                 var target = new TestTopLevel(impl.Object);
 
                 var input = new RawKeyEventArgs(
@@ -271,7 +250,6 @@ namespace Perspex.Controls.UnitTests
                     Key.A, InputModifiers.None);
                 impl.Object.Input(input);
 
-                var inputManagerMock = Mock.Get(InputManager.Instance);
                 inputManagerMock.Verify(x => x.Process(input));
             }
         }
@@ -279,10 +257,8 @@ namespace Perspex.Controls.UnitTests
         [Fact]
         public void Adding_Top_Level_As_Child_Should_Throw_Exception()
         {
-            using (PerspexLocator.EnterScope())
+            using (UnitTestApplication.Start(TestServices.StyledWindow))
             {
-                RegisterServices();
-
                 var impl = new Mock<ITopLevelImpl>();
                 impl.SetupAllProperties();
                 var target = new TestTopLevel(impl.Object);
@@ -305,31 +281,6 @@ namespace Perspex.Controls.UnitTests
                 });
         }
 
-        private void RegisterServices()
-        {
-            var fixture = new Fixture().Customize(new AutoMoqCustomization());
-            var l = PerspexLocator.CurrentMutable;
-
-            var formattedText = fixture.Create<IFormattedTextImpl>();
-            var globalStyles = new Mock<IGlobalStyles>();
-            var layoutManager = fixture.Create<ILayoutManager>();
-            var renderInterface = fixture.Create<IPlatformRenderInterface>();
-            var renderManager = fixture.Create<IRenderQueueManager>();
-            var windowImpl = new Mock<IWindowImpl>();
-            var theme = new Styles();
-
-            globalStyles.Setup(x => x.Styles).Returns(theme);
-
-            PerspexLocator.CurrentMutable
-                .Bind<IInputManager>().ToConstant(new Mock<IInputManager>().Object)
-                .Bind<IFocusManager>().ToConstant(new Mock<IFocusManager>().Object)
-                .Bind<IGlobalStyles>().ToConstant(globalStyles.Object)
-                .Bind<ILayoutManager>().ToConstant(layoutManager)
-                .Bind<IPlatformRenderInterface>().ToConstant(renderInterface)
-                .Bind<IRenderQueueManager>().ToConstant(renderManager)
-                .Bind<IStyler>().ToConstant(new Styler());
-        }
-
         private class TestTopLevel : TopLevel
         {
             public TestTopLevel(ITopLevelImpl impl)

+ 2 - 1
tests/Perspex.Controls.UnitTests/TreeViewTests.cs

@@ -8,6 +8,7 @@ using Perspex.Controls.Presenters;
 using Perspex.Controls.Templates;
 using Perspex.Input;
 using Perspex.LogicalTree;
+using Perspex.UnitTests;
 using Xunit;
 
 namespace Perspex.Controls.UnitTests
@@ -97,7 +98,7 @@ namespace Perspex.Controls.UnitTests
 
             Assert.NotNull(container);
 
-            container.RaiseEvent(new PointerPressEventArgs
+            container.RaiseEvent(new PointerPressedEventArgs
             {
                 RoutedEvent = InputElement.PointerPressedEvent,
                 MouseButton = MouseButton.Left,

+ 198 - 174
tests/Perspex.LeakTests/ControlTests.cs

@@ -9,6 +9,7 @@ using Perspex.Controls;
 using Perspex.Controls.Primitives;
 using Perspex.Controls.Templates;
 using Perspex.Layout;
+using Perspex.UnitTests;
 using Perspex.VisualTree;
 using Xunit;
 using Xunit.Abstractions;
@@ -20,282 +21,305 @@ namespace Perspex.LeakTests
     {
         public ControlTests(ITestOutputHelper atr)
         {
-            TestApp.Initialize();
             DotMemoryUnitTestOutput.SetOutputMethod(atr.WriteLine);
         }
 
         [Fact]
         public void Canvas_Is_Freed()
         {
-            Func<Window> run = () =>
+            using (UnitTestApplication.Start(TestServices.StyledWindow))
             {
-                var window = new Window
+                Func<Window> run = () =>
                 {
-                    Content = new Canvas()
-                };
+                    var window = new Window
+                    {
+                        Content = new Canvas()
+                    };
 
-                // Do a layout and make sure that Canvas gets added to visual tree.
-                LayoutManager.Instance.ExecuteInitialLayoutPass(window);
-                Assert.IsType<Canvas>(window.Presenter.Child);
+                    // Do a layout and make sure that Canvas gets added to visual tree.
+                    LayoutManager.Instance.ExecuteInitialLayoutPass(window);
+                    Assert.IsType<Canvas>(window.Presenter.Child);
 
-                // Clear the content and ensure the Canvas is removed.
-                window.Content = null;
-                LayoutManager.Instance.ExecuteLayoutPass();
-                Assert.Null(window.Presenter.Child);
+                    // Clear the content and ensure the Canvas is removed.
+                    window.Content = null;
+                    LayoutManager.Instance.ExecuteLayoutPass();
+                    Assert.Null(window.Presenter.Child);
 
-                return window;
-            };
+                    return window;
+                };
 
-            var result = run();
+                var result = run();
 
-            dotMemory.Check(memory =>
-                Assert.Equal(0, memory.GetObjects(where => where.Type.Is<Canvas>()).ObjectsCount));
+                dotMemory.Check(memory =>
+                    Assert.Equal(0, memory.GetObjects(where => where.Type.Is<Canvas>()).ObjectsCount));
+            }
         }
 
         [Fact]
         public void Named_Canvas_Is_Freed()
         {
-            Func<Window> run = () =>
+            using (UnitTestApplication.Start(TestServices.StyledWindow))
             {
-                var window = new Window
+                Func<Window> run = () =>
                 {
-                    Content = new Canvas
+                    var window = new Window
                     {
-                        Name = "foo"
-                    }
-                };
+                        Content = new Canvas
+                        {
+                            Name = "foo"
+                        }
+                    };
 
-                // Do a layout and make sure that Canvas gets added to visual tree.
-                LayoutManager.Instance.ExecuteInitialLayoutPass(window);
-                Assert.IsType<Canvas>(window.Find<Canvas>("foo"));
-                Assert.IsType<Canvas>(window.Presenter.Child);
+                    // Do a layout and make sure that Canvas gets added to visual tree.
+                    LayoutManager.Instance.ExecuteInitialLayoutPass(window);
+                    Assert.IsType<Canvas>(window.Find<Canvas>("foo"));
+                    Assert.IsType<Canvas>(window.Presenter.Child);
 
-                // Clear the content and ensure the Canvas is removed.
-                window.Content = null;
-                LayoutManager.Instance.ExecuteLayoutPass();
-                Assert.Null(window.Presenter.Child);
+                    // Clear the content and ensure the Canvas is removed.
+                    window.Content = null;
+                    LayoutManager.Instance.ExecuteLayoutPass();
+                    Assert.Null(window.Presenter.Child);
 
-                return window;
-            };
+                    return window;
+                };
 
-            var result = run();
+                var result = run();
 
-            dotMemory.Check(memory =>
-                Assert.Equal(0, memory.GetObjects(where => where.Type.Is<Canvas>()).ObjectsCount));
+                dotMemory.Check(memory =>
+                    Assert.Equal(0, memory.GetObjects(where => where.Type.Is<Canvas>()).ObjectsCount));
+            }
         }
 
         [Fact]
         public void Templated_Child_Is_Freed_When_Template_Cleared()
         {
-            Func<Window> run = () =>
+            using (UnitTestApplication.Start(TestServices.StyledWindow))
             {
-                var window = new Window
+                Func<Window> run = () =>
                 {
-                    Content = new TestTemplatedControl()
-                };
+                    var window = new Window
+                    {
+                        Content = new TestTemplatedControl()
+                    };
 
-                // Do a layout and make sure that the control gets added to visual tree and its
-                // template applied.
-                LayoutManager.Instance.ExecuteInitialLayoutPass(window);
-                Assert.IsType<TestTemplatedControl>(window.Presenter.Child);
-                Assert.IsType<Canvas>(window.Presenter.Child.GetVisualChildren().SingleOrDefault());
+                    // Do a layout and make sure that the control gets added to visual tree and its
+                    // template applied.
+                    LayoutManager.Instance.ExecuteInitialLayoutPass(window);
+                    Assert.IsType<TestTemplatedControl>(window.Presenter.Child);
+                    Assert.IsType<Canvas>(window.Presenter.Child.GetVisualChildren().SingleOrDefault());
 
-                // Clear the template and ensure the control template gets removed
-                ((TestTemplatedControl)window.Content).Template = null;
-                LayoutManager.Instance.ExecuteLayoutPass();
-                Assert.Equal(0, window.Presenter.Child.GetVisualChildren().Count());
+                    // Clear the template and ensure the control template gets removed
+                    ((TestTemplatedControl)window.Content).Template = null;
+                    LayoutManager.Instance.ExecuteLayoutPass();
+                    Assert.Equal(0, window.Presenter.Child.GetVisualChildren().Count());
 
-                return window;
-            };
+                    return window;
+                };
 
-            var result = run();
+                var result = run();
 
-            dotMemory.Check(memory =>
-                Assert.Equal(0, memory.GetObjects(where => where.Type.Is<Canvas>()).ObjectsCount));
+                dotMemory.Check(memory =>
+                    Assert.Equal(0, memory.GetObjects(where => where.Type.Is<Canvas>()).ObjectsCount));
+            }
         }
 
         [Fact]
         public void ScrollViewer_With_Content_Is_Freed()
         {
-            Func<Window> run = () =>
+            using (UnitTestApplication.Start(TestServices.StyledWindow))
             {
-                var window = new Window
+                Func<Window> run = () =>
                 {
-                    Content = new ScrollViewer
+                    var window = new Window
                     {
-                        Content = new Canvas()
-                    }
+                        Content = new ScrollViewer
+                        {
+                            Content = new Canvas()
+                        }
+                    };
+
+                    // Do a layout and make sure that ScrollViewer gets added to visual tree and its 
+                    // template applied.
+                    LayoutManager.Instance.ExecuteInitialLayoutPass(window);
+                    Assert.IsType<ScrollViewer>(window.Presenter.Child);
+                    Assert.IsType<Canvas>(((ScrollViewer)window.Presenter.Child).Presenter.Child);
+
+                    // Clear the content and ensure the ScrollViewer is removed.
+                    window.Content = null;
+                    LayoutManager.Instance.ExecuteLayoutPass();
+                    Assert.Null(window.Presenter.Child);
+
+                    return window;
                 };
 
-                // Do a layout and make sure that ScrollViewer gets added to visual tree and its 
-                // template applied.
-                LayoutManager.Instance.ExecuteInitialLayoutPass(window);
-                Assert.IsType<ScrollViewer>(window.Presenter.Child);
-                Assert.IsType<Canvas>(((ScrollViewer)window.Presenter.Child).Presenter.Child);
-
-                // Clear the content and ensure the ScrollViewer is removed.
-                window.Content = null;
-                LayoutManager.Instance.ExecuteLayoutPass();
-                Assert.Null(window.Presenter.Child);
+                var result = run();
 
-                return window;
-            };
-
-            var result = run();
-
-            dotMemory.Check(memory =>
-                Assert.Equal(0, memory.GetObjects(where => where.Type.Is<TextBox>()).ObjectsCount));
-            dotMemory.Check(memory =>
-                Assert.Equal(0, memory.GetObjects(where => where.Type.Is<Canvas>()).ObjectsCount));
+                dotMemory.Check(memory =>
+                    Assert.Equal(0, memory.GetObjects(where => where.Type.Is<TextBox>()).ObjectsCount));
+                dotMemory.Check(memory =>
+                    Assert.Equal(0, memory.GetObjects(where => where.Type.Is<Canvas>()).ObjectsCount));
+            }
         }
 
         [Fact]
         public void TextBox_Is_Freed()
         {
-            Func<Window> run = () =>
+            using (UnitTestApplication.Start(TestServices.StyledWindow))
             {
-                var window = new Window
+                Func<Window> run = () =>
                 {
-                    Content = new TextBox()
-                };
+                    var window = new Window
+                    {
+                        Content = new TextBox()
+                    };
 
-                // Do a layout and make sure that TextBox gets added to visual tree and its 
-                // template applied.
-                LayoutManager.Instance.ExecuteInitialLayoutPass(window);
-                Assert.IsType<TextBox>(window.Presenter.Child);
-                Assert.NotEqual(0, window.Presenter.Child.GetVisualChildren().Count());
+                    // Do a layout and make sure that TextBox gets added to visual tree and its 
+                    // template applied.
+                    LayoutManager.Instance.ExecuteInitialLayoutPass(window);
+                    Assert.IsType<TextBox>(window.Presenter.Child);
+                    Assert.NotEqual(0, window.Presenter.Child.GetVisualChildren().Count());
 
-                // Clear the content and ensure the TextBox is removed.
-                window.Content = null;
-                LayoutManager.Instance.ExecuteLayoutPass();
-                Assert.Null(window.Presenter.Child);
+                    // Clear the content and ensure the TextBox is removed.
+                    window.Content = null;
+                    LayoutManager.Instance.ExecuteLayoutPass();
+                    Assert.Null(window.Presenter.Child);
 
-                return window;
-            };
+                    return window;
+                };
 
-            var result = run();
+                var result = run();
 
-            dotMemory.Check(memory =>
-                Assert.Equal(0, memory.GetObjects(where => where.Type.Is<TextBox>()).ObjectsCount));
+                dotMemory.Check(memory =>
+                    Assert.Equal(0, memory.GetObjects(where => where.Type.Is<TextBox>()).ObjectsCount));
+            }
         }
 
         [Fact]
         public void TextBox_With_Xaml_Binding_Is_Freed()
         {
-            Func<Window> run = () =>
+            using (UnitTestApplication.Start(TestServices.StyledWindow))
             {
-                var window = new Window
+                Func<Window> run = () =>
                 {
-                    DataContext = new Node { Name = "foo" },
-                    Content = new TextBox()
-                };
+                    var window = new Window
+                    {
+                        DataContext = new Node { Name = "foo" },
+                        Content = new TextBox()
+                    };
 
-                var binding = new Perspex.Markup.Xaml.Data.Binding
-                {
-                    Path = "Name"
-                };
+                    var binding = new Perspex.Markup.Xaml.Data.Binding
+                    {
+                        Path = "Name"
+                    };
 
-                var textBox = (TextBox)window.Content;
-                textBox.Bind(TextBox.TextProperty, binding);
+                    var textBox = (TextBox)window.Content;
+                    textBox.Bind(TextBox.TextProperty, binding);
 
-                // Do a layout and make sure that TextBox gets added to visual tree and its 
-                // Text property set.
-                LayoutManager.Instance.ExecuteInitialLayoutPass(window);
-                Assert.IsType<TextBox>(window.Presenter.Child);
-                Assert.Equal("foo", ((TextBox)window.Presenter.Child).Text);
+                    // Do a layout and make sure that TextBox gets added to visual tree and its 
+                    // Text property set.
+                    LayoutManager.Instance.ExecuteInitialLayoutPass(window);
+                    Assert.IsType<TextBox>(window.Presenter.Child);
+                    Assert.Equal("foo", ((TextBox)window.Presenter.Child).Text);
 
-                // Clear the content and DataContext and ensure the TextBox is removed.
-                window.Content = null;
-                window.DataContext = null;
-                LayoutManager.Instance.ExecuteLayoutPass();
-                Assert.Null(window.Presenter.Child);
+                    // Clear the content and DataContext and ensure the TextBox is removed.
+                    window.Content = null;
+                    window.DataContext = null;
+                    LayoutManager.Instance.ExecuteLayoutPass();
+                    Assert.Null(window.Presenter.Child);
 
-                return window;
-            };
+                    return window;
+                };
 
-            var result = run();
+                var result = run();
 
-            dotMemory.Check(memory =>
-                Assert.Equal(0, memory.GetObjects(where => where.Type.Is<TextBox>()).ObjectsCount));
-            dotMemory.Check(memory =>
-                Assert.Equal(0, memory.GetObjects(where => where.Type.Is<Node>()).ObjectsCount));
+                dotMemory.Check(memory =>
+                    Assert.Equal(0, memory.GetObjects(where => where.Type.Is<TextBox>()).ObjectsCount));
+                dotMemory.Check(memory =>
+                    Assert.Equal(0, memory.GetObjects(where => where.Type.Is<Node>()).ObjectsCount));
+            }
         }
 
         [Fact]
         public void TextBox_ScrollViewer_Is_Freed_When_Template_Cleared()
         {
-            Func<Window> run = () =>
+            using (UnitTestApplication.Start(TestServices.StyledWindow))
             {
-                var window = new Window
+                Func<Window> run = () =>
                 {
-                    Content = new TextBox()
-                };
+                    var window = new Window
+                    {
+                        Content = new TextBox()
+                    };
 
-                // Do a layout and make sure that TextBox gets added to visual tree and its 
-                // template applied.
-                LayoutManager.Instance.ExecuteInitialLayoutPass(window);
-                Assert.IsType<TextBox>(window.Presenter.Child);
-                Assert.NotEqual(0, window.Presenter.Child.GetVisualChildren().Count());
+                    // Do a layout and make sure that TextBox gets added to visual tree and its 
+                    // template applied.
+                    LayoutManager.Instance.ExecuteInitialLayoutPass(window);
+                    Assert.IsType<TextBox>(window.Presenter.Child);
+                    Assert.NotEqual(0, window.Presenter.Child.GetVisualChildren().Count());
 
-                // Clear the template and ensure the TextBox template gets removed
-                ((TextBox)window.Content).Template = null;
-                LayoutManager.Instance.ExecuteLayoutPass();
-                Assert.Equal(0, window.Presenter.Child.GetVisualChildren().Count());
+                    // Clear the template and ensure the TextBox template gets removed
+                    ((TextBox)window.Content).Template = null;
+                    LayoutManager.Instance.ExecuteLayoutPass();
+                    Assert.Equal(0, window.Presenter.Child.GetVisualChildren().Count());
 
-                return window;
-            };
+                    return window;
+                };
 
-            var result = run();
+                var result = run();
 
-            dotMemory.Check(memory =>
-                Assert.Equal(0, memory.GetObjects(where => where.Type.Is<ScrollViewer>()).ObjectsCount));
+                dotMemory.Check(memory =>
+                    Assert.Equal(0, memory.GetObjects(where => where.Type.Is<ScrollViewer>()).ObjectsCount));
+            }
         }
 
         [Fact]
         public void TreeView_Is_Freed()
         {
-            Func<Window> run = () =>
+            using (UnitTestApplication.Start(TestServices.StyledWindow))
             {
-                var nodes = new[]
+                Func<Window> run = () =>
                 {
-                    new Node
+                    var nodes = new[]
                     {
-                        Children = new[] { new Node() },
-                    }
-                };
+                        new Node
+                        {
+                            Children = new[] { new Node() },
+                        }
+                    };
 
-                TreeView target;
+                    TreeView target;
 
-                var window = new Window
-                {
-                    Content = target = new TreeView
+                    var window = new Window
                     {
-                        DataTemplates = new DataTemplates
+                        Content = target = new TreeView
                         {
-                            new FuncTreeDataTemplate<Node>(
-                                x => new TextBlock { Text = x.Name },
-                                x => x.Children)
-                        },
-                        Items = nodes
-                    }
+                            DataTemplates = new DataTemplates
+                            {
+                                new FuncTreeDataTemplate<Node>(
+                                    x => new TextBlock { Text = x.Name },
+                                    x => x.Children)
+                            },
+                            Items = nodes
+                        }
+                    };
+
+                    // Do a layout and make sure that TreeViewItems get realized.
+                    LayoutManager.Instance.ExecuteInitialLayoutPass(window);
+                    Assert.Equal(1, target.ItemContainerGenerator.Containers.Count());
+
+                    // Clear the content and ensure the TreeView is removed.
+                    window.Content = null;
+                    LayoutManager.Instance.ExecuteLayoutPass();
+                    Assert.Null(window.Presenter.Child);
+
+                    return window;
                 };
 
-                // Do a layout and make sure that TreeViewItems get realized.
-                LayoutManager.Instance.ExecuteInitialLayoutPass(window);
-                Assert.Equal(1, target.ItemContainerGenerator.Containers.Count());
-
-                // Clear the content and ensure the TreeView is removed.
-                window.Content = null;
-                LayoutManager.Instance.ExecuteLayoutPass();
-                Assert.Null(window.Presenter.Child);
-
-                return window;
-            };
+                var result = run();
 
-            var result = run();
-
-            dotMemory.Check(memory =>
-                Assert.Equal(0, memory.GetObjects(where => where.Type.Is<TreeView>()).ObjectsCount));
+                dotMemory.Check(memory =>
+                    Assert.Equal(0, memory.GetObjects(where => where.Type.Is<TreeView>()).ObjectsCount));
+            }
         }
 
         private class TestTemplatedControl : TemplatedControl

+ 4 - 1
tests/Perspex.LeakTests/Perspex.LeakTests.csproj

@@ -93,7 +93,6 @@
   <ItemGroup>
     <Compile Include="ControlTests.cs" />
     <Compile Include="Properties\AssemblyInfo.cs" />
-    <Compile Include="TestApp.cs" />
   </ItemGroup>
   <ItemGroup>
     <None Include="app.config" />
@@ -152,6 +151,10 @@
       <Project>{5ccb5571-7c30-4e7d-967d-0e2158ebd91f}</Project>
       <Name>Perspex.Controls.UnitTests</Name>
     </ProjectReference>
+    <ProjectReference Include="..\Perspex.UnitTests\Perspex.UnitTests.csproj">
+      <Project>{88060192-33d5-4932-b0f9-8bd2763e857d}</Project>
+      <Name>Perspex.UnitTests</Name>
+    </ProjectReference>
   </ItemGroup>
   <ItemGroup>
     <Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />

+ 0 - 47
tests/Perspex.LeakTests/TestApp.cs

@@ -1,47 +0,0 @@
-// Copyright (c) The Perspex Project. All rights reserved.
-// Licensed under the MIT license. See licence.md file in the project root for full license information.
-
-using Moq;
-using Perspex.Controls.UnitTests;
-using Perspex.Layout;
-using Perspex.Platform;
-using Perspex.Shared.PlatformSupport;
-using Perspex.Themes.Default;
-using Ploeh.AutoFixture;
-using Ploeh.AutoFixture.AutoMoq;
-
-namespace Perspex.LeakTests
-{
-    internal class TestApp : Application
-    {
-        private TestApp()
-        {
-            RegisterServices();
-
-            var fixture = new Fixture().Customize(new AutoMoqCustomization());
-            var windowImpl = new Mock<IWindowImpl>();
-            var renderInterface = fixture.Create<IPlatformRenderInterface>();
-            var threadingInterface = Mock.Of<IPlatformThreadingInterface>(x =>
-                x.CurrentThreadIsLoopThread == true);
-
-            PerspexLocator.CurrentMutable
-                .Bind<IAssetLoader>().ToConstant(new AssetLoader())
-                .Bind<ILayoutManager>().ToConstant(new LayoutManager())
-                .Bind<IPclPlatformWrapper>().ToConstant(new PclPlatformWrapper())
-                .Bind<IPlatformRenderInterface>().ToConstant(renderInterface)
-                .Bind<IPlatformThreadingInterface>().ToConstant(threadingInterface)
-                .Bind<IStandardCursorFactory>().ToConstant(new Mock<IStandardCursorFactory>().Object)
-                .Bind<IWindowingPlatform>().ToConstant(new WindowingPlatformMock(() => windowImpl.Object));
-
-            Styles = new DefaultTheme();
-        }
-
-        public static void Initialize()
-        {
-            if (Current == null)
-            {
-                new TestApp();
-            }
-        }
-    }
-}

+ 1 - 0
tests/Perspex.Markup.UnitTests/ControlLocatorTests.cs

@@ -5,6 +5,7 @@ using System;
 using System.Collections.Generic;
 using System.Reactive.Linq;
 using Perspex.Controls;
+using Perspex.UnitTests;
 using Xunit;
 
 namespace Perspex.Markup.UnitTests

+ 0 - 5
tests/Perspex.Markup.UnitTests/Data/ExpressionSubjectTests.cs

@@ -176,11 +176,6 @@ namespace Perspex.Markup.UnitTests.Data
             public string StringValue { get; set; }
 
             public double DoubleValue { get; set; }
-
-            public void RaisePropertyChanged(string propertyName)
-            {
-                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
-            }
         }
     }
 }

+ 4 - 1
tests/Perspex.Markup.UnitTests/Perspex.Markup.UnitTests.csproj

@@ -99,7 +99,6 @@
     <Compile Include="Data\NotifyingBase.cs" />
     <Compile Include="DefaultValueConverterTests.cs" />
     <Compile Include="Properties\AssemblyInfo.cs" />
-    <Compile Include="TestRoot.cs" />
     <Compile Include="UnitTestSynchronizationContext.cs" />
   </ItemGroup>
   <ItemGroup>
@@ -142,6 +141,10 @@
       <Project>{f1baa01a-f176-4c6a-b39d-5b40bb1b148f}</Project>
       <Name>Perspex.Styling</Name>
     </ProjectReference>
+    <ProjectReference Include="..\Perspex.UnitTests\Perspex.UnitTests.csproj">
+      <Project>{88060192-33d5-4932-b0f9-8bd2763e857d}</Project>
+      <Name>Perspex.UnitTests</Name>
+    </ProjectReference>
   </ItemGroup>
   <ItemGroup>
     <Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />

+ 1 - 0
tests/Perspex.Markup.Xaml.UnitTests/Data/BindingTests_ElementName.cs

@@ -3,6 +3,7 @@
 
 using Perspex.Controls;
 using Perspex.Markup.Xaml.Data;
+using Perspex.UnitTests;
 using Xunit;
 
 namespace Perspex.Markup.Xaml.UnitTests.Data

+ 0 - 5
tests/Perspex.Markup.Xaml.UnitTests/Data/BindingTests_TemplatedParent.cs

@@ -55,11 +55,6 @@ namespace Perspex.Markup.Xaml.UnitTests.Data
                 BindingPriority.TemplatedParent));
         }
 
-        private Mock<IPerspexObject> CreateTarget(ITemplatedControl templatedParent)
-        {
-            return CreateTarget(templatedParent: templatedParent);
-        }
-
         private Mock<IControl> CreateTarget(
             ITemplatedControl templatedParent = null,
             string text = null)

+ 9 - 1
tests/Perspex.Markup.Xaml.UnitTests/Perspex.Markup.Xaml.UnitTests.csproj

@@ -112,7 +112,6 @@
     <Compile Include="StyleTests.cs" />
     <Compile Include="Templates\DataTemplateTests.cs" />
     <Compile Include="Templates\TreeDataTemplateTests.cs" />
-    <Compile Include="TestRoot.cs" />
     <Compile Include="TypeProviderMock.cs" />
     <Compile Include="ViewModelMock.cs" />
   </ItemGroup>
@@ -129,6 +128,10 @@
       <Project>{D211E587-D8BC-45B9-95A4-F297C8FA5200}</Project>
       <Name>Perspex.Animation</Name>
     </ProjectReference>
+    <ProjectReference Include="..\..\src\Perspex.Application\Perspex.Application.csproj">
+      <Project>{799a7bb5-3c2c-48b6-85a7-406a12c420da}</Project>
+      <Name>Perspex.Application</Name>
+    </ProjectReference>
     <ProjectReference Include="..\..\src\Perspex.Base\Perspex.Base.csproj">
       <Project>{B09B78D8-9B26-48B0-9149-D64A2F120F3F}</Project>
       <Name>Perspex.Base</Name>
@@ -165,8 +168,13 @@
       <Project>{3E10A5FA-E8DA-48B1-AD44-6A5B6CB7750F}</Project>
       <Name>Perspex.Themes.Default</Name>
     </ProjectReference>
+    <ProjectReference Include="..\Perspex.UnitTests\Perspex.UnitTests.csproj">
+      <Project>{88060192-33d5-4932-b0f9-8bd2763e857d}</Project>
+      <Name>Perspex.UnitTests</Name>
+    </ProjectReference>
   </ItemGroup>
   <ItemGroup>
+    <None Include="app.config" />
     <None Include="packages.config" />
   </ItemGroup>
   <ItemGroup>

+ 4 - 20
tests/Perspex.Markup.Xaml.UnitTests/StyleTests.cs

@@ -3,13 +3,11 @@
 
 using System.Linq;
 using System.Reactive.Linq;
-using Moq;
 using Perspex.Controls;
-using Perspex.Controls.Primitives;
 using Perspex.Data;
 using Perspex.Markup.Xaml.Data;
-using Perspex.Platform;
 using Perspex.Styling;
+using Perspex.UnitTests;
 using Xunit;
 
 namespace Perspex.Markup.Xaml.UnitTests
@@ -19,12 +17,8 @@ namespace Perspex.Markup.Xaml.UnitTests
         [Fact]
         public void Binding_Should_Be_Assigned_To_Setter_Value_Instead_Of_Bound()
         {
-            using (PerspexLocator.EnterScope())
+            using (UnitTestApplication.Start(TestServices.MockPlatformWrapper))
             {
-                PerspexLocator.CurrentMutable
-                    .Bind<IPclPlatformWrapper>()
-                    .ToConstant(Mock.Of<IPclPlatformWrapper>());
-
                 var xaml = "<Style xmlns='https://github.com/perspex'><Setter Value='{Binding}'/></Style>";
                 var loader = new PerspexXamlLoader();
                 var style = (Style)loader.Load(xaml);
@@ -37,13 +31,8 @@ namespace Perspex.Markup.Xaml.UnitTests
         [Fact]
         public void Setter_With_TwoWay_Binding_Should_Update_Source()
         {
-            using (PerspexLocator.EnterScope())
+            using (UnitTestApplication.Start(TestServices.MockThreadingInterface))
             {
-                PerspexLocator.CurrentMutable
-                    .Bind<IPlatformThreadingInterface>()
-                    .ToConstant(Mock.Of<IPlatformThreadingInterface>(x => 
-                        x.CurrentThreadIsLoopThread == true));
-
                 var data = new Data
                 {
                     Foo = "foo",
@@ -75,13 +64,8 @@ namespace Perspex.Markup.Xaml.UnitTests
         [Fact]
         public void Setter_With_TwoWay_Binding_And_Activator_Should_Update_Source()
         {
-            using (PerspexLocator.EnterScope())
+            using (UnitTestApplication.Start(TestServices.MockThreadingInterface))
             {
-                PerspexLocator.CurrentMutable
-                    .Bind<IPlatformThreadingInterface>()
-                    .ToConstant(Mock.Of<IPlatformThreadingInterface>(x =>
-                        x.CurrentThreadIsLoopThread == true));
-
                 var data = new Data
                 {
                     Foo = "foo",

+ 2 - 7
tests/Perspex.Markup.Xaml.UnitTests/Templates/TreeDataTemplateTests.cs

@@ -2,11 +2,10 @@
 // Licensed under the MIT license. See licence.md file in the project root for full license information.
 
 using System.Linq;
-using Moq;
 using Perspex.Controls.Templates;
 using Perspex.Markup.Xaml.Data;
 using Perspex.Markup.Xaml.Templates;
-using Perspex.Platform;
+using Perspex.UnitTests;
 using Xunit;
 
 namespace Perspex.Markup.Xaml.UnitTests
@@ -16,12 +15,8 @@ namespace Perspex.Markup.Xaml.UnitTests
         [Fact]
         public void Binding_Should_Be_Assigned_To_ItemsSource_Instead_Of_Bound()
         {
-            using (PerspexLocator.EnterScope())
+            using (UnitTestApplication.Start(TestServices.MockPlatformWrapper))
             {
-                PerspexLocator.CurrentMutable
-                    .Bind<IPclPlatformWrapper>()
-                    .ToConstant(Mock.Of<IPclPlatformWrapper>());
-
                 var xaml = "<DataTemplates xmlns='https://github.com/perspex'><TreeDataTemplate ItemsSource='{Binding}'/></DataTemplates>";
                 var loader = new PerspexXamlLoader();
                 var templates = (DataTemplates)loader.Load(xaml);

+ 0 - 67
tests/Perspex.Markup.Xaml.UnitTests/TestRoot.cs

@@ -1,67 +0,0 @@
-// Copyright (c) The Perspex Project. All rights reserved.
-// Licensed under the MIT license. See licence.md file in the project root for full license information.
-
-using System;
-using Perspex.Controls;
-using Perspex.Platform;
-using Perspex.Rendering;
-using Perspex.Styling;
-
-namespace Perspex.Markup.Xaml.UnitTests
-{
-    public class TestRoot : Decorator, IRenderRoot, INameScope, IStyleRoot
-    {
-        private readonly NameScope _nameScope = new NameScope();
-
-        event EventHandler<NameScopeEventArgs> INameScope.Registered
-        {
-            add { _nameScope.Registered += value; ++NameScopeRegisteredSubscribers; }
-            remove { _nameScope.Registered -= value; --NameScopeRegisteredSubscribers; }
-        }
-
-        public event EventHandler<NameScopeEventArgs> Unregistered
-        {
-            add { _nameScope.Unregistered += value; ++NameScopeUnregisteredSubscribers; }
-            remove { _nameScope.Unregistered -= value; --NameScopeUnregisteredSubscribers; }
-        }
-
-        public int NameScopeRegisteredSubscribers { get; private set; }
-
-        public int NameScopeUnregisteredSubscribers { get; private set; }
-
-        public IRenderTarget RenderTarget
-        {
-            get { throw new NotImplementedException(); }
-        }
-
-        public IRenderQueueManager RenderQueueManager
-        {
-            get { throw new NotImplementedException(); }
-        }
-
-        public Point PointToClient(Point p)
-        {
-            throw new NotImplementedException();
-        }
-
-        public Point PointToScreen(Point p)
-        {
-            throw new NotImplementedException();
-        }
-
-        public void Register(string name, object element)
-        {
-            _nameScope.Register(name, element);
-        }
-
-        public object Find(string name)
-        {
-            return _nameScope.Find(name);
-        }
-
-        public void Unregister(string name)
-        {
-            _nameScope.Unregister(name);
-        }
-    }
-}

+ 11 - 0
tests/Perspex.Markup.Xaml.UnitTests/app.config

@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<configuration>
+  <runtime>
+    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
+      <dependentAssembly>
+        <assemblyIdentity name="Moq" publicKeyToken="69f491c39445e920" culture="neutral" />
+        <bindingRedirect oldVersion="0.0.0.0-4.2.1510.2205" newVersion="4.2.1510.2205" />
+      </dependentAssembly>
+    </assemblyBinding>
+  </runtime>
+</configuration>

+ 10 - 2
tests/Perspex.RenderTests/app.config

@@ -15,8 +15,16 @@
         <bindingRedirect oldVersion="0.0.0.0-2.1.30214.0" newVersion="2.1.30214.0" />
       </dependentAssembly>
       <dependentAssembly>
-        <assemblyIdentity name="SharpDX" publicKeyToken="b4dcf0f35e5521f1" culture="neutral" />
-        <bindingRedirect oldVersion="0.0.0.0-2.6.3.0" newVersion="2.6.3.0" />
+        <assemblyIdentity name="SharpDX.Direct2D1" publicKeyToken="b4dcf0f35e5521f1" culture="neutral"/>
+        <bindingRedirect oldVersion="0.0.0.0-3.0.1.0" newVersion="3.0.1.0"/>
+      </dependentAssembly>
+      <dependentAssembly>
+        <assemblyIdentity name="SharpDX" publicKeyToken="b4dcf0f35e5521f1" culture="neutral"/>
+        <bindingRedirect oldVersion="0.0.0.0-3.0.1.0" newVersion="3.0.1.0"/>
+      </dependentAssembly>
+      <dependentAssembly>
+        <assemblyIdentity name="SharpDX.DXGI" publicKeyToken="b4dcf0f35e5521f1" culture="neutral"/>
+        <bindingRedirect oldVersion="0.0.0.0-3.0.1.0" newVersion="3.0.1.0"/>
       </dependentAssembly>
       <dependentAssembly>
         <assemblyIdentity name="Mono.Cairo" publicKeyToken="0738eb9f132ed756" culture="neutral" />

+ 0 - 12
tests/Perspex.Styling.UnitTests/ActivatedSubjectTests.cs

@@ -53,18 +53,6 @@ namespace Perspex.Styling.UnitTests
             Assert.NotNull(source.Error);
         }
 
-        private class Class1 : PerspexObject
-        {
-            public static readonly StyledProperty<string> FooProperty =
-                PerspexProperty.Register<Class1, string>("Foo", "foodefault");
-
-            public string Foo
-            {
-                get { return GetValue(FooProperty); }
-                set { SetValue(FooProperty, value); }
-            }
-        }
-
         private class TestSubject : ISubject<object>
         {
             private IObserver<object> _observer;

+ 4 - 1
tests/Perspex.Styling.UnitTests/Perspex.Styling.UnitTests.csproj

@@ -104,7 +104,6 @@
     <Compile Include="TestControlBase.cs" />
     <Compile Include="TestObservable.cs" />
     <Compile Include="TestObserver.cs" />
-    <Compile Include="TestRoot.cs" />
     <Compile Include="TestSelectors.cs" />
     <Compile Include="TestSubject.cs" />
     <Compile Include="TestTemplatedControl.cs" />
@@ -142,6 +141,10 @@
       <Project>{F1BAA01A-F176-4C6A-B39D-5B40BB1B148F}</Project>
       <Name>Perspex.Styling</Name>
     </ProjectReference>
+    <ProjectReference Include="..\Perspex.UnitTests\Perspex.UnitTests.csproj">
+      <Project>{88060192-33d5-4932-b0f9-8bd2763e857d}</Project>
+      <Name>Perspex.UnitTests</Name>
+    </ProjectReference>
   </ItemGroup>
   <ItemGroup>
     <None Include="app.config" />

+ 0 - 13
tests/Perspex.Styling.UnitTests/StyleActivatorTests.cs

@@ -165,18 +165,5 @@ namespace Perspex.Styling.UnitTests
 
             Assert.True(completed);
         }
-
-        private Recorded<Notification<bool>>[] OnNextValues(params bool[] values)
-        {
-            var result = new List<Recorded<Notification<bool>>>();
-            var time = 1;
-
-            foreach (var value in values)
-            {
-                result.Add(new Recorded<Notification<bool>>(time, Notification.CreateOnNext<bool>(value)));
-            }
-
-            return result.ToArray();
-        }
     }
 }

+ 1 - 0
tests/Perspex.Styling.UnitTests/StyleTests.cs

@@ -5,6 +5,7 @@ using System;
 using System.Collections.Generic;
 using System.Reactive.Subjects;
 using Perspex.Controls;
+using Perspex.UnitTests;
 using Xunit;
 
 namespace Perspex.Styling.UnitTests

+ 0 - 39
tests/Perspex.Styling.UnitTests/TestRoot.cs

@@ -1,39 +0,0 @@
-// Copyright (c) The Perspex Project. All rights reserved.
-// Licensed under the MIT license. See licence.md file in the project root for full license information.
-
-using System;
-using Moq;
-using Perspex.Controls;
-using Perspex.Layout;
-using Perspex.Platform;
-using Perspex.Rendering;
-
-namespace Perspex.Styling.UnitTests
-{
-    internal class TestRoot : Decorator, ILayoutRoot, IRenderRoot, IStyleRoot
-    {
-        public Size ClientSize => new Size(100, 100);
-
-        public Size MaxClientSize => Size.Infinity;
-
-        public IRenderTarget RenderTarget
-        {
-            get { throw new NotImplementedException(); }
-        }
-
-        public IRenderQueueManager RenderQueueManager
-        {
-            get { throw new NotImplementedException(); }
-        }
-
-        public Point PointToClient(Point p)
-        {
-            throw new NotImplementedException();
-        }
-
-        public Point PointToScreen(Point p)
-        {
-            return new Point();
-        }
-    }
-}

+ 30 - 0
tests/Perspex.UnitTests/MockWindowingPlatform.cs

@@ -0,0 +1,30 @@
+using System;
+using Moq;
+using Perspex.Platform;
+
+namespace Perspex.UnitTests
+{
+    public class MockWindowingPlatform : IWindowingPlatform
+    {
+        private readonly Func<IWindowImpl> _windowImpl;
+        private readonly Func<IPopupImpl> _popupImpl;
+
+        public MockWindowingPlatform(Func<IWindowImpl> windowImpl = null, Func<IPopupImpl> popupImpl = null )
+        {
+            _windowImpl = windowImpl;
+            _popupImpl = popupImpl;
+        }
+
+        public IWindowImpl CreateWindow()
+        {
+            return _windowImpl?.Invoke() ?? Mock.Of<IWindowImpl>();
+        }
+
+        public IWindowImpl CreateEmbeddableWindow()
+        {
+            throw new NotImplementedException();
+        }
+
+        public IPopupImpl CreatePopup() => _popupImpl?.Invoke() ?? Mock.Of<IPopupImpl>();
+    }
+}

+ 143 - 0
tests/Perspex.UnitTests/Perspex.UnitTests.csproj

@@ -0,0 +1,143 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <ProjectGuid>{88060192-33D5-4932-B0F9-8BD2763E857D}</ProjectGuid>
+    <ProjectGuid>{88060192-33D5-4932-B0F9-8BD2763E857D}</ProjectGuid>
+    <OutputType>Library</OutputType>
+    <AppDesignerFolder>Properties</AppDesignerFolder>
+    <RootNamespace>Perspex.UnitTests</RootNamespace>
+    <AssemblyName>Perspex.UnitTests</AssemblyName>
+    <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
+    <FileAlignment>512</FileAlignment>
+    <TargetFrameworkProfile />
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+    <DebugSymbols>true</DebugSymbols>
+    <DebugType>full</DebugType>
+    <Optimize>false</Optimize>
+    <OutputPath>bin\Debug\</OutputPath>
+    <DefineConstants>DEBUG;TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+    <DebugType>pdbonly</DebugType>
+    <Optimize>true</Optimize>
+    <OutputPath>bin\Release\</OutputPath>
+    <DefineConstants>TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <ItemGroup>
+    <Reference Include="Moq, Version=4.2.1510.2205, Culture=neutral, PublicKeyToken=69f491c39445e920, processorArchitecture=MSIL">
+      <HintPath>..\..\packages\Moq.4.2.1510.2205\lib\net40\Moq.dll</HintPath>
+      <Private>True</Private>
+    </Reference>
+    <Reference Include="Ploeh.AutoFixture, Version=3.40.0.0, Culture=neutral, PublicKeyToken=b24654c590009d4f, processorArchitecture=MSIL">
+      <HintPath>..\..\packages\AutoFixture.3.40.0\lib\net40\Ploeh.AutoFixture.dll</HintPath>
+      <Private>True</Private>
+    </Reference>
+    <Reference Include="Ploeh.AutoFixture.AutoMoq, Version=3.40.0.0, Culture=neutral, PublicKeyToken=b24654c590009d4f, processorArchitecture=MSIL">
+      <HintPath>..\..\packages\AutoFixture.AutoMoq.3.40.0\lib\net40\Ploeh.AutoFixture.AutoMoq.dll</HintPath>
+      <Private>True</Private>
+    </Reference>
+    <Reference Include="System" />
+    <Reference Include="System.Core" />
+    <Reference Include="System.Reactive.Core, Version=2.2.5.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
+      <HintPath>..\..\packages\Rx-Core.2.2.5\lib\net45\System.Reactive.Core.dll</HintPath>
+      <Private>True</Private>
+    </Reference>
+    <Reference Include="System.Reactive.Interfaces, Version=2.2.5.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
+      <HintPath>..\..\packages\Rx-Interfaces.2.2.5\lib\net45\System.Reactive.Interfaces.dll</HintPath>
+      <Private>True</Private>
+    </Reference>
+    <Reference Include="System.Reactive.Linq, Version=2.2.5.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
+      <HintPath>..\..\packages\Rx-Linq.2.2.5\lib\net45\System.Reactive.Linq.dll</HintPath>
+      <Private>True</Private>
+    </Reference>
+    <Reference Include="System.Reactive.PlatformServices, Version=2.2.5.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
+      <HintPath>..\..\packages\Rx-PlatformServices.2.2.5\lib\net45\System.Reactive.PlatformServices.dll</HintPath>
+      <Private>True</Private>
+    </Reference>
+    <Reference Include="System.Xml.Linq" />
+    <Reference Include="System.Data.DataSetExtensions" />
+    <Reference Include="Microsoft.CSharp" />
+    <Reference Include="System.Data" />
+    <Reference Include="System.Net.Http" />
+    <Reference Include="System.Xml" />
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="TestTemplatedRoot.cs" />
+    <Compile Include="TestRoot.cs" />
+    <Compile Include="TestServices.cs" />
+    <Compile Include="UnitTestApplication.cs" />
+    <Compile Include="Properties\AssemblyInfo.cs" />
+    <Compile Include="MockWindowingPlatform.cs" />
+  </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="..\..\src\Markup\Perspex.Markup.Xaml\Perspex.Markup.Xaml.csproj">
+      <Project>{3e53a01a-b331-47f3-b828-4a5717e77a24}</Project>
+      <Name>Perspex.Markup.Xaml</Name>
+    </ProjectReference>
+    <ProjectReference Include="..\..\src\Markup\Perspex.Markup\Perspex.Markup.csproj">
+      <Project>{6417e941-21bc-467b-a771-0de389353ce6}</Project>
+      <Name>Perspex.Markup</Name>
+    </ProjectReference>
+    <ProjectReference Include="..\..\src\Perspex.Animation\Perspex.Animation.csproj">
+      <Project>{d211e587-d8bc-45b9-95a4-f297c8fa5200}</Project>
+      <Name>Perspex.Animation</Name>
+    </ProjectReference>
+    <ProjectReference Include="..\..\src\Perspex.Application\Perspex.Application.csproj">
+      <Project>{799a7bb5-3c2c-48b6-85a7-406a12c420da}</Project>
+      <Name>Perspex.Application</Name>
+    </ProjectReference>
+    <ProjectReference Include="..\..\src\Perspex.Base\Perspex.Base.csproj">
+      <Project>{b09b78d8-9b26-48b0-9149-d64a2f120f3f}</Project>
+      <Name>Perspex.Base</Name>
+    </ProjectReference>
+    <ProjectReference Include="..\..\src\Perspex.Controls\Perspex.Controls.csproj">
+      <Project>{d2221c82-4a25-4583-9b43-d791e3f6820c}</Project>
+      <Name>Perspex.Controls</Name>
+    </ProjectReference>
+    <ProjectReference Include="..\..\src\Perspex.Input\Perspex.Input.csproj">
+      <Project>{62024b2d-53eb-4638-b26b-85eeaa54866e}</Project>
+      <Name>Perspex.Input</Name>
+    </ProjectReference>
+    <ProjectReference Include="..\..\src\Perspex.Interactivity\Perspex.Interactivity.csproj">
+      <Project>{6b0ed19d-a08b-461c-a9d9-a9ee40b0c06b}</Project>
+      <Name>Perspex.Interactivity</Name>
+    </ProjectReference>
+    <ProjectReference Include="..\..\src\Perspex.Layout\Perspex.Layout.csproj">
+      <Project>{42472427-4774-4c81-8aff-9f27b8e31721}</Project>
+      <Name>Perspex.Layout</Name>
+    </ProjectReference>
+    <ProjectReference Include="..\..\src\Perspex.SceneGraph\Perspex.SceneGraph.csproj">
+      <Project>{eb582467-6abb-43a1-b052-e981ba910e3a}</Project>
+      <Name>Perspex.SceneGraph</Name>
+    </ProjectReference>
+    <ProjectReference Include="..\..\src\Perspex.Styling\Perspex.Styling.csproj">
+      <Project>{f1baa01a-f176-4c6a-b39d-5b40bb1b148f}</Project>
+      <Name>Perspex.Styling</Name>
+    </ProjectReference>
+    <ProjectReference Include="..\..\src\Perspex.Themes.Default\Perspex.Themes.Default.csproj">
+      <Project>{3e10a5fa-e8da-48b1-ad44-6a5b6cb7750f}</Project>
+      <Name>Perspex.Themes.Default</Name>
+    </ProjectReference>
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="app.config" />
+    <None Include="packages.config" />
+  </ItemGroup>
+  <Import Project="..\..\src\Shared\PlatformSupport\PlatformSupport.projitems" Label="Shared" />
+  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+  <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 
+       Other similar extension points exist, see Microsoft.Common.targets.
+  <Target Name="BeforeBuild">
+  </Target>
+  <Target Name="AfterBuild">
+  </Target>
+  -->
+</Project>

+ 36 - 0
tests/Perspex.UnitTests/Properties/AssemblyInfo.cs

@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following 
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("Perspex.UnitTests")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("Perspex.UnitTests")]
+[assembly: AssemblyCopyright("Copyright ©  2016")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible 
+// to COM components.  If you need to access a type in this assembly from 
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("88060192-33d5-4932-b0f9-8bd2763e857d")]
+
+// Version information for an assembly consists of the following four values:
+//
+//      Major Version
+//      Minor Version 
+//      Build Number
+//      Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers 
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]

+ 16 - 21
tests/Perspex.Markup.UnitTests/TestRoot.cs → tests/Perspex.UnitTests/TestRoot.cs

@@ -3,13 +3,14 @@
 
 using System;
 using Perspex.Controls;
+using Perspex.Layout;
 using Perspex.Platform;
 using Perspex.Rendering;
 using Perspex.Styling;
 
-namespace Perspex.Markup.UnitTests
+namespace Perspex.UnitTests
 {
-    public class TestRoot : Decorator, IRenderRoot, INameScope, IStyleRoot
+    public class TestRoot : Decorator, ILayoutRoot, INameScope, IRenderRoot, IStyleRoot
     {
         private readonly NameScope _nameScope = new NameScope();
 
@@ -29,37 +30,31 @@ namespace Perspex.Markup.UnitTests
 
         public int NameScopeUnregisteredSubscribers { get; private set; }
 
-        public IRenderTarget RenderTarget
-        {
-            get { throw new NotImplementedException(); }
-        }
+        public Size ClientSize => new Size(100, 100);
 
-        public IRenderQueueManager RenderQueueManager
-        {
-            get { throw new NotImplementedException(); }
-        }
+        public Size MaxClientSize => Size.Infinity;
 
-        public Point PointToClient(Point p)
-        {
-            throw new NotImplementedException();
-        }
+        public ILayoutManager LayoutManager => PerspexLocator.Current.GetService<ILayoutManager>();
 
-        public Point PointToScreen(Point p)
-        {
-            throw new NotImplementedException();
-        }
+        public IRenderTarget RenderTarget => null;
+
+        public IRenderQueueManager RenderQueueManager => null;
+
+        public Point PointToClient(Point p) => p;
+
+        public Point PointToScreen(Point p) => p;
 
-        public void Register(string name, object element)
+        void INameScope.Register(string name, object element)
         {
             _nameScope.Register(name, element);
         }
 
-        public object Find(string name)
+        object INameScope.Find(string name)
         {
             return _nameScope.Find(name);
         }
 
-        public void Unregister(string name)
+        void INameScope.Unregister(string name)
         {
             _nameScope.Unregister(name);
         }

+ 110 - 0
tests/Perspex.UnitTests/TestServices.cs

@@ -0,0 +1,110 @@
+// Copyright (c) The Perspex Project. All rights reserved.
+// Licensed under the MIT license. See licence.md file in the project root for full license information.
+
+using System;
+using System.Reflection;
+using Moq;
+using Perspex.Input;
+using Perspex.Layout;
+using Perspex.Platform;
+using Perspex.Shared.PlatformSupport;
+using Perspex.Styling;
+using Perspex.Themes.Default;
+using Ploeh.AutoFixture;
+using Ploeh.AutoFixture.AutoMoq;
+
+namespace Perspex.UnitTests
+{
+    public class TestServices
+    {
+        private static IFixture s_fixture = new Fixture().Customize(new AutoMoqCustomization());
+
+        public static readonly TestServices StyledWindow = new TestServices(
+            assetLoader: new AssetLoader(),
+            layoutManager: new LayoutManager(),
+            platformWrapper: new PclPlatformWrapper(),
+            renderInterface: s_fixture.Create<IPlatformRenderInterface>(),
+            standardCursorFactory: Mock.Of<IStandardCursorFactory>(),
+            styler: new Styler(),
+            theme: () => new DefaultTheme(),
+            threadingInterface: Mock.Of<IPlatformThreadingInterface>(x => x.CurrentThreadIsLoopThread == true),
+            windowingPlatform: new MockWindowingPlatform());
+
+        public static readonly TestServices MockPlatformWrapper = new TestServices(
+            platformWrapper: Mock.Of<IPclPlatformWrapper>());
+
+        public static readonly TestServices MockStyler = new TestServices(
+            styler: Mock.Of<IStyler>());
+
+        public static readonly TestServices MockThreadingInterface = new TestServices(
+            threadingInterface: Mock.Of<IPlatformThreadingInterface>(x => x.CurrentThreadIsLoopThread == true));
+
+        public static readonly TestServices RealStyler = new TestServices(
+            styler: new Styler());
+
+        public TestServices(
+            IAssetLoader assetLoader = null,
+            IInputManager inputManager = null,
+            ILayoutManager layoutManager = null,
+            IPclPlatformWrapper platformWrapper = null,
+            IPlatformRenderInterface renderInterface = null,
+            IStandardCursorFactory standardCursorFactory = null,
+            IStyler styler = null,
+            Func<Styles> theme = null,
+            IPlatformThreadingInterface threadingInterface = null,
+            IWindowImpl windowImpl = null,
+            IWindowingPlatform windowingPlatform = null)
+        {
+            AssetLoader = assetLoader;
+            InputManager = inputManager;
+            LayoutManager = layoutManager;
+            PlatformWrapper = platformWrapper;
+            RenderInterface = renderInterface;
+            StandardCursorFactory = standardCursorFactory;
+            Styler = styler;
+            Theme = theme;
+            ThreadingInterface = threadingInterface;
+            WindowImpl = windowImpl;
+            WindowingPlatform = windowingPlatform;
+        }
+
+        public IAssetLoader AssetLoader { get; }
+        public IInputManager InputManager { get; }
+        public ILayoutManager LayoutManager { get; }
+        public IPclPlatformWrapper PlatformWrapper { get; }
+        public IPlatformRenderInterface RenderInterface { get; }
+        public IStandardCursorFactory StandardCursorFactory { get; }
+        public IStyler Styler { get; }
+        public Func<Styles> Theme { get; }
+        public IPlatformThreadingInterface ThreadingInterface { get; }
+        public IWindowImpl WindowImpl { get; }
+        public IWindowingPlatform WindowingPlatform { get; }
+
+        public TestServices With(
+            IAssetLoader assetLoader = null,
+            IInputManager inputManager = null,
+            ILayoutManager layoutManager = null,
+            IPclPlatformWrapper platformWrapper = null,
+            IPlatformRenderInterface renderInterface = null,
+            IStandardCursorFactory standardCursorFactory = null,
+            IStyler styler = null,
+            Func<Styles> theme = null,
+            IPlatformThreadingInterface threadingInterface = null,
+            IWindowImpl windowImpl = null,
+            IWindowingPlatform windowingPlatform = null)
+        {
+            return new TestServices(
+                assetLoader: assetLoader ?? AssetLoader,
+                inputManager: inputManager ?? InputManager,
+                layoutManager: layoutManager ?? LayoutManager,
+                platformWrapper: platformWrapper ?? PlatformWrapper,
+                renderInterface: renderInterface ?? RenderInterface,
+                standardCursorFactory: standardCursorFactory ?? StandardCursorFactory,
+                styler: styler ?? Styler,
+                theme: theme ?? Theme,
+                threadingInterface: threadingInterface ?? ThreadingInterface,
+                windowImpl: windowImpl ?? WindowImpl,
+                windowingPlatform: windowingPlatform ?? WindowingPlatform);
+        }
+    }
+}

+ 65 - 0
tests/Perspex.UnitTests/TestTemplatedRoot.cs

@@ -0,0 +1,65 @@
+// Copyright (c) The Perspex Project. All rights reserved.
+// Licensed under the MIT license. See licence.md file in the project root for full license information.
+
+using System;
+using Perspex.Controls;
+using Perspex.Controls.Presenters;
+using Perspex.Controls.Templates;
+using Perspex.Layout;
+using Perspex.Platform;
+using Perspex.Rendering;
+using Perspex.Styling;
+
+namespace Perspex.UnitTests
+{
+    public class TestTemplatedRoot : ContentControl, ILayoutRoot, INameScope, IRenderRoot, IStyleRoot
+    {
+        private readonly NameScope _nameScope = new NameScope();
+
+        public TestTemplatedRoot()
+        {
+            Template = new FuncControlTemplate<TestTemplatedRoot>(x => new ContentPresenter());
+        }
+
+        public event EventHandler<NameScopeEventArgs> Registered
+        {
+            add { _nameScope.Registered += value; }
+            remove { _nameScope.Registered -= value; }
+        }
+
+        public event EventHandler<NameScopeEventArgs> Unregistered
+        {
+            add { _nameScope.Unregistered += value; }
+            remove { _nameScope.Unregistered -= value; }
+        }
+
+        public Size ClientSize => new Size(100, 100);
+
+        public Size MaxClientSize => Size.Infinity;
+
+        public ILayoutManager LayoutManager => PerspexLocator.Current.GetService<ILayoutManager>();
+
+        public IRenderTarget RenderTarget => null;
+
+        public IRenderQueueManager RenderQueueManager => null;
+
+        public Point PointToClient(Point p) => p;
+
+        public Point PointToScreen(Point p) => p;
+
+        void INameScope.Register(string name, object element)
+        {
+            _nameScope.Register(name, element);
+        }
+
+        object INameScope.Find(string name)
+        {
+            return _nameScope.Find(name);
+        }
+
+        void INameScope.Unregister(string name)
+        {
+            _nameScope.Unregister(name);
+        }
+    }
+}

+ 47 - 0
tests/Perspex.UnitTests/UnitTestApplication.cs

@@ -0,0 +1,47 @@
+// Copyright (c) The Perspex Project. All rights reserved.
+// Licensed under the MIT license. See licence.md file in the project root for full license information.
+
+using System;
+using Perspex.Input;
+using Perspex.Layout;
+using Perspex.Platform;
+using Perspex.Styling;
+
+namespace Perspex.UnitTests
+{
+    public class UnitTestApplication : Application
+    {
+        public UnitTestApplication(TestServices services)
+        {
+            Services = services ?? new TestServices();
+            RegisterServices();
+            Styles = Services.Theme?.Invoke();
+        }
+
+        public static new UnitTestApplication Current => (UnitTestApplication)Application.Current;
+
+        public TestServices Services { get; }
+
+        public static IDisposable Start(TestServices services = null)
+        {
+            var scope = PerspexLocator.EnterScope();
+            var app = new UnitTestApplication(services);
+            return scope;
+        }
+
+        protected override void RegisterServices()
+        {
+            PerspexLocator.CurrentMutable
+                .Bind<IAssetLoader>().ToConstant(Services.AssetLoader)
+                .BindToSelf<IGlobalStyles>(this)
+                .Bind<IInputManager>().ToConstant(Services.InputManager)
+                .Bind<ILayoutManager>().ToConstant(Services.LayoutManager)
+                .Bind<IPclPlatformWrapper>().ToConstant(Services.PlatformWrapper)
+                .Bind<IPlatformRenderInterface>().ToConstant(Services.RenderInterface)
+                .Bind<IPlatformThreadingInterface>().ToConstant(Services.ThreadingInterface)
+                .Bind<IStandardCursorFactory>().ToConstant(Services.StandardCursorFactory)
+                .Bind<IStyler>().ToConstant(Services.Styler)
+                .Bind<IWindowingPlatform>().ToConstant(Services.WindowingPlatform);
+        }
+    }
+}

+ 11 - 0
tests/Perspex.UnitTests/app.config

@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<configuration>
+  <runtime>
+    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
+      <dependentAssembly>
+        <assemblyIdentity name="Moq" publicKeyToken="69f491c39445e920" culture="neutral" />
+        <bindingRedirect oldVersion="0.0.0.0-4.2.1510.2205" newVersion="4.2.1510.2205" />
+      </dependentAssembly>
+    </assemblyBinding>
+  </runtime>
+</configuration>

Some files were not shown because too many files changed in this diff