فهرست منبع

Merge branch 'master' into scroll

Steven Kirk 8 سال پیش
والد
کامیت
9186b63d18
100فایلهای تغییر یافته به همراه3252 افزوده شده و 601 حذف شده
  1. 6 0
      .gitignore
  2. 1 0
      .ncrunch/Avalonia.Direct2D1.RenderTests.v3.ncrunchproject
  3. 9 0
      .ncrunch/Avalonia.Markup.UnitTests.netcoreapp1.1.v3.ncrunchproject
  4. 1 0
      .ncrunch/Avalonia.Skia.RenderTests.v3.ncrunchproject
  5. 2 1
      .travis.yml
  6. 2 2
      appveyor.yml
  7. 1 1
      build.cake
  8. 1 1
      build/XUnit.props
  9. 7 6
      docs/guidelines/build.md
  10. 30 31
      packages.cake
  11. 1 1
      samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj
  12. 1 1
      samples/ControlCatalog/ControlCatalog.csproj
  13. 2 1
      samples/ControlCatalog/MainWindow.xaml.cs
  14. 36 17
      samples/ControlCatalog/Pages/ToolTipPage.xaml
  15. 1 1
      src/Avalonia.Animation/Avalonia.Animation.csproj
  16. 1 1
      src/Avalonia.Base/Avalonia.Base.csproj
  17. 3 0
      src/Avalonia.Base/AvaloniaPropertyRegistry.cs
  18. 34 20
      src/Avalonia.Base/Collections/AvaloniaDictionary.cs
  19. 48 3
      src/Avalonia.Controls/Application.cs
  20. 1 1
      src/Avalonia.Controls/Avalonia.Controls.csproj
  21. 112 22
      src/Avalonia.Controls/Control.cs
  22. 1 1
      src/Avalonia.Controls/DropDown.cs
  23. 1 0
      src/Avalonia.Controls/IControl.cs
  24. 6 1
      src/Avalonia.Controls/Platform/IWindowImpl.cs
  25. 1 1
      src/Avalonia.Controls/Presenters/TextPresenter.cs
  26. 16 10
      src/Avalonia.Controls/Primitives/Popup.cs
  27. 156 80
      src/Avalonia.Controls/ToolTip.cs
  28. 98 0
      src/Avalonia.Controls/ToolTipService.cs
  29. 22 2
      src/Avalonia.Controls/TopLevel.cs
  30. 21 3
      src/Avalonia.Controls/Window.cs
  31. 1 1
      src/Avalonia.DesignerSupport/Avalonia.DesignerSupport.csproj
  32. 1 1
      src/Avalonia.Diagnostics/Avalonia.Diagnostics.csproj
  33. 1 1
      src/Avalonia.DotNetCoreRuntime/Avalonia.DotNetCoreRuntime.csproj
  34. 20 74
      src/Avalonia.DotNetFrameworkRuntime/Avalonia.DotNetFrameworkRuntime.csproj
  35. 0 15
      src/Avalonia.DotNetFrameworkRuntime/Properties/AssemblyInfo.cs
  36. 1 1
      src/Avalonia.HtmlRenderer/Avalonia.HtmlRenderer.csproj
  37. 0 12
      src/Avalonia.HtmlRenderer/Compat/Api.cs
  38. 1 1
      src/Avalonia.Input/Avalonia.Input.csproj
  39. 1 1
      src/Avalonia.Interactivity/Avalonia.Interactivity.csproj
  40. 1 1
      src/Avalonia.Layout/Avalonia.Layout.csproj
  41. 1 1
      src/Avalonia.Logging.Serilog/Avalonia.Logging.Serilog.csproj
  42. 1 1
      src/Avalonia.ReactiveUI/Avalonia.ReactiveUI.csproj
  43. 2 1
      src/Avalonia.Styling/Avalonia.Styling.csproj
  44. 19 0
      src/Avalonia.Styling/Controls/IResourceDictionary.cs
  45. 15 0
      src/Avalonia.Styling/Controls/IResourceNode.cs
  46. 33 0
      src/Avalonia.Styling/Controls/IResourceProvider.cs
  47. 101 0
      src/Avalonia.Styling/Controls/ResourceDictionary.cs
  48. 65 0
      src/Avalonia.Styling/Controls/ResourceProviderExtensions.cs
  49. 11 0
      src/Avalonia.Styling/Controls/ResourcesChangedEventArgs.cs
  50. 11 0
      src/Avalonia.Styling/LogicalTree/ILogical.cs
  51. 2 0
      src/Avalonia.Styling/Properties/AssemblyInfo.cs
  52. 32 0
      src/Avalonia.Styling/Styling/ISetStyleParent.cs
  53. 3 10
      src/Avalonia.Styling/Styling/IStyle.cs
  54. 54 38
      src/Avalonia.Styling/Styling/Style.cs
  55. 0 42
      src/Avalonia.Styling/Styling/StyleExtensions.cs
  56. 0 90
      src/Avalonia.Styling/Styling/StyleResources.cs
  57. 135 14
      src/Avalonia.Styling/Styling/Styles.cs
  58. 1 1
      src/Avalonia.Themes.Default/Avalonia.Themes.Default.csproj
  59. 1 1
      src/Avalonia.Visuals/Avalonia.Visuals.csproj
  60. 10 8
      src/Gtk/Avalonia.Gtk/WindowImpl.cs
  61. 1 1
      src/Gtk/Avalonia.Gtk3/Avalonia.Gtk3.csproj
  62. 41 8
      src/Gtk/Avalonia.Gtk3/Interop/Native.cs
  63. 9 7
      src/Gtk/Avalonia.Gtk3/Interop/Resolver.cs
  64. 4 0
      src/Gtk/Avalonia.Gtk3/WindowImpl.cs
  65. 1 1
      src/Linux/Avalonia.LinuxFramebuffer/Avalonia.LinuxFramebuffer.csproj
  66. 5 1
      src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj
  67. 78 7
      src/Markup/Avalonia.Markup.Xaml/Data/DelayedBinding.cs
  68. 63 0
      src/Markup/Avalonia.Markup.Xaml/Data/ResourceInclude.cs
  69. 5 2
      src/Markup/Avalonia.Markup.Xaml/Data/StyleResourceBinding.cs
  70. 71 0
      src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/DynamicResourceExtension.cs
  71. 84 0
      src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/StaticResourceExtension.cs
  72. 1 1
      src/Markup/Avalonia.Markup.Xaml/PortableXaml/portable.xaml.github
  73. 30 11
      src/Markup/Avalonia.Markup.Xaml/Styling/StyleInclude.cs
  74. 1 1
      src/Markup/Avalonia.Markup/Avalonia.Markup.csproj
  75. 1 1
      src/Shared/PlatformSupport/StandardRuntimePlatform.cs
  76. 1 1
      src/Skia/Avalonia.Skia/Avalonia.Skia.csproj
  77. 1 1
      src/Windows/Avalonia.Direct2D1/Avalonia.Direct2D1.csproj
  78. 1 1
      src/Windows/Avalonia.Win32.NetStandard/Avalonia.Win32.NetStandard.csproj
  79. 37 8
      src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs
  80. 6 9
      src/Windows/Avalonia.Win32/SystemDialogImpl.cs
  81. 22 0
      src/Windows/Avalonia.Win32/WindowImpl.cs
  82. 1 1
      tests/Avalonia.Base.UnitTests/Avalonia.Base.UnitTests.csproj
  83. 1 1
      tests/Avalonia.Controls.UnitTests/Avalonia.Controls.UnitTests.csproj
  84. 217 0
      tests/Avalonia.Controls.UnitTests/ControlTests_Resources.cs
  85. 17 0
      tests/Avalonia.Controls.UnitTests/TopLevelTests.cs
  86. 1 1
      tests/Avalonia.Input.UnitTests/Avalonia.Input.UnitTests.csproj
  87. 1 1
      tests/Avalonia.Interactivity.UnitTests/Avalonia.Interactivity.UnitTests.csproj
  88. 1 1
      tests/Avalonia.Layout.UnitTests/Avalonia.Layout.UnitTests.csproj
  89. 4 0
      tests/Avalonia.Layout.UnitTests/FullLayoutTests.cs
  90. 1 1
      tests/Avalonia.Markup.UnitTests/Avalonia.Markup.UnitTests.csproj
  91. 1 1
      tests/Avalonia.Markup.Xaml.UnitTests/Avalonia.Markup.Xaml.UnitTests.csproj
  92. 55 0
      tests/Avalonia.Markup.Xaml.UnitTests/Data/ResourceIncludeTests.cs
  93. 660 0
      tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/DynamicResourceExtensionTests.cs
  94. 476 0
      tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/StaticResourceExtensionTests.cs
  95. 20 0
      tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/TestValueConverter.cs
  96. 3 3
      tests/Avalonia.Markup.Xaml.UnitTests/Xaml/BasicTests.cs
  97. 7 5
      tests/Avalonia.Markup.Xaml.UnitTests/Xaml/StyleTests.cs
  98. 1 1
      tests/Avalonia.RenderTests/Avalonia.Skia.RenderTests.csproj
  99. 1 1
      tests/Avalonia.Styling.UnitTests/Avalonia.Styling.UnitTests.csproj
  100. 175 0
      tests/Avalonia.Styling.UnitTests/ResourceDictionaryTests.cs

+ 6 - 0
.gitignore

@@ -159,6 +159,12 @@ $RECYCLE.BIN/
 *.userprefs
 *.nugetreferenceswitcher
 
+
+#################
+## Rider
+#################
+.idea
+
 #################
 ## Cake
 #################

+ 1 - 0
.ncrunch/Avalonia.Direct2D1.RenderTests.v3.ncrunchproject

@@ -1,6 +1,7 @@
 <ProjectConfiguration>
   <Settings>
     <DefaultTestTimeout>1000</DefaultTestTimeout>
+    <IgnoreThisComponentCompletely>True</IgnoreThisComponentCompletely>
     <PreviouslyBuiltSuccessfully>True</PreviouslyBuiltSuccessfully>
   </Settings>
 </ProjectConfiguration>

+ 9 - 0
.ncrunch/Avalonia.Markup.UnitTests.netcoreapp1.1.v3.ncrunchproject

@@ -0,0 +1,9 @@
+<ProjectConfiguration>
+  <Settings>
+    <IgnoredTests>
+      <NamedTestSelector>
+        <TestName>Avalonia.Markup.UnitTests.Data.Plugins.DataAnnotationsValidationPluginTests.Produces_Aggregate_BindingNotificationsx</TestName>
+      </NamedTestSelector>
+    </IgnoredTests>
+  </Settings>
+</ProjectConfiguration>

+ 1 - 0
.ncrunch/Avalonia.Skia.RenderTests.v3.ncrunchproject

@@ -1,6 +1,7 @@
 <ProjectConfiguration>
   <Settings>
     <DefaultTestTimeout>1000</DefaultTestTimeout>
+    <IgnoreThisComponentCompletely>True</IgnoreThisComponentCompletely>
     <PreviouslyBuiltSuccessfully>True</PreviouslyBuiltSuccessfully>
   </Settings>
 </ProjectConfiguration>

+ 2 - 1
.travis.yml

@@ -3,13 +3,14 @@ os:
   - linux
   - osx
 dist: trusty
+osx_image: xcode8.3
 env:
   global:
     - DOTNET_SKIP_FIRST_TIME_EXPERIENCE=1
     - DOTNET_CLI_TELEMETRY_OPTOUT=1
 mono:
   - latest
-dotnet: 1.0.1
+dotnet: 2.0.0
 script:
   - ./build.sh --target "Travis" --platform "Mono" --configuration "Release"
 notifications:

+ 2 - 2
appveyor.yml

@@ -17,9 +17,9 @@ init:
 - ps: if (Test-Path env:nuget_address) {[System.IO.File]::AppendAllText("C:\Windows\System32\drivers\etc\hosts", "`n$($env:nuget_address)`tapi.nuget.org")}
 install:
   - if not exist gtk-sharp-2.12.26.msi appveyor DownloadFile http://download.xamarin.com/GTKforWindows/Windows/gtk-sharp-2.12.26.msi
-  - if not exist dotnet-1.0.1.exe appveyor DownloadFile https://go.microsoft.com/fwlink/?linkid=843448 -FileName "dotnet-1.0.1.exe"
+  - if not exist dotnet-2.0.0.exe appveyor DownloadFile https://download.microsoft.com/download/0/F/D/0FD852A4-7EA1-4E2A-983A-0484AC19B92C/dotnet-sdk-2.0.0-win-x64.exe -FileName "dotnet-2.0.0.exe"
   - ps: Start-Process -FilePath "msiexec" -ArgumentList "/i gtk-sharp-2.12.26.msi /quiet /qn /norestart" -Wait
-  - ps: Start-Process -FilePath "dotnet-1.0.1.exe" -ArgumentList "/quiet" -Wait
+  - ps: Start-Process -FilePath "dotnet-2.0.0.exe" -ArgumentList "/quiet" -Wait
   - cmd: set PATH=%programfiles(x86)%\GtkSharp\2.12\bin\;%PATH%
 before_build:
 - git submodule update --init

+ 1 - 1
build.cake

@@ -162,7 +162,7 @@ void RunCoreTest(string project, Parameters parameters, bool coreOnly = false)
         project = System.IO.Path.Combine(project, System.IO.Path.GetFileName(project)+".csproj");
     Information("Running tests from " + project);
     DotNetCoreRestore(project);
-    var frameworks = new List<string>(){"netcoreapp1.1"};
+    var frameworks = new List<string>(){"netcoreapp2.0"};
     if(parameters.IsRunningOnWindows)
         frameworks.Add("net461");
     foreach(var fw in frameworks)

+ 1 - 1
build/XUnit.props

@@ -9,7 +9,7 @@
     <PackageReference Include="xunit.runner.console" Version="2.2.0" />
     <PackageReference Include="xunit.runner.visualstudio" Version="2.2.0" />
   </ItemGroup>
-  <ItemGroup Condition="'$(TargetFramework)' == 'netcoreapp1.1'">
+  <ItemGroup Condition="'$(TargetFramework)' == 'netcoreapp2.0'">
     <PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.0.0" />
   </ItemGroup>
   <PropertyGroup>

+ 7 - 6
docs/guidelines/build.md

@@ -80,10 +80,11 @@ mono ./samples/ControlCatalog.Desktop/bin/Debug/ControlCatalog.Desktop.exe
 
 ### Building Avalonia in MonoDevelop
 
-Unless you have a very current version of monodevelop (6.1.x or newer), it is necessary to manually
-restore the Nuget depdendencies as [mentioned above](#restore-nuget-packages). You must then
-disable MonoDevelop's inbuilt NuGet package manager add-in by going to `Tools -> Add-in Manager` or
-it will complain that a newer version of NuGet is needed.
+Flatpak version will *NOT* work. Version from https://github.com/cra0zy/monodevelop-run-installer/ might work if you are very lucky. Make sure that you have the latest version of Mono (from alpha update channel) and .NET Core SDK. Make sure to follow `FrameworkPathOverride` workaround from https://github.com/dotnet/sdk/issues/335
+
+### Building and running Avalonia in Rider
+
+For Linux/OSX you'll probably need to apply workaround from https://github.com/dotnet/sdk/issues/335
+
+Just add `export FrameworkPathOverride=/usr/lib/mono/4.6.1-api` (or `export FrameworkPathOverride=/Library/Frameworks/Mono.framework/Versions/Current/lib/mono/4.6.1-api` for OSX)
 
-Finally, select the `Debug | Mono` or `Release | Mono` build configuration and you should be good to
-go!

+ 30 - 31
packages.cake

@@ -43,7 +43,6 @@ public class Packages
         }
     }
         
-    //new NuSpecDependency() { Id = "System.Threading.ThreadPool", TargetFramework = "netcoreapp1.0", Version = "4.3.0" },
     public Packages(ICakeContext context, Parameters parameters)
     {
         // NUGET NUSPECS
@@ -186,12 +185,12 @@ public class Packages
         };
 
         var coreLibrariesFiles = coreLibraries.Select((lib) => {
-            return (FilePath)context.File(lib[0] + lib[1] + "/bin/" + parameters.DirSuffix + "/netstandard1.3/" + lib[1] + lib[2]);
+            return (FilePath)context.File(lib[0] + lib[1] + "/bin/" + parameters.DirSuffix + "/netstandard2.0/" + lib[1] + lib[2]);
         }).ToList();
 
         var coreLibrariesNuSpecContent = coreLibrariesFiles.Select((file) => {
             return new NuSpecContent { 
-                Source = file.FullPath, Target = "lib/netstandard1.3" 
+                Source = file.FullPath, Target = "lib/netstandard2.0" 
             };
         });
 
@@ -203,14 +202,14 @@ public class Packages
 
         var netcoreappCoreLibrariesNuSpecContent = coreLibrariesFiles.Select((file) => {
             return new NuSpecContent { 
-                Source = file.FullPath, Target = "lib/netcoreapp1.0" 
+                Source = file.FullPath, Target = "lib/netcoreapp2.0" 
             };
         });
 
         var net45RuntimePlatformExtensions = new [] {".xml", ".dll"};
         var net45RuntimePlatform = net45RuntimePlatformExtensions.Select(libSuffix => {
             return new NuSpecContent {
-                Source = ((FilePath)context.File("./src/Avalonia.DotNetFrameworkRuntime/bin/" + parameters.DirSuffix + "/Avalonia.DotNetFrameworkRuntime" + libSuffix)).FullPath, 
+                Source = ((FilePath)context.File("./src/Avalonia.DotNetFrameworkRuntime/bin/" + parameters.DirSuffix + "/net461/Avalonia.DotNetFrameworkRuntime" + libSuffix)).FullPath, 
                 Target = "lib/net45" 
             };
         });
@@ -218,8 +217,8 @@ public class Packages
         var netCoreRuntimePlatformExtensions = new [] {".xml", ".dll"};
         var netCoreRuntimePlatform = netCoreRuntimePlatformExtensions.Select(libSuffix => {
             return new NuSpecContent {
-                Source = ((FilePath)context.File("./src/Avalonia.DotNetCoreRuntime/bin/" + parameters.DirSuffix + "/netcoreapp1.0/Avalonia.DotNetCoreRuntime" + libSuffix)).FullPath, 
-                Target = "lib/netcoreapp1.0" 
+                Source = ((FilePath)context.File("./src/Avalonia.DotNetCoreRuntime/bin/" + parameters.DirSuffix + "/netcoreapp2.0/Avalonia.DotNetCoreRuntime" + libSuffix)).FullPath, 
+                Target = "lib/netcoreapp2.0" 
             };
         });
 
@@ -238,15 +237,15 @@ public class Packages
                     new NuSpecDependency() { Id = "Sprache", Version = SpracheVersion },
                     new NuSpecDependency() { Id = "System.Reactive", Version = SystemReactiveVersion },
                     //.NET Core
-                    new NuSpecDependency() { Id = "System.Threading.ThreadPool", TargetFramework = "netcoreapp1.0", Version = "4.3.0" },
-                    new NuSpecDependency() { Id = "Microsoft.Extensions.DependencyModel", TargetFramework = "netcoreapp1.0", Version = "1.1.0" },
-                    new NuSpecDependency() { Id = "NETStandard.Library", TargetFramework = "netcoreapp1.0", Version = "1.6.0" },
-                    new NuSpecDependency() { Id = "Splat", TargetFramework = "netcoreapp1.0", Version = SplatVersion },
-                    new NuSpecDependency() { Id = "Serilog", TargetFramework = "netcoreapp1.0", Version = SerilogVersion },
-                    new NuSpecDependency() { Id = "Sprache", TargetFramework = "netcoreapp1.0", Version = SpracheVersion },
-                    new NuSpecDependency() { Id = "System.Reactive", TargetFramework = "netcoreapp1.0", Version = SystemReactiveVersion },
+                    new NuSpecDependency() { Id = "System.Threading.ThreadPool", TargetFramework = "netcoreapp2.0", Version = "4.3.0" },
+                    new NuSpecDependency() { Id = "Microsoft.Extensions.DependencyModel", TargetFramework = "netcoreapp2.0", Version = "1.1.0" },
+                    new NuSpecDependency() { Id = "NETStandard.Library", TargetFramework = "netcoreapp2.0", Version = "1.6.0" },
+                    new NuSpecDependency() { Id = "Splat", TargetFramework = "netcoreapp2.0", Version = SplatVersion },
+                    new NuSpecDependency() { Id = "Serilog", TargetFramework = "netcoreapp2.0", Version = SerilogVersion },
+                    new NuSpecDependency() { Id = "Sprache", TargetFramework = "netcoreapp2.0", Version = SpracheVersion },
+                    new NuSpecDependency() { Id = "System.Reactive", TargetFramework = "netcoreapp2.0", Version = SystemReactiveVersion },
                 }
-                .Deps(new string[]{null, "netcoreapp1.0"},
+                .Deps(new string[]{null, "netcoreapp2.0"},
                     "System.ValueTuple", "System.ComponentModel.TypeConverter", "System.ComponentModel.Primitives",
                     "System.Runtime.Serialization.Primitives", "System.Xml.XmlDocument", "System.Xml.ReaderWriter")
                 .ToArray(),
@@ -269,9 +268,9 @@ public class Packages
                 },
                 Files = new []
                 {
-                    new NuSpecContent { Source = "Avalonia.HtmlRenderer.dll", Target = "lib/netstandard1.3" }
+                    new NuSpecContent { Source = "Avalonia.HtmlRenderer.dll", Target = "lib/netstandard2.0" }
                 },
-                BasePath = context.Directory("./src/Avalonia.HtmlRenderer/bin/" + parameters.DirSuffix + "/netstandard1.3"),
+                BasePath = context.Directory("./src/Avalonia.HtmlRenderer/bin/" + parameters.DirSuffix + "/netstandard2.0"),
                 OutputDirectory = parameters.NugetRoot
             }
         };
@@ -331,7 +330,7 @@ public class Packages
                 Files = new []
                 {
                     new NuSpecContent { Source = "Avalonia.Win32/bin/" + parameters.DirSuffix + "/Avalonia.Win32.dll", Target = "lib/net45" },
-                    new NuSpecContent { Source = "Avalonia.Win32.NetStandard/bin/" + parameters.DirSuffix + "/netstandard1.3/Avalonia.Win32.dll", Target = "lib/netstandard1.3" }
+                    new NuSpecContent { Source = "Avalonia.Win32.NetStandard/bin/" + parameters.DirSuffix + "/netstandard2.0/Avalonia.Win32.dll", Target = "lib/netstandard2.0" }
                 },
                 BasePath = context.Directory("./src/Windows"),
                 OutputDirectory = parameters.NugetRoot
@@ -352,9 +351,9 @@ public class Packages
                 },
                 Files = new []
                 {
-                    new NuSpecContent { Source = "Avalonia.Direct2D1.dll", Target = "lib/netstandard1.3" }
+                    new NuSpecContent { Source = "Avalonia.Direct2D1.dll", Target = "lib/netstandard2.0" }
                 },
-                BasePath = context.Directory("./src/Windows/Avalonia.Direct2D1/bin/" + parameters.DirSuffix + "/netstandard1.3"),
+                BasePath = context.Directory("./src/Windows/Avalonia.Direct2D1/bin/" + parameters.DirSuffix + "/netstandard2.0"),
                 OutputDirectory = parameters.NugetRoot
             },
             ///////////////////////////////////////////////////////////////////////////////
@@ -386,9 +385,9 @@ public class Packages
                 },
                 Files = new []
                 {
-                    new NuSpecContent { Source = "Avalonia.Gtk3.dll", Target = "lib/netstandard1.3" }
+                    new NuSpecContent { Source = "Avalonia.Gtk3.dll", Target = "lib/netstandard2.0" }
                 },
-                BasePath = context.Directory("./src/Gtk/Avalonia.Gtk3/bin/" + parameters.DirSuffix + "/netstandard1.3"),
+                BasePath = context.Directory("./src/Gtk/Avalonia.Gtk3/bin/" + parameters.DirSuffix + "/netstandard2.0"),
                 OutputDirectory = parameters.NugetRoot
             },
             ///////////////////////////////////////////////////////////////////////////////
@@ -418,18 +417,18 @@ public class Packages
                 {
                     new NuSpecDependency() { Id = "Avalonia", Version = parameters.Version },
                     new NuSpecDependency() { Id = "SkiaSharp", Version = SkiaSharpVersion },
-                    new NuSpecDependency() { Id = "Avalonia", Version = parameters.Version, TargetFramework="netcoreapp1.1" },
-                    new NuSpecDependency() { Id = "SkiaSharp", Version = SkiaSharpVersion, TargetFramework="netcoreapp1.1" },
-                    new NuSpecDependency() { Id = "Avalonia.Skia.Linux.Natives", Version = SkiaSharpLinuxVersion, TargetFramework="netcoreapp1.1" },
+                    new NuSpecDependency() { Id = "Avalonia", Version = parameters.Version, TargetFramework="netcoreapp2.0" },
+                    new NuSpecDependency() { Id = "SkiaSharp", Version = SkiaSharpVersion, TargetFramework="netcoreapp2.0" },
+                    new NuSpecDependency() { Id = "Avalonia.Skia.Linux.Natives", Version = SkiaSharpLinuxVersion, TargetFramework="netcoreapp2.0" },
                     new NuSpecDependency() { Id = "Avalonia", Version = parameters.Version, TargetFramework="net461" },
                     new NuSpecDependency() { Id = "SkiaSharp", Version = SkiaSharpVersion, TargetFramework="net461" },
                     new NuSpecDependency() { Id = "Avalonia.Skia.Linux.Natives", Version = SkiaSharpLinuxVersion, TargetFramework="net461" }
                 },
                 Files = new []
                 {
-                    new NuSpecContent { Source = "Avalonia.Skia.dll", Target = "lib/netstandard1.3" }
+                    new NuSpecContent { Source = "Avalonia.Skia.dll", Target = "lib/netstandard2.0" }
                 },
