浏览代码

Merge branch 'master' into issues/855_test

Jeremy Koritzinsky 8 年之前
父节点
当前提交
f8c4a890c6
共有 82 个文件被更改,包括 802 次插入1824 次删除
  1. 1 2
      .travis.yml
  2. 2 1
      Avalonia.sln
  3. 8 8
      build/XUnit.props
  4. 0 3
      docs/.gitignore
  5. 0 21
      docs/README.md
  6. 0 3
      docs/build.cmd
  7. 0 55
      docs/docfx.json
  8. 0 43
      docs/guidelines/build.md
  9. 0 60
      docs/guidelines/contributing.md
  10. 0 4
      docs/guidelines/toc.yml
  11. 二进制
      docs/images/avalonia-video.png
  12. 二进制
      docs/images/cross-platform.png
  13. 二进制
      docs/images/hello-world-xaml.png
  14. 二进制
      docs/images/inspection-support.png
  15. 二进制
      docs/images/screen.png
  16. 0 37
      docs/index.md
  17. 0 225
      docs/intro.md
  18. 0 2
      docs/serve.cmd
  19. 0 101
      docs/spec/architecture.md
  20. 0 156
      docs/spec/binding-from-code.md
  21. 0 99
      docs/spec/binding-from-xaml.md
  22. 0 199
      docs/spec/defining-properties.md
  23. 0 54
      docs/spec/logging.md
  24. 0 111
      docs/spec/styles.md
  25. 0 14
      docs/spec/toc.yml
  26. 0 102
      docs/spec/working-with-properties.md
  27. 0 13
      docs/template/partials/footer.tmpl.partial
  28. 0 10
      docs/toc.yml
  29. 0 161
      docs/tutorial/from-wpf.md
  30. 0 10
      docs/tutorial/gettingstarted.md
  31. 二进制
      docs/tutorial/images/add-dialogs.png
  32. 0 86
      docs/tutorial/nuget.md
  33. 0 6
      docs/tutorial/toc.yml
  34. 21 41
      readme.md
  35. 3 3
      samples/ControlCatalog/SideBar.xaml
  36. 3 3
      samples/RenderTest/SideBar.xaml
  37. 1 1
      samples/VirtualizationTest/ViewModels/MainWindowViewModel.cs
  38. 4 5
      scripts/ReplaceNugetCache.ps1
  39. 3 4
      scripts/ReplaceNugetCache.sh
  40. 4 5
      scripts/ReplaceNugetCacheRelease.ps1
  41. 51 24
      src/Avalonia.Controls/Control.cs
  42. 10 10
      src/Avalonia.Controls/DropDown.cs
  43. 4 4
      src/Avalonia.Controls/Primitives/SelectingItemsControl.cs
  44. 20 2
      src/Avalonia.Input/MouseDevice.cs
  45. 10 0
      src/Avalonia.Styling/LogicalTree/LogicalExtensions.cs
  46. 6 6
      src/Avalonia.Styling/Styling/Setter.cs
  47. 3 3
      src/Avalonia.Themes.Default/TabControl.xaml
  48. 5 5
      src/Avalonia.Themes.Default/TextBox.xaml
  49. 2 2
      src/Avalonia.Visuals/Rendering/DeferredRenderer.cs
  50. 6 2
      src/Avalonia.Visuals/Rendering/IRenderer.cs
  51. 2 2
      src/Avalonia.Visuals/Rendering/ImmediateRenderer.cs
  52. 6 3
      src/Avalonia.Visuals/Rendering/SceneGraph/Scene.cs
  53. 5 2
      src/Avalonia.Visuals/Rendering/SceneGraph/SceneBuilder.cs
  54. 2 2
      src/Avalonia.Visuals/Visual.cs
  55. 1 1
      src/Avalonia.Visuals/VisualTree/VisualExtensions.cs
  56. 3 0
      src/Gtk/Avalonia.Gtk3/Gtk3Platform.cs
  57. 10 2
      src/Gtk/Avalonia.Gtk3/Interop/Native.cs
  58. 1 0
      src/Gtk/Avalonia.Gtk3/PopupImpl.cs
  59. 31 5
      src/Gtk/Avalonia.Gtk3/WindowBaseImpl.cs
  60. 0 2
      src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj
  61. 0 25
      src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/StaticExtension.cs
  62. 0 23
      src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/TypeExtension.cs
  63. 2 0
      src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaXamlType.cs
  64. 1 1
      src/Markup/Avalonia.Markup.Xaml/PortableXaml/portable.xaml.github
  65. 18 1
      src/Markup/Avalonia.Markup/Data/ExpressionObserver.cs
  66. 11 0
      src/Markup/Avalonia.Markup/Data/ITransformNode.cs
  67. 12 1
      src/Markup/Avalonia.Markup/Data/LogicalNotNode.cs
  68. 172 2
      tests/Avalonia.Controls.UnitTests/ControlTests.cs
  69. 24 2
      tests/Avalonia.Controls.UnitTests/DropDownTests.cs
  70. 4 0
      tests/Avalonia.Controls.UnitTests/Presenters/ContentPresenterTests_Standalone.cs
  71. 20 3
      tests/Avalonia.Input.UnitTests/MouseDeviceTests.cs
  72. 1 1
      tests/Avalonia.LeakTests/ControlTests.cs
  73. 19 1
      tests/Avalonia.Markup.UnitTests/Data/ExpressionObserverTests_Negation.cs
  74. 4 2
      tests/Avalonia.Markup.UnitTests/Data/Plugins/DataAnnotationsValidationPluginTests.cs
  75. 4 3
      tests/Avalonia.Markup.Xaml.UnitTests/Xaml/BasicTests.cs
  76. 59 0
      tests/Avalonia.Markup.Xaml.UnitTests/Xaml/ControlBindingTests.cs
  77. 5 4
      tests/Avalonia.Markup.Xaml.UnitTests/Xaml/DataTemplateTests.cs
  78. 37 0
      tests/Avalonia.Styling.UnitTests/SetterTests.cs
  79. 16 16
      tests/Avalonia.Visuals.UnitTests/Rendering/DeferredRendererTests_HitTesting.cs
  80. 11 11
      tests/Avalonia.Visuals.UnitTests/Rendering/ImmediateRendererTests_HitTesting.cs
  81. 51 3
      tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/SceneBuilderTests.cs
  82. 103 0
      tests/Avalonia.Visuals.UnitTests/VisualTree/VisualExtensions_GetVisualsAt.cs

+ 1 - 2
.travis.yml

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

+ 2 - 1
Avalonia.sln

@@ -1,7 +1,7 @@
 
 Microsoft Visual Studio Solution File, Format Version 12.00
 # Visual Studio 15
-VisualStudioVersion = 15.0.26730.3
+VisualStudioVersion = 15.0.26730.10
 MinimumVisualStudioVersion = 10.0.40219.1
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Base", "src\Avalonia.Base\Avalonia.Base.csproj", "{B09B78D8-9B26-48B0-9149-D64A2F120F3F}"
 EndProject
@@ -204,6 +204,7 @@ Global
 		tests\Avalonia.RenderTests\Avalonia.RenderTests.projitems*{48840edd-24bf-495d-911e-2eb12ae75d3b}*SharedItemsImports = 13
 		src\Shared\PlatformSupport\PlatformSupport.projitems*{4a1abb09-9047-4bd5-a4ad-a055e52c5ee0}*SharedItemsImports = 4
 		src\Shared\PlatformSupport\PlatformSupport.projitems*{7863ea94-f0fb-4386-bf8c-e5bfa761560a}*SharedItemsImports = 4
+		src\Shared\PlatformSupport\PlatformSupport.projitems*{7b92af71-6287-4693-9dcb-bd5b6e927e23}*SharedItemsImports = 4
 		src\Shared\RenderHelpers\RenderHelpers.projitems*{7d2d3083-71dd-4cc9-8907-39a0d86fb322}*SharedItemsImports = 4
 		src\Windows\Avalonia.Win32\Avalonia.Win32.Shared.projitems*{811a76cf-1cf6-440f-963b-bbe31bd72a82}*SharedItemsImports = 4
 		src\Windows\Avalonia.Win32\Avalonia.Win32.Shared.projitems*{9defc6b7-845b-4d8f-afc0-d32bf0032b8c}*SharedItemsImports = 13

+ 8 - 8
build/XUnit.props

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

+ 0 - 3
docs/.gitignore

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

+ 0 - 21
docs/README.md

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

+ 0 - 3
docs/build.cmd

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

+ 0 - 55
docs/docfx.json

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

+ 0 - 43
docs/guidelines/build.md

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

+ 0 - 60
docs/guidelines/contributing.md

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

+ 0 - 4
docs/guidelines/toc.yml

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