-                BasePath = context.Directory("./src/Skia/Avalonia.Skia/bin/" + parameters.DirSuffix + "/netstandard1.3"),
+                BasePath = context.Directory("./src/Skia/Avalonia.Skia/bin/" + parameters.DirSuffix + "/netstandard2.0"),
                 OutputDirectory = parameters.NugetRoot
             },
             ///////////////////////////////////////////////////////////////////////////////
@@ -448,9 +447,9 @@ public class Packages
                     new NuSpecDependency() { Id = "Avalonia.Skia", TargetFramework="net45", Version = parameters.Version },
                     new NuSpecDependency() { Id = "Avalonia.Gtk3", TargetFramework="net45", Version = parameters.Version },
                     //.NET Core
-                    new NuSpecDependency() { Id = "Avalonia.Win32", TargetFramework="netcoreapp1.0", Version = parameters.Version },
-                    new NuSpecDependency() { Id = "Avalonia.Skia", TargetFramework="netcoreapp1.0", Version = parameters.Version },
-                    new NuSpecDependency() { Id = "Avalonia.Gtk3", TargetFramework="netcoreapp1.0", Version = parameters.Version }
+                    new NuSpecDependency() { Id = "Avalonia.Win32", TargetFramework="netcoreapp2.0", Version = parameters.Version },
+                    new NuSpecDependency() { Id = "Avalonia.Skia", TargetFramework="netcoreapp2.0", Version = parameters.Version },
+                    new NuSpecDependency() { Id = "Avalonia.Gtk3", TargetFramework="netcoreapp2.0", Version = parameters.Version }
                 },
                 Files = new NuSpecContent[]
                 {
@@ -488,7 +487,7 @@ public class Packages
                 },
                 Files = new []
                 {
-                    new NuSpecContent { Source = "Avalonia.LinuxFramebuffer/bin/" + parameters.DirSuffix + "/netstandard1.3/Avalonia.LinuxFramebuffer.dll", Target = "lib/netstandard1.3" }
+                    new NuSpecContent { Source = "Avalonia.LinuxFramebuffer/bin/" + parameters.DirSuffix + "/netstandard2.0/Avalonia.LinuxFramebuffer.dll", Target = "lib/netstandard2.0" }
                 },
                 BasePath = context.Directory("./src/Linux/"),
                 OutputDirectory = parameters.NugetRoot

+ 1 - 1
samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj

@@ -2,7 +2,7 @@
 
   <PropertyGroup>
     <OutputType>Exe</OutputType>
-    <TargetFramework>netcoreapp1.1</TargetFramework>
+    <TargetFramework>netcoreapp2.0</TargetFramework>
   </PropertyGroup>
 
   <ItemGroup>

+ 1 - 1
samples/ControlCatalog/ControlCatalog.csproj

@@ -1,6 +1,6 @@
 <Project Sdk="Microsoft.NET.Sdk">
   <PropertyGroup>
-    <TargetFramework>netstandard1.3</TargetFramework>
+    <TargetFramework>netstandard2.0</TargetFramework>
     <EnableDefaultCompileItems>False</EnableDefaultCompileItems>
     <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
   </PropertyGroup>

+ 2 - 1
samples/ControlCatalog/MainWindow.xaml.cs

@@ -1,6 +1,7 @@
 using Avalonia;
 using Avalonia.Controls;
 using Avalonia.Markup.Xaml;
+using System;
 
 namespace ControlCatalog
 {
@@ -19,7 +20,7 @@ namespace ControlCatalog
             // so we must refer to this resource DLL statically. For
             // now I am doing that here. But we need a better solution!!
             var theme = new Avalonia.Themes.Default.DefaultTheme();
-            theme.FindResource("Button");
+            theme.TryGetResource("Button", out _);
             AvaloniaXamlLoader.Load(this);
         }
     }

+ 36 - 17
samples/ControlCatalog/Pages/ToolTipPage.xaml

@@ -1,22 +1,41 @@
 <UserControl xmlns="https://github.com/avaloniaui">
-  <StackPanel Orientation="Vertical" Gap="4">
-    <TextBlock Classes="h1">ToolTip</TextBlock>
-    <TextBlock Classes="h2">A control which pops up a hint when a control is hovered</TextBlock>
+    <StackPanel Orientation="Vertical"
+                Gap="4">
+        <TextBlock Classes="h1">ToolTip</TextBlock>
+        <TextBlock Classes="h2">A control which pops up a hint when a control is hovered</TextBlock>
 
-    <StackPanel Orientation="Horizontal"
+        <Grid RowDefinitions="Auto,Auto"
+              ColumnDefinitions="Auto,Auto"
               Margin="0,16,0,0"
-              HorizontalAlignment="Center"
-              Gap="16">
-      <Border Background="{StyleResource ThemeAccentBrush}"
-              Padding="48,48,48,48">
-        <ToolTip.Tip>
-          <StackPanel>
-            <TextBlock Classes="h1">ToolTip</TextBlock>
-            <TextBlock Classes="h2">A control which pops up a hint when a control is hovered</TextBlock>
-          </StackPanel>
-        </ToolTip.Tip>
-        <TextBlock>Hover Here</TextBlock>
-      </Border>
+              HorizontalAlignment="Center">
+            <Border Grid.Column="0"
+                    Grid.Row="1"
+                    Background="{StyleResource ThemeAccentBrush}"
+                    Margin="5"
+                    Padding="50"
+                    ToolTip.Tip="This is a ToolTip">
+                <TextBlock>Hover Here</TextBlock>
+            </Border>
+            <CheckBox Grid.Column="1"
+                      Margin="5"
+                      Grid.Row="0"
+                      IsChecked="{Binding ElementName=Border, Path=(ToolTip.IsOpen)}"
+                      Content="ToolTip Open" />
+            <Border Name="Border"
+                    Grid.Column="1"
+                    Grid.Row="1"
+                    Background="{StyleResource ThemeAccentBrush}"
+                    Margin="5"
+                    Padding="50"
+                    ToolTip.Placement="Bottom">
+                <ToolTip.Tip>
+                    <StackPanel>
+                        <TextBlock Classes="h1">ToolTip</TextBlock>
+                        <TextBlock Classes="h2">A control which pops up a hint when a control is hovered</TextBlock>
+                    </StackPanel>
+                </ToolTip.Tip>
+                <TextBlock>ToolTip bottom placement</TextBlock>
+            </Border>
+        </Grid>
     </StackPanel>
-  </StackPanel>
 </UserControl>

+ 1 - 1
src/Avalonia.Animation/Avalonia.Animation.csproj

@@ -1,6 +1,6 @@
 <Project Sdk="Microsoft.NET.Sdk">
   <PropertyGroup>
-    <TargetFramework>netstandard1.3</TargetFramework>
+    <TargetFramework>netstandard2.0</TargetFramework>
     <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
   </PropertyGroup>
   <PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">

+ 1 - 1
src/Avalonia.Base/Avalonia.Base.csproj

@@ -1,6 +1,6 @@
 <Project Sdk="Microsoft.NET.Sdk">
   <PropertyGroup>
-    <TargetFramework>netstandard1.3</TargetFramework>
+    <TargetFramework>netstandard2.0</TargetFramework>
     <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
     <RootNamespace>Avalonia</RootNamespace>
   </PropertyGroup>

+ 3 - 0
src/Avalonia.Base/AvaloniaPropertyRegistry.cs

@@ -47,6 +47,9 @@ namespace Avalonia
         {
             Dictionary<int, AvaloniaProperty> inner;
 
+            // Ensure the type's static ctor has been run.
+            RuntimeHelpers.RunClassConstructor(ownerType.TypeHandle);
+
             if (_attached.TryGetValue(ownerType, out inner))
             {
                 return inner.Values;

+ 34 - 20
src/Avalonia.Base/Collections/AvaloniaDictionary.cs

@@ -16,6 +16,7 @@ namespace Avalonia.Collections
     /// <typeparam name="TKey">The type of the dictionary key.</typeparam>
     /// <typeparam name="TValue">The type of the dictionary value.</typeparam>
     public class AvaloniaDictionary<TKey, TValue> : IDictionary<TKey, TValue>,
+        IDictionary,
         INotifyCollectionChanged,
         INotifyPropertyChanged
     {
@@ -51,6 +52,16 @@ namespace Avalonia.Collections
         /// <inheritdoc/>
         public ICollection<TValue> Values => _inner.Values;
 
+        bool IDictionary.IsFixedSize => ((IDictionary)_inner).IsFixedSize;
+
+        ICollection IDictionary.Keys => ((IDictionary)_inner).Keys;
+
+        ICollection IDictionary.Values => ((IDictionary)_inner).Values;
+
+        bool ICollection.IsSynchronized => ((IDictionary)_inner).IsSynchronized;
+
+        object ICollection.SyncRoot => ((IDictionary)_inner).SyncRoot;
+
         /// <summary>
         /// Gets or sets the named resource.
         /// </summary>
@@ -89,6 +100,8 @@ namespace Avalonia.Collections
             }
         }
 
+        object IDictionary.this[object key] { get => ((IDictionary)_inner)[key]; set => ((IDictionary)_inner)[key] = value; }
+
         /// <inheritdoc/>
         public void Add(TKey key, TValue value)
         {
@@ -118,10 +131,7 @@ namespace Avalonia.Collections
         }
 
         /// <inheritdoc/>
-        public bool ContainsKey(TKey key)
-        {
-            return _inner.ContainsKey(key);
-        }
+        public bool ContainsKey(TKey key) => _inner.ContainsKey(key);
 
         /// <inheritdoc/>
         public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
@@ -130,21 +140,16 @@ namespace Avalonia.Collections
         }
 
         /// <inheritdoc/>
-        public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
-        {
-            return _inner.GetEnumerator();
-        }
+        public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator() => _inner.GetEnumerator();
 
         /// <inheritdoc/>
         public bool Remove(TKey key)
         {
-            TValue value;
-
-            if (_inner.TryGetValue(key, out value))
+            if (_inner.TryGetValue(key, out TValue value))
             {
                 PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Count"));
                 PropertyChanged?.Invoke(this, new PropertyChangedEventArgs($"Item[{key}]"));
-                
+
                 if (CollectionChanged != null)
                 {
                     var e = new NotifyCollectionChangedEventArgs(
@@ -163,16 +168,13 @@ namespace Avalonia.Collections
         }
 
         /// <inheritdoc/>
-        public bool TryGetValue(TKey key, out TValue value)
-        {
-            return _inner.TryGetValue(key, out value);
-        }
+        public bool TryGetValue(TKey key, out TValue value) => _inner.TryGetValue(key, out value);
 
         /// <inheritdoc/>
-        IEnumerator IEnumerable.GetEnumerator()
-        {
-            return _inner.GetEnumerator();
-        }
+        IEnumerator IEnumerable.GetEnumerator() => _inner.GetEnumerator();
+
+        /// <inheritdoc/>
+        void ICollection.CopyTo(Array array, int index) => ((ICollection)_inner).CopyTo(array, index);
 
         /// <inheritdoc/>
         void ICollection<KeyValuePair<TKey, TValue>>.Add(KeyValuePair<TKey, TValue> item)
@@ -192,6 +194,18 @@ namespace Avalonia.Collections
             return Remove(item.Key);
         }
 
+        /// <inheritdoc/>
+        void IDictionary.Add(object key, object value) => Add((TKey)key, (TValue)value);
+
+        /// <inheritdoc/>
+        bool IDictionary.Contains(object key) => ((IDictionary) _inner).Contains(key);
+
+        /// <inheritdoc/>
+        IDictionaryEnumerator IDictionary.GetEnumerator() => ((IDictionary)_inner).GetEnumerator();
+
+        /// <inheritdoc/>
+        void IDictionary.Remove(object key) => Remove((TKey)key);
+
         private void NotifyAdd(TKey key, TValue value)
         {
             PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Count"));

+ 48 - 3
src/Avalonia.Controls/Application.cs

@@ -29,7 +29,7 @@ namespace Avalonia
     /// method.
     /// - Tracks the lifetime of the application.
     /// </remarks>
-    public class Application : IGlobalDataTemplates, IGlobalStyles, IStyleRoot, IApplicationLifecycle
+    public class Application : IApplicationLifecycle, IGlobalDataTemplates, IGlobalStyles, IStyleRoot, IResourceNode
     {
         /// <summary>
         /// The application-global data templates.
@@ -40,6 +40,7 @@ namespace Avalonia
             new Lazy<IClipboard>(() => (IClipboard)AvaloniaLocator.Current.GetService(typeof(IClipboard)));
         private readonly Styler _styler = new Styler();
         private Styles _styles;
+        private IResourceDictionary _resources;
 
         /// <summary>
         /// Initializes a new instance of the <see cref="Application"/> class.
@@ -49,6 +50,9 @@ namespace Avalonia
             OnExit += OnExiting;
         }
 
+        /// <inheritdoc/>
+        public event EventHandler<ResourcesChangedEventArgs> ResourcesChanged;
+
         /// <summary>
         /// Gets the current instance of the <see cref="Application"/> class.
         /// </summary>
@@ -97,6 +101,34 @@ namespace Avalonia
         /// </summary>
         public IClipboard Clipboard => _clipboard.Value;
 
+        /// <summary>
+        /// Gets the application's global resource dictionary.
+        /// </summary>
+        public IResourceDictionary Resources
+        {
+            get => _resources ?? (Resources = new ResourceDictionary());
+            set
+            {
+                Contract.Requires<ArgumentNullException>(value != null);
+
+                var hadResources = false;
+
+                if (_resources != null)
+                {
+                    hadResources = _resources.Count > 0;
+                    _resources.ResourcesChanged -= ResourcesChanged;
+                }
+
+                _resources = value;
+                _resources.ResourcesChanged += ResourcesChanged;
+
+                if (hadResources || _resources.Count > 0)
+                {
+                    ResourcesChanged?.Invoke(this, new ResourcesChangedEventArgs());
+                }
+            }
+        }
+
         /// <summary>
         /// Gets the application's global styles.
         /// </summary>
@@ -119,6 +151,12 @@ namespace Avalonia
         /// <inheritdoc/>
         bool IStyleHost.IsStylesInitialized => _styles != null;
 
+        /// <inheritdoc/>
+        bool IResourceProvider.HasResources => _resources?.Count > 0;
+
+        /// <inheritdoc/>
+        IResourceNode IResourceNode.ResourceParent => null;
+
         /// <summary>
         /// Initializes the application by loading XAML etc.
         /// </summary>
@@ -145,13 +183,20 @@ namespace Avalonia
         {
             OnExit?.Invoke(this, EventArgs.Empty);
         }
-        
+
+        /// <inheritdoc/>
+        bool IResourceProvider.TryGetResource(string key, out object value)
+        {
+            value = null;
+            return (_resources?.TryGetResource(key, out value) ?? false) ||
+                   Styles.TryGetResource(key, out value);
+        }
+
         /// <summary>
         /// Sent when the application is exiting.
         /// </summary>
         public event EventHandler OnExit;
 
-
         /// <summary>
         /// Called when the application is exiting.
         /// </summary>

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

@@ -1,6 +1,6 @@
 <Project Sdk="Microsoft.NET.Sdk">
   <PropertyGroup>
-    <TargetFramework>netstandard1.3</TargetFramework>
+    <TargetFramework>netstandard2.0</TargetFramework>
     <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
   </PropertyGroup>
   <PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">

+ 112 - 22
src/Avalonia.Controls/Control.cs

@@ -97,8 +97,9 @@ namespace Avalonia.Controls
         private bool _isAttachedToLogicalTree;
         private IAvaloniaList<ILogical> _logicalChildren;
         private INameScope _nameScope;
-        private bool _styled;
+        private IResourceDictionary _resources;
         private Styles _styles;
+        private bool _styled;
         private Subject<IStyleable> _styleDetach = new Subject<IStyleable>();
 
         /// <summary>
@@ -153,6 +154,11 @@ namespace Avalonia.Controls
         /// </remarks>
         public event EventHandler Initialized;
 
+        /// <summary>
+        /// Occurs when a resource in this control or a parent control has changed.
+        /// </summary>
+        public event EventHandler<ResourcesChangedEventArgs> ResourcesChanged;
+
         /// <summary>
         /// Gets or sets the name of the control.
         /// </summary>
@@ -262,7 +268,32 @@ namespace Avalonia.Controls
         /// each control may in addition define its own styles which are applied to the control
         /// itself and its children.
         /// </remarks>
-        public Styles Styles => _styles ?? (_styles = new Styles());
+        public Styles Styles
+        {
+            get { return _styles ?? (Styles = new Styles()); }
+            set
+            {
+                Contract.Requires<ArgumentNullException>(value != null);
+
+                if (_styles != value)
+                {
+                    if (_styles != null)
+                    {
+                        (_styles as ISetStyleParent)?.SetParent(null);
+                        _styles.ResourcesChanged -= ThisResourcesChanged;
+                    }
+
+                    _styles = value;
+
+                    if (value is ISetStyleParent setParent && setParent.ResourceParent == null)
+                    {
+                        setParent.SetParent(this);
+                    } 
+
+                    _styles.ResourcesChanged += ThisResourcesChanged;
+                }
+            }
+        }
 
         /// <summary>
         /// Gets the control's logical parent.
@@ -278,6 +309,34 @@ namespace Avalonia.Controls
             set { SetValue(ContextMenuProperty, value); }
         }
 
+        /// <summary>
+        /// Gets or sets the control's resource dictionary.
+        /// </summary>
+        public IResourceDictionary Resources
+        {
+            get => _resources ?? (Resources = new ResourceDictionary());
+            set
+            {
+                Contract.Requires<ArgumentNullException>(value != null);
+
+                var hadResources = false;
+
+                if (_resources != null)
+                {
+                    hadResources = _resources.Count > 0;
+                    _resources.ResourcesChanged -= ThisResourcesChanged;
+                }
+
+                _resources = value;
+                _resources.ResourcesChanged += ThisResourcesChanged;
+
+                if (hadResources || _resources.Count > 0)
+                {
+                    ((ILogical)this).NotifyResourcesChanged(new ResourcesChangedEventArgs());
+                }
+            }
+        }
+
         /// <summary>
         /// Gets or sets a user-defined object attached to the control.
         /// </summary>
@@ -296,9 +355,35 @@ namespace Avalonia.Controls
             internal set { SetValue(TemplatedParentProperty, value); }
         }
 
+        /// <summary>
+        /// Gets the control's logical children.
+        /// </summary>
+        protected IAvaloniaList<ILogical> LogicalChildren
+        {
+            get
+            {
+                if (_logicalChildren == null)
+                {
+                    var list = new AvaloniaList<ILogical>();
+                    list.ResetBehavior = ResetBehavior.Remove;
+                    list.Validate = ValidateLogicalChild;
+                    list.CollectionChanged += LogicalChildrenCollectionChanged;
+                    _logicalChildren = list;
+                }
+
+                return _logicalChildren;
+            }
+        }
+
         /// <inheritdoc/>
         bool IDataTemplateHost.IsDataTemplatesInitialized => _dataTemplates != null;
 
+        /// <summary>
+        /// Gets the <see cref="Classes"/> collection in a form that allows adding and removing
+        /// pseudoclasses.
+        /// </summary>
+        protected IPseudoClasses PseudoClasses => Classes;
+
         /// <summary>
         /// Gets a value indicating whether the element is attached to a rooted logical tree.
         /// </summary>
@@ -314,6 +399,12 @@ namespace Avalonia.Controls
         /// </summary>
         IAvaloniaReadOnlyList<ILogical> ILogical.LogicalChildren => LogicalChildren;
 
+        /// <inheritdoc/>
+        bool IResourceProvider.HasResources => _resources?.Count > 0 || Styles.HasResources;
+
+        /// <inheritdoc/>
+        IResourceNode IResourceNode.ResourceParent => ((IStyleHost)this).StylingParent as IResourceNode;
+
         /// <inheritdoc/>
         IAvaloniaReadOnlyList<string> IStyleable.Classes => Classes;
 
@@ -390,31 +481,24 @@ namespace Avalonia.Controls
             this.OnDetachedFromLogicalTreeCore(e);
         }
 
-        /// <summary>
-        /// Gets the control's logical children.
-        /// </summary>
-        protected IAvaloniaList<ILogical> LogicalChildren
+        /// <inheritdoc/>
+        void ILogical.NotifyResourcesChanged(ResourcesChangedEventArgs e)
         {
-            get
-            {
-                if (_logicalChildren == null)
-                {
-                    var list = new AvaloniaList<ILogical>();
-                    list.ResetBehavior = ResetBehavior.Remove;
-                    list.Validate = ValidateLogicalChild;
-                    list.CollectionChanged += LogicalChildrenCollectionChanged;
-                    _logicalChildren = list;
-                }
+            ResourcesChanged?.Invoke(this, new ResourcesChangedEventArgs());
 
-                return _logicalChildren;
+            foreach (var child in LogicalChildren)
+            {
+                child.NotifyResourcesChanged(e);
             }
         }
 
-        /// <summary>
-        /// Gets the <see cref="Classes"/> collection in a form that allows adding and removing
-        /// pseudoclasses.
-        /// </summary>
-        protected IPseudoClasses PseudoClasses => Classes;
+        /// <inheritdoc/>
+        bool IResourceProvider.TryGetResource(string key, out object value)
+        {
+            value = null;
+            return (_resources?.TryGetResource(key, out value) ?? false) ||
+                   (_styles?.TryGetResource(key, out value) ?? false);
+        }
 
         /// <summary>
         /// Sets the control's logical parent.
@@ -450,6 +534,7 @@ namespace Avalonia.Controls
                 }
 
                 _parent = (IControl)parent;
+                ((ILogical)this).NotifyResourcesChanged(new ResourcesChangedEventArgs());
 
                 if (_parent is IStyleRoot || _parent?.IsAttachedToLogicalTree == true || this is IStyleRoot)
                 {
@@ -841,5 +926,10 @@ namespace Avalonia.Controls
                 }
             }
         }
+
+        private void ThisResourcesChanged(object sender, ResourcesChangedEventArgs e)
+        {
+            ((ILogical)this).NotifyResourcesChanged(e);
+        }
     }
 }

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

@@ -120,7 +120,7 @@ namespace Avalonia.Controls
         /// <inheritdoc/>
         protected override void OnPointerPressed(PointerPressedEventArgs e)
         {
-            if (!IsDropDownOpen && ((IVisual)e.Source).GetVisualRoot() != typeof(PopupRoot))
+            if (!IsDropDownOpen && ((IVisual)e.Source).GetVisualRoot() is PopupRoot)
             {
                 IsDropDownOpen = true;
                 e.Handled = true;

+ 1 - 0
src/Avalonia.Controls/IControl.cs

@@ -20,6 +20,7 @@ namespace Avalonia.Controls
         ILayoutable,
         IInputElement,
         INamed,
+        IResourceNode,
         IStyleable,
         IStyleHost
     {

+ 6 - 1
src/Avalonia.Controls/Platform/IWindowImpl.cs

@@ -31,7 +31,7 @@ namespace Avalonia.Platform
         IDisposable ShowDialog();
 
         /// <summary>
-        /// Enables of disables system window decorations (title bar, buttons, etc)
+        /// Enables or disables system window decorations (title bar, buttons, etc)
         /// </summary>
         void SetSystemDecorations(bool enabled);
 
@@ -39,5 +39,10 @@ namespace Avalonia.Platform
         /// Sets the icon of this window.
         /// </summary>
         void SetIcon(IWindowIconImpl icon);
+
+        /// <summary>
+        /// Enables or disables the taskbar icon
+        /// </summary>
+        void ShowTaskbarIcon(bool value);
     }
 }

+ 1 - 1
src/Avalonia.Controls/Presenters/TextPresenter.cs

@@ -115,7 +115,7 @@ namespace Avalonia.Controls.Presenters
 
                 if (_highlightBrush == null)
                 {
-                    _highlightBrush = (IBrush)this.FindStyleResource("HighlightBrush");
+                    _highlightBrush = (IBrush)this.FindResource("HighlightBrush");
                 }
 
                 foreach (var rect in rects)

+ 16 - 10
src/Avalonia.Controls/Primitives/Popup.cs

@@ -277,7 +277,7 @@ namespace Avalonia.Controls.Primitives
         {
             base.OnDetachedFromLogicalTree(e);
             _topLevel = null;
-            
+
             if (_popupRoot != null)
             {
                 ((ISetLogicalParent)_popupRoot).SetParent(null);
@@ -327,34 +327,40 @@ namespace Avalonia.Controls.Primitives
         /// </summary>
         /// <returns>The popup's position in screen coordinates.</returns>
         protected virtual Point GetPosition()
+        {
+            return GetPosition(PlacementTarget ?? this.GetVisualParent<Control>(), PlacementMode, PopupRoot, 
+                HorizontalOffset, VerticalOffset);
+        }
+
+        internal static Point GetPosition(Control target, PlacementMode placement, PopupRoot popupRoot, double horizontalOffset, double verticalOffset)
         {
             var zero = default(Point);
-            var mode = PlacementMode;
-            var target = PlacementTarget ?? this.GetVisualParent<Control>();
+            var mode = placement;
 
             if (target?.GetVisualRoot() == null)
             {
                 mode = PlacementMode.Pointer;
-            }            
+            }
 
             switch (mode)
             {
                 case PlacementMode.Pointer:
-                    if(PopupRoot != null)
+                    if (popupRoot != null)
                     {
                         // Scales the Horizontal and Vertical offset to screen co-ordinates.
-                        var screenOffset = new Point(HorizontalOffset * (PopupRoot as ILayoutRoot).LayoutScaling, VerticalOffset * (PopupRoot as ILayoutRoot).LayoutScaling);
-                        return (((IInputRoot)PopupRoot)?.MouseDevice?.Position ?? default(Point)) + screenOffset;
+                        var screenOffset = new Point(horizontalOffset * (popupRoot as ILayoutRoot).LayoutScaling,
+                            verticalOffset * (popupRoot as ILayoutRoot).LayoutScaling);
+                        return (((IInputRoot)popupRoot)?.MouseDevice?.Position ?? default(Point)) + screenOffset;
                     }
 
                     return default(Point);
 
                 case PlacementMode.Bottom:
-
-                    return target?.PointToScreen(new Point(0 + HorizontalOffset, target.Bounds.Height + VerticalOffset)) ?? zero;
+                    return target?.PointToScreen(new Point(0 + horizontalOffset, target.Bounds.Height + verticalOffset)) ??
+                           zero;
 
                 case PlacementMode.Right:
-                    return target?.PointToScreen(new Point(target.Bounds.Width + HorizontalOffset, 0 + VerticalOffset)) ?? zero;
+                    return target?.PointToScreen(new Point(target.Bounds.Width + horizontalOffset, 0 + verticalOffset)) ?? zero;
 
                 default:
                     throw new InvalidOperationException("Invalid value for Popup.PlacementMode");

+ 156 - 80
src/Avalonia.Controls/ToolTip.cs

@@ -3,11 +3,7 @@
 
 using System;
 using System.Reactive.Linq;
-using System.Reactive.Subjects;
 using Avalonia.Controls.Primitives;
-using Avalonia.Input;
-using Avalonia.Threading;
-using Avalonia.VisualTree;
 
 namespace Avalonia.Controls
 {
@@ -29,29 +25,50 @@ namespace Avalonia.Controls
             AvaloniaProperty.RegisterAttached<ToolTip, Control, object>("Tip");
 
         /// <summary>
-        /// The popup window used to display the active tooltip.
+        /// Defines the ToolTip.IsOpen attached property.
         /// </summary>
-        private static PopupRoot s_popup;
+        public static readonly AttachedProperty<bool> IsOpenProperty =
+            AvaloniaProperty.RegisterAttached<ToolTip, Control, bool>("IsOpen");
 
         /// <summary>
-        /// The control that the currently visible tooltip is attached to.
+        /// Defines the ToolTip.Placement property.
         /// </summary>
-        private static Control s_current;
+        public static readonly AttachedProperty<PlacementMode> PlacementProperty =
+            AvaloniaProperty.RegisterAttached<Popup, Control, PlacementMode>("Placement", defaultValue: PlacementMode.Pointer);
 
         /// <summary>
-        /// Observable fired when a tooltip should be displayed for a control. The output from this
-        /// observable is throttled and calls <see cref="ShowToolTip(Control)"/> when the time
-        /// period expires.
+        /// Defines the ToolTip.HorizontalOffset property.
         /// </summary>
-        private static readonly Subject<Control> s_show = new Subject<Control>();
+        public static readonly AttachedProperty<double> HorizontalOffsetProperty =
+            AvaloniaProperty.RegisterAttached<Popup, Control, double>("HorizontalOffset");
+
+        /// <summary>
+        /// Defines the ToolTip.VerticalOffset property.
+        /// </summary>
+        public static readonly AttachedProperty<double> VerticalOffsetProperty =
+            AvaloniaProperty.RegisterAttached<Popup, Control, double>("VerticalOffset", 20);
+
+        /// <summary>
+        /// Defines the ToolTip.ShowDelay property.
+        /// </summary>
+        public static readonly AttachedProperty<int> ShowDelayProperty =
+            AvaloniaProperty.RegisterAttached<Popup, Control, int>("ShowDelay", 400);
+
+        /// <summary>
+        /// Stores the curernt <see cref="ToolTip"/> instance in the control.
+        /// </summary>
+        private static readonly AttachedProperty<ToolTip> ToolTipProperty =
+            AvaloniaProperty.RegisterAttached<ToolTip, Control, ToolTip>("ToolTip");
+
+        private PopupRoot _popup;
 
         /// <summary>
         /// Initializes static members of the <see cref="ToolTip"/> class.
         /// </summary>
         static ToolTip()
         {
-            TipProperty.Changed.Subscribe(TipChanged);
-            s_show.Throttle(TimeSpan.FromSeconds(0.5), AvaloniaScheduler.Instance).Subscribe(ShowToolTip);
+            TipProperty.Changed.Subscribe(ToolTipService.Instance.TipChanged);
+            IsOpenProperty.Changed.Subscribe(IsOpenChanged);
         }
 
         /// <summary>
@@ -77,101 +94,160 @@ namespace Avalonia.Controls
         }
 
         /// <summary>
-        /// called when the <see cref="TipProperty"/> property changes on a control.
+        /// Gets the value of the ToolTip.IsOpen attached property.
         /// </summary>
-        /// <param name="e">The event args.</param>
-        private static void TipChanged(AvaloniaPropertyChangedEventArgs e)
+        /// <param name="element">The control to get the property from.</param>
+        /// <returns>
+        /// A value indicating whether the tool tip is visible.
+        /// </returns>
+        public static bool GetIsOpen(Control element)
         {
-            var control = (Control)e.Sender;
+            return element.GetValue(IsOpenProperty);
+        }
 
-            if (e.OldValue != null)
-            {
-                control.PointerEnter -= ControlPointerEnter;
-                control.PointerLeave -= ControlPointerLeave;
-            }
+        /// <summary>
+        /// Sets the value of the ToolTip.IsOpen attached property.
+        /// </summary>
+        /// <param name="element">The control to get the property from.</param>
+        /// <param name="value">A value indicating whether the tool tip is visible.</param>
+        public static void SetIsOpen(Control element, bool value)
+        {
+            element.SetValue(IsOpenProperty, value);
+        }
 
-            if (e.NewValue != null)
-            {
-                control.PointerEnter += ControlPointerEnter;
-                control.PointerLeave += ControlPointerLeave;
-            }
+        /// <summary>
+        /// Gets the value of the ToolTip.Placement attached property.
+        /// </summary>
+        /// <param name="element">The control to get the property from.</param>
+        /// <returns>
+        /// A value indicating how the tool tip is positioned.
+        /// </returns>
+        public static PlacementMode GetPlacement(Control element)
+        {
+            return element.GetValue(PlacementProperty);
         }
 
         /// <summary>
-        /// Shows a tooltip for the specified control.
+        /// Sets the value of the ToolTip.Placement attached property.
         /// </summary>
-        /// <param name="control">The control.</param>
-        private static void ShowToolTip(Control control)
+        /// <param name="element">The control to get the property from.</param>
+        /// <param name="value">A value indicating how the tool tip is positioned.</param>
+        public static void SetPlacement(Control element, PlacementMode value)
         {
-            if (control != null && control.IsVisible && control.GetVisualRoot() != null)
-            {
-                var cp = (control.GetVisualRoot() as IInputRoot)?.MouseDevice?.GetPosition(control);
+            element.SetValue(PlacementProperty, value);
+        }
 
-                if (cp.HasValue && control.IsVisible && new Rect(control.Bounds.Size).Contains(cp.Value))
-                {
-                    var position = control.PointToScreen(cp.Value) + new Vector(0, 22);
-
-                    if (s_popup == null)
-                    {
-                        s_popup = new PopupRoot();
-                        s_popup.Content = new ToolTip();
-                    }
-                    else
-                    {
-                        ((ISetLogicalParent)s_popup).SetParent(null);
-                    }
-
-                ((ISetLogicalParent)s_popup).SetParent(control);
-                    ((ToolTip)s_popup.Content).Content = GetTip(control);
-                    s_popup.Position = position;
-                    s_popup.Show();
-
-                    s_current = control;
-                }
-            }
+        /// <summary>
+        /// Gets the value of the ToolTip.HorizontalOffset attached property.
+        /// </summary>
+        /// <param name="element">The control to get the property from.</param>
+        /// <returns>
+        /// A value indicating how the tool tip is positioned.
+        /// </returns>
+        public static double GetHorizontalOffset(Control element)
+        {
+            return element.GetValue(HorizontalOffsetProperty);
+        }
+
+        /// <summary>
+        /// Sets the value of the ToolTip.HorizontalOffset attached property.
+        /// </summary>
+        /// <param name="element">The control to get the property from.</param>
+        /// <param name="value">A value indicating how the tool tip is positioned.</param>
+        public static void SetHorizontalOffset(Control element, double value)
+        {
+            element.SetValue(HorizontalOffsetProperty, value);
+        }
+
+        /// <summary>
+        /// Gets the value of the ToolTip.VerticalOffset attached property.
+        /// </summary>
+        /// <param name="element">The control to get the property from.</param>
+        /// <returns>
+        /// A value indicating how the tool tip is positioned.
+        /// </returns>
+        public static double GetVerticalOffset(Control element)
+        {
+            return element.GetValue(VerticalOffsetProperty);
+        }
+
+        /// <summary>
+        /// Sets the value of the ToolTip.VerticalOffset attached property.
+        /// </summary>
+        /// <param name="element">The control to get the property from.</param>
+        /// <param name="value">A value indicating how the tool tip is positioned.</param>
+        public static void SetVerticalOffset(Control element, double value)
+        {
+            element.SetValue(VerticalOffsetProperty, value);
         }
 
         /// <summary>
-        /// Called when the pointer enters a control with an attached tooltip.
+        /// Gets the value of the ToolTip.ShowDelay attached property.
         /// </summary>
-        /// <param name="sender">The event sender.</param>
-        /// <param name="e">The event args.</param>
-        private static void ControlPointerEnter(object sender, PointerEventArgs e)
+        /// <param name="element">The control to get the property from.</param>
+        /// <returns>
+        /// A value indicating the time, in milliseconds, before a tool tip opens.
+        /// </returns>
+        public static int GetShowDelay(Control element)
         {
-            s_current = (Control)sender;
-            s_show.OnNext(s_current);
+            return element.GetValue(ShowDelayProperty);
         }
 
         /// <summary>
-        /// Called when the pointer leaves a control with an attached tooltip.
+        /// Sets the value of the ToolTip.ShowDelay attached property.
         /// </summary>
-        /// <param name="sender">The event sender.</param>
-        /// <param name="e">The event args.</param>
-        private static void ControlPointerLeave(object sender, PointerEventArgs e)
+        /// <param name="element">The control to get the property from.</param>
+        /// <param name="value">A value indicating the time, in milliseconds, before a tool tip opens.</param>
+        public static void SetShowDelay(Control element, int value)
         {
-            var control = (Control)sender;
+            element.SetValue(ShowDelayProperty, value);
+        }
+
+        private static void IsOpenChanged(AvaloniaPropertyChangedEventArgs e)
+        {
+            var control = (Control)e.Sender;
 
-            if (control == s_current)
+            if ((bool)e.NewValue)
             {
-                if (s_popup != null)
+                var tip = GetTip(control);
+                if (tip == null) return;
+
+                var toolTip = control.GetValue(ToolTipProperty);
+                if (toolTip == null || (tip != toolTip && tip != toolTip.Content))
                 {
-                    DisposeTooltip();
-                    s_show.OnNext(null);
+                    toolTip?.Close();
+
+                    toolTip = tip as ToolTip ?? new ToolTip { Content = tip };
+                    control.SetValue(ToolTipProperty, toolTip);
                 }
+
+                toolTip.Open(control);
+            }
+            else
+            {
+                var toolTip = control.GetValue(ToolTipProperty);
+                toolTip?.Close();
             }
         }
 
-        private static void DisposeTooltip()
+        private void Open(Control control)
         {
-            if (s_popup != null)
-            {
-                // Clear the ToolTip's Content in case it has control content: this will
-                // reset its visual parent allowing it to be used again.
-                ((ToolTip)s_popup.Content).Content = null;
+            Close();
+
+            _popup = new PopupRoot { Content = this };
+            ((ISetLogicalParent)_popup).SetParent(control);
+            _popup.Position = Popup.GetPosition(control, GetPlacement(control), _popup,
+                GetHorizontalOffset(control), GetVerticalOffset(control));
+            _popup.Show();
+        }
 
-                // Dispose of the popup.
-                s_popup.Dispose();
-                s_popup = null;
+        private void Close()
+        {
+            if (_popup != null)
+            {
+                _popup.Content = null;
+                _popup.Hide();
+                _popup = null;
             }
         }
     }

+ 98 - 0
src/Avalonia.Controls/ToolTipService.cs

@@ -0,0 +1,98 @@
+using System;
+using Avalonia.Input;
+using Avalonia.Threading;
+
+namespace Avalonia.Controls
+{
+    /// <summary>
+    /// Handeles <see cref="ToolTip"/> interaction with controls.
+    /// </summary>
+    internal sealed class ToolTipService
+    {
+        public static ToolTipService Instance { get; } = new ToolTipService();
+
+        private DispatcherTimer _timer;
+
+        private ToolTipService() { }
+
+        /// <summary>
+        /// called when the <see cref="ToolTip.TipProperty"/> property changes on a control.
+        /// </summary>
+        /// <param name="e">The event args.</param>
+        internal void TipChanged(AvaloniaPropertyChangedEventArgs e)
+        {
+            var control = (Control)e.Sender;
+
+            if (e.OldValue != null)
+            {
+                control.PointerEnter -= ControlPointerEnter;
+                control.PointerLeave -= ControlPointerLeave;
+            }
+
+            if (e.NewValue != null)
+            {
+                control.PointerEnter += ControlPointerEnter;
+                control.PointerLeave += ControlPointerLeave;
+            }
+        }
+
+        /// <summary>
+        /// Called when the pointer enters a control with an attached tooltip.
+        /// </summary>
+        /// <param name="sender">The event sender.</param>
+        /// <param name="e">The event args.</param>
+        private void ControlPointerEnter(object sender, PointerEventArgs e)
+        {
+            StopTimer();
+
+            var control = (Control)sender;
+            var showDelay = ToolTip.GetShowDelay(control);
+            if (showDelay == 0)
+            {
+                Open(control);
+            }
+            else
+            {
+                StartShowTimer(showDelay, control);
+            }
+        }
+
+        /// <summary>
+        /// Called when the pointer leaves a control with an attached tooltip.
+        /// </summary>
+        /// <param name="sender">The event sender.</param>
+        /// <param name="e">The event args.</param>
+        private void ControlPointerLeave(object sender, PointerEventArgs e)
+        {
+            var control = (Control)sender;
+            Close(control);
+        }
+
+        private void StartShowTimer(int showDelay, Control control)
+        {
+            _timer = new DispatcherTimer { Interval = TimeSpan.FromMilliseconds(showDelay) };
+            _timer.Tick += (o, e) => Open(control);
+            _timer.Start();
+        }
+
+        private void Open(Control control)
+        {
+            StopTimer();
+
+            ToolTip.SetIsOpen(control, true);
+        }
+
+        private void Close(Control control)
+        {
+            StopTimer();
+
+            ToolTip.SetIsOpen(control, false);
+        }
+
+        private void StopTimer()
+        {
+            _timer?.Stop();
+            _timer = null;
+        }
+    }
+}

+ 22 - 2
src/Avalonia.Controls/TopLevel.cs

@@ -10,9 +10,11 @@ using Avalonia.Input;
 using Avalonia.Input.Raw;
 using Avalonia.Layout;
 using Avalonia.Logging;
+using Avalonia.LogicalTree;
 using Avalonia.Platform;
 using Avalonia.Rendering;
 using Avalonia.Styling;
+using Avalonia.Utilities;
 using Avalonia.VisualTree;
 using JetBrains.Annotations;
 
@@ -26,7 +28,13 @@ namespace Avalonia.Controls
     /// It handles scheduling layout, styling and rendering as well as
     /// tracking the widget's <see cref="ClientSize"/>.
     /// </remarks>
-    public abstract class TopLevel : ContentControl, IInputRoot, ILayoutRoot, IRenderRoot, ICloseable, IStyleRoot
+    public abstract class TopLevel : ContentControl,
+        IInputRoot,
+        ILayoutRoot,
+        IRenderRoot,
+        ICloseable,
+        IStyleRoot,
+        IWeakSubscriber<ResourcesChangedEventArgs>
     {
         /// <summary>
         /// Defines the <see cref="ClientSize"/> property.
@@ -100,7 +108,6 @@ namespace Avalonia.Controls
             impl.Resized = HandleResized;
             impl.ScalingChanged = HandleScalingChanged;
 
-
             _keyboardNavigationHandler?.SetOwner(this);
             _accessKeyHandler?.SetOwner(this);
             styler?.ApplyStyles(this);
@@ -116,6 +123,14 @@ namespace Avalonia.Controls
             {
                 _applicationLifecycle.OnExit += OnApplicationExiting;
             }
+
+            if (((IStyleHost)this).StylingParent is IResourceProvider applicationResources)
+            {
+                WeakSubscriptionManager.Subscribe(
+                    applicationResources,
+                    nameof(IResourceProvider.ResourcesChanged),
+                    this);
+            }
         }
 
         /// <summary>
@@ -165,6 +180,11 @@ namespace Avalonia.Controls
         /// <inheritdoc/>
         IMouseDevice IInputRoot.MouseDevice => PlatformImpl?.MouseDevice;
 
+        void IWeakSubscriber<ResourcesChangedEventArgs>.OnEvent(object sender, ResourcesChangedEventArgs e)
+        {
+            ((ILogical)this).NotifyResourcesChanged(e);
+        }
+
         /// <summary>
         /// Gets or sets a value indicating whether access keys are shown in the window.
         /// </summary>

+ 21 - 3
src/Avalonia.Controls/Window.cs

@@ -61,11 +61,17 @@ namespace Avalonia.Controls
             AvaloniaProperty.Register<Window, SizeToContent>(nameof(SizeToContent));
 
         /// <summary>
-        /// Enables of disables system window decorations (title bar, buttons, etc)
+        /// Enables or disables system window decorations (title bar, buttons, etc)
         /// </summary>
         public static readonly StyledProperty<bool> HasSystemDecorationsProperty =
             AvaloniaProperty.Register<Window, bool>(nameof(HasSystemDecorations), true);
-
+        
+        /// <summary>
+        /// Enables or disables the taskbar icon
+        /// </summary>
+        public static readonly StyledProperty<bool> ShowInTaskbarProperty =
+            AvaloniaProperty.Register<Window, bool>(nameof(ShowInTaskbar), true);
+        
         /// <summary>
         /// Defines the <see cref="Title"/> property.
         /// </summary>
@@ -92,6 +98,8 @@ namespace Avalonia.Controls
             HasSystemDecorationsProperty.Changed.AddClassHandler<Window>(
                 (s, e) => s.PlatformImpl?.SetSystemDecorations((bool) e.NewValue));
 
+            ShowInTaskbarProperty.Changed.AddClassHandler<Window>((w, e) => w.PlatformImpl?.ShowTaskbarIcon((bool)e.NewValue));
+
             IconProperty.Changed.AddClassHandler<Window>((s, e) => s.PlatformImpl?.SetIcon(((WindowIcon)e.NewValue).PlatformImpl));
         }
 
@@ -152,7 +160,7 @@ namespace Avalonia.Controls
         }
 
         /// <summary>
-        /// Enables of disables system window decorations (title bar, buttons, etc)
+        /// Enables or disables system window decorations (title bar, buttons, etc)
         /// </summary>
         /// 
         public bool HasSystemDecorations
@@ -160,6 +168,16 @@ namespace Avalonia.Controls
             get { return GetValue(HasSystemDecorationsProperty); }
             set { SetValue(HasSystemDecorationsProperty, value); }
         }
+        
+        /// <summary>
+        /// Enables or disables the taskbar icon
+        /// </summary>
+        /// 
+        public bool ShowInTaskbar
+        {
+            get { return GetValue(ShowInTaskbarProperty); }
+            set { SetValue(ShowInTaskbarProperty, value); }
+        }
 
         /// <summary>
         /// Gets or sets the minimized/maximized state of the window.

+ 1 - 1
src/Avalonia.DesignerSupport/Avalonia.DesignerSupport.csproj

@@ -1,6 +1,6 @@
 <Project Sdk="Microsoft.NET.Sdk">
   <PropertyGroup>
-    <TargetFramework>netstandard1.3</TargetFramework>
+    <TargetFramework>netstandard2.0</TargetFramework>
     <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
   </PropertyGroup>
   <PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">

+ 1 - 1
src/Avalonia.Diagnostics/Avalonia.Diagnostics.csproj

@@ -1,6 +1,6 @@
 <Project Sdk="Microsoft.NET.Sdk">
   <PropertyGroup>
-    <TargetFramework>netstandard1.3</TargetFramework>
+    <TargetFramework>netstandard2.0</TargetFramework>
     <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
   </PropertyGroup>
   <PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">

+ 1 - 1
src/Avalonia.DotNetCoreRuntime/Avalonia.DotNetCoreRuntime.csproj

@@ -1,6 +1,6 @@
 <Project Sdk="Microsoft.NET.Sdk">
   <PropertyGroup>
-    <TargetFramework>netcoreapp1.0</TargetFramework>
+    <TargetFramework>netcoreapp2.0</TargetFramework>
     <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
   </PropertyGroup>
   <PropertyGroup>

+ 20 - 74
src/Avalonia.DotNetFrameworkRuntime/Avalonia.DotNetFrameworkRuntime.csproj

@@ -1,75 +1,21 @@
-<?xml version="1.0" encoding="utf-8"?>
-<Project ToolsVersion="15.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>{4A1ABB09-9047-4BD5-A4AD-A055E52C5EE0}</ProjectGuid>
-    <OutputType>Library</OutputType>
-    <AppDesignerFolder>Properties</AppDesignerFolder>
-    <RootNamespace>Avalonia.DotNetFrameworkRuntime</RootNamespace>
-    <AssemblyName>Avalonia.DotNetFrameworkRuntime</AssemblyName>
-    <TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
-    <FileAlignment>512</FileAlignment>
-    <TargetFrameworkProfile />
-  </PropertyGroup>
-  <PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
-    <DebugSymbols>true</DebugSymbols>
-    <DebugType>full</DebugType>
-    <Optimize>false</Optimize>
-    <OutputPath>bin\Debug\</OutputPath>
-    <DefineConstants>TRACE;DEBUG;FULLDOTNET</DefineConstants>
-    <ErrorReport>prompt</ErrorReport>
-    <WarningLevel>4</WarningLevel>
-    <DocumentationFile>bin\Debug\Avalonia.DotNetFrameworkRuntime.xml</DocumentationFile>
-  </PropertyGroup>
-  <PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
-    <DebugType>pdbonly</DebugType>
-    <Optimize>true</Optimize>
-    <OutputPath>bin\Release\</OutputPath>
-    <DefineConstants>TRACE;FULLDOTNET</DefineConstants>
-    <ErrorReport>prompt</ErrorReport>
-    <WarningLevel>4</WarningLevel>
-    <DocumentationFile>bin\Release\Avalonia.DotNetFrameworkRuntime.xml</DocumentationFile>
-    <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
-  </PropertyGroup>
-  <ItemGroup>
-    <Reference Include="System" />
-    <Reference Include="System.Core" />
-    <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="..\Shared\SharedAssemblyInfo.cs">
-      <Link>Properties\SharedAssemblyInfo.cs</Link>
-    </Compile>
-    <Compile Include="AppBuilder.cs" />
-    <Compile Include="Properties\AssemblyInfo.cs" />
-    <Compile Include="RuntimeInfo.cs" />
-  </ItemGroup>
-  <ItemGroup>
-    <ProjectReference Include="..\Avalonia.Base\Avalonia.Base.csproj">
-      <Project>{B09B78D8-9B26-48B0-9149-D64A2F120F3F}</Project>
-      <Name>Avalonia.Base</Name>
-    </ProjectReference>
-    <ProjectReference Include="..\Avalonia.Controls\Avalonia.Controls.csproj">
-      <Project>{D2221C82-4A25-4583-9B43-D791E3F6820C}</Project>
-      <Name>Avalonia.Controls</Name>
-    </ProjectReference>
-    <ProjectReference Include="..\Avalonia.Visuals\Avalonia.Visuals.csproj">
-      <Project>{eb582467-6abb-43a1-b052-e981ba910e3a}</Project>
-      <Name>Avalonia.Visuals</Name>
-    </ProjectReference>
-    <ProjectReference Include="..\Avalonia.Styling\Avalonia.Styling.csproj">
-      <Project>{f1baa01a-f176-4c6a-b39d-5b40bb1b148f}</Project>
-      <Name>Avalonia.Styling</Name>
-    </ProjectReference>
-  </ItemGroup>
-  <Import Project="..\Shared\PlatformSupport\PlatformSupport.projitems" Label="Shared" />
-  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
-  <Import Project="..\..\build\Rx.props" />
+<Project Sdk="Microsoft.NET.Sdk">
+    <PropertyGroup>
+        <TargetFramework>net461</TargetFramework>
+        <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
+        <DocumentationFile>bin\$(Configuration)\Avalonia.DotNetFrameworkRuntime.xml</DocumentationFile>
+        <DefineConstants>$(DefineConstants);FULLDOTNET</DefineConstants>
+        <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
+    </PropertyGroup>
+    <ItemGroup>
+        <Compile Include="..\Shared\SharedAssemblyInfo.cs">
+            <Link>Properties\SharedAssemblyInfo.cs</Link>
+        </Compile>
+
+        <ProjectReference Include="..\Avalonia.Base\Avalonia.Base.csproj" />
+        <ProjectReference Include="..\Avalonia.Controls\Avalonia.Controls.csproj" />
+        <ProjectReference Include="..\Avalonia.Visuals\Avalonia.Visuals.csproj" />
+        <ProjectReference Include="..\Avalonia.Styling\Avalonia.Styling.csproj" />
+    </ItemGroup>
+    <Import Project="..\Shared\PlatformSupport\PlatformSupport.projitems" Label="Shared" />
+    <Import Project="..\..\build\Rx.props" />
 </Project>

+ 0 - 15
src/Avalonia.DotNetFrameworkRuntime/Properties/AssemblyInfo.cs

@@ -1,15 +0,0 @@
-using System.Reflection;
-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("Avalonia.DotNetFrameworkRuntime")]
-
-// 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("4a1abb09-9047-4bd5-a4ad-a055e52c5ee0")]