二进制
docs/images/avalonia-video.png


二进制
docs/images/cross-platform.png


二进制
docs/images/hello-world-xaml.png


二进制
docs/images/inspection-support.png


二进制
docs/images/screen.png


+ 0 - 37
docs/index.md

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

+ 0 - 225
docs/intro.md

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

+ 0 - 2
docs/serve.cmd

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

+ 0 - 101
docs/spec/architecture.md

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

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

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

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

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

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

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

+ 0 - 54
docs/spec/logging.md

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

+ 0 - 111
docs/spec/styles.md

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

+ 0 - 14
docs/spec/toc.yml

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

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

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

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

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

+ 0 - 10
docs/toc.yml

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

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

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

+ 0 - 10
docs/tutorial/gettingstarted.md

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

二进制
docs/tutorial/images/add-dialogs.png


+ 0 - 86
docs/tutorial/nuget.md

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

+ 0 - 6
docs/tutorial/toc.yml

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

+ 21 - 41
readme.md

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

+ 3 - 3
samples/ControlCatalog/SideBar.xaml

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

+ 3 - 3
samples/RenderTest/SideBar.xaml

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

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

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

+ 4 - 5
scripts/ReplaceNugetCache.ps1

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

+ 3 - 4
scripts/ReplaceNugetCache.sh

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

+ 4 - 5
scripts/ReplaceNugetCacheRelease.ps1

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

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

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

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

@@ -120,21 +120,21 @@ namespace Avalonia.Controls
         /// <inheritdoc/>
         protected override void OnPointerPressed(PointerPressedEventArgs e)
         {
-            if (!IsDropDownOpen && ((IVisual)e.Source).GetVisualRoot() is PopupRoot)
-            {
-                IsDropDownOpen = true;
-                e.Handled = true;
-            }
-
             if (!e.Handled)
             {
-                if (UpdateSelectionFromEventSource(e.Source))
+                if (((IVisual)e.Source).GetVisualRoot() is PopupRoot)
                 {
-                    _popup?.Close();
-                    e.Handled = true;
+                    if (UpdateSelectionFromEventSource(e.Source))
+                    {
+                        _popup?.Close();
+                        e.Handled = true;
+                    }
+                }
+                else
+                {
+                    IsDropDownOpen = !IsDropDownOpen;
                 }
             }
-
             base.OnPointerPressed(e);
         }
 

+ 4 - 4
src/Avalonia.Controls/Primitives/SelectingItemsControl.cs

@@ -414,16 +414,16 @@ namespace Avalonia.Controls.Primitives
         }
 
         /// <inheritdoc/>
-        protected override void OnDataContextChanging()
+        protected override void OnDataContextBeginUpdate()
         {
-            base.OnDataContextChanging();
+            base.OnDataContextBeginUpdate();
             ++_updateCount;
         }
 
         /// <inheritdoc/>
-        protected override void OnDataContextChanged()
+        protected override void OnDataContextEndUpdate()
         {
-            base.OnDataContextChanged();
+            base.OnDataContextEndUpdate();
 
             if (--_updateCount == 0)
             {

+ 20 - 2
src/Avalonia.Input/MouseDevice.cs

@@ -20,6 +20,8 @@ namespace Avalonia.Input
         private int _clickCount;
         private Rect _lastClickRect;
         private uint _lastClickTime;
+        private IInputElement _captured;
+        private IDisposable _capturedSubscription;
        
         /// <summary>
         /// Gets the control that is currently capturing by the mouse, if any.
@@ -31,8 +33,23 @@ namespace Avalonia.Input
         /// </remarks>
         public IInputElement Captured
         {
-            get;
-            protected set;
+            get => _captured;
+            protected set
+            {
+                _capturedSubscription?.Dispose();
+                _capturedSubscription = null;
+
+                if (value != null)
+                {
+                    _capturedSubscription = Observable.FromEventPattern<VisualTreeAttachmentEventArgs>(
+                        x => value.DetachedFromVisualTree += x,
+                        x => value.DetachedFromVisualTree -= x)
+                        .Take(1)
+                        .Subscribe(_ => Captured = null);
+                }
+
+                _captured = value;
+            }
         }
         
         /// <summary>
@@ -55,6 +72,7 @@ namespace Avalonia.Input
         /// </remarks>
         public virtual void Capture(IInputElement control)
         {
+            // TODO: Check visibility and enabled state before setting capture.
             Captured = control;
         }
 

+ 10 - 0
src/Avalonia.Styling/LogicalTree/LogicalExtensions.cs

@@ -50,6 +50,16 @@ namespace Avalonia.LogicalTree
             }
         }
 
+        public static IEnumerable<ILogical> GetSelfAndLogicalDescendants(this ILogical logical)
+        {
+            yield return logical;
+
+            foreach (var descendent in logical.GetLogicalDescendants())
+            {
+                yield return descendent;
+            }
+        }
+
         public static ILogical GetLogicalParent(this ILogical logical)
         {
             return logical.LogicalParent;

+ 6 - 6
src/Avalonia.Styling/Styling/Setter.cs

@@ -141,20 +141,20 @@ namespace Avalonia.Styling
             {
                 var description = style?.ToString();
 
-                if (sourceInstance.Subject != null)
+                if (sourceInstance.Mode == BindingMode.TwoWay || sourceInstance.Mode == BindingMode.OneWayToSource)
                 {
                     var activated = new ActivatedSubject(activator, sourceInstance.Subject, description);
                     cloned = new InstancedBinding(activated, sourceInstance.Mode, BindingPriority.StyleTrigger);
                 }
-                else if (sourceInstance.Observable != null)
+                else if (sourceInstance.Mode == BindingMode.OneTime)
                 {
-                    var activated = new ActivatedObservable(activator, sourceInstance.Observable, description);
-                    cloned = new InstancedBinding(activated, sourceInstance.Mode, BindingPriority.StyleTrigger);
+                    var activated = new ActivatedValue(activator, sourceInstance.Value, description);
+                    cloned = new InstancedBinding(activated, BindingMode.OneWay, BindingPriority.StyleTrigger);
                 }
                 else
                 {
-                    var activated = new ActivatedValue(activator, sourceInstance.Value, description);
-                    cloned = new InstancedBinding(activated, BindingMode.OneWay, BindingPriority.StyleTrigger);
+                    var activated = new ActivatedObservable(activator, sourceInstance.Observable ?? sourceInstance.Subject, description);
+                    cloned = new InstancedBinding(activated, sourceInstance.Mode, BindingPriority.StyleTrigger);
                 }
             }
             else

+ 3 - 3
src/Avalonia.Themes.Default/TabControl.xaml

@@ -1,4 +1,4 @@
-<Styles xmlns="https://github.com/avaloniaui">
+<Styles xmlns="https://github.com/avaloniaui"  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
   <Style Selector="TabControl">
     <Setter Property="Template">
       <ControlTemplate>
@@ -7,11 +7,11 @@
                 BorderThickness="{TemplateBinding BorderThickness}">
           <DockPanel>
             <TabStrip Name="PART_TabStrip"
-                      MemberSelector="{Static TabControl.HeaderSelector}"
+                      MemberSelector="{x:Static TabControl.HeaderSelector}"
                       Items="{TemplateBinding Items}"
                       SelectedIndex="{TemplateBinding Path=SelectedIndex, Mode=TwoWay}"/>
             <Carousel Name="PART_Content"
-                      MemberSelector="{Static TabControl.ContentSelector}"
+                      MemberSelector="{x:Static TabControl.ContentSelector}"
                       Items="{TemplateBinding Items}"
                       SelectedIndex="{TemplateBinding Path=SelectedIndex}"
                       Transition="{TemplateBinding Transition}"

+ 5 - 5
src/Avalonia.Themes.Default/TextBox.xaml

@@ -1,4 +1,4 @@
-<Styles xmlns="https://github.com/avaloniaui">
+<Styles xmlns="https://github.com/avaloniaui" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
   <Style Selector="TextBox">
     <Setter Property="Background" Value="{DynamicResource ThemeBackgroundBrush}"/>
     <Setter Property="BorderBrush" Value="{DynamicResource ThemeBorderMidBrush}"/>
@@ -18,12 +18,12 @@
                        Text="{TemplateBinding Watermark}"
                        DockPanel.Dock="Top">
               <TextBlock.IsVisible>
-                <MultiBinding Converter="{Static BoolConverters.And}">
+                <MultiBinding Converter="{x:Static BoolConverters.And}">
                   <Binding RelativeSource="{RelativeSource TemplatedParent}"
                            Path="UseFloatingWatermark"/>
                   <Binding RelativeSource="{RelativeSource TemplatedParent}"
                            Path="Text"
-                           Converter="{Static StringConverters.NotNullOrEmpty}"/>
+                           Converter="{x:Static StringConverters.NotNullOrEmpty}"/>
                 </MultiBinding>
               </TextBlock.IsVisible>
             </TextBlock>
@@ -44,12 +44,12 @@
                 <TextBlock Name="watermark"
                            Opacity="0.5"
                            Text="{TemplateBinding Watermark}"
-                           IsVisible="{TemplateBinding Path=Text, Converter={Static StringConverters.NullOrEmpty}}"/>
+                           IsVisible="{TemplateBinding Path=Text, Converter={x:Static StringConverters.NullOrEmpty}}"/>
                 <TextPresenter Name="PART_TextPresenter"
+                               Text="{TemplateBinding Text, Mode=TwoWay}"
                                CaretIndex="{TemplateBinding CaretIndex}"
                                SelectionStart="{TemplateBinding SelectionStart}"
                                SelectionEnd="{TemplateBinding SelectionEnd}"
-                               Text="{TemplateBinding Text, Mode=TwoWay}"
                                TextAlignment="{TemplateBinding TextAlignment}"
                                TextWrapping="{TemplateBinding TextWrapping}"/>
               </Panel>

+ 2 - 2
src/Avalonia.Visuals/Rendering/DeferredRenderer.cs

@@ -106,7 +106,7 @@ namespace Avalonia.Rendering
         public void Dispose() => Stop();
 
         /// <inheritdoc/>
-        public IEnumerable<IVisual> HitTest(Point p, Func<IVisual, bool> filter)
+        public IEnumerable<IVisual> HitTest(Point p, IVisual root, Func<IVisual, bool> filter)
         {
             if (_renderLoop == null && (_dirty == null || _dirty.Count > 0))
             {
@@ -114,7 +114,7 @@ namespace Avalonia.Rendering
                 UpdateScene();
             }
 
-            return _scene?.HitTest(p, filter) ?? Enumerable.Empty<IVisual>();
+            return _scene?.HitTest(p, root, filter) ?? Enumerable.Empty<IVisual>();
         }
 
         /// <inheritdoc/>

+ 6 - 2
src/Avalonia.Visuals/Rendering/IRenderer.cs

@@ -33,9 +33,13 @@ namespace Avalonia.Rendering
         /// Hit tests a location to find the visuals at the specified point.
         /// </summary>
         /// <param name="p">The point, in client coordinates.</param>
-        /// <param name="filter">An optional filter.</param>
+        /// <param name="root">The root of the subtree to search.</param>
+        /// <param name="filter">
+        /// A filter predicate. If the predicate returns false then the visual and all its
+        /// children will be excluded from the results.
+        /// </param>
         /// <returns>The visuals at the specified point, topmost first.</returns>
-        IEnumerable<IVisual> HitTest(Point p, Func<IVisual, bool> filter);
+        IEnumerable<IVisual> HitTest(Point p, IVisual root, Func<IVisual, bool> filter);
 
         /// <summary>
         /// Called when a resize notification is received by the control being rendered.

+ 2 - 2
src/Avalonia.Visuals/Rendering/ImmediateRenderer.cs

@@ -136,9 +136,9 @@ namespace Avalonia.Rendering
         }
 
         /// <inheritdoc/>
-        public IEnumerable<IVisual> HitTest(Point p, Func<IVisual, bool> filter)
+        public IEnumerable<IVisual> HitTest(Point p, IVisual root, Func<IVisual, bool> filter)
         {
-            return HitTest(_root, p, filter);
+            return HitTest(root, p, filter);
         }
 
         /// <inheritdoc/>

+ 6 - 3
src/Avalonia.Visuals/Rendering/SceneGraph/Scene.cs

@@ -3,6 +3,7 @@
 
 using System;
 using System.Collections.Generic;
+using System.Linq;
 using Avalonia.VisualTree;
 
 namespace Avalonia.Rendering.SceneGraph
@@ -113,11 +114,13 @@ namespace Avalonia.Rendering.SceneGraph
         /// Gets the visuals at a point in the scene.
         /// </summary>
         /// <param name="p">The point.</param>
+        /// <param name="root">The root of the subtree to search.</param>
         /// <param name="filter">A filter. May be null.</param>
         /// <returns>The visuals at the specified point.</returns>
-        public IEnumerable<IVisual> HitTest(Point p, Func<IVisual, bool> filter)
+        public IEnumerable<IVisual> HitTest(Point p, IVisual root, Func<IVisual, bool> filter)
         {
-            return HitTest(Root, p, null, filter);
+            var node = FindNode(root);
+            return (node != null) ? HitTest(node, p, null, filter) : Enumerable.Empty<IVisual>();
         }
 
         /// <summary>
@@ -173,7 +176,7 @@ namespace Avalonia.Rendering.SceneGraph
                         }
                     }
 
-                    if (node.HitTest(p))
+                    if (node.HitTest(p) && node.Visual.IsAttachedToVisualTree)
                     {
                         yield return node.Visual;
                     }

+ 5 - 2
src/Avalonia.Visuals/Rendering/SceneGraph/SceneBuilder.cs

@@ -168,11 +168,14 @@ namespace Avalonia.Rendering.SceneGraph
                 using (context.PushTransformContainer())
                 {
                     var startLayer = opacity < 1 || visual.OpacityMask != null;
+                    var clipBounds = bounds.TransformToAABB(contextImpl.Transform).Intersect(clip);
 
-                    forceRecurse = forceRecurse || node.Transform != contextImpl.Transform;
+                    forceRecurse = forceRecurse ||
+                        node.Transform != contextImpl.Transform ||
+                        node.ClipBounds != clipBounds;
 
                     node.Transform = contextImpl.Transform;
-                    node.ClipBounds = bounds.TransformToAABB(node.Transform).Intersect(clip);
+                    node.ClipBounds = clipBounds;
                     node.ClipToBounds = clipToBounds;
                     node.GeometryClip = visual.Clip?.PlatformImpl;
                     node.Opacity = opacity;

+ 2 - 2
src/Avalonia.Visuals/Visual.cs

@@ -329,6 +329,7 @@ namespace Avalonia
             }
 
             OnAttachedToVisualTree(e);
+            AttachedToVisualTree?.Invoke(this, e);
             InvalidateVisual();
 
             if (VisualChildren != null)
@@ -357,6 +358,7 @@ namespace Avalonia
             }
 
             OnDetachedFromVisualTree(e);
+            DetachedFromVisualTree?.Invoke(this, e);
             e.Root?.Renderer?.AddDirty(this);
 
             if (VisualChildren != null)
@@ -374,7 +376,6 @@ namespace Avalonia
         /// <param name="e">The event args.</param>
         protected virtual void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
         {
-            AttachedToVisualTree?.Invoke(this, e);
         }
 
         /// <summary>
@@ -383,7 +384,6 @@ namespace Avalonia
         /// <param name="e">The event args.</param>
         protected virtual void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
         {
-            DetachedFromVisualTree?.Invoke(this, e);
         }
 
         /// <summary>

+ 1 - 1
src/Avalonia.Visuals/VisualTree/VisualExtensions.cs

@@ -133,7 +133,7 @@ namespace Avalonia.VisualTree
 
             var root = visual.GetVisualRoot();
             p = visual.TranslatePoint(p, root);
-            return root.Renderer.HitTest(p, filter);
+            return root.Renderer.HitTest(p, visual, filter);
         }
 
         /// <summary>

+ 3 - 0
src/Gtk/Avalonia.Gtk3/Gtk3Platform.cs

@@ -35,6 +35,9 @@ namespace Avalonia.Gtk3
                     X11.XInitThreads();
                 }catch{}
                 Resolver.Resolve();
+                if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
+                    using (var backends = new Utf8Buffer("x11"))
+                        Native.GdkSetAllowedBackends?.Invoke(backends);
                 Native.GtkInit(0, IntPtr.Zero);
                 var disp = Native.GdkGetDefaultDisplay();
                 DisplayClassName =

+ 10 - 2
src/Gtk/Avalonia.Gtk3/Interop/Native.cs

@@ -48,10 +48,13 @@ namespace Avalonia.Gtk3.Interop
             public delegate void gtk_main_iteration();
 
             [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)]
-            public delegate GtkWindow gtk_window_new(GtkWindowType windowType);
-
+            public delegate GtkWindow gtk_window_new(GtkWindowType windowType);           
+            
             [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)]
             public delegate IntPtr gtk_init(int argc, IntPtr argv);
+            
+            [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gdk, optional: true)]
+            public delegate IntPtr gdk_set_allowed_backends (Utf8Buffer backends);
 
             [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)]
             public delegate void gtk_window_present(GtkWindow gtkWindow);