+ 1 - 1
src/Avalonia.HtmlRenderer/Avalonia.HtmlRenderer.csproj

@@ -1,6 +1,6 @@
 <Project Sdk="Microsoft.NET.Sdk">
   <PropertyGroup>
-    <TargetFramework>netstandard1.3</TargetFramework>
+    <TargetFramework>netstandard2.0</TargetFramework>
     <EnableDefaultCompileItems>False</EnableDefaultCompileItems>
     <EnableDefaultItems>False</EnableDefaultItems>
     <GenerateAssemblyInfo>false</GenerateAssemblyInfo>

+ 0 - 12
src/Avalonia.HtmlRenderer/Compat/Api.cs

@@ -4,18 +4,6 @@ using System.Text;
 
 namespace System.Net
 {
-    internal class AsyncCompletedEventArgs
-    {
-        public object UserState { get; set; }
-        public Exception Error { get; set; }
-        public bool Cancelled { get; set; }
-
-        public AsyncCompletedEventArgs(Exception error, bool cancelled, object userState)
-        {
-
-        }
-    }
-
     class WebException : Exception
     {
         public object Response { get; set; }

+ 1 - 1
src/Avalonia.Input/Avalonia.Input.csproj

@@ -1,6 +1,6 @@
 <Project Sdk="Microsoft.NET.Sdk">
   <PropertyGroup>
-    <TargetFramework>netstandard1.3</TargetFramework>
+    <TargetFramework>netstandard2.0</TargetFramework>
     <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
   </PropertyGroup>
   <PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">

+ 1 - 1
src/Avalonia.Interactivity/Avalonia.Interactivity.csproj

@@ -1,6 +1,6 @@
 <Project Sdk="Microsoft.NET.Sdk">
   <PropertyGroup>
-    <TargetFramework>netstandard1.3</TargetFramework>
+    <TargetFramework>netstandard2.0</TargetFramework>
     <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
   </PropertyGroup>
   <PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">

+ 1 - 1
src/Avalonia.Layout/Avalonia.Layout.csproj

@@ -1,6 +1,6 @@
 <Project Sdk="Microsoft.NET.Sdk">
   <PropertyGroup>
-    <TargetFramework>netstandard1.3</TargetFramework>
+    <TargetFramework>netstandard2.0</TargetFramework>
     <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
   </PropertyGroup>
   <PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">

+ 1 - 1
src/Avalonia.Logging.Serilog/Avalonia.Logging.Serilog.csproj

@@ -1,6 +1,6 @@
 <Project Sdk="Microsoft.NET.Sdk">
   <PropertyGroup>
-    <TargetFramework>netstandard1.3</TargetFramework>
+    <TargetFramework>netstandard2.0</TargetFramework>
     <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
   </PropertyGroup>
   <PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">

+ 1 - 1
src/Avalonia.ReactiveUI/Avalonia.ReactiveUI.csproj

@@ -1,6 +1,6 @@
 <Project Sdk="Microsoft.NET.Sdk">
   <PropertyGroup>
-    <TargetFramework>netstandard1.3</TargetFramework>
+    <TargetFramework>netstandard2.0</TargetFramework>
     <EnableDefaultCompileItems>False</EnableDefaultCompileItems>
     <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
   </PropertyGroup>

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

@@ -1,7 +1,8 @@
 <Project Sdk="Microsoft.NET.Sdk">
   <PropertyGroup>
-    <TargetFramework>netstandard1.3</TargetFramework>
+    <TargetFramework>netstandard2.0</TargetFramework>
     <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
+    <RootNamespace>Avalonia</RootNamespace>
   </PropertyGroup>
   <PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
     <DebugSymbols>true</DebugSymbols>

+ 19 - 0
src/Avalonia.Styling/Controls/IResourceDictionary.cs

@@ -0,0 +1,19 @@
+// Copyright (c) The Avalonia 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.Generic;
+
+namespace Avalonia.Controls
+{
+    /// <summary>
+    /// An indexed dictionary of resources.
+    /// </summary>
+    public interface IResourceDictionary : IResourceProvider, IDictionary<object, object>
+    {
+        /// <summary>
+        /// Gets a collection of child resource dictionaries.
+        /// </summary>
+        IList<IResourceProvider> MergedDictionaries { get; }
+    }
+}

+ 15 - 0
src/Avalonia.Styling/Controls/IResourceNode.cs

@@ -0,0 +1,15 @@
+using System;
+
+namespace Avalonia.Controls
+{
+    /// <summary>
+    /// Represents resource provider in a tree.
+    /// </summary>
+    public interface IResourceNode : IResourceProvider
+    {
+        /// <summary>
+        /// Gets the parent resource node, if any.
+        /// </summary>
+        IResourceNode ResourceParent { get; }
+    }
+}

+ 33 - 0
src/Avalonia.Styling/Controls/IResourceProvider.cs

@@ -0,0 +1,33 @@
+using System;
+
+namespace Avalonia.Controls
+{
+    /// <summary>
+    /// Represents an object that can be queried for resources.
+    /// </summary>
+    public interface IResourceProvider
+    {
+        /// <summary>
+        /// Raised when resources in the provider are changed.
+        /// </summary>
+        event EventHandler<ResourcesChangedEventArgs> ResourcesChanged;
+
+        /// <summary>
+        /// Gets a value indicating whether the element has resources.
+        /// </summary>
+        bool HasResources { get; }
+
+        /// <summary>
+        /// Tries to find a resource within the provider.
+        /// </summary>
+        /// <param name="key">The resource key.</param>
+        /// <param name="value">
+        /// When this method returns, contains the value associated with the specified key,
+        /// if the key is found; otherwise, null.
+        /// </param>
+        /// <returns>
+        /// True if the resource if found, otherwise false.
+        /// </returns>
+        bool TryGetResource(string key, out object value);
+    }
+}

+ 101 - 0
src/Avalonia.Styling/Controls/ResourceDictionary.cs

@@ -0,0 +1,101 @@
+// Copyright (c) The Avalonia 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.Generic;
+using System.Collections.Specialized;
+using System.Linq;
+using Avalonia.Collections;
+
+namespace Avalonia.Controls
+{
+    /// <summary>
+    /// An indexed dictionary of resources.
+    /// </summary>
+    public class ResourceDictionary : AvaloniaDictionary<object, object>, IResourceDictionary
+    {
+        private AvaloniaList<IResourceProvider> _mergedDictionaries;
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="ResourceDictionary"/> class.
+        /// </summary>
+        public ResourceDictionary()
+        {
+            CollectionChanged += OnCollectionChanged;
+        }
+
+        /// <inheritdoc/>
+        public event EventHandler<ResourcesChangedEventArgs> ResourcesChanged;
+
+        /// <inheritdoc/>
+        public IList<IResourceProvider> MergedDictionaries
+        {
+            get
+            {
+                if (_mergedDictionaries == null)
+                {
+                    _mergedDictionaries = new AvaloniaList<IResourceProvider>();
+                    _mergedDictionaries.ResetBehavior = ResetBehavior.Remove;
+                    _mergedDictionaries.ForEachItem(
+                        x =>
+                        {
+                            if (x.HasResources)
+                            {
+                                OnResourcesChanged();
+                            }
+
+                            x.ResourcesChanged += MergedDictionaryResourcesChanged;
+                        },
+                        x =>
+                        {
+                            if (x.HasResources)
+                            {
+                                OnResourcesChanged();
+                            }
+
+                            x.ResourcesChanged -= MergedDictionaryResourcesChanged;
+                        },
+                        () => { });
+                }
+
+                return _mergedDictionaries;
+            }
+        }
+
+        /// <inheritdoc/>
+        bool IResourceProvider.HasResources
+        {
+            get => Count > 0 || (_mergedDictionaries?.Any(x => x.HasResources) ?? false);
+        }
+
+        /// <inheritdoc/>
+        public bool TryGetResource(string key, out object value)
+        {
+            if (TryGetValue(key, out value))
+            {
+                return true;
+            }
+
+            if (_mergedDictionaries != null)
+            {
+                for (var i = _mergedDictionaries.Count - 1; i >= 0; --i)
+                {
+                    if (_mergedDictionaries[i].TryGetResource(key, out value))
+                    {
+                        return true;
+                    }
+                }
+            }
+
+            return false;
+        }
+
+        private void OnResourcesChanged()
+        {
+            ResourcesChanged?.Invoke(this, new ResourcesChangedEventArgs());
+        }
+
+        private void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) => OnResourcesChanged();
+        private void MergedDictionaryResourcesChanged(object sender, ResourcesChangedEventArgs e) => OnResourcesChanged();
+    }
+}

+ 65 - 0
src/Avalonia.Styling/Controls/ResourceProviderExtensions.cs

@@ -0,0 +1,65 @@
+using System;
+using System.Reactive;
+using System.Reactive.Linq;
+
+namespace Avalonia.Controls
+{
+    public static class ResourceProviderExtensions
+    {
+        /// <summary>
+        /// Finds the specified resource by searching up the logical tree and then global styles.
+        /// </summary>
+        /// <param name="control">The control.</param>
+        /// <param name="key">The resource key.</param>
+        /// <returns>The resource, or <see cref="AvaloniaProperty.UnsetValue"/> if not found.</returns>
+        public static object FindResource(this IResourceNode control, string key)
+        {
+            if (control.TryFindResource(key, out var value))
+            {
+                return value;
+            }
+
+            return AvaloniaProperty.UnsetValue;
+        }
+
+        /// <summary>
+        /// Tries to the specified resource by searching up the logical tree and then global styles.
+        /// </summary>
+        /// <param name="control">The control.</param>
+        /// <param name="key">The resource key.</param>
+        /// <param name="value">On return, contains the resource if found, otherwise null.</param>
+        /// <returns>True if the resource was found; otherwise false.</returns>
+        public static bool TryFindResource(this IResourceNode control, string key, out object value)
+        {
+            Contract.Requires<ArgumentNullException>(control != null);
+            Contract.Requires<ArgumentNullException>(key != null);
+
+            var current = control;
+
+            while (current != null)
+            {
+                if (current is IResourceNode host)
+                {
+                    if (host.TryGetResource(key, out value))
+                    {
+                        return true;
+                    }
+                }
+
+                current = current.ResourceParent;
+            }
+
+            value = null;
+            return false;
+        }
+
+        public static IObservable<object> GetResourceObservable(this IResourceNode target, string key)
+        {
+            return Observable.FromEventPattern<ResourcesChangedEventArgs>(
+                x => target.ResourcesChanged += x,
+                x => target.ResourcesChanged -= x)
+                .StartWith((EventPattern<ResourcesChangedEventArgs>)null)
+                .Select(x => target.FindResource(key));
+        }
+    }
+}

+ 11 - 0
src/Avalonia.Styling/Controls/ResourcesChangedEventArgs.cs

@@ -0,0 +1,11 @@
+// Copyright (c) The Avalonia Project. All rights reserved.
+// Licensed under the MIT license. See licence.md file in the project root for full license information.
+
+using System;
+
+namespace Avalonia.Controls
+{
+    public class ResourcesChangedEventArgs : EventArgs
+    {
+    }
+}

+ 11 - 0
src/Avalonia.Styling/LogicalTree/ILogical.cs

@@ -3,6 +3,7 @@
 
 using System;
 using Avalonia.Collections;
+using Avalonia.Controls;
 
 namespace Avalonia.LogicalTree
 {
@@ -55,5 +56,15 @@ namespace Avalonia.LogicalTree
         /// this method yourself.
         /// </remarks>
         void NotifyDetachedFromLogicalTree(LogicalTreeAttachmentEventArgs e);
+
+        /// <summary>
+        /// Notifies the control that a change has been made to resources that apply to it.
+        /// </summary>
+        /// <param name="e">The event args.</param>
+        /// <remarks>
+        /// This method will be called automatically by the framework, you should not need to call
+        /// this method yourself.
+        /// </remarks>
+        void NotifyResourcesChanged(ResourcesChangedEventArgs e);
     }
 }

+ 2 - 0
src/Avalonia.Styling/Properties/AssemblyInfo.cs

@@ -6,5 +6,7 @@ using System.Runtime.CompilerServices;
 using Avalonia.Metadata;
 
 [assembly: AssemblyTitle("Avalonia.Styling")]
+[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Controls")]
+[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.LogicalTree")]
 [assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Styling")]
 [assembly: InternalsVisibleTo("Avalonia.Styling.UnitTests")]

+ 32 - 0
src/Avalonia.Styling/Styling/ISetStyleParent.cs

@@ -0,0 +1,32 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using Avalonia.Controls;
+
+namespace Avalonia.Styling
+{
+    /// <summary>
+    /// Defines an interface through which a <see cref="Style"/>'s parent can be set.
+    /// </summary>
+    /// <remarks>
+    /// You should not usually need to use this interface - it is for internal use only.
+    /// </remarks>
+    public interface ISetStyleParent : IStyle
+    {
+        /// <summary>
+        /// Sets the style parent.
+        /// </summary>
+        /// <param name="parent">The parent.</param>
+        void SetParent(IResourceNode parent);
+
+        /// <summary>
+        /// Notifies the style that a change has been made to resources that apply to it.
+        /// </summary>
+        /// <param name="e">The event args.</param>
+        /// <remarks>
+        /// This method will be called automatically by the framework, you should not need to call
+        /// this method yourself.
+        /// </remarks>
+        void NotifyResourcesChanged(ResourcesChangedEventArgs e);
+    }
+}

+ 3 - 10
src/Avalonia.Styling/Styling/IStyle.cs

@@ -1,12 +1,14 @@
 // Copyright (c) The Avalonia Project. All rights reserved.
 // Licensed under the MIT license. See licence.md file in the project root for full license information.
 
+using Avalonia.Controls;
+
 namespace Avalonia.Styling
 {
     /// <summary>
     /// Defines the interface for styles.
     /// </summary>
-    public interface IStyle
+    public interface IStyle : IResourceNode
     {
         /// <summary>
         /// Attaches the style to a control if the style's selector matches.
@@ -16,14 +18,5 @@ namespace Avalonia.Styling
         /// The control that contains this style. May be null.
         /// </param>
         void Attach(IStyleable control, IStyleHost container);
-
-        /// <summary>
-        /// Tries to find a named resource within the style.
-        /// </summary>
-        /// <param name="name">The resource name.</param>
-        /// <returns>
-        /// The resource if found, otherwise <see cref="AvaloniaProperty.UnsetValue"/>.
-        /// </returns>
-        object FindResource(string name);
     }
 }

+ 54 - 38
src/Avalonia.Styling/Styling/Style.cs

@@ -3,7 +3,9 @@
 
 using System;
 using System.Collections.Generic;
+using System.Collections.Specialized;
 using System.Reactive.Linq;
+using Avalonia.Controls;
 using Avalonia.Metadata;
 
 namespace Avalonia.Styling
@@ -11,12 +13,12 @@ namespace Avalonia.Styling
     /// <summary>
     /// Defines a style.
     /// </summary>
-    public class Style : IStyle
+    public class Style : IStyle, ISetStyleParent
     {
         private static Dictionary<IStyleable, List<IDisposable>> _applied =
             new Dictionary<IStyleable, List<IDisposable>>();
-
-        private StyleResources _resources;
+        private IResourceNode _parent;
+        private IResourceDictionary _resources;
 
         /// <summary>
         /// Initializes a new instance of the <see cref="Style"/> class.
@@ -34,33 +36,33 @@ namespace Avalonia.Styling
             Selector = selector(null);
         }
 
+        /// <inheritdoc/>
+        public event EventHandler<ResourcesChangedEventArgs> ResourcesChanged;
+
         /// <summary>
         /// Gets or sets a dictionary of style resources.
         /// </summary>
-        public StyleResources Resources
+        public IResourceDictionary Resources
         {
-            get
+            get => _resources ?? (Resources = new ResourceDictionary());
+            set
             {
-                if (_resources == null)
+                Contract.Requires<ArgumentNullException>(value != null);
+
+                var hadResources = false;
+
+                if (_resources != null)
                 {
-                    _resources = new StyleResources();
+                    hadResources = _resources.Count > 0;
+                    _resources.ResourcesChanged -= ResourceDictionaryChanged;
                 }
 
-                return _resources;
-            }
+                _resources = value;
+                _resources.ResourcesChanged += ResourceDictionaryChanged;
 
-            set
-            {
-                
-                var resources = Resources;
-                if (!Equals(resources, value))
+                if (hadResources || _resources.Count > 0)
                 {
-                    foreach (var i in value)
-                    {
-                        resources[i.Key] = i.Value;
-                        //resources.Add(i.Key, i.Value);
-                        //(resources as IDictionary<string,object>).Add(i);
-                    }
+                    ((ISetStyleParent)this).NotifyResourcesChanged(new ResourcesChangedEventArgs());
                 }
             }
         }
@@ -76,6 +78,12 @@ namespace Avalonia.Styling
         [Content]
         public IList<ISetter> Setters { get; set; } = new List<ISetter>();
 
+        /// <inheritdoc/>
+        IResourceNode IResourceNode.ResourceParent => _parent;
+
+        /// <inheritdoc/>
+        bool IResourceProvider.HasResources => _resources?.Count > 0;
+
         /// <summary>
         /// Attaches the style to a control if the style's selector matches.
         /// </summary>
@@ -112,25 +120,11 @@ namespace Avalonia.Styling
             }
         }
 
-        /// <summary>
-        /// Tries to find a named resource within the style.
-        /// </summary>
-        /// <param name="name">The resource name.</param>
-        /// <returns>
-        /// The resource if found, otherwise <see cref="AvaloniaProperty.UnsetValue"/>.
-        /// </returns>
-        public object FindResource(string name)
+        /// <inheritdoc/>
+        public bool TryGetResource(string key, out object result)
         {
-            object result = null;
-
-            if (_resources?.TryGetValue(name, out result) == true)
-            {
-                return result;
-            }
-            else
-            {
-                return AvaloniaProperty.UnsetValue;
-            }
+            result = null;
+            return _resources?.TryGetResource(key, out result) ?? false;
         }
 
         /// <summary>
@@ -149,6 +143,23 @@ namespace Avalonia.Styling
             }
         }
 
+        /// <inheritdoc/>
+        void ISetStyleParent.NotifyResourcesChanged(ResourcesChangedEventArgs e)
+        {
+            ResourcesChanged?.Invoke(this, e);
+        }
+
+        /// <inheritdoc/>
+        void ISetStyleParent.SetParent(IResourceNode parent)
+        {
+            if (_parent != null && parent != null)
+            {
+                throw new InvalidOperationException("The Style already has a parent.");
+            }
+
+            _parent = parent;
+        }
+
         private static List<IDisposable> GetSubscriptions(IStyleable control)
         {
             List<IDisposable> subscriptions;
@@ -179,5 +190,10 @@ namespace Avalonia.Styling
 
             _applied.Remove(control);
         }
+
+        private void ResourceDictionaryChanged(object sender, ResourcesChangedEventArgs e)
+        {
+            ResourcesChanged?.Invoke(this, e);
+        }
     }
 }

+ 0 - 42
src/Avalonia.Styling/Styling/StyleExtensions.cs

@@ -1,42 +0,0 @@
-// Copyright (c) The Avalonia Project. All rights reserved.
-// Licensed under the MIT license. See licence.md file in the project root for full license information.
-
-using System;
-
-namespace Avalonia.Styling
-{
-    public static class StyleExtensions
-    {
-        /// <summary>
-        /// Tries to find a named style resource.
-        /// </summary>
-        /// <param name="control">The control from which to find the resource.</param>
-        /// <param name="name">The resource name.</param>
-        /// <returns>
-        /// The resource if found, otherwise <see cref="AvaloniaProperty.UnsetValue"/>.
-        /// </returns>
-        public static object FindStyleResource(this IStyleHost control, string name)
-        {
-            Contract.Requires<ArgumentNullException>(control != null);
-            Contract.Requires<ArgumentNullException>(name != null);
-            Contract.Requires<ArgumentException>(!string.IsNullOrWhiteSpace(name));
-
-            while (control != null)
-            {
-                if (control.IsStylesInitialized)
-                {
-                    var result = control.Styles.FindResource(name);
-
-                    if (result != AvaloniaProperty.UnsetValue)
-                    {
-                        return result;
-                    }
-                }
-
-                control = control.StylingParent;
-            }
-
-            return AvaloniaProperty.UnsetValue;
-        }
-    }
-}

+ 0 - 90
src/Avalonia.Styling/Styling/StyleResources.cs

@@ -1,90 +0,0 @@
-using System;
-using System.Collections;
-using System.Collections.Generic;
-
-namespace Avalonia.Styling
-{
-    /// <summary>
-    /// Holds resources for a <see cref="Style"/>.
-    /// </summary>
-    public class StyleResources : IDictionary<string, object>, IDictionary
-    {
-        private Dictionary<string, object> _inner = new Dictionary<string, object>();
-
-        public object this[string key]
-        {
-            get { return _inner[key]; }
-            set { _inner[key] = value; }
-        }
-
-        public int Count => _inner.Count;
-
-        ICollection<string> IDictionary<string, object>.Keys => _inner.Keys;
-
-        ICollection<object> IDictionary<string, object>.Values => _inner.Values;
-
-        bool ICollection<KeyValuePair<string, object>>.IsReadOnly => false;
-
-        object IDictionary.this[object key]
-        {
-            get { return ((IDictionary)_inner)[key]; }
-            set { ((IDictionary)_inner)[key] = value; }
-        }
-
-        ICollection IDictionary.Keys => _inner.Keys;
-
-        ICollection IDictionary.Values => _inner.Values;
-
-        bool ICollection.IsSynchronized => false;
-
-        object ICollection.SyncRoot => ((IDictionary)_inner).SyncRoot;
-
-        bool IDictionary.IsFixedSize => false;
-
-        bool IDictionary.IsReadOnly => false;
-
-        public void Add(string key, object value) => _inner.Add(key, value);
-
-        public void Clear() => _inner.Clear();
-
-        public bool ContainsKey(string key) => _inner.ContainsKey(key);
-
-        public bool Remove(string key) => _inner.Remove(key);
-
-        public IEnumerator<KeyValuePair<string, object>> GetEnumerator() => _inner.GetEnumerator();
-
-        public bool TryGetValue(string key, out object value) => _inner.TryGetValue(key, out value);
-
-        bool ICollection<KeyValuePair<string, object>>.Contains(KeyValuePair<string, object> item)
-        {
-            return ((IDictionary<string, object>)_inner).Contains(item);
-        }
-
-        void ICollection<KeyValuePair<string, object>>.Add(KeyValuePair<string, object> item)
-        {
-            ((IDictionary<string, object>)_inner).Add(item);
-        }
-
-        void ICollection<KeyValuePair<string, object>>.CopyTo(KeyValuePair<string, object>[] array, int arrayIndex)
-        {
-            ((IDictionary<string, object>)_inner).CopyTo(array, arrayIndex);
-        }
-
-        bool ICollection<KeyValuePair<string, object>>.Remove(KeyValuePair<string, object> item)
-        {
-            return ((IDictionary<string, object>)_inner).Remove(item);
-        }
-
-        void ICollection.CopyTo(Array array, int index) => ((IDictionary)_inner).CopyTo(array, index);
-
-        IEnumerator IEnumerable.GetEnumerator() => _inner.GetEnumerator();
-
-        IDictionaryEnumerator IDictionary.GetEnumerator() => ((IDictionary)_inner).GetEnumerator();
-
-        void IDictionary.Add(object key, object value) => ((IDictionary)_inner).Add(key, value);
-
-        bool IDictionary.Contains(object key) => ((IDictionary)_inner).Contains(key);
-
-        void IDictionary.Remove(object key) => ((IDictionary)_inner).Remove(key);
-    }
-}

+ 135 - 14
src/Avalonia.Styling/Styling/Styles.cs

@@ -1,16 +1,95 @@
 // Copyright (c) The Avalonia 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.Linq;
 using Avalonia.Collections;
+using Avalonia.Controls;
 
 namespace Avalonia.Styling
 {
     /// <summary>
     /// A style that consists of a number of child styles.
     /// </summary>
-    public class Styles : AvaloniaList<IStyle>, IStyle
+    public class Styles : AvaloniaList<IStyle>, IStyle, ISetStyleParent
     {
+        private IResourceNode _parent;
+        private IResourceDictionary _resources;
+
+        public Styles()
+        {
+            ResetBehavior = ResetBehavior.Remove;
+            this.ForEachItem(
+                x =>
+                {
+                    if (x.ResourceParent == null && x is ISetStyleParent setParent)
+                    {
+                        setParent.SetParent(this);
+                        setParent.NotifyResourcesChanged(new ResourcesChangedEventArgs());
+                    }
+
+                    if (x.HasResources)
+                    {
+                        ResourcesChanged?.Invoke(this, new ResourcesChangedEventArgs());
+                    }
+
+                    x.ResourcesChanged += SubResourceChanged;
+                },
+                x =>
+                {
+                    if (x.ResourceParent == this && x is ISetStyleParent setParent)
+                    {
+                        setParent.SetParent(null);
+                        setParent.NotifyResourcesChanged(new ResourcesChangedEventArgs());
+                    }
+
+                    if (x.HasResources)
+                    {
+                        ResourcesChanged?.Invoke(this, new ResourcesChangedEventArgs());
+                    }
+
+                    x.ResourcesChanged -= SubResourceChanged;
+                },
+                () => { });
+        }
+
+        /// <inheritdoc/>
+        public event EventHandler<ResourcesChangedEventArgs> ResourcesChanged;
+
+        /// <inheritdoc/>
+        public bool HasResources => _resources?.Count > 0 || this.Any(x => x.HasResources);
+
+        /// <summary>
+        /// Gets or sets a dictionary of style resources.
+        /// </summary>
+        public IResourceDictionary Resources
+        {
+            get => _resources ?? (Resources = new ResourceDictionary());
+            set
+            {
+                Contract.Requires<ArgumentNullException>(value != null);
+
+                var hadResources = false;
+
+                if (_resources != null)
+                {
+                    hadResources = _resources.Count > 0;
+                    _resources.ResourcesChanged -= ResourceDictionaryChanged;
+                }
+
+                _resources = value;
+                _resources.ResourcesChanged += ResourceDictionaryChanged;
+
+                if (hadResources || _resources.Count > 0)
+                {
+                    ((ISetStyleParent)this).NotifyResourcesChanged(new ResourcesChangedEventArgs());
+                }
+            }
+        }
+
+        /// <inheritdoc/>
+        IResourceNode IResourceNode.ResourceParent => _parent;
+
         /// <summary>
         /// Attaches the style to a control if the style's selector matches.
         /// </summary>
@@ -26,26 +105,68 @@ namespace Avalonia.Styling
             }
         }
 
-        /// <summary>
-        /// Tries to find a named resource within the style.
-        /// </summary>
-        /// <param name="name">The resource name.</param>
-        /// <returns>
-        /// The resource if found, otherwise <see cref="AvaloniaProperty.UnsetValue"/>.
-        /// </returns>
-        public object FindResource(string name)
+        /// <inheritdoc/>
+        public bool TryGetResource(string key, out object value)
+        {
+            if (_resources != null && _resources.TryGetValue(key, out value))
+            {
+                return true;
+            }
+
+            for (var i = Count - 1; i >= 0; --i)
+            {
+                if (this[i].TryGetResource(key, out value))
+                {
+                    return true;
+                }
+            }
+
+            value = null;
+            return false;
+        }
+
+        /// <inheritdoc/>
+        void ISetStyleParent.SetParent(IResourceNode parent)
+        {
+            if (_parent != null && parent != null)
+            {
+                throw new InvalidOperationException("The Style already has a parent.");
+            }
+
+            _parent = parent;
+        }
+
+        /// <inheritdoc/>
+        void ISetStyleParent.NotifyResourcesChanged(ResourcesChangedEventArgs e)
+        {
+            ResourcesChanged?.Invoke(this, e);
+        }
+
+        private void ResourceDictionaryChanged(object sender, ResourcesChangedEventArgs e)
         {
-            foreach (var style in this.Reverse())
+            foreach (var child in this)
             {
-                var result = style.FindResource(name);
+                (child as ISetStyleParent)?.NotifyResourcesChanged(e);
+            }
+
+            ResourcesChanged?.Invoke(this, e);
+        }
 
-                if (result != AvaloniaProperty.UnsetValue)
+        private void SubResourceChanged(object sender, ResourcesChangedEventArgs e)
+        {
+            var foundSource = false;
+
+            foreach (var child in this)
+            {
+                if (foundSource)
                 {
-                    return result;
+                    (child as ISetStyleParent)?.NotifyResourcesChanged(e);
                 }
+
+                foundSource |= child == sender;
             }
 
-            return AvaloniaProperty.UnsetValue;
+            ResourcesChanged?.Invoke(this, e);
         }
     }
 }

+ 1 - 1
src/Avalonia.Themes.Default/Avalonia.Themes.Default.csproj

@@ -1,6 +1,6 @@
 <Project Sdk="Microsoft.NET.Sdk">
   <PropertyGroup>
-    <TargetFramework>netstandard1.3</TargetFramework>
+    <TargetFramework>netstandard2.0</TargetFramework>
     <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
   </PropertyGroup>
   <PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">

+ 1 - 1
src/Avalonia.Visuals/Avalonia.Visuals.csproj

@@ -1,6 +1,6 @@
 <Project Sdk="Microsoft.NET.Sdk">
   <PropertyGroup>
-    <TargetFramework>netstandard1.3</TargetFramework>
+    <TargetFramework>netstandard2.0</TargetFramework>
     <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
     <RootNamespace>Avalonia</RootNamespace>
   </PropertyGroup>

+ 10 - 8
src/Gtk/Avalonia.Gtk/WindowImpl.cs

@@ -6,11 +6,12 @@ using Gdk;
 namespace Avalonia.Gtk
 {
     using Gtk = global::Gtk;
+
     public class WindowImpl : TopLevelImpl, IWindowImpl
     {
         private Gtk.Window _window;
-        private Gtk.Window Window => _window ?? (_window = (Gtk.Window) Widget);
-        
+        private Gtk.Window Window => _window ?? (_window = (Gtk.Window)Widget);
+
         public WindowImpl(Gtk.WindowType type) : base(new PlatformHandleAwareWindow(type))
         {
             Init();
@@ -29,8 +30,10 @@ namespace Avalonia.Gtk
             _lastClientSize = ClientSize;
             _lastPosition = Position;
         }
+
         private Size _lastClientSize;
         private Point _lastPosition;
+
         void OnConfigureEvent(object o, Gtk.ConfigureEventArgs args)
         {
             var evnt = args.Event;
@@ -44,7 +47,7 @@ namespace Avalonia.Gtk
             }
 
             var newPosition = new Point(evnt.X, evnt.Y);
-            
+
             if (newPosition != _lastPosition)
             {
                 PositionChanged(newPosition);
@@ -107,10 +110,7 @@ namespace Avalonia.Gtk
                 Window.GetPosition(out x, out y);
                 return new Point(x, y);
             }
-            set
-            {
-                Window.Move((int)value.X, (int)value.Y);
-            }
+            set { Window.Move((int)value.X, (int)value.Y); }
         }
 
         public IDisposable ShowDialog()
@@ -127,5 +127,7 @@ namespace Avalonia.Gtk
         {
             Window.Icon = ((IconImpl)icon).Pixbuf;
         }
+
+        public void ShowTaskbarIcon(bool value) => Window.SkipTaskbarHint = !value;
     }
-}
+}

+ 1 - 1
src/Gtk/Avalonia.Gtk3/Avalonia.Gtk3.csproj

@@ -1,6 +1,6 @@
 <Project Sdk="Microsoft.NET.Sdk">
   <PropertyGroup>
-    <TargetFramework>netstandard1.3</TargetFramework>
+    <TargetFramework>netstandard2.0</TargetFramework>
     <EnableDefaultCompileItems>False</EnableDefaultCompileItems>
     <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
   </PropertyGroup>

+ 41 - 8
src/Gtk/Avalonia.Gtk3/Interop/Native.cs

@@ -38,6 +38,9 @@ namespace Avalonia.Gtk3.Interop
             [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)]
             public delegate void gtk_widget_hide(GtkWidget gtkWidget);
 
+            [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)]
+            public delegate void gtk_widget_show(GtkWidget gtkWidget);
+
             [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)]
             public delegate void gtk_window_set_icon(GtkWindow window, Pixbuf pixbuf);
 
@@ -56,14 +59,17 @@ namespace Avalonia.Gtk3.Interop
 
             [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)]
             public delegate IntPtr gtk_widget_get_screen(GtkWidget gtkWidget);
+            
             [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)]
             public delegate IntPtr gtk_widget_set_double_buffered(GtkWidget gtkWidget, bool value);
+            
             [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)]
             public delegate IntPtr gtk_widget_set_events(GtkWidget gtkWidget, uint flags);
 
 
             [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gdk)]
             public delegate int gdk_screen_get_height(IntPtr screen);
+            
             [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gdk)]
             public delegate int gdk_screen_get_width(IntPtr screen);
 
@@ -72,10 +78,10 @@ namespace Avalonia.Gtk3.Interop
 
             [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gdk)]
             public delegate int gdk_window_get_origin(IntPtr gdkWindow, out int x, out int y);
+            
             [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gdk)]
             public delegate void gdk_window_resize(IntPtr gtkWindow, int width, int height);
 
-
             [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)]
             public delegate void gtk_widget_realize(GtkWidget gtkWidget);
 
@@ -86,33 +92,51 @@ namespace Avalonia.Gtk3.Interop
             [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)]
             public delegate void gtk_window_set_decorated(GtkWindow gtkWindow, bool decorated);
 
+            [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)]
+            public delegate void gtk_window_set_skip_taskbar_hint(GtkWindow gtkWindow, bool setting); 
+            
+             [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)]
+            public delegate bool gtk_window_get_skip_taskbar_hint(GtkWindow gtkWindow);
+            
+            [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)]
+            public delegate void gtk_window_set_skip_pager_hint(GtkWindow gtkWindow, bool setting); 
+            
+            [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)]
+            public delegate bool gtk_window_get_skip_pager_hint(GtkWindow gtkWindow); 
+
             [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)]
             public delegate void gtk_window_get_size(GtkWindow gtkWindow, out int width, out int height);
+            
             [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)]
             public delegate void gtk_window_resize(GtkWindow gtkWindow, int width, int height);
+            
             [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)]
             public delegate void gtk_widget_set_size_request(GtkWidget widget, int width, int height);
+            
             [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)]
             public delegate void gtk_window_set_default_size(GtkWindow gtkWindow, int width, int height);
+            
             [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)]
             public delegate void gtk_window_get_position(GtkWindow gtkWindow, out int x, out int y);
+            
             [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)]
             public delegate void gtk_window_move(GtkWindow gtkWindow, int x, int y);
 
             [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)]
             public delegate GtkFileChooser gtk_file_chooser_dialog_new(Utf8Buffer title, GtkWindow parent, GtkFileChooserAction action, IntPtr ignore);
+            
             [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)]
             public unsafe delegate GSList* gtk_file_chooser_get_filenames(GtkFileChooser chooser);
+            
             [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)]
             public delegate void gtk_file_chooser_set_select_multiple(GtkFileChooser chooser, bool allow);
+            
             [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)]
             public delegate void gtk_file_chooser_set_filename(GtkFileChooser chooser, Utf8Buffer file);
+            
             [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)]
             public delegate void gtk_dialog_add_button(GtkDialog raw, Utf8Buffer button_text, GtkResponseType response_id);
 
-
-
-
             [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Cairo)]
             public delegate CairoSurface cairo_image_surface_create(int format, int width, int height);
 
@@ -206,7 +230,6 @@ namespace Avalonia.Gtk3.Interop
             [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)]
             public delegate void gtk_clipboard_clear(IntPtr clipboard);
 
-
             [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.GdkPixBuf)]
             public delegate IntPtr gdk_pixbuf_new_from_file(Utf8Buffer filename, out IntPtr error);
 
@@ -229,21 +252,27 @@ namespace Avalonia.Gtk3.Interop
             public delegate bool gdk_pixbuf_save_to_bufferv(Pixbuf pixbuf, out IntPtr buffer, out IntPtr buffer_size,
                             Utf8Buffer type, IntPtr option_keys, IntPtr option_values, out IntPtr error);
 
-
             [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gobject)]
             public delegate void g_object_unref(IntPtr instance);
+            
             [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gobject)]
             public delegate void g_object_ref(GObject instance);
+            
             [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gobject)]
             public delegate ulong g_signal_connect_object(GObject instance, Utf8Buffer signal, IntPtr handler, IntPtr userData, int flags);
+            
             [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gobject)]
             public delegate ulong g_signal_handler_disconnect(GObject instance, ulong connectionId);
+            
             [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Glib)]
             public delegate ulong g_timeout_add(uint interval, timeout_callback callback, IntPtr data);
+            
             [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Glib)]
             public delegate ulong g_free(IntPtr data);
+            
             [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Glib)]
             public unsafe delegate void g_slist_free(GSList* data);
+            
             [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gio)]
             public delegate GInputStream g_memory_input_stream_new_from_data(IntPtr ptr, IntPtr len, IntPtr destroyCallback);
 
@@ -271,6 +300,10 @@ namespace Avalonia.Gtk3.Interop
 
 
         public static D.gtk_window_set_decorated GtkWindowSetDecorated;
+        public static D.gtk_window_set_skip_taskbar_hint GtkWindowSetSkipTaskbarHint;
+        public static D.gtk_window_get_skip_taskbar_hint GtkWindowGetSkipTaskbarHint;
+        public static D.gtk_window_set_skip_pager_hint GtkWindowSetSkipPagerHint;
+        public static D.gtk_window_get_skip_pager_hint GtkWindowGetSkipPagerHint;
         public static D.gtk_window_set_title GtkWindowSetTitle;
         public static D.gtk_application_new GtkApplicationNew;
         public static D.gtk_main_iteration GtkMainIteration;
@@ -280,6 +313,7 @@ namespace Avalonia.Gtk3.Interop
         public static D.gtk_init GtkInit;
         public static D.gtk_window_present GtkWindowPresent;
         public static D.gtk_widget_hide GtkWidgetHide;
+        public static D.gtk_widget_show GtkWidgetShow;
         public static D.gdk_get_native_handle GetNativeGdkWindowHandle;
         public static D.gtk_widget_get_window GtkWidgetGetWindow;
         public static D.gtk_widget_get_scale_factor GtkWidgetGetScaleFactor;
@@ -314,8 +348,7 @@ namespace Avalonia.Gtk3.Interop
         public static D.gtk_clipboard_request_text GtkClipboardRequestText;
         public static D.gtk_clipboard_set_text GtkClipboardSetText;
         public static D.gtk_clipboard_clear GtkClipboardRequestClear;
-
-
+        
         public static D.gtk_im_multicontext_new GtkImMulticontextNew;
         public static D.gtk_im_context_filter_keypress GtkImContextFilterKeypress;
         public static D.gtk_im_context_set_client_window GtkImContextSetClientWindow;

+ 9 - 7
src/Gtk/Avalonia.Gtk3/Interop/Resolver.cs

@@ -104,14 +104,16 @@ namespace Avalonia.Gtk3.Interop
             var path = Custom?.Lookup(dll);
             if (path == null && Custom?.BasePath != null)
                 path = Path.Combine(Custom.BasePath, name);
-
-            try
-            {
-                return loader.LoadLibrary(path);
-            }
-            catch (Exception e)
+            if (path != null)
             {
-                exceptions.Add(e);
+                try
+                {
+                    return loader.LoadLibrary(path);
+                }
+                catch (Exception e)
+                {
+                    exceptions.Add(e);
+                }
             }
             throw new AggregateException("Unable to load " + dll, exceptions);
         }

+ 4 - 0
src/Gtk/Avalonia.Gtk3/WindowImpl.cs

@@ -2,6 +2,7 @@
 using Avalonia.Controls;
 using Avalonia.Gtk3.Interop;
 using Avalonia.Platform;
+using System.Runtime.InteropServices;
 
 namespace Avalonia.Gtk3
 {
@@ -59,6 +60,9 @@ namespace Avalonia.Gtk3
             //Why do we even have that?
         }
 
+        public void ShowTaskbarIcon(bool value) => Native.GtkWindowSetSkipTaskbarHint(GtkWidget, !value);
+        
+
         class EmptyDisposable : IDisposable
         {
             public void Dispose()

+ 1 - 1
src/Linux/Avalonia.LinuxFramebuffer/Avalonia.LinuxFramebuffer.csproj

@@ -1,6 +1,6 @@
 <Project Sdk="Microsoft.NET.Sdk">
     <PropertyGroup>
-        <TargetFramework>netstandard1.3</TargetFramework>
+        <TargetFramework>netstandard2.0</TargetFramework>
         <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
     </PropertyGroup>
     <ItemGroup>

+ 5 - 1
src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj

@@ -1,6 +1,6 @@
 <Project Sdk="Microsoft.NET.Sdk">
   <PropertyGroup>
-    <TargetFramework>netstandard1.3</TargetFramework>
+    <TargetFramework>netstandard2.0</TargetFramework>
     <EnableDefaultCompileItems>False</EnableDefaultCompileItems>
     <EnableDefaultItems>false</EnableDefaultItems>
     <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
@@ -34,6 +34,9 @@
         <Compile Include="Converters\MatrixTypeConverter.cs" />
         <Compile Include="Converters\RectTypeConverter.cs" />
         <Compile Include="Converters\SetterValueTypeConverter.cs" />
+        <Compile Include="Data\ResourceInclude.cs" />
+        <Compile Include="MarkupExtensions\DynamicResourceExtension.cs" />
+        <Compile Include="MarkupExtensions\StaticResourceExtension.cs" />
         <Compile Include="MarkupExtensions\StyleIncludeExtension.cs" />
         <Compile Include="PortableXaml\AvaloniaXamlContext.cs" />
         <Compile Include="PortableXaml\XamlBinding.cs" />
@@ -94,6 +97,7 @@
         <Compile Include="Templates\TemplateLoader.cs" />
         <Compile Include="Templates\TreeDataTemplate.cs" />
         <Compile Include="PortableXaml\portable.xaml.github\src\Portable.Xaml\**\*.cs" Exclude="PortableXaml\portable.xaml.github\src\Portable.Xaml\Assembly\**\*.cs" />
+        <Compile Remove="**\UriTypeConverter.cs" />
     </ItemGroup>
   <ItemGroup>
     <ProjectReference Include="..\..\Avalonia.Animation\Avalonia.Animation.csproj" />

+ 78 - 7
src/Markup/Avalonia.Markup.Xaml/Data/DelayedBinding.cs

@@ -3,9 +3,11 @@
 
 using System;
 using System.Collections.Generic;
+using System.Reflection;
 using System.Runtime.CompilerServices;
 using Avalonia.Controls;
 using Avalonia.Data;
+using Avalonia.Logging;
 
 namespace Avalonia.Markup.Xaml.Data
 {
@@ -50,7 +52,36 @@ namespace Avalonia.Markup.Xaml.Data
                     target.Initialized += ApplyBindings;
                 }
 
-                bindings.Add(new Entry(binding, property));
+                bindings.Add(new BindingEntry(property, binding));
+            }
+        }
+
+        /// <summary>
+        /// Adds a delayed value to a control.
+        /// </summary>
+        /// <param name="target">The control.</param>
+        /// <param name="property">The property on the control to bind to.</param>
+        /// <param name="value">A function which returns the value.</param>
+        public static void Add(IControl target, PropertyInfo property, Func<IControl, object> value)
+        {
+            if (target.IsInitialized)
+            {
+                property.SetValue(target, value(target));
+            }
+            else
+            {
+                List<Entry> bindings;
+
+                if (!_entries.TryGetValue(target, out bindings))
+                {
+                    bindings = new List<Entry>();
+                    _entries.Add(target, bindings);
+
+                    // TODO: Make this a weak event listener.
+                    target.Initialized += ApplyBindings;
+                }
+
+                bindings.Add(new ClrPropertyValueEntry(property, value));
             }
         }
 
@@ -60,13 +91,13 @@ namespace Avalonia.Markup.Xaml.Data
         /// <param name="control">The control.</param>
         public static void ApplyBindings(IControl control)
         {
-            List<Entry> bindings;
+            List<Entry> entries;
 
-            if (_entries.TryGetValue(control, out bindings))
+            if (_entries.TryGetValue(control, out entries))
             {
-                foreach (var binding in bindings)
+                foreach (var entry in entries)
                 {
-                    control.Bind(binding.Property, binding.Binding);
+                    entry.Apply(control);
                 }
 
                 _entries.Remove(control);
@@ -80,9 +111,14 @@ namespace Avalonia.Markup.Xaml.Data
             target.Initialized -= ApplyBindings;
         }
 
-        private class Entry
+        private abstract class Entry
+        {
+            public abstract void Apply(IControl control);
+        }
+
+        private class BindingEntry : Entry
         {
-            public Entry(IBinding binding, AvaloniaProperty property)
+            public BindingEntry(AvaloniaProperty property, IBinding binding)
             {
                 Binding = binding;
                 Property = property;
@@ -90,6 +126,41 @@ namespace Avalonia.Markup.Xaml.Data
 
             public IBinding Binding { get; }
             public AvaloniaProperty Property { get; }
+
+            public override void Apply(IControl control)
+            {
+                control.Bind(Property, Binding);
+            }
+        }
+
+        private class ClrPropertyValueEntry : Entry
+        {
+            public ClrPropertyValueEntry(PropertyInfo property, Func<IControl, object> value)
+            {
+                Property = property;
+                Value = value;
+            }
+
+            public PropertyInfo Property { get; }
+            public Func<IControl, object> Value { get; }
+
+            public override void Apply(IControl control)
+            {
+                try
+                {
+                    Property.SetValue(control, Value(control));
+                }
+                catch (Exception e)
+                {
+                    Logger.Error(
+                        LogArea.Property,
+                        control,
+                        "Error setting {Property} on {Target}: {Exception}",
+                        Property.Name,
+                        control,
+                        e);
+                }
+            }
         }
     }
 }

+ 63 - 0
src/Markup/Avalonia.Markup.Xaml/Data/ResourceInclude.cs

@@ -0,0 +1,63 @@
+using System;
+using System.ComponentModel;
+using Avalonia.Controls;
+using Portable.Xaml.ComponentModel;
+using Portable.Xaml.Markup;
+
+namespace Avalonia.Markup.Xaml.Data
+{
+    /// <summary>
+    /// Loads a resource dictionary from a specified URL.
+    /// </summary>
+    public class ResourceInclude : MarkupExtension, IResourceProvider
+    {
+        private Uri _baseUri;
+        private IResourceDictionary _loaded;
+
+        public event EventHandler<ResourcesChangedEventArgs> ResourcesChanged;
+
+        /// <summary>
+        /// Gets the loaded resource dictionary.
+        /// </summary>
+        public IResourceDictionary Loaded
+        {
+            get
+            {
+                if (_loaded == null)
+                {
+                    var loader = new AvaloniaXamlLoader();
+                    _loaded = (IResourceDictionary)loader.Load(Source, _baseUri);
+
+                    if (_loaded.HasResources)
+                    {
+                        ResourcesChanged?.Invoke(this, new ResourcesChangedEventArgs());
+                    }
+                }
+
+                return _loaded;
+            }
+        }
+
+        /// <summary>
+        /// Gets or sets the source URL.
+        /// </summary>
+        public Uri Source { get; set; }
+
+        /// <inhertidoc/>
+        bool IResourceProvider.HasResources => Loaded.HasResources;
+
+        /// <inhertidoc/>
+        bool IResourceProvider.TryGetResource(string key, out object value)
+        {
+            return Loaded.TryGetResource(key, out value);
+        }
+
+        /// <inhertidoc/>
+        public override object ProvideValue(IServiceProvider serviceProvider)
+        {
+            var tdc = (ITypeDescriptorContext)serviceProvider;
+            _baseUri = tdc?.GetBaseUri();
+            return this;
+        }
+    }
+}

+ 5 - 2
src/Markup/Avalonia.Markup.Xaml/Data/StyleResourceBinding.cs

@@ -46,11 +46,14 @@ namespace Avalonia.Markup.Xaml.Data
 
             if (host != null)
             {
-                resource = host.FindStyleResource(Name);
+                resource = host.FindResource(Name);
             }
             else if (style != null)
             {
-                resource = style.FindResource(Name);
+                if (!style.TryGetResource(Name, out resource))
+                {
+                    resource = AvaloniaProperty.UnsetValue;
+                }
             }
 
             if (resource != AvaloniaProperty.UnsetValue)

+ 71 - 0
src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/DynamicResourceExtension.cs