@@ -102,6 +105,9 @@ namespace Avalonia.Gtk3.Interop
             
             [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gdk)]
             public delegate void gdk_window_resize(IntPtr gtkWindow, int width, int height);
+            
+            [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gdk)]
+            public delegate void gdk_window_set_override_redirect(IntPtr gdkWindow, bool value);
 
             [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)]
             public delegate void gtk_widget_realize(GtkWidget gtkWidget);
@@ -390,6 +396,7 @@ namespace Avalonia.Gtk3.Interop
         public static D.gtk_window_new GtkWindowNew;
         public static D.gtk_window_set_icon GtkWindowSetIcon;
         public static D.gtk_window_set_modal GtkWindowSetModal;
+        public static D.gdk_set_allowed_backends GdkSetAllowedBackends;
         public static D.gtk_init GtkInit;
         public static D.gtk_window_present GtkWindowPresent;
         public static D.gtk_widget_hide GtkWidgetHide;
@@ -402,6 +409,7 @@ namespace Avalonia.Gtk3.Interop
         public static D.gtk_window_get_size GtkWindowGetSize;
         public static D.gtk_window_resize GtkWindowResize;
         public static D.gdk_window_resize GdkWindowResize;
+        public static D.gdk_window_set_override_redirect GdkWindowSetOverrideRedirect;
         public static D.gtk_widget_set_size_request GtkWindowSetSizeRequest;
         public static D.gtk_window_set_default_size GtkWindowSetDefaultSize;
         public static D.gtk_window_get_position GtkWindowGetPosition;

+ 1 - 0
src/Gtk/Avalonia.Gtk3/PopupImpl.cs

@@ -19,6 +19,7 @@ namespace Avalonia.Gtk3
 
         public PopupImpl() : base(CreateWindow())
         {
+            OverrideRedirect = true;
         }
     }
 }

+ 31 - 5
src/Gtk/Avalonia.Gtk3/WindowBaseImpl.cs

@@ -32,6 +32,7 @@ namespace Avalonia.Gtk3
         private IDeferredRenderOperation _nextRenderOperation;
         private readonly AutoResetEvent _canSetNextOperation = new AutoResetEvent(true);
         internal IntPtr? GdkWindowHandle;
+        private bool _overrideRedirect;
         public WindowBaseImpl(GtkWindow gtkWidget)
         {
             
@@ -69,12 +70,15 @@ namespace Avalonia.Gtk3
         private bool OnConfigured(IntPtr gtkwidget, IntPtr ev, IntPtr userdata)
         {
             int w, h;
-            Native.GtkWindowGetSize(GtkWidget, out w, out h);
-            var size = ClientSize = new Size(w, h);
-            if (_lastSize != size)
+            if (!OverrideRedirect)
             {
-                Resized?.Invoke(size);
-                _lastSize = size;
+                Native.GtkWindowGetSize(GtkWidget, out w, out h);
+                var size = ClientSize = new Size(w, h);
+                if (_lastSize != size)
+                {
+                    Resized?.Invoke(size);
+                    _lastSize = size;
+                }
             }
             var pos = Position;
             if (_lastPosition != pos)
@@ -406,6 +410,28 @@ namespace Avalonia.Gtk3
             if (GtkWidget.IsClosed)
                 return;
             Native.GtkWindowResize(GtkWidget, (int)value.Width, (int)value.Height);
+            if (OverrideRedirect)
+            {
+                var size = ClientSize = value;
+                if (_lastSize != size)
+                {
+                    Resized?.Invoke(size);
+                    _lastSize = size;
+                }
+            }
+        }
+
+        public bool OverrideRedirect
+        {
+            get => _overrideRedirect;
+            set
+            {
+                if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
+                {
+                    Native.GdkWindowSetOverrideRedirect(Native.GtkWidgetGetWindow(GtkWidget), value);
+                    _overrideRedirect = value;
+                }
+            }
         }
         
         public IScreenImpl Screen

+ 0 - 2
src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj

@@ -77,9 +77,7 @@
         <Compile Include="MarkupExtensions\StyleResourceExtension.cs" />
         <Compile Include="MarkupExtensions\BindingExtension.cs" />
         <Compile Include="MarkupExtensions\RelativeSourceExtension.cs" />
-        <Compile Include="MarkupExtensions\StaticExtension.cs" />
         <Compile Include="MarkupExtensions\TemplateBindingExtension.cs" />
-        <Compile Include="MarkupExtensions\TypeExtension.cs" />
         <Compile Include="Parsers\SelectorGrammar.cs" />
         <Compile Include="Parsers\SelectorParser.cs" />
         <Compile Include="PortableXaml\AvaloniaTypeAttributeProvider.cs" />

+ 0 - 25
src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/StaticExtension.cs

@@ -1,25 +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.Markup.Xaml.MarkupExtensions
-{
-    public class StaticExtension : Portable.Xaml.Markup.StaticExtension
-    {
-        public StaticExtension()
-        {
-        }
-
-        public StaticExtension(string member)
-            : base(member)
-        {
-        }
-
-        public override object ProvideValue(IServiceProvider serviceProvider)
-        {
-            return base.ProvideValue(serviceProvider);
-        }
-    }
-    
-}

+ 0 - 23
src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/TypeExtension.cs

@@ -1,23 +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.Markup.Xaml.MarkupExtensions
-{
-    public class TypeExtension : Portable.Xaml.Markup.TypeExtension
-    {
-        public TypeExtension()
-        {
-        }
-
-        public TypeExtension(string typeName) : base(typeName)
-        {
-        }
-
-        public override object ProvideValue(IServiceProvider serviceProvider)
-        {
-            return base.ProvideValue(serviceProvider);
-        }
-    }
-}

+ 2 - 0
src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaXamlType.cs

@@ -135,6 +135,8 @@ namespace Avalonia.Markup.Xaml.PortableXaml
             };
         }
 
+        protected override bool LookupIsUnknown() => false;
+
         protected override XamlType LookupType()
         {
             var propType = GetPropertyType();

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

@@ -1 +1 @@
-Subproject commit eebf9dbb9275ecc48c18ec24f6fbad8cb494857f
+Subproject commit f226a516fe2bc191145c1fbf732f5a087f121a33

+ 18 - 1
src/Markup/Avalonia.Markup/Data/ExpressionObserver.cs

@@ -154,7 +154,24 @@ namespace Avalonia.Markup.Data
         /// </returns>
         public bool SetValue(object value, BindingPriority priority = BindingPriority.LocalValue)
         {
-            return (Leaf as ISettableNode)?.SetTargetValue(value, priority) ?? false;
+            if (Leaf is ISettableNode settable)
+            {
+                var node = _node;
+                while (node != null)
+                {
+                    if (node is ITransformNode transform)
+                    {
+                        value = transform.Transform(value);
+                        if (value is BindingNotification)
+                        {
+                            return false;
+                        }
+                    }
+                    node = node.Next;
+                }
+                return settable.SetTargetValue(value, priority);
+            }
+            return false;
         }
 
         /// <summary>

+ 11 - 0
src/Markup/Avalonia.Markup/Data/ITransformNode.cs

@@ -0,0 +1,11 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Avalonia.Markup.Data
+{
+    interface ITransformNode
+    {
+        object Transform(object value);
+    }
+}

+ 12 - 1
src/Markup/Avalonia.Markup/Data/LogicalNotNode.cs

@@ -7,7 +7,7 @@ using Avalonia.Data;
 
 namespace Avalonia.Markup.Data
 {
-    internal class LogicalNotNode : ExpressionNode
+    internal class LogicalNotNode : ExpressionNode, ITransformNode
     {
         public override string Description => "!";
 
@@ -61,5 +61,16 @@ namespace Avalonia.Markup.Data
 
             return AvaloniaProperty.UnsetValue;
         }
+
+        public object Transform(object value)
+        {
+            var originalType = value.GetType();
+            var negated = Negate(value);
+            if (negated is BindingNotification)
+            {
+                return negated;
+            }
+            return Convert.ChangeType(negated, originalType);
+        }
     }
 }

+ 172 - 2
tests/Avalonia.Controls.UnitTests/ControlTests.cs

@@ -8,6 +8,7 @@ using Moq;
 using Avalonia.Styling;
 using Avalonia.UnitTests;
 using Xunit;
+using Avalonia.LogicalTree;
 
 namespace Avalonia.Controls.UnitTests
 {
@@ -123,7 +124,26 @@ namespace Avalonia.Controls.UnitTests
         }
 
         [Fact]