@@ -0,0 +1,71 @@
+// Copyright (c) The Avalonia 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.ComponentModel;
+using System.Linq;
+using System.Reactive.Linq;
+using Avalonia.Controls;
+using Avalonia.Data;
+using Portable.Xaml;
+using Portable.Xaml.ComponentModel;
+using Portable.Xaml.Markup;
+
+namespace Avalonia.Markup.Xaml.MarkupExtensions
+{
+    public class DynamicResourceExtension : MarkupExtension, IBinding
+    {
+        private IResourceNode _anchor;
+
+        public DynamicResourceExtension()
+        {
+        }
+
+        public DynamicResourceExtension(string resourceKey)
+        {
+            ResourceKey = resourceKey;
+        }
+
+        public string ResourceKey { get; set; }
+
+        public override object ProvideValue(IServiceProvider serviceProvider)
+        {
+            var context = (ITypeDescriptorContext)serviceProvider;
+            var provideTarget = context.GetService<IProvideValueTarget>();
+
+            if (!(provideTarget.TargetObject is IResourceNode))
+            {
+                _anchor = GetAnchor<IResourceNode>(context);
+            }
+
+            return this;
+        }
+
+        InstancedBinding IBinding.Initiate(
+            IAvaloniaObject target,
+            AvaloniaProperty targetProperty,
+            object anchor,
+            bool enableDataValidation)
+        {
+            var control = target as IResourceNode ?? _anchor;
+
+            if (control != null)
+            {
+                return new InstancedBinding(control.GetResourceObservable(ResourceKey));
+            }
+
+            return null;
+        }
+
+        private T GetAnchor<T>(ITypeDescriptorContext context) where T : class
+        {
+            var schemaContext = context.GetService<IXamlSchemaContextProvider>().SchemaContext;
+            var ambientProvider = context.GetService<IAmbientProvider>();
+            var xamlType = schemaContext.GetXamlType(typeof(T));
+
+            // We override XamlType.CanAssignTo in BindingXamlType so the results we get back
+            // from GetAllAmbientValues aren't necessarily of the correct type.
+            return ambientProvider.GetAllAmbientValues(xamlType).OfType<T>().FirstOrDefault();
+        }
+    }
+}

+ 84 - 0
src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/StaticResourceExtension.cs

@@ -0,0 +1,84 @@
+// Copyright (c) The Avalonia 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.Generic;
+using System.ComponentModel;
+using System.Reflection;
+using Avalonia.Controls;
+using Avalonia.Markup.Xaml.Data;
+using Portable.Xaml;
+using Portable.Xaml.ComponentModel;
+using Portable.Xaml.Markup;
+
+namespace Avalonia.Markup.Xaml.MarkupExtensions
+{
+    public class StaticResourceExtension : MarkupExtension
+    {
+        public StaticResourceExtension()
+        {
+        }
+
+        public StaticResourceExtension(string resourceKey)
+        {
+            ResourceKey = resourceKey;
+        }
+
+        public string ResourceKey { get; set; }
+
+        public override object ProvideValue(IServiceProvider serviceProvider)
+        {
+            var context = (ITypeDescriptorContext)serviceProvider;
+            var schemaContext = context.GetService<IXamlSchemaContextProvider>().SchemaContext;
+            var ambientProvider = context.GetService<IAmbientProvider>();
+            var resourceProviderType = schemaContext.GetXamlType(typeof(IResourceNode));
+            var ambientValues = ambientProvider.GetAllAmbientValues(resourceProviderType);
+
+            // Look upwards though the ambient context for IResourceProviders which might be able
+            // to give us the resource.
+            //
+            // TODO: If we're in a template then only the ambient values since the root of the
+            // template wil be included here. We need some way to get hold of the parent ambient
+            // context and search that. See the test:
+            //
+            //   StaticResource_Can_Be_Assigned_To_Property_In_ControlTemplate_In_Styles_File
+            //
+            foreach (var ambientValue in ambientValues)
+            {
+                // We override XamlType.CanAssignTo in BindingXamlType so the results we get back
+                // from GetAllAmbientValues aren't necessarily of the correct type.
+                if (ambientValue is IResourceNode resourceProvider)
+                {
+                    if (resourceProvider is IControl control && control.StylingParent != null)
+                    {
+                        // If we've got to a control that has a StylingParent then it's probably
+                        // a top level control and its StylingParent is pointing to the global
+                        // styles. If this is case just do a FindResource on it.
+                        return control.FindResource(ResourceKey);
+                    }
+                    else if (resourceProvider.TryGetResource(ResourceKey, out var value))
+                    {
+                        return value;
+                    }
+                }
+            }
+
+            // The resource still hasn't been found, so add a delayed one-time binding.
+            var provideTarget = context.GetService<IProvideValueTarget>();
+
+            if (provideTarget.TargetObject is IControl target &&
+                provideTarget.TargetProperty is PropertyInfo property)
+            {
+                DelayedBinding.Add(target, property, GetValue);
+                return AvaloniaProperty.UnsetValue;
+            }
+
+            throw new KeyNotFoundException($"Static resource '{ResourceKey}' not found.");
+        }
+
+        private object GetValue(IControl control)
+        {
+            return control.FindResource(ResourceKey);
+        }
+    }
+}

+ 1 - 1
src/Markup/Avalonia.Markup.Xaml/PortableXaml/portable.xaml.github

@@ -1 +1 @@
-Subproject commit dfc5affa5d8f4ddf5a7707e3202d5593519de640
+Subproject commit eebf9dbb9275ecc48c18ec24f6fbad8cb494857f

+ 30 - 11
src/Markup/Avalonia.Markup.Xaml/Styling/StyleInclude.cs

@@ -3,27 +3,31 @@
 
 using Avalonia.Styling;
 using System;
+using Avalonia.Controls;
 
 namespace Avalonia.Markup.Xaml.Styling
 {
     /// <summary>
     /// Includes a style from a URL.
     /// </summary>
-    public class StyleInclude : IStyle
+    public class StyleInclude : IStyle, ISetStyleParent
     {
         private Uri _baseUri;
         private IStyle _loaded;
+        private IResourceNode _parent;
 
         /// <summary>
         /// Initializes a new instance of the <see cref="StyleInclude"/> class.
         /// </summary>
         /// <param name="baseUri"></param>
-
         public StyleInclude(Uri baseUri)
         {
             _baseUri = baseUri;
         }
 
+        /// <inheritdoc/>
+        public event EventHandler<ResourcesChangedEventArgs> ResourcesChanged;
+
         /// <summary>
         /// Gets or sets the source URL.
         /// </summary>
@@ -40,12 +44,19 @@ namespace Avalonia.Markup.Xaml.Styling
                 {
                     var loader = new AvaloniaXamlLoader();
                     _loaded = (IStyle)loader.Load(Source, _baseUri);
+                    (_loaded as ISetStyleParent)?.SetParent(this);
                 }
 
                 return _loaded;
             }
         }
 
+        /// <inheritdoc/>
+        bool IResourceProvider.HasResources => Loaded.HasResources;
+
+        /// <inheritdoc/>
+        IResourceNode IResourceNode.ResourceParent => _parent;
+
         /// <inheritdoc/>
         public void Attach(IStyleable control, IStyleHost container)
         {
@@ -55,16 +66,24 @@ namespace Avalonia.Markup.Xaml.Styling
             }
         }
 
-        /// <summary>
-        /// Tries to find a named resource within the style.
-        /// </summary>
-        /// <param name="name">The resource name.</param>
-        /// <returns>
-        /// The resource if found, otherwise <see cref="AvaloniaProperty.UnsetValue"/>.
-        /// </returns>
-        public object FindResource(string name)
+        /// <inheritdoc/>
+        public bool TryGetResource(string key, out object value) => Loaded.TryGetResource(key, out value);
+
+        /// <inheritdoc/>
+        void ISetStyleParent.NotifyResourcesChanged(ResourcesChangedEventArgs e)
         {
-            return Loaded.FindResource(name);
+            (Loaded as ISetStyleParent)?.NotifyResourcesChanged(e);
+        }
+
+        /// <inheritdoc/>
+        void ISetStyleParent.SetParent(IResourceNode parent)
+        {
+            if (_parent != null && parent != null)
+            {
+                throw new InvalidOperationException("The Style already has a parent.");
+            }
+
+            _parent = parent;
         }
     }
 }

+ 1 - 1
src/Markup/Avalonia.Markup/Avalonia.Markup.csproj

@@ -1,6 +1,6 @@
 <Project Sdk="Microsoft.NET.Sdk">
   <PropertyGroup>
-    <TargetFramework>netstandard1.3</TargetFramework>
+    <TargetFramework>netstandard2.0</TargetFramework>
     <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
   </PropertyGroup>
   <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">

+ 1 - 1
src/Shared/PlatformSupport/StandardRuntimePlatform.cs

@@ -12,7 +12,7 @@ namespace Avalonia.Shared.PlatformSupport
     internal partial class StandardRuntimePlatform : IRuntimePlatform
     {
 
-#if NETCOREAPP1_0
+#if NETCOREAPP2_0
         public void PostThreadPoolItem(Action cb) =>  ThreadPool.QueueUserWorkItem(_ => cb(), null);
 #else
         public Assembly[] GetLoadedAssemblies() => AppDomain.CurrentDomain.GetAssemblies();

+ 1 - 1
src/Skia/Avalonia.Skia/Avalonia.Skia.csproj

@@ -1,6 +1,6 @@
 <Project Sdk="Microsoft.NET.Sdk">
   <PropertyGroup>
-    <TargetFramework>netstandard1.3</TargetFramework>
+    <TargetFramework>netstandard2.0</TargetFramework>
     <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
     <RootNamespace>Avalonia.Skia</RootNamespace>
     <AssemblyName>Avalonia.Skia</AssemblyName>

+ 1 - 1
src/Windows/Avalonia.Direct2D1/Avalonia.Direct2D1.csproj

@@ -1,6 +1,6 @@
 <Project Sdk="Microsoft.NET.Sdk">
   <PropertyGroup>
-    <TargetFramework>netstandard1.3</TargetFramework>
+    <TargetFramework>netstandard2.0</TargetFramework>
     <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
   </PropertyGroup>
   <ItemGroup>

+ 1 - 1
src/Windows/Avalonia.Win32.NetStandard/Avalonia.Win32.NetStandard.csproj

@@ -1,6 +1,6 @@
 <Project Sdk="Microsoft.NET.Sdk">
   <PropertyGroup>
-    <TargetFramework>netstandard1.3</TargetFramework>
+    <TargetFramework>netstandard2.0</TargetFramework>
     <EnableDefaultCompileItems>False</EnableDefaultCompileItems>
     <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
     <RootNamespace>Avalonia.Win32</RootNamespace>

+ 37 - 8
src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs

@@ -699,10 +699,40 @@ namespace Avalonia.Win32.Interop
         public static extern int GetSystemMetrics(SystemMetric smIndex);
 
         [DllImport("user32.dll", SetLastError = true)]
-        public static extern uint GetWindowLong(IntPtr hWnd, int nIndex);
+        public static extern uint GetWindowLongPtr(IntPtr hWnd, int nIndex);
+
+        [DllImport("user32.dll", SetLastError = true, EntryPoint = "GetWindowLong")]
+        public static extern uint GetWindowLong32b(IntPtr hWnd, int nIndex);
+
+        public static uint GetWindowLong(IntPtr hWnd, int nIndex)
+        {
+            if(IntPtr.Size == 4)
+            {
+                return GetWindowLong32b(hWnd, nIndex);
+            }
+            else
+            {
+                return GetWindowLongPtr(hWnd, nIndex);
+            }
+        }
+
+        [DllImport("user32.dll", SetLastError = true, EntryPoint = "SetWindowLong")]
+        private static extern uint SetWindowLong32b(IntPtr hWnd, int nIndex, uint value);
 
         [DllImport("user32.dll", SetLastError = true)]
-        public static extern uint SetWindowLong(IntPtr hWnd, int nIndex, uint value);
+        private static extern uint SetWindowLongPtr(IntPtr hWnd, int nIndex, uint value);
+
+        public static uint SetWindowLong(IntPtr hWnd, int nIndex, uint value)
+        {
+            if (IntPtr.Size == 4)
+            {
+                return SetWindowLong32b(hWnd, nIndex, value);
+            }
+            else
+            {
+                return SetWindowLongPtr(hWnd, nIndex, value);
+            }
+        }
 
         [DllImport("user32.dll", SetLastError = true)]
         public static extern bool GetWindowPlacement(IntPtr hWnd, ref WINDOWPLACEMENT lpwndpl);
@@ -811,14 +841,14 @@ namespace Avalonia.Win32.Interop
 
             return SetClassLong64(hWnd, nIndex, dwNewLong);
         }
-#if !NETSTANDARD && !NETSTANDARD1_3
-        [ComImport, ClassInterface(ClassInterfaceType.None), TypeLibType(TypeLibTypeFlags.FCanCreate), Guid("DC1C5A9C-E88A-4DDE-A5A1-60F82A20AEF7")]
-        internal class FileOpenDialogRCW { }
+
+        [DllImport("ole32.dll", PreserveSig = true)]
+        internal static extern int CoCreateInstance(ref Guid clsid,
+            IntPtr ignore1, int ignore2, ref Guid iid, [MarshalAs(UnmanagedType.IUnknown), Out] out object pUnkOuter);
 
         
         [DllImport("shell32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
         internal static extern int SHCreateItemFromParsingName([MarshalAs(UnmanagedType.LPWStr)] string pszPath, IntPtr pbc, ref Guid riid, [MarshalAs(UnmanagedType.Interface)] out IShellItem ppv);
-#endif
 
         [DllImport("user32.dll", SetLastError = true)]
         public static extern bool OpenClipboard(IntPtr hWndOwner);
@@ -1153,7 +1183,7 @@ namespace Avalonia.Win32.Interop
             public int flagsEx;
         }        
     }
-#if !NETSTANDARD && !NETSTANDARD1_3
+
     [ComImport(), Guid("42F85136-DB7E-439C-85F1-E4075D135FC8"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
     internal interface IFileDialog
     {
@@ -1253,5 +1283,4 @@ namespace Avalonia.Win32.Interop
         uint Compare([In, MarshalAs(UnmanagedType.Interface)] IShellItem psi, [In] uint hint, out int piOrder);
         
     }
-#endif
 }

+ 6 - 9
src/Windows/Avalonia.Win32/SystemDialogImpl.cs

@@ -100,17 +100,14 @@ namespace Avalonia.Win32
                     var pofn = &ofn;
 
                     // We should save the current directory to restore it later.
-#if !NETSTANDARD
                     var currentDirectory = Environment.CurrentDirectory;
-#endif
+
                     var res = dialog is OpenFileDialog
                         ? UnmanagedMethods.GetOpenFileName(new IntPtr(pofn))
                         : UnmanagedMethods.GetSaveFileName(new IntPtr(pofn));
 
                     // Restore the old current directory, since GetOpenFileName and GetSaveFileName change it after they're called
-#if !NETSTANDARD
                     Environment.CurrentDirectory = currentDirectory;
-#endif
 
                     if (!res)
                         return null;
@@ -155,15 +152,16 @@ namespace Avalonia.Win32
 
         public Task<string> ShowFolderDialogAsync(OpenFolderDialog dialog, IWindowImpl parent)
         {
-#if NETSTANDARD
-            throw new NotImplementedException();
-#else
             return Task.Factory.StartNew(() =>
             {
                 string result = string.Empty;
 
                 var hWnd = parent?.Handle?.Handle ?? IntPtr.Zero;
-                var frm = (IFileDialog)(new UnmanagedMethods.FileOpenDialogRCW());
+                var clsid = Guid.Parse("DC1C5A9C-E88A-4DDE-A5A1-60F82A20AEF7");
+                var iid  = Guid.Parse("42F85136-DB7E-439C-85F1-E4075D135FC8");
+
+                UnmanagedMethods.CoCreateInstance(ref clsid, IntPtr.Zero, 1, ref iid, out var unk);
+                var frm = (IFileDialog)unk;
                 uint options;
                 frm.GetOptions(out options);
                 options |= (uint)(UnmanagedMethods.FOS.FOS_PICKFOLDERS | UnmanagedMethods.FOS.FOS_FORCEFILESYSTEM | UnmanagedMethods.FOS.FOS_NOVALIDATE | UnmanagedMethods.FOS.FOS_NOTESTFILECREATE | UnmanagedMethods.FOS.FOS_DONTADDTORECENT);
@@ -214,7 +212,6 @@ namespace Avalonia.Win32
 
                 return result;
             });
-#endif
         }
     }
 }

+ 22 - 0
src/Windows/Avalonia.Win32/WindowImpl.cs

@@ -750,5 +750,27 @@ namespace Avalonia.Win32
 
             return (int)(ptr.ToInt64() & 0xffffffff);
         }
+
+        public void ShowTaskbarIcon(bool value)
+        {
+            var style = (UnmanagedMethods.WindowStyles)UnmanagedMethods.GetWindowLong(_hwnd, -20);
+            
+            style &= ~(UnmanagedMethods.WindowStyles.WS_VISIBLE);   
+
+            style |= UnmanagedMethods.WindowStyles.WS_EX_TOOLWINDOW;   
+            if (value)
+                style |= UnmanagedMethods.WindowStyles.WS_EX_APPWINDOW;
+            else
+                style &= ~(UnmanagedMethods.WindowStyles.WS_EX_APPWINDOW);
+
+            WINDOWPLACEMENT windowPlacement = UnmanagedMethods.WINDOWPLACEMENT.Default;
+            if (UnmanagedMethods.GetWindowPlacement(_hwnd, ref windowPlacement))
+            {
+                //Toggle to make the styles stick
+                UnmanagedMethods.ShowWindow(_hwnd, ShowWindowCommand.Hide);
+                UnmanagedMethods.SetWindowLong(_hwnd, -20, (uint)style);
+                UnmanagedMethods.ShowWindow(_hwnd, windowPlacement.ShowCmd);
+            }
+        }
     }
 }

+ 1 - 1
tests/Avalonia.Base.UnitTests/Avalonia.Base.UnitTests.csproj

@@ -1,6 +1,6 @@
 <Project Sdk="Microsoft.NET.Sdk" ToolsVersion="15.0">
   <PropertyGroup>
-    <TargetFrameworks>net461;netcoreapp1.1</TargetFrameworks>
+    <TargetFrameworks>net461;netcoreapp2.0</TargetFrameworks>
     <OutputType>Library</OutputType>
   </PropertyGroup>
   <Import Project="..\..\build\UnitTests.NetCore.targets" />

+ 1 - 1
tests/Avalonia.Controls.UnitTests/Avalonia.Controls.UnitTests.csproj

@@ -1,6 +1,6 @@
 <Project Sdk="Microsoft.NET.Sdk" ToolsVersion="15.0">
   <PropertyGroup>
-    <TargetFrameworks>net461;netcoreapp1.1</TargetFrameworks>
+    <TargetFrameworks>net461;netcoreapp2.0</TargetFrameworks>
     <OutputType>Library</OutputType>
   </PropertyGroup>
   <Import Project="..\..\build\UnitTests.NetCore.targets" />

+ 217 - 0
tests/Avalonia.Controls.UnitTests/ControlTests_Resources.cs

@@ -0,0 +1,217 @@
+// Copyright (c) The Avalonia Project. All rights reserved.
+// Licensed under the MIT license. See licence.md file in the project root for full license information.
+
+using System;
+using Avalonia.Controls.Presenters;
+using Avalonia.Controls.Templates;
+using Avalonia.Styling;
+using Avalonia.UnitTests;
+using Xunit;
+
+namespace Avalonia.Controls.UnitTests
+{
+    public class ControlTests_Resources
+    {
+        [Fact]
+        public void FindResource_Should_Find_Control_Resource()
+        {
+            var target = new Control
+            {
+                Resources =
+                {
+                    { "foo", "foo-value" },
+                }
+            };
+
+            Assert.Equal("foo-value", target.FindResource("foo"));
+        }
+
+        [Fact]
+        public void FindResource_Should_Find_Control_Resource_In_Parent()
+        {
+            Control target;
+
+            var root = new Decorator
+            {
+                Resources =
+                {
+                    { "foo", "foo-value" },
+                },
+                Child = target = new Control(),
+            };
+
+            Assert.Equal("foo-value", target.FindResource("foo"));
+        }
+
+        [Fact]
+        public void FindResource_Should_Find_Application_Resource()
+        {
+            Control target;
+
+            var app = new Application
+            {
+                Resources =
+                {
+                    { "foo", "foo-value" },
+                },
+            };
+
+            var root = new TestRoot
+            {
+                Child = target = new Control(),
+                StylingParent = app,
+            };
+
+            Assert.Equal("foo-value", target.FindResource("foo"));
+        }
+
+        [Fact]
+        public void FindResource_Should_Find_Style_Resource()
+        {
+            var target = new Control
+            {
+                Styles =
+                {
+                    new Style
+                    {
+                        Resources =
+                        {
+                            { "foo", "foo-value" },
+                        }
+                    }
+                },
+                Resources =
+                {
+                    { "bar", "bar-value" },
+                },
+            };
+
+            Assert.Equal("foo-value", target.FindResource("foo"));
+        }
+
+        [Fact]
+        public void FindResource_Should_Find_Styles_Resource()
+        {
+            var target = new Control
+            {
+                Styles =
+                {
+                    new Styles
+                    {
+                        Resources =
+                        {
+                            { "foo", "foo-value" },
+                        }
+                    }
+                },
+                Resources =
+                {
+                    { "bar", "bar-value" },
+                },
+            };
+
+            Assert.Equal("foo-value", target.FindResource("foo"));
+        }
+
+        [Fact]
+        public void FindResource_Should_Find_Application_Style_Resource()
+        {
+            Control target;
+
+            var app = new Application
+            {
+                Styles =
+                {
+                    new Style
+                    {
+                        Resources =
+                        {
+                            { "foo", "foo-value" },
+                        },
+                    }
+                },
+                Resources =
+                {
+                    { "bar", "bar-value" },
+                },
+            };
+
+            var root = new TestRoot
+            {
+                Child = target = new Control(),
+                StylingParent = app,
+            };
+
+            Assert.Equal("foo-value", target.FindResource("foo"));
+        }
+
+        [Fact]
+        public void Adding_Resource_Should_Call_Raise_ResourceChanged_On_Logical_Children()
+        {
+            Border child;
+
+            var target = new ContentControl
+            {
+                Content = child = new Border(),
+                Template = ContentControlTemplate(),
+            };
+
+            var raisedOnTarget = false;
+            var raisedOnPresenter = false;
+            var raisedOnChild = false;
+
+            target.Measure(Size.Infinity);
+            target.ResourcesChanged += (_, __) => raisedOnTarget = true;
+            target.Presenter.ResourcesChanged += (_, __) => raisedOnPresenter = true;
+            child.ResourcesChanged += (_, __) => raisedOnChild = true;
+
+            target.Resources.Add("foo", "bar");
+
+            Assert.True(raisedOnTarget);
+            Assert.False(raisedOnPresenter);
+            Assert.True(raisedOnChild);
+        }
+
+        [Fact]
+        public void Adding_Resource_To_Styles_Should_Raise_ResourceChanged()
+        {
+            var target = new Decorator();
+            var raised = false;
+
+            target.ResourcesChanged += (_, __) => raised = true;
+            target.Styles.Resources.Add("foo", "bar");
+
+            Assert.True(raised);
+        }
+
+        [Fact]
+        public void Adding_Resource_To_Nested_Style_Should_Raise_ResourceChanged()
+        {
+            Style style;
+            var target = new Decorator
+            {
+                Styles =
+                {
+                    (style = new Style()),
+                }
+            };
+
+            var raised = false;
+
+            target.ResourcesChanged += (_, __) => raised = true;
+            style.Resources.Add("foo", "bar");
+
+            Assert.True(raised);
+        }
+
+        private IControlTemplate ContentControlTemplate()
+        {
+            return new FuncControlTemplate<ContentControl>(x =>
+                new ContentPresenter
+                {
+                    Name = "PART_ContentPresenter",
+                    [!ContentPresenter.ContentProperty] = x[!ContentControl.ContentProperty],
+                });
+        }
+    }
+}

+ 17 - 0
tests/Avalonia.Controls.UnitTests/TopLevelTests.cs

@@ -219,6 +219,23 @@ namespace Avalonia.Controls.UnitTests
             }
         }
 