-        public void DetachedToLogicalParent_Should_Be_Called_When_Removed_From_Tree()
+        public void AttachedToLogicalParent_Should_Not_Be_Called_With_GlobalStyles_As_Root()
+        {
+            var globalStyles = Mock.Of<IGlobalStyles>();
+            var root = new TestRoot { StylingParent = globalStyles };
+            var child = new Border();
+            var raised = false;
+
+            child.AttachedToLogicalTree += (s, e) =>
+            {
+                Assert.Equal(root, e.Root);
+                raised = true;
+            };
+
+            root.Child = child;
+
+            Assert.True(raised);
+        }
+
+        [Fact]
+        public void DetachedFromLogicalParent_Should_Be_Called_When_Removed_From_Tree()
         {
             var root = new TestRoot();
             var parent = new Border();
@@ -148,6 +168,26 @@ namespace Avalonia.Controls.UnitTests
             Assert.True(grandchildRaised);
         }
 
+        [Fact]
+        public void DetachedFromLogicalParent_Should_Not_Be_Called_With_GlobalStyles_As_Root()
+        {
+            var globalStyles = Mock.Of<IGlobalStyles>();
+            var root = new TestRoot { StylingParent = globalStyles };
+            var child = new Border();
+            var raised = false;
+
+            child.DetachedFromLogicalTree += (s, e) =>
+            {
+                Assert.Equal(root, e.Root);
+                raised = true;
+            };
+
+            root.Child = child;
+            root.Child = null;
+
+            Assert.True(raised);
+        }
+
         [Fact]
         public void Adding_Tree_To_IStyleRoot_Should_Style_Controls()
         {
@@ -289,9 +329,139 @@ namespace Avalonia.Controls.UnitTests
             Assert.True(target.IsInitialized);
         }
 
-        private class TestControl : Control
+        [Fact]
+        public void DataContextChanged_Should_Be_Called()
         {
+            var root = new TestStackPanel
+            {
+                Name = "root",
+                Children =
+                {
+                    new TestControl
+                    {
+                        Name = "a1",
+                        Child = new TestControl
+                        {
+                            Name = "b1",
+                        }
+                    },
+                    new TestControl
+                    {
+                        Name = "a2",
+                        DataContext = "foo",
+                    },
+                }
+            };
+
+            var called = new List<string>();
+            void Record(object sender, EventArgs e) => called.Add(((Control)sender).Name);
+
+            root.DataContextChanged += Record;
+
+            foreach (TestControl c in root.GetLogicalDescendants())
+            {
+                c.DataContextChanged += Record;
+            }
+
+            root.DataContext = "foo";
+
+            Assert.Equal(new[] { "root", "a1", "b1", }, called);
+        }
+
+        [Fact]
+        public void DataContext_Notifications_Should_Be_Called_In_Correct_Order()
+        {
+            var root = new TestStackPanel
+            {
+                Name = "root",
+                Children =
+                {
+                    new TestControl
+                    {
+                        Name = "a1",
+                        Child = new TestControl
+                        {
+                            Name = "b1",
+                        }
+                    },
+                    new TestControl
+                    {
+                        Name = "a2",
+                        DataContext = "foo",
+                    },
+                }
+            };
+
+            var called = new List<string>();
+
+            foreach (IDataContextEvents c in root.GetSelfAndLogicalDescendants())
+            {
+                c.DataContextBeginUpdate += (s, e) => called.Add("begin " + ((Control)s).Name);
+                c.DataContextChanged += (s, e) => called.Add("changed " + ((Control)s).Name);
+                c.DataContextEndUpdate += (s, e) => called.Add("end " + ((Control)s).Name);
+            }
+
+            root.DataContext = "foo";
+
+            Assert.Equal(
+                new[] 
+                {
+                    "begin root",
+                    "begin a1",
+                    "begin b1",
+                    "changed root",
+                    "changed a1",
+                    "changed b1",
+                    "end b1",
+                    "end a1",
+                    "end root",
+                },
+                called);
+        }
+
+        private interface IDataContextEvents
+        {
+            event EventHandler DataContextBeginUpdate;
+            event EventHandler DataContextChanged;
+            event EventHandler DataContextEndUpdate;
+        }
+
+        private class TestControl : Decorator, IDataContextEvents
+        {
+            public event EventHandler DataContextBeginUpdate;
+            public event EventHandler DataContextEndUpdate;
+
             public new IAvaloniaObject InheritanceParent => base.InheritanceParent;
+
+            protected override void OnDataContextBeginUpdate()
+            {
+                DataContextBeginUpdate?.Invoke(this, EventArgs.Empty);
+                base.OnDataContextBeginUpdate();
+            }
+
+            protected override void OnDataContextEndUpdate()
+            {
+                DataContextEndUpdate?.Invoke(this, EventArgs.Empty);
+                base.OnDataContextEndUpdate();
+            }
+        }
+
+        private class TestStackPanel : StackPanel, IDataContextEvents
+        {
+            public event EventHandler DataContextBeginUpdate;
+            public event EventHandler DataContextEndUpdate;
+
+            protected override void OnDataContextBeginUpdate()
+            {
+                DataContextBeginUpdate?.Invoke(this, EventArgs.Empty);
+                base.OnDataContextBeginUpdate();
+            }
+
+            protected override void OnDataContextEndUpdate()
+            {
+                DataContextEndUpdate?.Invoke(this, EventArgs.Empty);
+                base.OnDataContextEndUpdate();
+            }
         }
     }
 }

+ 24 - 2
tests/Avalonia.Controls.UnitTests/DropDownTests.cs

@@ -1,21 +1,43 @@
 // 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.Linq;
 using Avalonia.Controls.Presenters;
 using Avalonia.Controls.Primitives;
 using Avalonia.Controls.Shapes;
 using Avalonia.Controls.Templates;
+using Avalonia.Input;
 using Avalonia.LogicalTree;
 using Avalonia.Media;
 using Avalonia.UnitTests;
-using Avalonia.VisualTree;
 using Xunit;
 
 namespace Avalonia.Controls.UnitTests
 {
     public class DropDownTests
     {
+        [Fact]
+        public void Clicking_On_Control_Toggles_IsDropDownOpen()
+        {
+            var target = new DropDown
+            {
+                Items = new[] { "Foo", "Bar" },
+            };
+
+            target.RaiseEvent(new PointerPressedEventArgs
+            {
+                RoutedEvent = InputElement.PointerPressedEvent,
+            });
+
+            Assert.True(target.IsDropDownOpen);
+
+            target.RaiseEvent(new PointerPressedEventArgs
+            {
+                RoutedEvent = InputElement.PointerPressedEvent,
+            });
+
+            Assert.False(target.IsDropDownOpen);
+        }
+
         [Fact]
         public void SelectionBoxItem_Is_Rectangle_With_VisualBrush_When_Selection_Is_Control()
         {

+ 4 - 0
tests/Avalonia.Controls.UnitTests/Presenters/ContentPresenterTests_Standalone.cs

@@ -12,6 +12,7 @@ using Moq;
 using System;
 using System.Linq;
 using Xunit;
+using Avalonia.Rendering;
 
 namespace Avalonia.Controls.UnitTests.Presenters
 {
@@ -56,6 +57,7 @@ namespace Avalonia.Controls.UnitTests.Presenters
 
             var parentMock = new Mock<Control>();
             parentMock.As<IContentPresenterHost>();
+            parentMock.As<IRenderRoot>();
             parentMock.As<IStyleRoot>();
 
             (target as ISetLogicalParent).SetParent(parentMock.Object);
@@ -100,6 +102,7 @@ namespace Avalonia.Controls.UnitTests.Presenters
             };
 
             var parentMock = new Mock<Control>();
+            parentMock.As<IRenderRoot>();
             parentMock.As<IStyleRoot>();
             parentMock.As<ILogical>().SetupGet(l => l.IsAttachedToLogicalTree).Returns(true);
 
@@ -144,6 +147,7 @@ namespace Avalonia.Controls.UnitTests.Presenters
 
             var parentMock = new Mock<Control>();
             parentMock.As<IContentPresenterHost>();
+            parentMock.As<IRenderRoot>();
             parentMock.As<IStyleRoot>();
 
             (target as ISetLogicalParent).SetParent(parentMock.Object);

+ 20 - 3
tests/Avalonia.Input.UnitTests/MouseDeviceTests.cs

@@ -1,6 +1,5 @@
 using Avalonia.Controls;
 using Avalonia.Input.Raw;
-using Avalonia.Layout;
 using Avalonia.Rendering;
 using Avalonia.UnitTests;
 using Avalonia.VisualTree;
@@ -12,6 +11,24 @@ namespace Avalonia.Input.UnitTests
 {
     public class MouseDeviceTests
     {
+        [Fact]
+        public void Capture_Is_Cleared_When_Control_Removed()
+        {
+            Canvas control;
+            var root = new TestRoot
+            {
+                Child = control = new Canvas(),
+            };
+            var target = new MouseDevice();
+
+            target.Capture(control);
+            Assert.Same(control, target.Captured);
+
+            root.Child = null;
+
+            Assert.Null(target.Captured);
+        }
+
         [Fact]
         public void MouseMove_Should_Update_PointerOver()
         {
@@ -42,7 +59,7 @@ namespace Avalonia.Input.UnitTests
                     }
                 };
 
-                renderer.Setup(x => x.HitTest(It.IsAny<Point>(), It.IsAny<Func<IVisual, bool>>()))
+                renderer.Setup(x => x.HitTest(It.IsAny<Point>(), It.IsAny<IVisual>(), It.IsAny<Func<IVisual, bool>>()))
                     .Returns(new[] { decorator });
 
                 inputManager.ProcessInput(new RawMouseEventArgs(
@@ -58,7 +75,7 @@ namespace Avalonia.Input.UnitTests
                 Assert.False(canvas.IsPointerOver);
                 Assert.True(root.IsPointerOver);
 
-                renderer.Setup(x => x.HitTest(It.IsAny<Point>(), It.IsAny<Func<IVisual, bool>>()))
+                renderer.Setup(x => x.HitTest(It.IsAny<Point>(), It.IsAny<IVisual>(), It.IsAny<Func<IVisual, bool>>()))
                     .Returns(new[] { canvas });
 
                 inputManager.ProcessInput(new RawMouseEventArgs(

+ 1 - 1
tests/Avalonia.LeakTests/ControlTests.cs

@@ -357,7 +357,7 @@ namespace Avalonia.LeakTests
             {
             }
 
-            public IEnumerable<IVisual> HitTest(Point p, Func<IVisual, bool> filter) => null;
+            public IEnumerable<IVisual> HitTest(Point p, IVisual root, Func<IVisual, bool> filter) => null;
 
             public void Paint(Rect rect)
             {

+ 19 - 1
tests/Avalonia.Markup.UnitTests/Data/ExpressionObserverTests_Negation.cs

@@ -105,14 +105,32 @@ namespace Avalonia.Markup.UnitTests.Data
         }
 
         [Fact]
-        public void SetValue_Should_Return_False()
+        public void SetValue_Should_Return_False_For_Invalid_Value()
         {
             var data = new { Foo = "foo" };
             var target = new ExpressionObserver(data, "!Foo");
+            target.Subscribe(_ => { });
 
             Assert.False(target.SetValue("bar"));
 
             GC.KeepAlive(data);
         }
+
+        [Fact]
+        public void Can_SetValue_For_Valid_Value()
+        {
+            var data = new Test { Foo = true };
+            var target = new ExpressionObserver(data, "!Foo");
+            target.Subscribe(_ => { });
+
+            Assert.True(target.SetValue(true));
+
+            Assert.False(data.Foo);
+        }
+
+        private class Test
+        {
+            public bool Foo { get; set; }
+        }
     }
 }

+ 4 - 2
tests/Avalonia.Markup.UnitTests/Data/Plugins/DataAnnotationsValidationPluginTests.cs

@@ -49,6 +49,8 @@ namespace Avalonia.Markup.UnitTests.Data.Plugins
             var accessor = inpcAccessorPlugin.Start(new WeakReference(data), nameof(data.Between5And10));
             var validator = validatorPlugin.Start(new WeakReference(data), nameof(data.Between5And10), accessor);
             var result = new List<object>();
+            
+            var errmsg = new RangeAttribute(5, 10).FormatErrorMessage(nameof(Data.Between5And10));
 
             validator.Subscribe(x => result.Add(x));
             validator.SetValue(3, BindingPriority.LocalValue);
@@ -59,12 +61,12 @@ namespace Avalonia.Markup.UnitTests.Data.Plugins
             {
                 new BindingNotification(5),
                 new BindingNotification(
-                    new ValidationException("The field Between5And10 must be between 5 and 10."),
+                    new ValidationException(errmsg),
                     BindingErrorType.DataValidationError,
                     3),
                 new BindingNotification(7),
                 new BindingNotification(
-                    new ValidationException("The field Between5And10 must be between 5 and 10."),
+                    new ValidationException(errmsg),
                     BindingErrorType.DataValidationError,
                     11),
             }, result);

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

@@ -609,8 +609,8 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml
         public void Multi_Xaml_Binding_Is_Parsed()
         {
             var xaml =
-@"<MultiBinding xmlns='https://github.com/avaloniaui'
-        Converter='{Static BoolConverters.And}'>
+@"<MultiBinding xmlns='https://github.com/avaloniaui' xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'
+    Converter ='{x:Static BoolConverters.And}'>
      <Binding Path='Foo' />
      <Binding Path='Bar' />
 </MultiBinding>";
@@ -818,10 +818,11 @@ do we need it?")]
         {
             var xaml =
 @"<ContentControl xmlns='https://github.com/avaloniaui'
+            xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'
             xmlns:local='clr-namespace:Avalonia.Markup.Xaml.UnitTests.Xaml;assembly=Avalonia.Markup.Xaml.UnitTests'>
     <ContentControl.ContentTemplate>
         <DataTemplate>
-            <TextBlock  Tag='{Static local:NonControl.StringProperty}'/>
+            <TextBlock  Tag='{x:Static local:NonControl.StringProperty}'/>
         </DataTemplate>
     </ContentControl.ContentTemplate>
 </ContentControl>";

+ 59 - 0
tests/Avalonia.Markup.Xaml.UnitTests/Xaml/ControlBindingTests.cs

@@ -1,7 +1,10 @@
 // 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.Collections.Generic;
 using Avalonia.Controls;
+using Avalonia.Controls.Primitives;
+using Avalonia.Layout;
 using Avalonia.Logging;
 using Avalonia.UnitTests;
 using Xunit;
@@ -67,5 +70,61 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml
                 Assert.True(called);
             }
         }
+
+        [Fact]
+        public void Can_Bind_Between_TabStrip_And_Carousel()
+        {
+            using (UnitTestApplication.Start(TestServices.StyledWindow))
+            {
+                var xaml = @"
+<Window xmlns='https://github.com/avaloniaui'>
+    <DockPanel>
+        <TabStrip Name='strip' DockPanel.Dock='Top' Items='{Binding Items}' SelectedIndex='0'>
+          <TabStrip.DataTemplates>
+            <DataTemplate>
+              <TextBlock Text='{Binding Header}'/>
+            </DataTemplate>
+          </TabStrip.DataTemplates>
+        </TabStrip>
+        <Carousel Name='carousel' Items='{Binding Items}' SelectedIndex='{Binding #strip.SelectedIndex}'>
+          <Carousel.DataTemplates>
+            <DataTemplate>
+              <TextBlock Text='{Binding Detail}'/>
+            </DataTemplate>
+          </Carousel.DataTemplates>
+        </Carousel>
+    </DockPanel>
+</Window>";
+                var loader = new AvaloniaXamlLoader();
+                var window = (Window)loader.Load(xaml);
+                var strip = window.FindControl<TabStrip>("strip");
+                var carousel = window.FindControl<Carousel>("carousel");
+
+                window.DataContext = new ItemsViewModel
+                {
+                    Items = new[]
+                    {
+                        new ItemViewModel { Header = "Item1", Detail = "Detail1" },
+                        new ItemViewModel { Header = "Item2", Detail = "Detail2" },
+                    }
+                };
+
+                window.Show();
+
+                Assert.Equal(0, strip.SelectedIndex);
+                Assert.Equal(0, carousel.SelectedIndex);
+            }
+        }
+
+        private class ItemsViewModel
+        {
+            public IList<ItemViewModel> Items { get; set; }
+        }
+
+        private class ItemViewModel
+        {
+            public string Header { get; set; }
+            public string Detail { get; set; }
+        }
     }
 }

+ 5 - 4
tests/Avalonia.Markup.Xaml.UnitTests/Xaml/DataTemplateTests.cs

@@ -17,9 +17,10 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml
             {
                 var xaml = @"
 <Window xmlns='https://github.com/avaloniaui'
-        xmlns:sys='clr-namespace:System;assembly=mscorlib'>
+        xmlns:sys='clr-namespace:System;assembly=mscorlib'
+        xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
     <Window.DataTemplates>
-        <DataTemplate DataType='{Type sys:String}'>
+        <DataTemplate DataType='{x:Type sys:String}'>
             <Canvas Name='foo'/>
         </DataTemplate>
     </Window.DataTemplates>
@@ -43,10 +44,10 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml
             using (UnitTestApplication.Start(TestServices.StyledWindow))
             {
                 var xaml = @"
-<Window xmlns='https://github.com/avaloniaui'
+<Window xmlns='https://github.com/avaloniaui' xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'
         xmlns:local='clr-namespace:Avalonia.Markup.Xaml.UnitTests;assembly=Avalonia.Markup.Xaml.UnitTests'>
     <Window.DataTemplates>
-        <DataTemplate DataType='{Type local:TestViewModel}'>
+        <DataTemplate DataType='{x:Type local:TestViewModel}'>
             <Canvas Name='foo' DataContext='{Binding Child}'/>
         </DataTemplate>
     </Window.DataTemplates>

+ 37 - 0
tests/Avalonia.Styling.UnitTests/SetterTests.cs

@@ -8,6 +8,9 @@ using Avalonia.Data;
 using Xunit;
 using System;
 using Avalonia.Controls.Templates;
+using Avalonia.Markup.Xaml.Data;
+using Avalonia.Markup;
+using System.Globalization;
 
 namespace Avalonia.Styling.UnitTests
 {
@@ -61,5 +64,39 @@ namespace Avalonia.Styling.UnitTests
 
             Assert.NotNull(NameScope.GetNameScope((Control)control.Child));
         }
+
+        [Fact]
+        public void Does_Not_Call_Converter_ConvertBack_On_OneWay_Binding()
+        {
+            var control = new Decorator { Name = "foo" };
+            var style = Mock.Of<IStyle>();
+            var binding = new Binding("Name", BindingMode.OneWay)
+            {
+                Converter = new TestConverter(),
+                RelativeSource = new RelativeSource(RelativeSourceMode.Self),
+            };
+            var setter = new Setter(Decorator.TagProperty, binding);
+            var activator = new BehaviorSubject<bool>(true);
+
+            setter.Apply(style, control, activator);
+            Assert.Equal("foobar", control.Tag);
+
+            // Issue #1218 caused TestConverter.ConvertBack to throw here.
+            activator.OnNext(false);
+            Assert.Null(control.Tag);
+        }
+
+        private class TestConverter : IValueConverter
+        {
+            public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+            {
+                return value.ToString() + "bar";
+            }
+
+            public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+            {
+                throw new NotImplementedException();
+            }
+        }
     }
 }

+ 16 - 16
tests/Avalonia.Visuals.UnitTests/Rendering/DeferredRendererTests_HitTesting.cs

@@ -42,7 +42,7 @@ namespace Avalonia.Visuals.UnitTests.Rendering
                 root.Measure(Size.Infinity);
                 root.Arrange(new Rect(root.DesiredSize));
 
-                var result = root.Renderer.HitTest(new Point(100, 100), null);
+                var result = root.Renderer.HitTest(new Point(100, 100), root, null);
 
                 Assert.Equal(new[] { root.Child }, result);
             }
@@ -70,7 +70,7 @@ namespace Avalonia.Visuals.UnitTests.Rendering
                 root.Measure(Size.Infinity);
                 root.Arrange(new Rect(root.DesiredSize));
 
-                var result = root.Renderer.HitTest(new Point(100, 100), null);
+                var result = root.Renderer.HitTest(new Point(100, 100), root, null);
 
                 Assert.Empty(result);
             }
@@ -107,7 +107,7 @@ namespace Avalonia.Visuals.UnitTests.Rendering
                 root.Measure(Size.Infinity);
                 root.Arrange(new Rect(root.DesiredSize));
 
-                var result = root.Renderer.HitTest(new Point(100, 100), null);
+                var result = root.Renderer.HitTest(new Point(100, 100), root, null);
 
                 Assert.Empty(result);
             }