+        [Fact]
+        public void Adding_Resource_To_Application_Should_Raise_ResourcesChanged()
+        {
+            using (UnitTestApplication.Start(TestServices.StyledWindow))
+            {
+                var impl = new Mock<ITopLevelImpl>();
+                impl.SetupAllProperties();
+                var target = new TestTopLevel(impl.Object);
+                var raised = false;
+
+                target.ResourcesChanged += (_, __) => raised = true;
+                Application.Current.Resources.Add("foo", "bar");
+
+                Assert.True(raised);
+            }
+        }
+
         private FuncControlTemplate<TestTopLevel> CreateTemplate()
         {
             return new FuncControlTemplate<TestTopLevel>(x =>

+ 1 - 1
tests/Avalonia.Input.UnitTests/Avalonia.Input.UnitTests.csproj

@@ -1,6 +1,6 @@
 <Project Sdk="Microsoft.NET.Sdk" ToolsVersion="15.0">
   <PropertyGroup>
-    <TargetFrameworks>net461;netcoreapp1.1</TargetFrameworks>
+    <TargetFrameworks>net461;netcoreapp2.0</TargetFrameworks>
     <OutputType>Library</OutputType>
   </PropertyGroup>
   <Import Project="..\..\build\UnitTests.NetCore.targets" />

+ 1 - 1
tests/Avalonia.Interactivity.UnitTests/Avalonia.Interactivity.UnitTests.csproj

@@ -1,6 +1,6 @@
 <Project Sdk="Microsoft.NET.Sdk" ToolsVersion="15.0">
   <PropertyGroup>
-    <TargetFrameworks>net461;netcoreapp1.1</TargetFrameworks>
+    <TargetFrameworks>net461;netcoreapp2.0</TargetFrameworks>
     <OutputType>Library</OutputType>
   </PropertyGroup>
   <Import Project="..\..\build\UnitTests.NetCore.targets" />

+ 1 - 1
tests/Avalonia.Layout.UnitTests/Avalonia.Layout.UnitTests.csproj

@@ -1,6 +1,6 @@
 <Project Sdk="Microsoft.NET.Sdk" ToolsVersion="15.0">
   <PropertyGroup>
-    <TargetFrameworks>net461;netcoreapp1.1</TargetFrameworks>
+    <TargetFrameworks>net461;netcoreapp2.0</TargetFrameworks>
     <OutputType>Library</OutputType>
   </PropertyGroup>
   <Import Project="..\..\build\UnitTests.NetCore.targets" />

+ 4 - 0
tests/Avalonia.Layout.UnitTests/FullLayoutTests.cs

@@ -162,6 +162,10 @@ namespace Avalonia.Layout.UnitTests
         private void RegisterServices()
         {
             var globalStyles = new Mock<IGlobalStyles>();
+            var globalStylesResources = globalStyles.As<IResourceNode>();
+            var outObj = (object)10;
+            globalStylesResources.Setup(x => x.TryGetResource("FontSizeNormal", out outObj)).Returns(true);
+
             var renderInterface = new Mock<IPlatformRenderInterface>();
             renderInterface.Setup(x =>
                 x.CreateFormattedText(

+ 1 - 1
tests/Avalonia.Markup.UnitTests/Avalonia.Markup.UnitTests.csproj

@@ -1,6 +1,6 @@
 <Project Sdk="Microsoft.NET.Sdk" ToolsVersion="15.0">
   <PropertyGroup>
-    <TargetFrameworks>net461;netcoreapp1.1</TargetFrameworks>
+    <TargetFrameworks>net461;netcoreapp2.0</TargetFrameworks>
     <OutputType>Library</OutputType>
   </PropertyGroup>
   <Import Project="..\..\build\UnitTests.NetCore.targets" />

+ 1 - 1
tests/Avalonia.Markup.Xaml.UnitTests/Avalonia.Markup.Xaml.UnitTests.csproj

@@ -1,6 +1,6 @@
 <Project Sdk="Microsoft.NET.Sdk" ToolsVersion="15.0">
   <PropertyGroup>
-    <TargetFrameworks>net461;netcoreapp1.1</TargetFrameworks>
+    <TargetFrameworks>net461;netcoreapp2.0</TargetFrameworks>
     <OutputType>Library</OutputType>
   </PropertyGroup>
   <Import Project="..\..\build\UnitTests.NetCore.targets" />

+ 55 - 0
tests/Avalonia.Markup.Xaml.UnitTests/Data/ResourceIncludeTests.cs

@@ -0,0 +1,55 @@
+using System;
+using Avalonia.Controls;
+using Avalonia.Media;
+using Avalonia.UnitTests;
+using Xunit;
+
+namespace Avalonia.Markup.Xaml.UnitTests.Data
+{
+    public class ResourceIncludeTests
+    {
+        public class StaticResourceExtensionTests
+        {
+            [Fact]
+            public void ResourceInclude_Loads_ResourceDictionary()
+            {
+                var includeXaml = @"
+<ResourceDictionary xmlns='https://github.com/avaloniaui'
+                    xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
+    <SolidColorBrush x:Key='brush'>#ff506070</SolidColorBrush>
+</ResourceDictionary>
+";
+                using (StartWithResources(("test:include.xaml", includeXaml)))
+                {
+                    var xaml = @"
+<UserControl xmlns='https://github.com/avaloniaui'
+             xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
+    <UserControl.Resources>
+        <ResourceDictionary>
+            <ResourceDictionary.MergedDictionaries>
+                <ResourceInclude Source='test:include.xaml'/>
+            </ResourceDictionary.MergedDictionaries>
+        </ResourceDictionary>
+    </UserControl.Resources>
+
+    <Border Name='border' Background='{StaticResource brush}'/>
+</UserControl>";
+
+                    var loader = new AvaloniaXamlLoader();
+                    var userControl = (UserControl)loader.Load(xaml);
+                    var border = userControl.FindControl<Border>("border");
+
+                    var brush = (SolidColorBrush)border.Background;
+                    Assert.Equal(0xff506070, brush.Color.ToUint32());
+                }
+            }
+
+            private IDisposable StartWithResources(params (string, string)[] assets)
+            {
+                var assetLoader = new MockAssetLoader(assets);
+                var services = new TestServices(assetLoader: assetLoader);
+                return UnitTestApplication.Start(services);
+            }
+        }
+    }
+}

+ 660 - 0
tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/DynamicResourceExtensionTests.cs

@@ -0,0 +1,660 @@
+// Copyright (c) The Avalonia 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.Linq;
+using Avalonia.Controls;
+using Avalonia.Controls.Presenters;
+using Avalonia.Controls.Templates;
+using Avalonia.Markup.Xaml.Data;
+using Avalonia.Media;
+using Avalonia.Styling;
+using Avalonia.UnitTests;
+using Avalonia.VisualTree;
+using Xunit;
+
+namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
+{
+    public class DynamicResourceExtensionTests
+    {
+        [Fact]
+        public void DynamicResource_Can_Be_Assigned_To_Property()
+        {
+            var xaml = @"
+<UserControl xmlns='https://github.com/avaloniaui'
+             xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
+    <UserControl.Resources>
+        <SolidColorBrush x:Key='brush'>#ff506070</SolidColorBrush>
+    </UserControl.Resources>
+
+    <Border Name='border' Background='{DynamicResource brush}'/>
+</UserControl>";
+
+            var loader = new AvaloniaXamlLoader();
+            var userControl = (UserControl)loader.Load(xaml);
+            var border = userControl.FindControl<Border>("border");
+
+            DelayedBinding.ApplyBindings(border);
+
+            var brush = (SolidColorBrush)border.Background;
+            Assert.Equal(0xff506070, brush.Color.ToUint32());
+        }
+
+        [Fact]
+        public void DynamicResource_Can_Be_Assigned_To_Attached_Property()
+        {
+            var xaml = @"
+<UserControl xmlns='https://github.com/avaloniaui'
+             xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
+    <UserControl.Resources>
+        <x:Int32 x:Key='col'>5</x:Int32>
+    </UserControl.Resources>
+
+    <Border Name='border' Grid.Column='{DynamicResource col}'/>
+</UserControl>";
+
+            var loader = new AvaloniaXamlLoader();
+            var userControl = (UserControl)loader.Load(xaml);
+            var border = userControl.FindControl<Border>("border");
+
+            DelayedBinding.ApplyBindings(border);
+
+            Assert.Equal(5, Grid.GetColumn(border));
+        }
+
+        [Fact]
+        public void DynamicResource_From_Style_Can_Be_Assigned_To_Property()
+        {
+            var xaml = @"
+<UserControl xmlns='https://github.com/avaloniaui'
+             xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
+    <UserControl.Styles>
+        <Style>
+            <Style.Resources>
+                <SolidColorBrush x:Key='brush'>#ff506070</SolidColorBrush>
+            </Style.Resources>
+        </Style>
+    </UserControl.Styles>
+
+    <Border Name='border' Background='{DynamicResource brush}'/>
+</UserControl>";
+
+            var loader = new AvaloniaXamlLoader();
+            var userControl = (UserControl)loader.Load(xaml);
+            var border = userControl.FindControl<Border>("border");
+
+            DelayedBinding.ApplyBindings(border);
+
+            var brush = (SolidColorBrush)border.Background;
+            Assert.Equal(0xff506070, brush.Color.ToUint32());
+        }
+
+        [Fact]
+        public void DynamicResource_From_MergedDictionary_Can_Be_Assigned_To_Property()
+        {
+            var xaml = @"
+<UserControl xmlns='https://github.com/avaloniaui'
+             xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
+    <UserControl.Resources>
+        <ResourceDictionary>
+            <ResourceDictionary.MergedDictionaries>
+                <ResourceDictionary>
+                    <SolidColorBrush x:Key='brush'>#ff506070</SolidColorBrush>
+                </ResourceDictionary>
+            </ResourceDictionary.MergedDictionaries>
+        </ResourceDictionary>
+    </UserControl.Resources>
+
+    <Border Name='border' Background='{DynamicResource brush}'/>
+</UserControl>";
+
+            var loader = new AvaloniaXamlLoader();
+            var userControl = (UserControl)loader.Load(xaml);
+            var border = userControl.FindControl<Border>("border");
+
+            DelayedBinding.ApplyBindings(border);
+
+            var brush = (SolidColorBrush)border.Background;
+            Assert.Equal(0xff506070, brush.Color.ToUint32());
+        }
+
+        [Fact]
+        public void DynamicResource_From_MergedDictionary_In_Style_Can_Be_Assigned_To_Property()
+        {
+            var xaml = @"
+<UserControl xmlns='https://github.com/avaloniaui'
+             xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
+    <UserControl.Styles>
+        <Style>
+            <Style.Resources>
+                <ResourceDictionary>
+                    <ResourceDictionary.MergedDictionaries>
+                        <ResourceDictionary>
+                            <SolidColorBrush x:Key='brush'>#ff506070</SolidColorBrush>
+                        </ResourceDictionary>
+                    </ResourceDictionary.MergedDictionaries>
+                </ResourceDictionary>
+            </Style.Resources>
+        </Style>
+    </UserControl.Styles>
+
+    <Border Name='border' Background='{DynamicResource brush}'/>
+</UserControl>";
+
+            var loader = new AvaloniaXamlLoader();
+            var userControl = (UserControl)loader.Load(xaml);
+            var border = userControl.FindControl<Border>("border");
+
+            DelayedBinding.ApplyBindings(border);
+
+            var brush = (SolidColorBrush)border.Background;
+            Assert.Equal(0xff506070, brush.Color.ToUint32());
+        }
+
+        [Fact]
+        public void DynamicResource_From_Application_Can_Be_Assigned_To_Property_In_Window()
+        {
+            using (StyledWindow())
+            {
+                Application.Current.Resources.Add("brush", new SolidColorBrush(0xff506070));
+
+                var xaml = @"
+<Window xmlns='https://github.com/avaloniaui'
+             xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
+    <Border Name='border' Background='{DynamicResource brush}'/>
+</Window>";
+
+                var loader = new AvaloniaXamlLoader();
+                var window = (Window)loader.Load(xaml);
+                var border = window.FindControl<Border>("border");
+
+                var brush = (SolidColorBrush)border.Background;
+                Assert.Equal(0xff506070, brush.Color.ToUint32());
+            }
+        }
+
+        [Fact]
+        public void DynamicResource_From_Application_Can_Be_Assigned_To_Property_In_UserControl()
+        {
+            using (UnitTestApplication.Start(TestServices.StyledWindow))
+            {
+                Application.Current.Resources.Add("brush", new SolidColorBrush(0xff506070));
+
+                var xaml = @"
+<UserControl xmlns='https://github.com/avaloniaui'
+             xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
+    <Border Name='border' Background='{DynamicResource brush}'/>
+</UserControl>";
+
+                var loader = new AvaloniaXamlLoader();
+                var userControl = (UserControl)loader.Load(xaml);
+                var border = userControl.FindControl<Border>("border");
+
+                // We don't actually know where the global styles are until we attach the control
+                // to a window, as Window has StylingParent set to Application.
+                var window = new Window { Content = userControl };
+                window.Show();
+
+                var brush = (SolidColorBrush)border.Background;
+                Assert.Equal(0xff506070, brush.Color.ToUint32());
+            }
+        }
+
+        [Fact]
+        public void DynamicResource_Can_Be_Assigned_To_Setter()
+        {
+            using (StyledWindow())
+            {
+                var xaml = @"
+<Window xmlns='https://github.com/avaloniaui'
+        xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
+    <Window.Resources>
+        <SolidColorBrush x:Key='brush'>#ff506070</SolidColorBrush>
+    </Window.Resources>
+    <Window.Styles>
+        <Style Selector='Button'>
+            <Setter Property='Background' Value='{DynamicResource brush}'/>
+        </Style>
+    </Window.Styles>
+    <Button Name='button'/>
+</Window>";
+
+                var loader = new AvaloniaXamlLoader();
+                var window = (Window)loader.Load(xaml);
+                var button = window.FindControl<Button>("button");
+                var brush = (SolidColorBrush)button.Background;
+
+                Assert.Equal(0xff506070, brush.Color.ToUint32());
+            }
+        }
+
+        [Fact]
+        public void DynamicResource_From_Style_Can_Be_Assigned_To_Setter()
+        {
+            using (StyledWindow())
+            {
+                var xaml = @"
+<Window xmlns='https://github.com/avaloniaui'
+        xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
+    <Window.Styles>
+        <Style>
+            <Style.Resources>
+                <SolidColorBrush x:Key='brush'>#ff506070</SolidColorBrush>
+            </Style.Resources>
+        </Style>
+        <Style Selector='Button'>
+            <Setter Property='Background' Value='{DynamicResource brush}'/>
+        </Style>
+    </Window.Styles>
+    <Button Name='button'/>
+</Window>";
+
+                var loader = new AvaloniaXamlLoader();
+                var window = (Window)loader.Load(xaml);
+                var button = window.FindControl<Button>("button");
+                var brush = (SolidColorBrush)button.Background;
+
+                Assert.Equal(0xff506070, brush.Color.ToUint32());
+            }
+        }
+
+        [Fact]
+        public void DynamicResource_Can_Be_Assigned_To_Setter_In_Styles_File()
+        {
+            var styleXaml = @"
+<Styles xmlns='https://github.com/avaloniaui'
+        xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
+    <Styles.Resources>
+        <SolidColorBrush x:Key='brush'>#ff506070</SolidColorBrush>
+    </Styles.Resources>
+
+    <Style Selector='Border'>
+        <Setter Property='Background' Value='{DynamicResource brush}'/>
+    </Style>
+</Styles>";
+
+            using (StyledWindow(assets: ("test:style.xaml", styleXaml)))
+            {
+                var xaml = @"
+<Window xmlns='https://github.com/avaloniaui'
+        xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
+    <Window.Styles>
+        <StyleInclude Source='test:style.xaml'/>
+    </Window.Styles>
+    <Border Name='border'/>
+</Window>";
+
+                var loader = new AvaloniaXamlLoader();
+                var window = (Window)loader.Load(xaml);
+                var border = window.FindControl<Border>("border");
+                var brush = (SolidColorBrush)border.Background;
+
+                Assert.Equal(0xff506070, brush.Color.ToUint32());
+            }
+        }
+
+        [Fact]
+        public void DynamicResource_Can_Be_Assigned_To_Property_In_ControlTemplate_In_Styles_File()
+        {
+            var styleXaml = @"
+<Styles xmlns='https://github.com/avaloniaui'
+        xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
+    <Styles.Resources>
+        <SolidColorBrush x:Key='brush'>#ff506070</SolidColorBrush>
+    </Styles.Resources>
+
+    <Style Selector='Button'>
+        <Setter Property='Template'>
+            <ControlTemplate>
+                <Border Name='border' Background='{DynamicResource brush}'/>
+            </ControlTemplate>
+        </Setter>
+    </Style>
+</Styles>";
+
+            using (StyledWindow(assets: ("test:style.xaml", styleXaml)))
+            {
+                var xaml = @"
+<Window xmlns='https://github.com/avaloniaui'
+        xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
+    <Window.Styles>
+        <StyleInclude Source='test:style.xaml'/>
+    </Window.Styles>
+    <Button Name='button'/>
+</Window>";
+
+                var loader = new AvaloniaXamlLoader();
+                var window = (Window)loader.Load(xaml);
+                var button = window.FindControl<Button>("button");
+
+                window.Show();
+
+                var border = (Border)button.GetVisualChildren().Single();
+                var brush = (SolidColorBrush)border.Background;
+
+                Assert.Equal(0xff506070, brush.Color.ToUint32());
+            }
+        }
+
+        [Fact]
+        public void DynamicResource_Can_Be_Assigned_To_Resource_Property()
+        {
+            var xaml = @"
+<UserControl xmlns='https://github.com/avaloniaui'
+             xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
+    <UserControl.Resources>
+        <Color x:Key='color'>#ff506070</Color>
+        <SolidColorBrush x:Key='brush' Color='{DynamicResource color}'/>
+    </UserControl.Resources>
+
+    <Border Name='border' Background='{DynamicResource brush}'/>
+</UserControl>";
+
+            var loader = new AvaloniaXamlLoader();
+            var userControl = (UserControl)loader.Load(xaml);
+            var border = userControl.FindControl<Border>("border");
+
+            DelayedBinding.ApplyBindings(border);
+
+            var brush = (SolidColorBrush)border.Background;
+            Assert.Equal(0xff506070, brush.Color.ToUint32());
+        }
+
+
+        [Fact]
+        public void DynamicResource_Can_Be_Assigned_To_ItemTemplate_Property()
+        {
+            var xaml = @"
+<UserControl xmlns='https://github.com/avaloniaui'
+             xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
+    <UserControl.Resources>
+        <DataTemplate x:Key='PurpleData'>
+          <TextBlock Text='{Binding Name}' Background='Purple'/>
+        </DataTemplate>
+    </UserControl.Resources>
+
+    <ListBox Name='listBox' ItemTemplate='{DynamicResource PurpleData}'/>
+</UserControl>";
+
+            var loader = new AvaloniaXamlLoader();
+            var userControl = (UserControl)loader.Load(xaml);
+            var listBox = userControl.FindControl<ListBox>("listBox");
+
+            DelayedBinding.ApplyBindings(listBox);
+
+            Assert.NotNull(listBox.ItemTemplate);
+        }
+
+        [Fact]
+        public void DynamicResource_Tracks_Added_Resource()
+        {
+            var xaml = @"
+<UserControl xmlns='https://github.com/avaloniaui'
+             xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
+    <Border Name='border' Background='{DynamicResource brush}'/>
+</UserControl>";
+
+            var loader = new AvaloniaXamlLoader();
+            var userControl = (UserControl)loader.Load(xaml);
+            var border = userControl.FindControl<Border>("border");
+
+            DelayedBinding.ApplyBindings(border);
+
+            Assert.Null(border.Background);
+
+            userControl.Resources.Add("brush", new SolidColorBrush(0xff506070));
+
+            var brush = (SolidColorBrush)border.Background;
+            Assert.NotNull(brush);
+            Assert.Equal(0xff506070, brush.Color.ToUint32());
+        }
+
+        [Fact]
+        public void DynamicResource_Tracks_Added_Style_Resource()
+        {
+            var xaml = @"
+<UserControl xmlns='https://github.com/avaloniaui'
+             xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
+    <Border Name='border' Background='{DynamicResource brush}'/>
+</UserControl>";
+
+            var loader = new AvaloniaXamlLoader();
+            var userControl = (UserControl)loader.Load(xaml);
+            var border = userControl.FindControl<Border>("border");
+
+            DelayedBinding.ApplyBindings(border);
+
+            Assert.Null(border.Background);
+
+            userControl.Styles.Resources.Add("brush", new SolidColorBrush(0xff506070));
+
+            var brush = (SolidColorBrush)border.Background;
+            Assert.NotNull(brush);
+            Assert.Equal(0xff506070, brush.Color.ToUint32());
+        }
+
+        [Fact]
+        public void DynamicResource_Tracks_Added_Nested_Style_Resource()
+        {
+            var xaml = @"
+<UserControl xmlns='https://github.com/avaloniaui'
+             xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
+    <UserControl.Styles>
+        <Style>
+        </Style>
+    </UserControl.Styles>
+    <Border Name='border' Background='{DynamicResource brush}'/>
+</UserControl>";
+
+            var loader = new AvaloniaXamlLoader();
+            var userControl = (UserControl)loader.Load(xaml);
+            var border = userControl.FindControl<Border>("border");
+
+            DelayedBinding.ApplyBindings(border);
+
+            Assert.Null(border.Background);
+
+            ((Style)userControl.Styles[0]).Resources.Add("brush", new SolidColorBrush(0xff506070));
+
+            var brush = (SolidColorBrush)border.Background;
+            Assert.NotNull(brush);
+            Assert.Equal(0xff506070, brush.Color.ToUint32());
+        }
+
+        [Fact]
+        public void DynamicResource_Tracks_Added_MergedResource()
+        {
+            var xaml = @"
+<UserControl xmlns='https://github.com/avaloniaui'
+             xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
+    <UserControl.Resources>
+        <ResourceDictionary>
+            <ResourceDictionary.MergedDictionaries>
+                <ResourceDictionary/>
+            </ResourceDictionary.MergedDictionaries>
+        </ResourceDictionary>
+    </UserControl.Resources>
+    <Border Name='border' Background='{DynamicResource brush}'/>
+</UserControl>";
+
+            var loader = new AvaloniaXamlLoader();
+            var userControl = (UserControl)loader.Load(xaml);
+            var border = userControl.FindControl<Border>("border");
+
+            DelayedBinding.ApplyBindings(border);
+
+            Assert.Null(border.Background);
+
+            ((IResourceDictionary)userControl.Resources.MergedDictionaries[0]).Add("brush", new SolidColorBrush(0xff506070));
+
+            var brush = (SolidColorBrush)border.Background;
+            Assert.NotNull(brush);
+            Assert.Equal(0xff506070, brush.Color.ToUint32());
+        }
+
+        [Fact]
+        public void DynamicResource_Tracks_Added_MergedResource_Dictionary()
+        {
+            var xaml = @"
+<UserControl xmlns='https://github.com/avaloniaui'
+             xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
+    <Border Name='border' Background='{DynamicResource brush}'/>
+</UserControl>";
+
+            var loader = new AvaloniaXamlLoader();
+            var userControl = (UserControl)loader.Load(xaml);
+            var border = userControl.FindControl<Border>("border");
+
+            DelayedBinding.ApplyBindings(border);
+
+            Assert.Null(border.Background);
+
+            var dictionary = new ResourceDictionary
+            {
+                { "brush", new SolidColorBrush(0xff506070) },
+            };
+
+            userControl.Resources.MergedDictionaries.Add(dictionary);
+
+            var brush = (SolidColorBrush)border.Background;
+            Assert.NotNull(brush);
+            Assert.Equal(0xff506070, brush.Color.ToUint32());
+        }
+
+        [Fact]
+        public void DynamicResource_Tracks_Added_Style_MergedResource_Dictionary()
+        {
+            var xaml = @"
+<UserControl xmlns='https://github.com/avaloniaui'
+             xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
+    <UserControl.Styles>
+        <Style>
+        </Style>
+    </UserControl.Styles>
+    <Border Name='border' Background='{DynamicResource brush}'/>
+</UserControl>";
+
+            var loader = new AvaloniaXamlLoader();
+            var userControl = (UserControl)loader.Load(xaml);
+            var border = userControl.FindControl<Border>("border");
+
+            DelayedBinding.ApplyBindings(border);
+
+            Assert.Null(border.Background);
+
+            var dictionary = new ResourceDictionary
+            {
+                { "brush", new SolidColorBrush(0xff506070) },
+            };
+
+            ((Style)userControl.Styles[0]).Resources.MergedDictionaries.Add(dictionary);
+
+            var brush = (SolidColorBrush)border.Background;
+            Assert.NotNull(brush);
+            Assert.Equal(0xff506070, brush.Color.ToUint32());
+        }
+
+        [Fact]
+        public void DynamicResource_Can_Be_Found_Across_Xaml_Style_Files()
+        {
+            var style1Xaml = @"
+<Style xmlns='https://github.com/avaloniaui'
+       xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
+  <Style.Resources>
+    <Color x:Key='Red'>Red</Color>
+  </Style.Resources>
+</Style>";
+            var style2Xaml = @"
+<Style xmlns='https://github.com/avaloniaui'
+       xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
+  <Style.Resources>
+    <SolidColorBrush x:Key='RedBrush' Color='{DynamicResource Red}'/>
+  </Style.Resources>
+</Style>";
+            using (StyledWindow(
+                ("test:style1.xaml", style1Xaml), 
+                ("test:style2.xaml", style2Xaml)))
+            {
+                var xaml = @"
+<Window xmlns='https://github.com/avaloniaui'
+        xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
+    <Window.Styles>
+        <StyleInclude Source='test:style1.xaml'/>
+        <StyleInclude Source='test:style2.xaml'/>
+    </Window.Styles>
+    <Border Name='border' Background='{DynamicResource RedBrush}'/>
+</Window>";
+
+                var loader = new AvaloniaXamlLoader();
+                var window = (Window)loader.Load(xaml);
+                var border = window.FindControl<Border>("border");
+                var borderBrush = (ISolidColorBrush)border.Background;
+
+                Assert.NotNull(borderBrush);
+                Assert.Equal(0xffff0000, borderBrush.Color.ToUint32());
+            }
+        }
+
+        [Fact]
+        public void Control_Property_Is_Updated_When_Parent_Is_Changed()
+        {
+            var xaml = @"
+<UserControl xmlns='https://github.com/avaloniaui'
+             xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
+    <UserControl.Resources>
+        <SolidColorBrush x:Key='brush'>#ff506070</SolidColorBrush>
+    </UserControl.Resources>
+
+    <Border Name='border' Background='{DynamicResource brush}'/>
+</UserControl>";
+
+            var loader = new AvaloniaXamlLoader();
+            var userControl = (UserControl)loader.Load(xaml);
+            var border = userControl.FindControl<Border>("border");
+
+            DelayedBinding.ApplyBindings(border);
+
+            var brush = (SolidColorBrush)border.Background;
+            Assert.Equal(0xff506070, brush.Color.ToUint32());
+
+            userControl.Content = null;
+
+            Assert.Null(border.Background);
+
+            userControl.Content = border;
+
+            brush = (SolidColorBrush)border.Background;
+            Assert.Equal(0xff506070, brush.Color.ToUint32());
+        }
+
+        private IDisposable StyledWindow(params (string, string)[] assets)
+        {
+            var services = TestServices.StyledWindow.With(
+                assetLoader: new MockAssetLoader(assets),
+                theme: () => new Styles
+                {
+                    WindowStyle(),
+                });
+
+            return UnitTestApplication.Start(services);
+        }
+
+        private Style WindowStyle()
+        {
+            return new Style(x => x.OfType<Window>())
+            {
+                Setters =
+                {
+                    new Setter(
+                        Window.TemplateProperty,
+                        new FuncControlTemplate<Window>(x =>
+                            new ContentPresenter
+                            {
+                                Name = "PART_ContentPresenter",
+                                [!ContentPresenter.ContentProperty] = x[!Window.ContentProperty],
+                            }))
+                }
+            };
+        }
+    }
+}

+ 476 - 0
tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/StaticResourceExtensionTests.cs

@@ -0,0 +1,476 @@
+// Copyright (c) The Avalonia 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.Linq;
+using Avalonia.Controls;
+using Avalonia.Controls.Presenters;
+using Avalonia.Controls.Templates;
+using Avalonia.Media;
+using Avalonia.Styling;
+using Avalonia.UnitTests;
+using Avalonia.VisualTree;
+using Xunit;
+
+namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
+{
+    public class StaticResourceExtensionTests
+    {
+        [Fact]
+        public void StaticResource_Can_Be_Assigned_To_Property()
+        {
+            var xaml = @"
+<UserControl xmlns='https://github.com/avaloniaui'
+             xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
+    <UserControl.Resources>
+        <SolidColorBrush x:Key='brush'>#ff506070</SolidColorBrush>
+    </UserControl.Resources>
+
+    <Border Name='border' Background='{StaticResource brush}'/>
+</UserControl>";
+
+            var loader = new AvaloniaXamlLoader();
+            var userControl = (UserControl)loader.Load(xaml);
+            var border = userControl.FindControl<Border>("border");
+
+            var brush = (SolidColorBrush)border.Background;
+            Assert.Equal(0xff506070, brush.Color.ToUint32());
+        }
+
+        [Fact]
+        public void StaticResource_Can_Be_Assigned_To_Attached_Property()
+        {
+            var xaml = @"
+<UserControl xmlns='https://github.com/avaloniaui'
+             xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
+    <UserControl.Resources>
+        <x:Int32 x:Key='col'>5</x:Int32>
+    </UserControl.Resources>
+
+    <Border Name='border' Grid.Column='{StaticResource col}'/>
+</UserControl>";
+
+            var loader = new AvaloniaXamlLoader();
+            var userControl = (UserControl)loader.Load(xaml);
+            var border = userControl.FindControl<Border>("border");
+
+            Assert.Equal(5, Grid.GetColumn(border));
+        }
+
+        [Fact]
+        public void StaticResource_From_Style_Can_Be_Assigned_To_Property()
+        {
+            var xaml = @"
+<UserControl xmlns='https://github.com/avaloniaui'
+             xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
+    <UserControl.Styles>
+        <Style>
+            <Style.Resources>
+                <SolidColorBrush x:Key='brush'>#ff506070</SolidColorBrush>
+            </Style.Resources>
+        </Style>
+    </UserControl.Styles>
+
+    <Border Name='border' Background='{StaticResource brush}'/>
+</UserControl>";
+
+            var loader = new AvaloniaXamlLoader();
+            var userControl = (UserControl)loader.Load(xaml);
+            var border = userControl.FindControl<Border>("border");
+
+            var brush = (SolidColorBrush)border.Background;
+            Assert.Equal(0xff506070, brush.Color.ToUint32());
+        }
+
+        [Fact]
+        public void StaticResource_From_Application_Can_Be_Assigned_To_Property_In_Window()
+        {
+            using (StyledWindow())
+            {
+                Application.Current.Resources.Add("brush", new SolidColorBrush(0xff506070));
+
+                var xaml = @"
+<Window xmlns='https://github.com/avaloniaui'
+             xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
+    <Border Name='border' Background='{StaticResource brush}'/>
+</Window>";
+
+                var loader = new AvaloniaXamlLoader();
+                var window = (Window)loader.Load(xaml);
+                var border = window.FindControl<Border>("border");
+
+                var brush = (SolidColorBrush)border.Background;
+                Assert.Equal(0xff506070, brush.Color.ToUint32());
+            }
+        }
+
+        [Fact]
+        public void StaticResource_From_MergedDictionary_Can_Be_Assigned_To_Property()
+        {
+            var xaml = @"
+<UserControl xmlns='https://github.com/avaloniaui'
+             xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
+    <UserControl.Resources>
+        <ResourceDictionary>
+            <ResourceDictionary.MergedDictionaries>
+                <ResourceDictionary>
+                    <SolidColorBrush x:Key='brush'>#ff506070</SolidColorBrush>
+                </ResourceDictionary>
+            </ResourceDictionary.MergedDictionaries>
+        </ResourceDictionary>
+    </UserControl.Resources>
+
+    <Border Name='border' Background='{StaticResource brush}'/>
+</UserControl>";
+
+            var loader = new AvaloniaXamlLoader();
+            var userControl = (UserControl)loader.Load(xaml);
+            var border = userControl.FindControl<Border>("border");
+
+            var brush = (SolidColorBrush)border.Background;
+            Assert.Equal(0xff506070, brush.Color.ToUint32());
+        }
+
+        [Fact]
+        public void StaticResource_From_MergedDictionary_In_Style_Can_Be_Assigned_To_Property()
+        {
+            var xaml = @"
+<UserControl xmlns='https://github.com/avaloniaui'
+             xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
+    <UserControl.Styles>
+        <Style>
+            <Style.Resources>
+                <ResourceDictionary>
+                    <ResourceDictionary.MergedDictionaries>
+                        <ResourceDictionary>
+                            <SolidColorBrush x:Key='brush'>#ff506070</SolidColorBrush>
+                        </ResourceDictionary>
+                    </ResourceDictionary.MergedDictionaries>
+                </ResourceDictionary>
+            </Style.Resources>
+        </Style>
+    </UserControl.Styles>
+
+    <Border Name='border' Background='{StaticResource brush}'/>
+</UserControl>";
+
+            var loader = new AvaloniaXamlLoader();
+            var userControl = (UserControl)loader.Load(xaml);
+            var border = userControl.FindControl<Border>("border");
+
+            var brush = (SolidColorBrush)border.Background;
+            Assert.Equal(0xff506070, brush.Color.ToUint32());
+        }
+
+        [Fact]
+        public void StaticResource_From_Application_Can_Be_Assigned_To_Property_In_UserControl()
+        {
+            using (UnitTestApplication.Start(TestServices.StyledWindow))
+            {
+                Application.Current.Resources.Add("brush", new SolidColorBrush(0xff506070));
+
+                var xaml = @"
+<UserControl xmlns='https://github.com/avaloniaui'
+             xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
+    <Border Name='border' Background='{StaticResource brush}'/>
+</UserControl>";
+
+                var loader = new AvaloniaXamlLoader();
+                var userControl = (UserControl)loader.Load(xaml);
+                var border = userControl.FindControl<Border>("border");
+
+                // We don't actually know where the global styles are until we attach the control
+                // to a window, as Window has StylingParent set to Application.
+                var window = new Window { Content = userControl };
+                window.Show();
+
+                var brush = (SolidColorBrush)border.Background;
+                Assert.Equal(0xff506070, brush.Color.ToUint32());
+            }
+        }
+
+        [Fact]
+        public void StaticResource_Can_Be_Assigned_To_Setter()
+        {
+            using (StyledWindow())
+            {
+                var xaml = @"
+<Window xmlns='https://github.com/avaloniaui'
+        xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
+    <Window.Resources>
+        <SolidColorBrush x:Key='brush'>#ff506070</SolidColorBrush>
+    </Window.Resources>
+    <Window.Styles>
+        <Style Selector='Button'>
+            <Setter Property='Background' Value='{StaticResource brush}'/>
+        </Style>
+    </Window.Styles>
+    <Button Name='button'/>
+</Window>";
+
+                var loader = new AvaloniaXamlLoader();
+                var window = (Window)loader.Load(xaml);
+                var button = window.FindControl<Button>("button");
+                var brush = (SolidColorBrush)button.Background;
+
+                Assert.Equal(0xff506070, brush.Color.ToUint32());
+            }
+        }
+
+        [Fact]
+        public void StaticResource_From_Style_Can_Be_Assigned_To_Setter()
+        {
+            using (StyledWindow())
+            {
+                var xaml = @"
+<Window xmlns='https://github.com/avaloniaui'
+        xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
+    <Window.Styles>
+        <Style>
+            <Style.Resources>
+                <SolidColorBrush x:Key='brush'>#ff506070</SolidColorBrush>
+            </Style.Resources>
+        </Style>
+        <Style Selector='Button'>
+            <Setter Property='Background' Value='{StaticResource brush}'/>
+        </Style>
+    </Window.Styles>
+    <Button Name='button'/>
+</Window>";
+
+                var loader = new AvaloniaXamlLoader();
+                var window = (Window)loader.Load(xaml);
+                var button = window.FindControl<Button>("button");
+                var brush = (SolidColorBrush)button.Background;
+
+                Assert.Equal(0xff506070, brush.Color.ToUint32());
+            }
+        }
+
+        [Fact]
+        public void StaticResource_Can_Be_Assigned_To_Setter_In_Styles_File()
+        {
+            var styleXaml = @"
+<Styles xmlns='https://github.com/avaloniaui'
+        xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
+    <Styles.Resources>
+        <SolidColorBrush x:Key='brush'>#ff506070</SolidColorBrush>
+    </Styles.Resources>
+
+    <Style Selector='Border'>
+        <Setter Property='Background' Value='{StaticResource brush}'/>
+    </Style>
+</Styles>";
+
+            using (StyledWindow(assets: ("test:style.xaml", styleXaml)))
+            {
+                var xaml = @"
+<Window xmlns='https://github.com/avaloniaui'
+        xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
+    <Window.Styles>
+        <StyleInclude Source='test:style.xaml'/>
+    </Window.Styles>
+    <Border Name='border'/>
+</Window>";
+
+                var loader = new AvaloniaXamlLoader();
+                var window = (Window)loader.Load(xaml);
+                var border = window.FindControl<Border>("border");
+                var brush = (SolidColorBrush)border.Background;
+
+                Assert.Equal(0xff506070, brush.Color.ToUint32());
+            }
+        }
+
+        [Fact]
+        public void StaticResource_Can_Be_Assigned_To_Resource_Property()
+        {
+            var xaml = @"
+<UserControl xmlns='https://github.com/avaloniaui'
+             xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
+    <UserControl.Resources>
+        <Color x:Key='color'>#ff506070</Color>
+        <SolidColorBrush x:Key='brush' Color='{StaticResource color}'/>
+    </UserControl.Resources>
+
+    <Border Name='border' Background='{StaticResource brush}'/>
+</UserControl>";
+
+            var loader = new AvaloniaXamlLoader();
+            var userControl = (UserControl)loader.Load(xaml);
+            var border = userControl.FindControl<Border>("border");
+
+            var brush = (SolidColorBrush)border.Background;
+            Assert.Equal(0xff506070, brush.Color.ToUint32());
+        }
+
+        [Fact]
+        public void StaticResource_Can_Be_Assigned_To_Resource_Property_In_Styles_File()
+        {
+            var xaml = @"
+<Styles xmlns='https://github.com/avaloniaui'
+             xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
+    <Styles.Resources>
+        <Color x:Key='color'>#ff506070</Color>
+        <SolidColorBrush x:Key='brush' Color='{StaticResource color}'/>
+    </Styles.Resources>
+</Styles>";
+
+            var loader = new AvaloniaXamlLoader();
+            var styles = (Styles)loader.Load(xaml);
+            var brush = (SolidColorBrush)styles.Resources["brush"];
+
+            Assert.Equal(0xff506070, brush.Color.ToUint32());
+        }
+
+        [Fact(Skip = "Not yet supported by Portable.Xaml")]
+        public void StaticResource_Can_Be_Assigned_To_Property_In_ControlTemplate_In_Styles_File()
+        {
+            var styleXaml = @"
+<Styles xmlns='https://github.com/avaloniaui'
+        xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
+    <Styles.Resources>
+        <SolidColorBrush x:Key='brush'>#ff506070</SolidColorBrush>
+    </Styles.Resources>
+
+    <Style Selector='Button'>
+        <Setter Property='Template'>
+            <ControlTemplate>
+                <Border Name='border' Background='{StaticResource brush}'/>
+            </ControlTemplate>
+        </Setter>
+    </Style>
+</Styles>";
+
+            using (StyledWindow(assets: ("test:style.xaml", styleXaml)))
+            {
+                var xaml = @"
+<Window xmlns='https://github.com/avaloniaui'
+        xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
+    <Window.Styles>
+        <StyleInclude Source='test:style.xaml'/>
+    </Window.Styles>
+    <Button Name='button'/>
+</Window>";
+
+                var loader = new AvaloniaXamlLoader();
+                var window = (Window)loader.Load(xaml);
+                var button = window.FindControl<Button>("button");
+
+                window.Show();
+
+                var border = (Border)button.GetVisualChildren().Single();
+                var brush = (SolidColorBrush)border.Background;
+
+                // To make this work we somehow need to be able to get hold of the parent ambient
+                // context from Portable.Xaml. See TODO in StaticResourceExtension.
+                Assert.Equal(0xff506070, brush.Color.ToUint32());
+            }
+        }
+
+        [Fact]
+        public void StaticResource_Can_Be_Assigned_To_ItemTemplate_Property()
+        {
+            var xaml = @"
+<UserControl xmlns='https://github.com/avaloniaui'
+             xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
+    <UserControl.Resources>
+        <DataTemplate x:Key='PurpleData'>
+          <TextBlock Text='{Binding Name}' Background='Purple'/>
+        </DataTemplate>
+    </UserControl.Resources>
+
+    <ListBox Name='listBox' ItemTemplate='{StaticResource PurpleData}'/>
+</UserControl>";
+
+            var loader = new AvaloniaXamlLoader();
+            var userControl = (UserControl)loader.Load(xaml);
+            var listBox = userControl.FindControl<ListBox>("listBox");
+
+            Assert.NotNull(listBox.ItemTemplate);
+        }
+
+        [Fact]
+        public void StaticResource_Can_Be_Assigned_To_Converter()
+        {
+            using (StyledWindow())
+            {
+                var xaml = @"
+<Window xmlns='https://github.com/avaloniaui'
+             xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'
+             xmlns:local='clr-namespace:Avalonia.Markup.Xaml.UnitTests.MarkupExtensions;assembly=Avalonia.Markup.Xaml.UnitTests'>
+    <Window.Resources>
+        <local:TestValueConverter x:Key='converter' Append='bar'/>
+    </Window.Resources>
+
+    <TextBlock Name='textBlock' Text='{Binding Converter={StaticResource converter}}'/>
+</Window>";
+
+                var loader = new AvaloniaXamlLoader();
+                var window = (Window)loader.Load(xaml);
+                var textBlock = window.FindControl<TextBlock>("textBlock");
+
+                window.DataContext = "foo";
+                window.ApplyTemplate();
+
+                Assert.Equal("foobar", textBlock.Text);
+            }
+        }
+
+        [Fact]
+        public void Control_Property_Is_Not_Updated_When_Parent_Is_Changed()
+        {
+            var xaml = @"
+<UserControl xmlns='https://github.com/avaloniaui'
+             xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
+    <UserControl.Resources>
+        <SolidColorBrush x:Key='brush'>#ff506070</SolidColorBrush>
+    </UserControl.Resources>
+
+    <Border Name='border' Background='{StaticResource brush}'/>
+</UserControl>";
+
+            var loader = new AvaloniaXamlLoader();
+            var userControl = (UserControl)loader.Load(xaml);
+            var border = userControl.FindControl<Border>("border");
+
+            var brush = (SolidColorBrush)border.Background;
+            Assert.Equal(0xff506070, brush.Color.ToUint32());
+
+            userControl.Content = null;
+
+            brush = (SolidColorBrush)border.Background;
+            Assert.Equal(0xff506070, brush.Color.ToUint32());
+        }
+
+        private IDisposable StyledWindow(params (string, string)[] assets)
+        {
+            var services = TestServices.StyledWindow.With(
+                assetLoader: new MockAssetLoader(assets),
+                theme: () => new Styles
+                {
+                    WindowStyle(),
+                });
+
+            return UnitTestApplication.Start(services);
+        }
+
+        private Style WindowStyle()
+        {
+            return new Style(x => x.OfType<Window>())
+            {
+                Setters =
+                {
+                    new Setter(
+                        Window.TemplateProperty,
+                        new FuncControlTemplate<Window>(x =>
+                            new ContentPresenter
+                            {
+                                Name = "PART_ContentPresenter",
+                                [!ContentPresenter.ContentProperty] = x[!Window.ContentProperty],
+                            }))
+                }
+            };
+        }
+    }
+}

+ 20 - 0
tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/TestValueConverter.cs

@@ -0,0 +1,20 @@
+using System;
+using System.Globalization;
+
+namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
+{
+    public class TestValueConverter : IValueConverter
+    {
+        public string Append { get; set; }
+
+        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+        {
+            return value.ToString() + Append;
+        }
+
+        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+        {
+            throw new NotImplementedException();
+        }
+    }
+}

+ 3 - 3
tests/Avalonia.Markup.Xaml.UnitTests/Xaml/BasicTests.cs

@@ -480,13 +480,13 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml
 
             Assert.True(style.Resources.Count > 0);
 
-            var brush = style.FindResource("Brush") as SolidColorBrush;
+            style.TryGetResource("Brush", out var brush);
 
             Assert.NotNull(brush);
 
-            Assert.Equal(Colors.White, brush.Color);
+            Assert.Equal(Colors.White, ((SolidColorBrush)brush).Color);
 
-            var d = (double)style.FindResource("Double");
+            style.TryGetResource("Double", out var d);
 
             Assert.Equal(10.0, d);
         }

+ 7 - 5
tests/Avalonia.Markup.Xaml.UnitTests/Xaml/StyleTests.cs

@@ -141,7 +141,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml
 
                 var loader = new AvaloniaXamlLoader();
                 var window = (Window)loader.Load(xaml);
-                var brush = (ISolidColorBrush)window.FindStyleResource("brush");
+                var brush = (ISolidColorBrush)window.FindResource("brush");
                 var button = window.FindControl<Button>("button");
 
                 DelayedBinding.ApplyBindings(button);
@@ -169,9 +169,10 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml
 
             var loader = new AvaloniaXamlLoader();
             var styles = (Styles)loader.Load(xaml);
-            var brush = (ISolidColorBrush)styles.FindResource("brush");
 
-            Assert.Equal(0xff506070, brush.Color.ToUint32());
+            styles.TryGetResource("brush", out var brush);
+
+            Assert.Equal(0xff506070, ((SolidColorBrush)brush).Color.ToUint32());
         }
 
         [Fact]
@@ -194,9 +195,10 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml
 
             var loader = new AvaloniaXamlLoader();
             var styles = (Styles)loader.Load(xaml);
-            var brush = (ISolidColorBrush)styles.FindResource("brush");
 
-            Assert.Equal(0xff506070, brush.Color.ToUint32());
+            styles.TryGetResource("brush", out var brush);
+
+            Assert.Equal(0xff506070, ((SolidColorBrush)brush).Color.ToUint32());
         }
 
         [Fact(Skip = "TODO: Issue #492")]

+ 1 - 1
tests/Avalonia.RenderTests/Avalonia.Skia.RenderTests.csproj

@@ -6,7 +6,7 @@
   <Import Project="Sdk.props" Sdk="Microsoft.NET.Sdk" />
   
   <PropertyGroup>
-    <TargetFrameworks>netcoreapp1.1</TargetFrameworks>
+    <TargetFrameworks>netcoreapp2.0</TargetFrameworks>
     <OutputPath>bin\Skia\$(Configuration)</OutputPath>
     <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
     <EnableDefaultCompileItems>False</EnableDefaultCompileItems>

+ 1 - 1
tests/Avalonia.Styling.UnitTests/Avalonia.Styling.UnitTests.csproj

@@ -1,6 +1,6 @@
 <Project Sdk="Microsoft.NET.Sdk" ToolsVersion="15.0">
   <PropertyGroup>
-    <TargetFrameworks>net461;netcoreapp1.1</TargetFrameworks>
+    <TargetFrameworks>net461;netcoreapp2.0</TargetFrameworks>
     <OutputType>Library</OutputType>
     <NoWarn>CS0067</NoWarn>
   </PropertyGroup>

+ 175 - 0
tests/Avalonia.Styling.UnitTests/ResourceDictionaryTests.cs

@@ -0,0 +1,175 @@
+// Copyright (c) The Avalonia Project. All rights reserved.
+// Licensed under the MIT license. See licence.md file in the project root for full license information.
+
+using System;
+using Avalonia.Controls;
+using Xunit;
+
+namespace Avalonia.Styling.UnitTests
+{
+    public class ResourceDictionaryTests
+    {
+        [Fact]
+        public void TryGetResource_Should_Find_Resource()
+        {
+            var target = new ResourceDictionary
+            {
+                { "foo", "bar" },
+            };
+
+            Assert.True(target.TryGetResource("foo", out var result));
+            Assert.Equal("bar", result);
+        }
+
+        [Fact]
+        public void TryGetResource_Should_Find_Resource_From_Merged_Dictionary()
+        {
+            var target = new ResourceDictionary
+            {
+                MergedDictionaries =
+                {
+                    new ResourceDictionary
+                    {
+                        { "foo", "bar" },
+                    }
+                }
+            };
+
+            Assert.True(target.TryGetResource("foo", out var result));
+            Assert.Equal("bar", result);
+        }
+
+        [Fact]
+        public void TryGetResource_Should_Find_Resource_From_Itself_Before_Merged_Dictionary()
+        {
+            var target = new ResourceDictionary
+            {
+                { "foo", "bar" },
+            };
+
+            target.MergedDictionaries.Add(new ResourceDictionary
+            {
+                { "foo", "baz" },
+            });
+
+            Assert.True(target.TryGetResource("foo", out var result));
+            Assert.Equal("bar", result);
+        }
+
+        [Fact]
+        public void TryGetResource_Should_Find_Resource_From_Later_Merged_Dictionary()
+        {
+            var target = new ResourceDictionary
+            {
+                MergedDictionaries =
+                {
+                    new ResourceDictionary
+                    {
+                        { "foo", "bar" },
+                    },
+                    new ResourceDictionary
+                    {
+                        { "foo", "baz" },
+                    }
+                }
+            };
+
+            Assert.True(target.TryGetResource("foo", out var result));
+            Assert.Equal("baz", result);
+        }
+
+        [Fact]
+        public void ResourcesChanged_Should_Be_Raised_On_Resource_Add()
+        {
+            var target = new ResourceDictionary();
+            var raised = false;
+
+            target.ResourcesChanged += (_, __) => raised = true;
+            target.Add("foo", "bar");
+
+            Assert.True(raised);
+        }
+
+        [Fact]
+        public void ResourcesChanged_Should_Be_Raised_On_MergedDictionary_Add()
+        {
+            var target = new ResourceDictionary();
+            var raised = false;
+
+            target.ResourcesChanged += (_, __) => raised = true;
+            target.MergedDictionaries.Add(new ResourceDictionary
+            {
+                { "foo", "bar" },
+            });
+
+            Assert.True(raised);
+        }
+
+        [Fact]
+        public void ResourcesChanged_Should_Not_Be_Raised_On_Empty_MergedDictionary_Add()
+        {
+            var target = new ResourceDictionary();
+            var raised = false;
+
+            target.ResourcesChanged += (_, __) => raised = true;
+            target.MergedDictionaries.Add(new ResourceDictionary());
+
+            Assert.False(raised);
+        }
+
+        [Fact]
+        public void ResourcesChanged_Should_Be_Raised_On_MergedDictionary_Remove()
+        {
+            var target = new ResourceDictionary
+            {
+                MergedDictionaries =
+                {
+                    new ResourceDictionary { { "foo", "bar" } },
+                }
+            };
+            var raised = false;
+
+            target.ResourcesChanged += (_, __) => raised = true;
+            target.MergedDictionaries.RemoveAt(0);
+
+            Assert.True(raised);
+        }
+
+        [Fact]
+        public void ResourcesChanged_Should_Not_Be_Raised_On_Empty_MergedDictionary_Remove()
+        {
+            var target = new ResourceDictionary
+            {
+                MergedDictionaries =
+                {
+                    new ResourceDictionary(),
+                }
+            };
+            var raised = false;
+
+            target.ResourcesChanged += (_, __) => raised = true;
+            target.MergedDictionaries.RemoveAt(0);
+
+            Assert.False(raised);
+        }
+
+        [Fact]
+        public void ResourcesChanged_Should_Be_Raised_On_MergedDictionary_Resource_Add()
+        {
+            var target = new ResourceDictionary
+            {
+                MergedDictionaries =
+                {
+                    new ResourceDictionary(),
+                }
+            };
+
+            var raised = false;
+
+            target.ResourcesChanged += (_, __) => raised = true;
+            ((IResourceDictionary)target.MergedDictionaries[0]).Add("foo", "bar");
+
+            Assert.True(raised);
+        }
+    }
+}

برخی فایل ها در این مقایسه diff نمایش داده نمی شوند زیرا تعداد فایل ها بسیار زیاد است