@@ -136,7 +136,7 @@ namespace Avalonia.Visuals.UnitTests.Rendering
                 root.Measure(Size.Infinity);
                 root.Arrange(new Rect(root.DesiredSize));
 
-                var result = root.Renderer.HitTest(new Point(10, 10), null);
+                var result = root.Renderer.HitTest(new Point(10, 10), root, null);
 
                 Assert.Empty(result);
             }
@@ -180,7 +180,7 @@ namespace Avalonia.Visuals.UnitTests.Rendering
                 root.Measure(Size.Infinity);
                 root.Arrange(new Rect(container.DesiredSize));
 
-                var result = root.Renderer.HitTest(new Point(100, 100), null);
+                var result = root.Renderer.HitTest(new Point(100, 100), root, null);
 
                 Assert.Equal(new[] { container.Children[1], container.Children[0] }, result);
             }
@@ -234,7 +234,7 @@ namespace Avalonia.Visuals.UnitTests.Rendering
                 root.Measure(Size.Infinity);
                 root.Arrange(new Rect(container.DesiredSize));
 
-                var result = root.Renderer.HitTest(new Point(100, 100), null);
+                var result = root.Renderer.HitTest(new Point(100, 100), root, null);
 
                 Assert.Equal(new[] { container.Children[2], container.Children[0], container.Children[1] }, result);
             }
@@ -283,7 +283,7 @@ namespace Avalonia.Visuals.UnitTests.Rendering
                 container.Measure(Size.Infinity);
                 container.Arrange(new Rect(container.DesiredSize));
 
-                var result = root.Renderer.HitTest(new Point(120, 120), null);
+                var result = root.Renderer.HitTest(new Point(120, 120), root, null);
 
                 Assert.Equal(new IVisual[] { target, container }, result);
             }
@@ -331,7 +331,7 @@ namespace Avalonia.Visuals.UnitTests.Rendering
                 root.Measure(Size.Infinity);
                 root.Arrange(new Rect(container.DesiredSize));
 
-                var result = root.Renderer.HitTest(new Point(50, 50), null);
+                var result = root.Renderer.HitTest(new Point(50, 50), root, null);
 
                 Assert.Equal(new[] { container }, result);
             }
@@ -404,11 +404,11 @@ namespace Avalonia.Visuals.UnitTests.Rendering
                 root.Measure(Size.Infinity);
                 root.Arrange(new Rect(container.DesiredSize));
 
-                var result = root.Renderer.HitTest(new Point(50, 150), null).First();
+                var result = root.Renderer.HitTest(new Point(50, 150), root, null).First();
 
                 Assert.Equal(item1, result);
 
-                result = root.Renderer.HitTest(new Point(50, 50), null).First();
+                result = root.Renderer.HitTest(new Point(50, 50), root, null).First();
 
                 Assert.Equal(target, result);
 
@@ -419,10 +419,10 @@ namespace Avalonia.Visuals.UnitTests.Rendering
                 container.InvalidateArrange();
                 container.Arrange(new Rect(container.DesiredSize));
 
-                result = root.Renderer.HitTest(new Point(50, 150), null).First();
+                result = root.Renderer.HitTest(new Point(50, 150), root, null).First();
                 Assert.Equal(item2, result);
 
-                result = root.Renderer.HitTest(new Point(50, 50), null).First();
+                result = root.Renderer.HitTest(new Point(50, 50), root, null).First();
                 Assert.Equal(target, result);
             }
         }
@@ -452,10 +452,10 @@ namespace Avalonia.Visuals.UnitTests.Rendering
 
                 var context = new DrawingContext(Mock.Of<IDrawingContextImpl>());
 
-                var result = root.Renderer.HitTest(new Point(100, 100), null);
+                var result = root.Renderer.HitTest(new Point(100, 100), root, null);
                 Assert.Equal(new[] { path }, result);
 
-                result = root.Renderer.HitTest(new Point(10, 10), null);
+                result = root.Renderer.HitTest(new Point(10, 10), root, null);
                 Assert.Empty(result);
             }
         }
@@ -492,10 +492,10 @@ namespace Avalonia.Visuals.UnitTests.Rendering
 
                 var context = new DrawingContext(Mock.Of<IDrawingContextImpl>());
 
-                var result = root.Renderer.HitTest(new Point(200, 200), null);
+                var result = root.Renderer.HitTest(new Point(200, 200), root, null);
                 Assert.Equal(new IVisual[] { canvas, border }, result);
 
-                result = root.Renderer.HitTest(new Point(110, 110), null);
+                result = root.Renderer.HitTest(new Point(110, 110), root, null);
                 Assert.Empty(result);
             }
         }

+ 11 - 11
tests/Avalonia.Visuals.UnitTests/Rendering/ImmediateRendererTests_HitTesting.cs

@@ -40,7 +40,7 @@ namespace Avalonia.Visuals.UnitTests.Rendering
                 root.Arrange(new Rect(root.DesiredSize));
                 root.Renderer.Paint(new Rect(root.ClientSize));
 
-                var result = root.Renderer.HitTest(new Point(100, 100), null);
+                var result = root.Renderer.HitTest(new Point(100, 100), root, null);
 
                 Assert.Equal(new[] { root.Child, root }, result);
             }
@@ -78,7 +78,7 @@ namespace Avalonia.Visuals.UnitTests.Rendering
                 root.Arrange(new Rect(root.DesiredSize));
                 root.Renderer.Paint(new Rect(root.ClientSize));
 
-                var result = root.Renderer.HitTest(new Point(100, 100), null);
+                var result = root.Renderer.HitTest(new Point(100, 100), root, null);
 
                 Assert.Equal(new[] { root }, result);
             }
@@ -108,7 +108,7 @@ namespace Avalonia.Visuals.UnitTests.Rendering
                 root.Arrange(new Rect(root.DesiredSize));
                 root.Renderer.Paint(new Rect(root.ClientSize));
 
-                var result = root.Renderer.HitTest(new Point(10, 10), null);
+                var result = root.Renderer.HitTest(new Point(10, 10), root, null);
 
                 Assert.Equal(new[] { root }, result);
             }
@@ -153,7 +153,7 @@ namespace Avalonia.Visuals.UnitTests.Rendering
                 root.Arrange(new Rect(container.DesiredSize));
                 root.Renderer.Paint(new Rect(root.ClientSize));
 
-                var result = root.Renderer.HitTest(new Point(100, 100), null);
+                var result = root.Renderer.HitTest(new Point(100, 100), root, null);
 
                 Assert.Equal(new[] { container.Children[1], container.Children[0], container, root }, result);
             }
@@ -208,7 +208,7 @@ namespace Avalonia.Visuals.UnitTests.Rendering
                 root.Arrange(new Rect(container.DesiredSize));
                 root.Renderer.Paint(new Rect(root.ClientSize));
 
-                var result = root.Renderer.HitTest(new Point(100, 100), null);
+                var result = root.Renderer.HitTest(new Point(100, 100), root, null);
 
                 Assert.Equal(
                     new[] 
@@ -267,7 +267,7 @@ namespace Avalonia.Visuals.UnitTests.Rendering
                 container.Arrange(new Rect(container.DesiredSize));
                 root.Renderer.Paint(new Rect(root.ClientSize));
 
-                var result = root.Renderer.HitTest(new Point(120, 120), null);
+                var result = root.Renderer.HitTest(new Point(120, 120), root, null);
 
                 Assert.Equal(new IVisual[] { target, container }, result);
             }
@@ -316,7 +316,7 @@ namespace Avalonia.Visuals.UnitTests.Rendering
                 root.Arrange(new Rect(container.DesiredSize));
                 root.Renderer.Paint(new Rect(root.ClientSize));
 
-                var result = root.Renderer.HitTest(new Point(50, 50), null);
+                var result = root.Renderer.HitTest(new Point(50, 50), root, null);
 
                 Assert.Equal(new IVisual[] { container, root }, result);
             }
@@ -390,11 +390,11 @@ namespace Avalonia.Visuals.UnitTests.Rendering
                 root.Arrange(new Rect(container.DesiredSize));
                 root.Renderer.Paint(new Rect(root.ClientSize));
 
-                var result = root.Renderer.HitTest(new Point(50, 150), null).First();
+                var result = root.Renderer.HitTest(new Point(50, 150), root, null).First();
 
                 Assert.Equal(item1, result);
 
-                result = root.Renderer.HitTest(new Point(50, 50), null).First();
+                result = root.Renderer.HitTest(new Point(50, 50), root, null).First();
 
                 Assert.Equal(target, result);
 
@@ -406,10 +406,10 @@ namespace Avalonia.Visuals.UnitTests.Rendering
                 container.Arrange(new Rect(container.DesiredSize));
                 root.Renderer.Paint(new Rect(root.ClientSize));
 
-                result = root.Renderer.HitTest(new Point(50, 150), null).First();
+                result = root.Renderer.HitTest(new Point(50, 150), root, null).First();
                 Assert.Equal(item2, result);
 
-                result = root.Renderer.HitTest(new Point(50, 50), null).First();
+                result = root.Renderer.HitTest(new Point(50, 50), root, null).First();
                 Assert.Equal(target, result);
             }
         }

+ 51 - 3
tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/SceneBuilderTests.cs

@@ -143,13 +143,61 @@ namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph
 
                 var borderNode = scene.FindNode(border);
                 Assert.Equal(new Rect(50, 50, 50, 50), borderNode.ClipBounds);
+            }
+        }
+
+        [Fact]
+        public void Should_Update_Descendent_ClipBounds_When_Margin_Changed()
+        {
+            using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface))
+            {
+                Border border;
+                Canvas canvas;
+                var tree = new TestRoot
+                {
+                    Width = 200,
+                    Height = 300,
+                    Child = canvas = new Canvas
+                    {
+                        ClipToBounds = true,
+                        Width = 100,
+                        Height = 100,
+                        HorizontalAlignment = HorizontalAlignment.Left,
+                        VerticalAlignment = VerticalAlignment.Top,
+                        Children =
+                        {
+                            (border = new Border
+                            {
+                                Background = Brushes.AliceBlue,
+                                Width = 100,
+                                Height = 100,
+                                [Canvas.LeftProperty] = 50,
+                                [Canvas.TopProperty] = 50,
+                            })
+                        }
+                    }
+                };
+
+                tree.Measure(Size.Infinity);
+                tree.Arrange(new Rect(tree.DesiredSize));
 
-                // Initial ClipBounds are correct, make sure they're still correct after updating border.
+                var scene = new Scene(tree);
+                var sceneBuilder = new SceneBuilder();
+                sceneBuilder.UpdateAll(scene);
+
+                var borderNode = scene.FindNode(border);
+                Assert.Equal(new Rect(50, 50, 50, 50), borderNode.ClipBounds);
+
+                canvas.Width = canvas.Height = 125;
+                canvas.Measure(Size.Infinity);
+                canvas.Arrange(new Rect(tree.DesiredSize));
+
+                // Initial ClipBounds are correct, make sure they're still correct after updating canvas.
                 scene = scene.Clone();
-                Assert.True(sceneBuilder.Update(scene, border));
+                Assert.True(sceneBuilder.Update(scene, canvas));
 
                 borderNode = scene.FindNode(border);
-                Assert.Equal(new Rect(50, 50, 50, 50), borderNode.ClipBounds);
+                Assert.Equal(new Rect(50, 50, 75, 75), borderNode.ClipBounds);
             }
         }
 

+ 103 - 0
tests/Avalonia.Visuals.UnitTests/VisualTree/VisualExtensions_GetVisualsAt.cs

@@ -0,0 +1,103 @@
+using System;
+using System.Linq;
+using Avalonia.Controls;
+using Avalonia.Media;
+using Avalonia.Rendering;
+using Avalonia.UnitTests;
+using Avalonia.VisualTree;
+using Xunit;
+
+namespace Avalonia.Visuals.UnitTests.VisualTree
+{
+    public class VisualExtensions_GetVisualsAt
+    {
+        [Fact]
+        public void Should_Find_Control()
+        {
+            using (TestApplication())
+            {
+                Border target;
+                var root = new TestRoot
+                {
+                    Width = 200,
+                    Height = 200,
+                    Child = new StackPanel
+                    {
+                        Background = Brushes.White,
+                        Children =
+                        {
+                            (target = new Border
+                            {
+                                Width = 100,
+                                Height = 200,
+                                Background = Brushes.Red,
+                            }),
+                            new Border
+                            {
+                                Width = 100,
+                                Height = 200,
+                                Background = Brushes.Green,
+                            }
+                        },
+                        Orientation = Orientation.Horizontal,
+                    }
+                };
+
+                root.Renderer = new DeferredRenderer(root, null);
+                root.Measure(Size.Infinity);
+                root.Arrange(new Rect(root.DesiredSize));
+
+                var result = target.GetVisualsAt(new Point(50, 50));
+
+                Assert.Same(target, result.Single());
+            }
+        }
+
+        [Fact]
+        public void Should_Not_Find_Sibling_Control()
+        {
+            using (TestApplication())
+            {
+                Border target;
+                var root = new TestRoot
+                {
+                    Width = 200,
+                    Height = 200,
+                    Child = new StackPanel
+                    {
+                        Background = Brushes.White,
+                        Children =
+                        {
+                            (target = new Border
+                            {
+                                Width = 100,
+                                Height = 200,
+                                Background = Brushes.Red,
+                            }),
+                            new Border
+                            {
+                                Width = 100,
+                                Height = 200,
+                                Background = Brushes.Green,
+                            }
+                        },
+                        Orientation = Orientation.Horizontal,
+                    }
+                };
+
+                root.Renderer = new DeferredRenderer(root, null);
+                root.Measure(Size.Infinity);
+                root.Arrange(new Rect(root.DesiredSize));
+
+                var result = target.GetVisualsAt(new Point(150, 50));
+
+                Assert.Empty(result);
+            }
+        }
+
+        private IDisposable TestApplication()
+        {
+            return UnitTestApplication.Start(TestServices.MockPlatformRenderInterface);
+        }
+    }
+}