Browse Source

Merge branch 'master' into experimental/itemsrepeater

Steven Kirk 6 years ago
parent
commit
cbe7cb5e50
100 changed files with 970 additions and 780 deletions
  1. 8 0
      .gitignore
  2. 1 1
      azure-pipelines.yml
  3. 1 1
      build-native.sh
  4. 1 1
      build/SharedVersion.props
  5. 11 10
      native/Avalonia.Native/src/OSX/KeyTransform.mm
  6. 25 12
      nukebuild/Build.cs
  7. 0 3
      readme.md
  8. 1 1
      samples/ControlCatalog.Android/ControlCatalog.Android.csproj
  9. 1 0
      samples/ControlCatalog/MainView.xaml
  10. 0 4
      samples/ControlCatalog/Pages/AutoCompleteBoxPage.xaml
  11. 33 0
      samples/ControlCatalog/Pages/TabStripPage.xaml
  12. 45 0
      samples/ControlCatalog/Pages/TabStripPage.xaml.cs
  13. 1 2
      samples/ControlCatalog/SideBar.xaml
  14. 2 3
      samples/RenderDemo/SideBar.xaml
  15. 1 1
      src/Android/Avalonia.Android/Avalonia.Android.csproj
  16. 2 1
      src/Android/Avalonia.AndroidTestApplication/Avalonia.AndroidTestApplication.csproj
  17. 1 1
      src/Avalonia.Base/Data/Converters/DefaultValueConverter.cs
  18. 106 0
      src/Avalonia.Base/Utilities/SynchronousCompletionAsyncResult.cs
  19. 5 5
      src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs
  20. 1 2
      src/Avalonia.Controls.DataGrid/Themes/Default.xaml
  21. 1 27
      src/Avalonia.Controls/AutoCompleteBox.cs
  22. 1 2
      src/Avalonia.Controls/ComboBox.cs
  23. 32 7
      src/Avalonia.Controls/ContextMenu.cs
  24. 1 20
      src/Avalonia.Controls/Embedding/EmbeddableControlRoot.cs
  25. 0 19
      src/Avalonia.Controls/Embedding/Offscreen/OffscreenTopLevel.cs
  26. 2 10
      src/Avalonia.Controls/Generators/IItemContainerGenerator.cs
  27. 3 14
      src/Avalonia.Controls/Generators/ItemContainerGenerator.cs
  28. 4 10
      src/Avalonia.Controls/Generators/ItemContainerGenerator`1.cs
  29. 10 5
      src/Avalonia.Controls/Generators/TreeItemContainerGenerator.cs
  30. 0 15
      src/Avalonia.Controls/ItemsControl.cs
  31. 1 1
      src/Avalonia.Controls/LayoutTransformControl.cs
  32. 7 1
      src/Avalonia.Controls/ListBox.cs
  33. 25 21
      src/Avalonia.Controls/Menu.cs
  34. 2 3
      src/Avalonia.Controls/MenuBase.cs
  35. 1 1
      src/Avalonia.Controls/Presenters/CarouselPresenter.cs
  36. 0 6
      src/Avalonia.Controls/Presenters/ContentPresenter.cs
  37. 1 1
      src/Avalonia.Controls/Presenters/ItemContainerSync.cs
  38. 1 1
      src/Avalonia.Controls/Presenters/ItemVirtualizerNone.cs
  39. 3 6
      src/Avalonia.Controls/Presenters/ItemVirtualizerSimple.cs
  40. 0 15
      src/Avalonia.Controls/Presenters/ItemsPresenterBase.cs
  41. 30 5
      src/Avalonia.Controls/Primitives/Popup.cs
  42. 36 18
      src/Avalonia.Controls/Primitives/SelectingItemsControl.cs
  43. 0 9
      src/Avalonia.Controls/Primitives/TabStrip.cs
  44. 5 25
      src/Avalonia.Controls/Primitives/TemplatedControl.cs
  45. 8 2
      src/Avalonia.Controls/Templates/FuncControlTemplate.cs
  46. 3 3
      src/Avalonia.Controls/Templates/FuncControlTemplate`2.cs
  47. 4 4
      src/Avalonia.Controls/Templates/FuncDataTemplate.cs
  48. 22 4
      src/Avalonia.Controls/Templates/FuncDataTemplate`1.cs
  49. 0 35
      src/Avalonia.Controls/Templates/FuncMemberSelector.cs
  50. 14 0
      src/Avalonia.Controls/Templates/FuncTemplateNameScopeExtensions.cs
  51. 11 4
      src/Avalonia.Controls/Templates/FuncTemplate`2.cs
  52. 2 2
      src/Avalonia.Controls/Templates/FuncTreeDataTemplate.cs
  53. 13 2
      src/Avalonia.Controls/Templates/FuncTreeDataTemplate`1.cs
  54. 20 2
      src/Avalonia.Controls/Templates/IControlTemplate.cs
  55. 0 18
      src/Avalonia.Controls/Templates/IMemberSelector.cs
  56. 2 2
      src/Avalonia.Controls/Templates/ITemplate`2.cs
  57. 74 31
      src/Avalonia.Controls/TreeView.cs
  58. 1 33
      src/Avalonia.Controls/UserControl.cs
  59. 1 33
      src/Avalonia.Controls/Window.cs
  60. 19 18
      src/Avalonia.Diagnostics/DevTools.xaml
  61. 6 2
      src/Avalonia.Diagnostics/DevTools.xaml.cs
  62. 1 1
      src/Avalonia.Diagnostics/ViewLocator.cs
  63. 22 43
      src/Avalonia.Diagnostics/ViewModels/DevToolsViewModel.cs
  64. 7 6
      src/Avalonia.Diagnostics/ViewModels/EventsViewModel.cs
  65. 16 0
      src/Avalonia.Diagnostics/ViewModels/IDevToolViewModel.cs
  66. 5 2
      src/Avalonia.Diagnostics/ViewModels/TreePageViewModel.cs
  67. 5 11
      src/Avalonia.Diagnostics/Views/ControlDetailsView.cs
  68. 4 1
      src/Avalonia.Diagnostics/Views/PropertyChangedExtensions.cs
  69. 3 0
      src/Avalonia.Diagnostics/Views/TreePage.xaml.cs
  70. 14 4
      src/Avalonia.OpenGL/GlInterface.cs
  71. 2 2
      src/Avalonia.ReactiveUI/AutoDataTemplateBindingHook.cs
  72. 73 0
      src/Avalonia.Styling/Controls/ChildNameScope.cs
  73. 20 14
      src/Avalonia.Styling/Controls/INameScope.cs
  74. 38 61
      src/Avalonia.Styling/Controls/NameScope.cs
  75. 41 0
      src/Avalonia.Styling/Controls/NameScopeExtensions.cs
  76. 62 0
      src/Avalonia.Styling/Controls/NameScopeLocator.cs
  77. 3 71
      src/Avalonia.Styling/LogicalTree/ControlLocator.cs
  78. 0 21
      src/Avalonia.Styling/StyledElement.cs
  79. 0 1
      src/Avalonia.Styling/Styling/Setter.cs
  80. 1 1
      src/Avalonia.Styling/Styling/Styles.cs
  81. 0 1
      src/Avalonia.Themes.Default/AutoCompleteBox.xaml
  82. 0 1
      src/Avalonia.Themes.Default/Avalonia.Themes.Default.csproj
  83. 1 2
      src/Avalonia.Themes.Default/Carousel.xaml
  84. 0 1
      src/Avalonia.Themes.Default/ComboBox.xaml
  85. 1 1
      src/Avalonia.Themes.Default/DataValidationErrors.xaml
  86. 2 3
      src/Avalonia.Themes.Default/ItemsControl.xaml
  87. 1 2
      src/Avalonia.Themes.Default/ListBox.xaml
  88. 2 4
      src/Avalonia.Themes.Default/MenuItem.xaml
  89. 1 2
      src/Avalonia.Themes.Default/TabControl.xaml
  90. 1 2
      src/Avalonia.Themes.Default/TabStrip.xaml
  91. 1 2
      src/Avalonia.Themes.Default/TreeView.xaml
  92. 1 2
      src/Avalonia.Themes.Default/TreeViewItem.xaml
  93. 3 2
      src/Avalonia.Visuals/Rendering/RenderLayers.cs
  94. 13 0
      src/Avalonia.X11/Glx/GlxDisplay.cs
  95. 8 0
      src/Avalonia.X11/X11Platform.cs
  96. 0 2
      src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj
  97. 0 24
      src/Markup/Avalonia.Markup.Xaml/Converters/MemberSelectorTypeConverter.cs
  98. 2 1
      src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/BindingExtension.cs
  99. 2 2
      src/Markup/Avalonia.Markup.Xaml/Templates/ControlTemplate.cs
  100. 2 2
      src/Markup/Avalonia.Markup.Xaml/Templates/DataTemplate.cs

+ 8 - 0
.gitignore

@@ -198,3 +198,11 @@ info.plist
 build-intermediate
 obj-Direct2D1/
 obj-Skia/
+
+##################
+# Vim
+##################
+.vim
+coc-settings.json
+.ccls-cache
+.ccls

+ 1 - 1
azure-pipelines.yml

@@ -102,7 +102,7 @@ jobs:
 
 - job: Windows
   pool:
-    vmImage: 'vs2017-win2016'
+    vmImage: 'windows-2019'
   steps:
   - task: CmdLine@2
     displayName: 'Install Nuke'

+ 1 - 1
build-native.sh

@@ -2,4 +2,4 @@
   <ItemGroup>
     <PackageReference Include="System.ValueTuple" Version="4.5.0" />
   </ItemGroup>
-</Project>
+</Project>

+ 1 - 1
build/SharedVersion.props

@@ -2,7 +2,7 @@
   xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
   <PropertyGroup>
     <Product>Avalonia</Product>
-    <Version>0.8.1</Version>
+    <Version>0.8.999</Version>
     <Copyright>Copyright 2019 &#169; The AvaloniaUI Project</Copyright>
     <PackageLicenseUrl>https://github.com/AvaloniaUI/Avalonia/blob/master/licence.md</PackageLicenseUrl>
     <PackageProjectUrl>https://github.com/AvaloniaUI/Avalonia/</PackageProjectUrl>

+ 11 - 10
native/Avalonia.Native/src/OSX/KeyTransform.mm

@@ -26,7 +26,7 @@ const int kVK_ANSI_3 = 0x14;
 const int kVK_ANSI_4 = 0x15;
 const int kVK_ANSI_6 = 0x16;
 const int kVK_ANSI_5 = 0x17;
-//const int kVK_ANSI_Equal = 0x18;
+const int kVK_ANSI_Equal = 0x18;
 const int kVK_ANSI_9 = 0x19;
 const int kVK_ANSI_7 = 0x1A;
 const int kVK_ANSI_Minus = 0x1B;
@@ -45,11 +45,11 @@ const int kVK_ANSI_K = 0x28;
 const int kVK_ANSI_Semicolon = 0x29;
 const int kVK_ANSI_Backslash = 0x2A;
 const int kVK_ANSI_Comma = 0x2B;
-//const int kVK_ANSI_Slash = 0x2C;
+const int kVK_ANSI_Slash = 0x2C;
 const int kVK_ANSI_N = 0x2D;
 const int kVK_ANSI_M = 0x2E;
 const int kVK_ANSI_Period = 0x2F;
-//const int kVK_ANSI_Grave = 0x32;
+const int kVK_ANSI_Grave = 0x32;
 const int kVK_ANSI_KeypadDecimal = 0x41;
 const int kVK_ANSI_KeypadMultiply = 0x43;
 const int kVK_ANSI_KeypadPlus = 0x45;
@@ -57,7 +57,7 @@ const int kVK_ANSI_KeypadClear = 0x47;
 const int kVK_ANSI_KeypadDivide = 0x4B;
 const int kVK_ANSI_KeypadEnter = 0x4C;
 const int kVK_ANSI_KeypadMinus = 0x4E;
-//const int kVK_ANSI_KeypadEquals = 0x51;
+const int kVK_ANSI_KeypadEquals = 0x51;
 const int kVK_ANSI_Keypad0 = 0x52;
 const int kVK_ANSI_Keypad1 = 0x53;
 const int kVK_ANSI_Keypad2 = 0x54;
@@ -121,7 +121,7 @@ const int kVK_UpArrow = 0x7E;
 //const int kVK_JIS_Underscore = 0x5E;
 //const int kVK_JIS_KeypadComma = 0x5F;
 //const int kVK_JIS_Eisu = 0x66;
-//const int kVK_JIS_Kana = 0x68;
+const int kVK_JIS_Kana = 0x68;
 
  std::map<int, AvnKey> s_KeyMap =
  {
@@ -148,7 +148,7 @@ const int kVK_UpArrow = 0x7E;
     {kVK_ANSI_4, D4},
     {kVK_ANSI_6, D6},
     {kVK_ANSI_5, D5},
-    //{kVK_ANSI_Equal, ?},
+    {kVK_ANSI_Equal, OemPlus},
     {kVK_ANSI_9, D9},
     {kVK_ANSI_7, D7},
     {kVK_ANSI_Minus, OemMinus},
@@ -167,11 +167,11 @@ const int kVK_UpArrow = 0x7E;
     {kVK_ANSI_Semicolon, OemSemicolon},
     {kVK_ANSI_Backslash, OemBackslash},
     {kVK_ANSI_Comma, OemComma},
-    //{kVK_ANSI_Slash, ?},
+    {kVK_ANSI_Slash, Oem2},
     {kVK_ANSI_N, N},
     {kVK_ANSI_M, M},
     {kVK_ANSI_Period, OemPeriod},
-    //{kVK_ANSI_Grave, ?},
+    {kVK_ANSI_Grave, OemTilde},
     {kVK_ANSI_KeypadDecimal, Decimal},
     {kVK_ANSI_KeypadMultiply, Multiply},
     {kVK_ANSI_KeypadPlus, OemPlus},
@@ -179,7 +179,7 @@ const int kVK_UpArrow = 0x7E;
     {kVK_ANSI_KeypadDivide, Divide},
     {kVK_ANSI_KeypadEnter, AvnKeyEnter},
     {kVK_ANSI_KeypadMinus, OemMinus},
-    //{kVK_ANSI_KeypadEquals, ?},
+    {kVK_ANSI_KeypadEquals, OemPlus},
     {kVK_ANSI_Keypad0, NumPad0},
     {kVK_ANSI_Keypad1, NumPad1},
     {kVK_ANSI_Keypad2, NumPad2},
@@ -237,5 +237,6 @@ const int kVK_UpArrow = 0x7E;
     {kVK_LeftArrow, Left},
     {kVK_RightArrow, Right},
     {kVK_DownArrow, Down},
-    {kVK_UpArrow, Up}
+    {kVK_UpArrow, Up},
+    {kVK_JIS_Kana, AvnKeyKanaMode},
 };

+ 25 - 12
nukebuild/Build.cs

@@ -89,6 +89,29 @@ partial class Build : NukeBuild
 
     }
 
+    IReadOnlyCollection<Output> MsBuildCommon(
+        string projectFile,
+        Configure<MSBuildSettings> configurator = null)
+    {
+        return MSBuild(projectFile, c =>
+        {
+            // This is required for VS2019 image on Azure Pipelines
+            if (Parameters.IsRunningOnWindows && Parameters.IsRunningOnAzure)
+            {
+                var javaSdk = Environment.GetEnvironmentVariable("JAVA_HOME_8_X64");
+                if (javaSdk != null)
+                    c = c.AddProperty("JavaSdkDirectory", javaSdk);
+            }
+
+            c = c.AddProperty("PackageVersion", Parameters.Version)
+                .AddProperty("iOSRoslynPathHackRequired", "true")
+                .SetToolPath(MsBuildExe.Value)
+                .SetConfiguration(Parameters.Configuration)
+                .SetVerbosity(MSBuildVerbosity.Minimal);
+            c = configurator?.Invoke(c) ?? c;
+            return c;
+        });
+    }
     Target Clean => _ => _.Executes(() =>
     {
         DeleteDirectories(Parameters.BuildDirs);
@@ -105,13 +128,8 @@ partial class Build : NukeBuild
         .Executes(() =>
         {
             if (Parameters.IsRunningOnWindows)
-                MSBuild(Parameters.MSBuildSolution, c => c
+                MsBuildCommon(Parameters.MSBuildSolution, c => c
                     .SetArgumentConfigurator(a => a.Add("/r"))
-                    .SetConfiguration(Parameters.Configuration)
-                    .SetVerbosity(MSBuildVerbosity.Minimal)
-                    .AddProperty("PackageVersion", Parameters.Version)
-                    .AddProperty("iOSRoslynPathHackRequired", "true")
-                    .SetToolPath(MsBuildExe.Value)
                     .AddTargets("Build")
                 );
 
@@ -237,12 +255,7 @@ partial class Build : NukeBuild
         {
             if (Parameters.IsRunningOnWindows)
 
-                MSBuild(Parameters.MSBuildSolution, c => c
-                    .SetConfiguration(Parameters.Configuration)
-                    .SetVerbosity(MSBuildVerbosity.Minimal)
-                    .AddProperty("PackageVersion", Parameters.Version)
-                    .AddProperty("iOSRoslynPathHackRequired", "true")
-                    .SetToolPath(MsBuildExe.Value)
+                MsBuildCommon(Parameters.MSBuildSolution, c => c
                     .AddTargets("Pack"));
             else
                 DotNetPack(Parameters.MSBuildSolution, c =>

+ 0 - 3
readme.md

@@ -32,9 +32,6 @@ 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
-
 or use nightly build feeds as described here:
 https://github.com/AvaloniaUI/Avalonia/wiki/Using-nightly-build-feed
 

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

@@ -16,7 +16,7 @@
     <AndroidResgenFile>Resources\Resource.Designer.cs</AndroidResgenFile>
     <GenerateSerializationAssemblies>Off</GenerateSerializationAssemblies>
     <AndroidUseLatestPlatformSdk>False</AndroidUseLatestPlatformSdk>
-    <TargetFrameworkVersion>v4.4</TargetFrameworkVersion>
+    <TargetFrameworkVersion>v8.0</TargetFrameworkVersion>
     <AndroidManifest>Properties\AndroidManifest.xml</AndroidManifest>
   </PropertyGroup>
   <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">

+ 1 - 0
samples/ControlCatalog/MainView.xaml

@@ -37,6 +37,7 @@
       <TabItem Header="RadioButton"><pages:RadioButtonPage/></TabItem>
       <TabItem Header="Slider"><pages:SliderPage/></TabItem>
       <TabItem Header="TabControl"><pages:TabControlPage/></TabItem>
+      <TabItem Header="TabStrip"><pages:TabStripPage/></TabItem>
       <TabItem Header="TextBox"><pages:TextBoxPage/></TabItem>
       <TabItem Header="ToolTip"><pages:ToolTipPage/></TabItem>
       <TabItem Header="TreeView"><pages:TreeViewPage/></TabItem>

+ 0 - 4
samples/ControlCatalog/Pages/AutoCompleteBoxPage.xaml

@@ -37,10 +37,6 @@
 
       <StackPanel Orientation="Vertical">
         
-        <TextBlock Text="ValueMemeberSelector"/>
-        <AutoCompleteBox Width="200"
-                         Margin="0,0,0,8"
-                         ValueMemberSelector="Capital"/>
         <TextBlock Text="ValueMemberBinding"/>
         <AutoCompleteBox Width="200"
                          Margin="0,0,0,8"

+ 33 - 0
samples/ControlCatalog/Pages/TabStripPage.xaml

@@ -0,0 +1,33 @@
+<UserControl xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+             x:Class="ControlCatalog.Pages.TabStripPage"
+             xmlns="https://github.com/avaloniaui">
+    <StackPanel Orientation="Vertical" Spacing="4">
+        <TextBlock Classes="h1">TabStrip</TextBlock>
+        <TextBlock Classes="h2">A control which displays a selectable strip of tabs</TextBlock>
+
+        <Separator Margin="0 16"/>
+        
+        <TextBlock Classes="h1">Defined in XAML</TextBlock>
+        <TabStrip>
+            <TabStripItem>Item 1</TabStripItem>
+            <TabStripItem>Item 2</TabStripItem>
+            <TabStripItem IsEnabled="False">Disabled</TabStripItem>
+        </TabStrip>
+
+        <Separator Margin="0 16"/>
+
+        <TextBlock Classes="h1">Dynamically generated</TextBlock>
+        <TabStrip Items="{Binding}">
+            <TabStrip.Styles>
+                <Style Selector="TabStripItem">
+                    <Setter Property="IsEnabled" Value="{Binding IsEnabled}"/>
+                </Style>
+            </TabStrip.Styles>
+            <TabStrip.ItemTemplate>
+                <DataTemplate>
+                    <TextBlock Text="{Binding Header}"/>
+                </DataTemplate>
+            </TabStrip.ItemTemplate>
+        </TabStrip>
+    </StackPanel>
+</UserControl>

+ 45 - 0
samples/ControlCatalog/Pages/TabStripPage.xaml.cs

@@ -0,0 +1,45 @@
+using System;
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Markup.Xaml;
+using Avalonia.Media.Imaging;
+using Avalonia.Platform;
+
+namespace ControlCatalog.Pages
+{
+    public class TabStripPage : UserControl
+    {
+        public TabStripPage()
+        {
+            InitializeComponent();
+
+            DataContext = new[]
+            {
+                new TabStripItemViewModel
+                {
+                    Header = "Item 1",
+                },
+                new TabStripItemViewModel
+                {
+                    Header = "Item 2",
+                },
+                new TabStripItemViewModel
+                {
+                    Header = "Disabled",
+                    IsEnabled = false,
+                },
+            };
+        }
+
+        private void InitializeComponent()
+        {
+            AvaloniaXamlLoader.Load(this);
+        }
+
+        private class TabStripItemViewModel
+        {
+            public string Header { get; set; }
+            public bool IsEnabled { get; set; } = true;
+        }
+    }
+}

+ 1 - 2
samples/ControlCatalog/SideBar.xaml

@@ -29,8 +29,7 @@
                                 Name="PART_ItemsPresenter"                          
                                 Items="{TemplateBinding Items}"
                                 ItemsPanel="{TemplateBinding ItemsPanel}"
-                                ItemTemplate="{TemplateBinding ItemTemplate}"
-                                MemberSelector="{TemplateBinding MemberSelector}">
+                                ItemTemplate="{TemplateBinding ItemTemplate}">
                             </ItemsPresenter>
                         </ScrollViewer>
                         <ContentPresenter

+ 2 - 3
samples/RenderDemo/SideBar.xaml

@@ -20,8 +20,7 @@
                                 Name="PART_ItemsPresenter"                          
                                 Items="{TemplateBinding Items}"
                                 ItemsPanel="{TemplateBinding ItemsPanel}"
-                                ItemTemplate="{TemplateBinding ItemTemplate}"
-                                MemberSelector="{TemplateBinding MemberSelector}">
+                                ItemTemplate="{TemplateBinding ItemTemplate}">
                             </ItemsPresenter>
                         </ScrollViewer>
                         <ContentPresenter
@@ -63,4 +62,4 @@
     <Style Selector="TabControl.sidebar > TabItem:selected /template/ ContentPresenter#PART_ContentPresenter">
         <Setter Property="Background" Value="{DynamicResource ThemeAccentBrush2}"/>
     </Style>
-</Styles>
+</Styles>

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

@@ -1,6 +1,6 @@
 <Project Sdk="MSBuild.Sdk.Extras">
   <PropertyGroup>
-    <TargetFramework>monoandroid44</TargetFramework>
+    <TargetFramework>monoandroid80</TargetFramework>
     <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
   </PropertyGroup>
   <ItemGroup>

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

@@ -16,7 +16,7 @@
     <AndroidResgenFile>Resources\Resource.Designer.cs</AndroidResgenFile>
     <GenerateSerializationAssemblies>Off</GenerateSerializationAssemblies>
     <AndroidUseLatestPlatformSdk>False</AndroidUseLatestPlatformSdk>
-    <TargetFrameworkVersion>v4.4</TargetFrameworkVersion>
+    <TargetFrameworkVersion>v8.0</TargetFrameworkVersion>
     <AndroidManifest>Properties\AndroidManifest.xml</AndroidManifest>
   </PropertyGroup>
   <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
@@ -150,6 +150,7 @@
   </ItemGroup>
   <Import Project="$(MSBuildExtensionsPath)\Xamarin\Android\Xamarin.Android.CSharp.targets" />
   <Import Project="..\..\..\build\Serilog.props" />
+  <Import Project="..\..\..\build\Base.props" />
   <Import Project="..\..\..\build\Rx.props" />
   <Import Project="..\..\..\build\System.Memory.props" />
   <Import Project="..\..\..\build\AndroidWorkarounds.props" />

+ 1 - 1
src/Avalonia.Base/Data/Converters/DefaultValueConverter.cs

@@ -31,7 +31,7 @@ namespace Avalonia.Data.Converters
         {
             if (value == null)
             {
-                return AvaloniaProperty.UnsetValue;
+                return targetType.IsValueType ? AvaloniaProperty.UnsetValue : null;
             }
 
             if (typeof(ICommand).IsAssignableFrom(targetType) && value is Delegate d && d.Method.GetParameters().Length <= 1)

+ 106 - 0
src/Avalonia.Base/Utilities/SynchronousCompletionAsyncResult.cs

@@ -0,0 +1,106 @@
+using System;
+using System.Collections.Generic;
+using System.Runtime.CompilerServices;
+
+namespace Avalonia.Utilities
+{
+    /// <summary>
+    /// A task-like operation that is guaranteed to finish continuations synchronously,
+    /// can be used for parametrized one-shot events
+    /// </summary>
+    public struct SynchronousCompletionAsyncResult<T> : INotifyCompletion
+    {
+        private readonly SynchronousCompletionAsyncResultSource<T> _source;
+        private readonly T _result;
+        private readonly bool _isValid;
+        internal SynchronousCompletionAsyncResult(SynchronousCompletionAsyncResultSource<T> source)
+        {
+            _source = source;
+            _result = default;
+            _isValid = true;
+        }
+
+        public SynchronousCompletionAsyncResult(T result)
+        {
+            _result = result;
+            _source = null;
+            _isValid = true;
+        }
+
+        static void ThrowNotInitialized() =>
+            throw new InvalidOperationException("This SynchronousCompletionAsyncResult was not initialized");
+
+        public bool IsCompleted
+        {
+            get
+            {
+                if (!_isValid)
+                    ThrowNotInitialized();
+                return _source == null || _source.IsCompleted;
+            }
+        }
+
+        public T GetResult()
+        {
+            if (!_isValid)
+                ThrowNotInitialized();
+            return _source == null ? _result : _source.Result;
+        }
+
+
+        public void OnCompleted(Action continuation)
+        {
+            if (!_isValid)
+                ThrowNotInitialized();
+            if (_source == null)
+                continuation();
+            else
+                _source.OnCompleted(continuation);
+        }
+
+        public SynchronousCompletionAsyncResult<T> GetAwaiter() => this;
+    }
+
+    /// <summary>
+    /// Source for incomplete SynchronousCompletionAsyncResult
+    /// </summary>
+    /// <typeparam name="T"></typeparam>
+    public class SynchronousCompletionAsyncResultSource<T>
+    {
+        private T _result;
+        internal bool IsCompleted { get; private set; }
+        public SynchronousCompletionAsyncResult<T> AsyncResult => new SynchronousCompletionAsyncResult<T>(this);
+        
+        internal T Result => IsCompleted ?
+            _result :
+            throw new InvalidOperationException("Asynchronous operation is not yet completed");
+        
+        private List<Action> _continuations;
+
+        internal void OnCompleted(Action continuation)
+        {
+            if(_continuations==null)
+                _continuations = new List<Action>();
+            _continuations.Add(continuation);
+        }
+
+        public void SetResult(T result)
+        {
+            if (IsCompleted)
+                throw new InvalidOperationException("Asynchronous operation is already completed");
+            _result = result;
+            IsCompleted = true;
+            if(_continuations!=null)
+                foreach (var c in _continuations)
+                    c();
+            _continuations = null;
+        }
+
+        public void TrySetResult(T result)
+        {
+            if(IsCompleted)
+                return;
+            SetResult(result);
+        }
+    }
+}

+ 5 - 5
src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs

@@ -75,9 +75,9 @@ namespace Avalonia.Build.Tasks
                     .First(c => c.Parameters.Count == 1));
 
             var runtimeHelpers = typeSystem.GetType("Avalonia.Markup.Xaml.XamlIl.Runtime.XamlIlRuntimeHelpers");
-            var rootServiceProviderField = asm.MainModule.ImportReference(
-                typeSystem.GetTypeReference(runtimeHelpers).Resolve().Fields
-                    .First(x => x.Name == "RootServiceProviderV1"));
+            var createRootServiceProviderMethod = asm.MainModule.ImportReference(
+                typeSystem.GetTypeReference(runtimeHelpers).Resolve().Methods
+                    .First(x => x.Name == "CreateRootServiceProviderV2"));
             
             var loaderDispatcherDef = new TypeDefinition("CompiledAvaloniaXaml", "!XamlLoader",
                 TypeAttributes.Class, asm.MainModule.TypeSystem.Object);
@@ -211,7 +211,7 @@ namespace Avalonia.Build.Tasks
                             trampoline.Parameters.Add(new ParameterDefinition(classTypeDefinition));
                             classTypeDefinition.Methods.Add(trampoline);
 
-                            var regularStart = Instruction.Create(OpCodes.Ldsfld, rootServiceProviderField);
+                            var regularStart = Instruction.Create(OpCodes.Call, createRootServiceProviderMethod);
                             
                             trampoline.Body.Instructions.Add(Instruction.Create(OpCodes.Ldsfld, designLoaderField));
                             trampoline.Body.Instructions.Add(Instruction.Create(OpCodes.Brfalse, regularStart));
@@ -307,7 +307,7 @@ namespace Avalonia.Build.Tasks
                                     i.Add(Instruction.Create(OpCodes.Newobj, parameterlessConstructor));
                                 else
                                 {
-                                    i.Add(Instruction.Create(OpCodes.Ldsfld, rootServiceProviderField));
+                                    i.Add(Instruction.Create(OpCodes.Call, createRootServiceProviderMethod));
                                     i.Add(Instruction.Create(OpCodes.Call, compiledBuildMethod));
                                 }
 

+ 1 - 2
src/Avalonia.Controls.DataGrid/Themes/Default.xaml

@@ -195,7 +195,6 @@
     <Setter Property="GridLinesVisibility" Value="Vertical" />
     <Setter Property="HorizontalGridLinesBrush" Value="{DynamicResource ThemeBorderLightBrush}" />
     <Setter Property="VerticalGridLinesBrush" Value="{DynamicResource ThemeBorderLightBrush}" />
-    <Setter Property="HeadersVisibility" Value="Column" />
     <Setter Property="BorderBrush" Value="{DynamicResource ThemeBorderDarkBrush}"/>
     <Setter Property="BorderThickness" Value="{DynamicResource ThemeBorderThickness}" />
     <Setter Property="DropLocationIndicatorTemplate">
@@ -230,4 +229,4 @@
       </ControlTemplate>
     </Setter>
   </Style>
-</Styles>
+</Styles>

+ 1 - 27
src/Avalonia.Controls/AutoCompleteBox.cs

@@ -345,7 +345,6 @@ namespace Avalonia.Controls
         /// </summary>
         private IDisposable _collectionChangeSubscription;
 
-        private IMemberSelector _valueMemberSelector;
         private Func<string, CancellationToken, Task<IEnumerable<object>>> _asyncPopulator;
         private CancellationTokenSource _populationCancellationTokenSource;
 
@@ -541,12 +540,6 @@ namespace Avalonia.Controls
                 o => o.Items,
                 (o, v) => o.Items = v);
 
-        public static readonly DirectProperty<AutoCompleteBox, IMemberSelector> ValueMemberSelectorProperty =
-            AvaloniaProperty.RegisterDirect<AutoCompleteBox, IMemberSelector>(
-                nameof(ValueMemberSelector),
-                o => o.ValueMemberSelector,
-                (o, v) => o.ValueMemberSelector = v);
-
         public static readonly DirectProperty<AutoCompleteBox, Func<string, CancellationToken, Task<IEnumerable<object>>>> AsyncPopulatorProperty =
             AvaloniaProperty.RegisterDirect<AutoCompleteBox, Func<string, CancellationToken, Task<IEnumerable<object>>>>(
                 nameof(AsyncPopulator),
@@ -795,7 +788,7 @@ namespace Avalonia.Controls
                 var template =
                     new FuncDataTemplate(
                         typeof(object),
-                        o =>
+                        (o, _) =>
                         {
                             var control = new ContentControl();
                             control.Bind(ContentControl.ContentProperty, value);
@@ -958,20 +951,6 @@ namespace Avalonia.Controls
             }
         }
 
-        /// <summary>
-        /// Gets or sets the MemberSelector that is used to get values for
-        /// display in the text portion of the
-        /// <see cref="T:Avalonia.Controls.AutoCompleteBox" /> control.
-        /// </summary>
-        /// <value>The MemberSelector that is used to get values for display in
-        /// the text portion of the
-        /// <see cref="T:Avalonia.Controls.AutoCompleteBox" /> control.</value>
-        public IMemberSelector ValueMemberSelector
-        {
-            get { return _valueMemberSelector; }
-            set { SetAndRaise(ValueMemberSelectorProperty, ref _valueMemberSelector, value); }
-        }
-
         /// <summary>
         /// Gets or sets the selected item in the drop-down.
         /// </summary>
@@ -1841,11 +1820,6 @@ namespace Avalonia.Controls
                 return _valueBindingEvaluator.GetDynamicValue(value) ?? String.Empty;
             }
 
-            if (_valueMemberSelector != null)
-            {
-                value = _valueMemberSelector.Select(value);
-            }
-
             return value == null ? String.Empty : value.ToString();
         }
 

+ 1 - 2
src/Avalonia.Controls/ComboBox.cs

@@ -333,8 +333,7 @@ namespace Avalonia.Controls
             }
             else
             {
-                var selector = MemberSelector;
-                SelectionBoxItem = selector != null ? selector.Select(item) : item;
+                SelectionBoxItem = item;
             }
         }
 

+ 32 - 7
src/Avalonia.Controls/ContextMenu.cs

@@ -1,12 +1,12 @@
 using System;
 using System.ComponentModel;
 using System.Linq;
-using System.Reactive.Linq;
 using Avalonia.Controls.Generators;
 using Avalonia.Controls.Platform;
 using Avalonia.Controls.Primitives;
 using Avalonia.Controls.Templates;
 using Avalonia.Input;
+using Avalonia.Interactivity;
 using Avalonia.Layout;
 using Avalonia.LogicalTree;
 
@@ -91,9 +91,14 @@ namespace Avalonia.Controls
         /// <param name="control">The control.</param>
         public void Open(Control control)
         {
+            if (IsOpen)
+            {
+                return;
+            }
+
             if (_popup == null)
             {
-                _popup = new Popup()
+                _popup = new Popup
                 {
                     PlacementMode = PlacementMode.Pointer,
                     PlacementTarget = control,
@@ -108,7 +113,14 @@ namespace Avalonia.Controls
             ((ISetLogicalParent)_popup).SetParent(control);
             _popup.Child = this;
             _popup.IsOpen = true;
+
             IsOpen = true;
+
+            RaiseEvent(new RoutedEventArgs
+            {
+                RoutedEvent = MenuOpenedEvent,
+                Source = this,
+            });
         }
 
         /// <summary>
@@ -116,13 +128,15 @@ namespace Avalonia.Controls
         /// </summary>
         public override void Close()
         {
+            if (!IsOpen)
+            {
+                return;
+            }
+
             if (_popup != null && _popup.IsVisible)
             {
                 _popup.IsOpen = false;
             }
-
-            SelectedIndex = -1;
-            IsOpen = false;
         }
 
         protected override IItemContainerGenerator CreateItemContainerGenerator()
@@ -130,6 +144,18 @@ namespace Avalonia.Controls
             return new MenuItemContainerGenerator(this);
         }
 
+        private void CloseCore()
+        {
+            SelectedIndex = -1;
+            IsOpen = false;
+
+            RaiseEvent(new RoutedEventArgs
+            {
+                RoutedEvent = MenuClosedEvent,
+                Source = this,
+            });
+        }
+
         private void PopupOpened(object sender, EventArgs e)
         {
             Focus();
@@ -146,8 +172,7 @@ namespace Avalonia.Controls
                     i.IsSubMenuOpen = false;
                 }
 
-                contextMenu.IsOpen = false;
-                contextMenu.SelectedIndex = -1;
+                contextMenu.CloseCore();
             }
         }
 

+ 1 - 20
src/Avalonia.Controls/Embedding/EmbeddableControlRoot.cs

@@ -8,7 +8,7 @@ using JetBrains.Annotations;
 
 namespace Avalonia.Controls.Embedding
 {
-    public class EmbeddableControlRoot : TopLevel, IStyleable, IFocusScope, INameScope, IDisposable
+    public class EmbeddableControlRoot : TopLevel, IStyleable, IFocusScope, IDisposable
     {
         public EmbeddableControlRoot(IEmbeddableWindowImpl impl) : base(impl)
         {
@@ -51,25 +51,6 @@ namespace Avalonia.Controls.Embedding
             return rv;
         }
 
-        private readonly NameScope _nameScope = new NameScope();
-        public event EventHandler<NameScopeEventArgs> Registered
-        {
-            add { _nameScope.Registered += value; }
-            remove { _nameScope.Registered -= value; }
-        }
-
-        public event EventHandler<NameScopeEventArgs> Unregistered
-        {
-            add { _nameScope.Unregistered += value; }
-            remove { _nameScope.Unregistered -= value; }
-        }
-
-        public void Register(string name, object element) => _nameScope.Register(name, element);
-
-        public object Find(string name) => _nameScope.Find(name);
-
-        public void Unregister(string name) => _nameScope.Unregister(name);
-
         Type IStyleable.StyleKey => typeof(EmbeddableControlRoot);
         public void Dispose() => PlatformImpl?.Dispose();
     }

+ 0 - 19
src/Avalonia.Controls/Embedding/Offscreen/OffscreenTopLevel.cs

@@ -30,25 +30,6 @@ namespace Avalonia.Controls.Embedding.Offscreen
                 init.EndInit();
             }
         }
-        
-        private readonly NameScope _nameScope = new NameScope();
-        public event EventHandler<NameScopeEventArgs> Registered
-        {
-            add { _nameScope.Registered += value; }
-            remove { _nameScope.Registered -= value; }
-        }
-
-        public event EventHandler<NameScopeEventArgs> Unregistered
-        {
-            add { _nameScope.Unregistered += value; }
-            remove { _nameScope.Unregistered -= value; }
-        }
-
-        public void Register(string name, object element) => _nameScope.Register(name, element);
-
-        public object Find(string name) => _nameScope.Find(name);
-
-        public void Unregister(string name) => _nameScope.Unregister(name);
 
         Type IStyleable.StyleKey => typeof(EmbeddableControlRoot);
         public void Dispose()

+ 2 - 10
src/Avalonia.Controls/Generators/IItemContainerGenerator.cs

@@ -49,12 +49,8 @@ namespace Avalonia.Controls.Generators
         /// The index of the item of data in the control's items.
         /// </param>
         /// <param name="item">The item.</param>
-        /// <param name="selector">An optional member selector.</param>
         /// <returns>The created controls.</returns>
-        ItemContainerInfo Materialize(
-            int index,
-            object item,
-            IMemberSelector selector);
+        ItemContainerInfo Materialize(int index, object item);
 
         /// <summary>
         /// Removes a set of created containers.
@@ -84,11 +80,7 @@ namespace Avalonia.Controls.Generators
         /// <returns>The removed containers.</returns>
         IEnumerable<ItemContainerInfo> RemoveRange(int startingIndex, int count);
 
-        bool TryRecycle(
-            int oldIndex,
-            int newIndex,
-            object item,
-            IMemberSelector selector);
+        bool TryRecycle(int oldIndex, int newIndex, object item);
 
         /// <summary>
         /// Clears all created containers and returns the removed controls.

+ 3 - 14
src/Avalonia.Controls/Generators/ItemContainerGenerator.cs

@@ -54,13 +54,9 @@ namespace Avalonia.Controls.Generators
         public virtual Type ContainerType => null;
 
         /// <inheritdoc/>
-        public ItemContainerInfo Materialize(
-            int index,
-            object item,
-            IMemberSelector selector)
+        public ItemContainerInfo Materialize(int index, object item)
         {
-            var i = selector != null ? selector.Select(item) : item;
-            var container = new ItemContainerInfo(CreateContainer(i), item, index);
+            var container = new ItemContainerInfo(CreateContainer(item), item, index);
 
             _containers.Add(container.Index, container);
             Materialized?.Invoke(this, new ItemContainerEventArgs(container));
@@ -138,14 +134,7 @@ namespace Avalonia.Controls.Generators
         }
 
         /// <inheritdoc/>
-        public virtual bool TryRecycle(
-            int oldIndex,
-            int newIndex,
-            object item,
-            IMemberSelector selector)
-        {
-            return false;
-        }
+        public virtual bool TryRecycle(int oldIndex, int newIndex, object item) => false;
 
         /// <inheritdoc/>
         public virtual IEnumerable<ItemContainerInfo> Clear()

+ 4 - 10
src/Avalonia.Controls/Generators/ItemContainerGenerator`1.cs

@@ -79,11 +79,7 @@ namespace Avalonia.Controls.Generators
         }
 
         /// <inheritdoc/>
-        public override bool TryRecycle(
-            int oldIndex,
-            int newIndex,
-            object item,
-            IMemberSelector selector)
+        public override bool TryRecycle(int oldIndex, int newIndex, object item)
         {
             var container = ContainerFromIndex(oldIndex);
 
@@ -92,16 +88,14 @@ namespace Avalonia.Controls.Generators
                 throw new IndexOutOfRangeException("Could not recycle container: not materialized.");
             }
 
-            var i = selector != null ? selector.Select(item) : item;
-
-            container.SetValue(ContentProperty, i);
+            container.SetValue(ContentProperty, item);
 
             if (!(item is IControl))
             {
-                container.DataContext = i;
+                container.DataContext = item;
             }
 
-            var info = MoveContainer(oldIndex, newIndex, i);
+            var info = MoveContainer(oldIndex, newIndex, item);
             RaiseRecycled(new ItemContainerEventArgs(info));
 
             return true;

+ 10 - 5
src/Avalonia.Controls/Generators/TreeItemContainerGenerator.cs

@@ -92,7 +92,6 @@ namespace Avalonia.Controls.Generators
                     result.DataContext = item;
                 }
 
-                NameScope.SetNameScope((Control)(object)result, new NameScope());
                 Index.Add(item, result);
 
                 return result;
@@ -118,16 +117,22 @@ namespace Avalonia.Controls.Generators
             return base.RemoveRange(startingIndex, count);
         }
 
-        public override bool TryRecycle(int oldIndex, int newIndex, object item, IMemberSelector selector)
+        public override bool TryRecycle(int oldIndex, int newIndex, object item) => false;
+
+        class WrapperTreeDataTemplate : ITreeDataTemplate
         {
-            return false;
+            private readonly IDataTemplate _inner;
+            public WrapperTreeDataTemplate(IDataTemplate inner) => _inner = inner;
+            public IControl Build(object param) => _inner.Build(param);
+            public bool SupportsRecycling => _inner.SupportsRecycling;
+            public bool Match(object data) => _inner.Match(data);
+            public InstancedBinding ItemsSelector(object item) => null;
         }
 
         private ITreeDataTemplate GetTreeDataTemplate(object item, IDataTemplate primary)
         {
             var template = Owner.FindDataTemplate(item, primary) ?? FuncDataTemplate.Default;
-            var treeTemplate = template as ITreeDataTemplate ??
-                new FuncTreeDataTemplate(typeof(object), template.Build, x => null);
+            var treeTemplate = template as ITreeDataTemplate ?? new WrapperTreeDataTemplate(template);
             return treeTemplate;
         }
     }

+ 0 - 15
src/Avalonia.Controls/ItemsControl.cs

@@ -54,12 +54,6 @@ namespace Avalonia.Controls
         public static readonly StyledProperty<IDataTemplate> ItemTemplateProperty =
             AvaloniaProperty.Register<ItemsControl, IDataTemplate>(nameof(ItemTemplate));
 
-        /// <summary>
-        /// Defines the <see cref="MemberSelector"/> property.
-        /// </summary>
-        public static readonly StyledProperty<IMemberSelector> MemberSelectorProperty =
-            AvaloniaProperty.Register<ItemsControl, IMemberSelector>(nameof(MemberSelector));
-
         private IEnumerable _items = new AvaloniaList<object>();
         private int _itemCount;
         private IItemContainerGenerator _itemContainerGenerator;
@@ -144,15 +138,6 @@ namespace Avalonia.Controls
             set { SetValue(ItemTemplateProperty, value); }
         }
 
-        /// <summary>
-        /// Selects a member from <see cref="Items"/> to use as the list item.
-        /// </summary>
-        public IMemberSelector MemberSelector
-        {
-            get { return GetValue(MemberSelectorProperty); }
-            set { SetValue(MemberSelectorProperty, value); }
-        }
-
         /// <summary>
         /// Gets the items presenter control.
         /// </summary>

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

@@ -45,7 +45,7 @@ namespace Avalonia.Controls
         }
 
         /// <summary>
-        /// Utilize the <see cref="RenderTransformProperty"/> for layout transforms.
+        /// Utilize the <see cref="Visual.RenderTransformProperty"/> for layout transforms.
         /// </summary>
         public bool UseRenderTransform
         {

+ 7 - 1
src/Avalonia.Controls/ListBox.cs

@@ -68,7 +68,13 @@ namespace Avalonia.Controls
         /// <inheritdoc/>
         public new IList SelectedItems => base.SelectedItems;
 
-        /// <inheritdoc/>
+        /// <summary>
+        /// Gets or sets the selection mode.
+        /// </summary>
+        /// <remarks>
+        /// Note that the selection mode only applies to selections made via user interaction.
+        /// Multiple selections can be made programatically regardless of the value of this property.
+        /// </remarks>
         public new SelectionMode SelectionMode
         {
             get { return base.SelectionMode; }

+ 25 - 21
src/Avalonia.Controls/Menu.cs

@@ -41,37 +41,41 @@ namespace Avalonia.Controls
         /// <inheritdoc/>
         public override void Close()
         {
-            if (IsOpen)
+            if (!IsOpen)
             {
-                foreach (var i in ((IMenu)this).SubItems)
-                {
-                    i.Close();
-                }
-
-                IsOpen = false;
-                SelectedIndex = -1;
+                return;
+            }
 
-                RaiseEvent(new RoutedEventArgs
-                {
-                    RoutedEvent = MenuClosedEvent,
-                    Source = this,
-                });
+            foreach (var i in ((IMenu)this).SubItems)
+            {
+                i.Close();
             }
+
+            IsOpen = false;
+            SelectedIndex = -1;
+
+            RaiseEvent(new RoutedEventArgs
+            {
+                RoutedEvent = MenuClosedEvent,
+                Source = this,
+            });
         }
 
         /// <inheritdoc/>
         public override void Open()
         {
-            if (!IsOpen)
+            if (IsOpen)
             {
-                IsOpen = true;
-
-                RaiseEvent(new RoutedEventArgs
-                {
-                    RoutedEvent = MenuOpenedEvent,
-                    Source = this,
-                });
+                return;
             }
+
+            IsOpen = true;
+
+            RaiseEvent(new RoutedEventArgs
+            {
+                RoutedEvent = MenuOpenedEvent,
+                Source = this,
+            });
         }
 
         /// <inheritdoc/>

+ 2 - 3
src/Avalonia.Controls/MenuBase.cs

@@ -7,7 +7,6 @@ using System.Linq;
 using Avalonia.Controls.Generators;
 using Avalonia.Controls.Platform;
 using Avalonia.Controls.Primitives;
-using Avalonia.Controls.Templates;
 using Avalonia.Input;
 using Avalonia.Interactivity;
 using Avalonia.LogicalTree;
@@ -31,13 +30,13 @@ namespace Avalonia.Controls
         /// Defines the <see cref="MenuOpened"/> event.
         /// </summary>
         public static readonly RoutedEvent<RoutedEventArgs> MenuOpenedEvent =
-            RoutedEvent.Register<MenuItem, RoutedEventArgs>(nameof(MenuOpened), RoutingStrategies.Bubble);
+            RoutedEvent.Register<MenuBase, RoutedEventArgs>(nameof(MenuOpened), RoutingStrategies.Bubble);
 
         /// <summary>
         /// Defines the <see cref="MenuClosed"/> event.
         /// </summary>
         public static readonly RoutedEvent<RoutedEventArgs> MenuClosedEvent =
-            RoutedEvent.Register<MenuItem, RoutedEventArgs>(nameof(MenuClosed), RoutingStrategies.Bubble);
+            RoutedEvent.Register<MenuBase, RoutedEventArgs>(nameof(MenuClosed), RoutingStrategies.Bubble);
 
         private bool _isOpen;
 

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

@@ -213,7 +213,7 @@ namespace Avalonia.Controls.Presenters
             if (container == null && IsVirtualized)
             {
                 var item = Items.Cast<object>().ElementAt(index);
-                var materialized = ItemContainerGenerator.Materialize(index, item, MemberSelector);
+                var materialized = ItemContainerGenerator.Materialize(index, item);
                 Panel.Children.Add(materialized.ContainerControl);
                 container = materialized.ContainerControl;
             }

+ 0 - 6
src/Avalonia.Controls/Presenters/ContentPresenter.cs

@@ -325,12 +325,6 @@ namespace Avalonia.Controls.Presenters
                 {
                     _dataTemplate = dataTemplate;
                     newChild = _dataTemplate.Build(content);
-
-                    // Give the new control its own name scope.
-                    if (newChild is Control controlResult)
-                    {
-                        NameScope.SetNameScope(controlResult, new NameScope());
-                    }
                 }
             }
             else

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

@@ -88,7 +88,7 @@ namespace Avalonia.Controls.Presenters
 
             foreach (var item in items)
             {
-                var i = generator.Materialize(index++, item, owner.MemberSelector);
+                var i = generator.Materialize(index++, item);
 
                 if (i.ContainerControl != null)
                 {

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

@@ -90,7 +90,7 @@ namespace Avalonia.Controls.Presenters
 
             foreach (var item in items)
             {
-                var i = generator.Materialize(index++, item, Owner.MemberSelector);
+                var i = generator.Materialize(index++, item);
 
                 if (i.ContainerControl != null)
                 {

+ 3 - 6
src/Avalonia.Controls/Presenters/ItemVirtualizerSimple.cs

@@ -314,7 +314,6 @@ namespace Avalonia.Controls.Presenters
 
             if (!panel.IsFull && Items != null && panel.IsAttachedToVisualTree)
             {
-                var memberSelector = Owner.MemberSelector;
                 var index = NextIndex;
                 var step = 1;
 
@@ -337,7 +336,7 @@ namespace Avalonia.Controls.Presenters
                         }
                     }
 
-                    var materialized = generator.Materialize(index, Items.ElementAt(index), memberSelector);
+                    var materialized = generator.Materialize(index, Items.ElementAt(index));
 
                     if (step == 1)
                     {
@@ -383,7 +382,6 @@ namespace Avalonia.Controls.Presenters
         {
             var panel = VirtualizingPanel;
             var generator = Owner.ItemContainerGenerator;
-            var selector = Owner.MemberSelector;
             var containers = generator.Containers.ToList();
             var itemIndex = FirstIndex;
 
@@ -393,7 +391,7 @@ namespace Avalonia.Controls.Presenters
 
                 if (!object.Equals(container.Item, item))
                 {
-                    if (!generator.TryRecycle(itemIndex, itemIndex, item, selector))
+                    if (!generator.TryRecycle(itemIndex, itemIndex, item))
                     {
                         throw new NotImplementedException();
                     }
@@ -420,7 +418,6 @@ namespace Avalonia.Controls.Presenters
         {
             var panel = VirtualizingPanel;
             var generator = Owner.ItemContainerGenerator;
-            var selector = Owner.MemberSelector;
 
             //validate delta it should never overflow last index or generate index < 0 
             delta = MathUtilities.Clamp(delta, -FirstIndex, ItemCount - FirstIndex - panel.Children.Count);
@@ -437,7 +434,7 @@ namespace Avalonia.Controls.Presenters
 
                 var item = Items.ElementAt(newItemIndex);
 
-                if (!generator.TryRecycle(oldItemIndex, newItemIndex, item, selector))
+                if (!generator.TryRecycle(oldItemIndex, newItemIndex, item))
                 {
                     throw new NotImplementedException();
                 }

+ 0 - 15
src/Avalonia.Controls/Presenters/ItemsPresenterBase.cs

@@ -35,12 +35,6 @@ namespace Avalonia.Controls.Presenters
         public static readonly StyledProperty<IDataTemplate> ItemTemplateProperty =
             ItemsControl.ItemTemplateProperty.AddOwner<ItemsPresenterBase>();
 
-        /// <summary>
-        /// Defines the <see cref="MemberSelector"/> property.
-        /// </summary>
-        public static readonly StyledProperty<IMemberSelector> MemberSelectorProperty =
-            ItemsControl.MemberSelectorProperty.AddOwner<ItemsPresenterBase>();
-
         private IEnumerable _items;
         private IDisposable _itemsSubscription;
         private bool _createdPanel;
@@ -127,15 +121,6 @@ namespace Avalonia.Controls.Presenters
             set { SetValue(ItemTemplateProperty, value); }
         }
 
-        /// <summary>
-        /// Selects a member from <see cref="Items"/> to use as the list item.
-        /// </summary>
-        public IMemberSelector MemberSelector
-        {
-            get { return GetValue(MemberSelectorProperty); }
-            set { SetValue(MemberSelectorProperty, value); }
-        }
-
         /// <summary>
         /// Gets the panel used to display the items.
         /// </summary>

+ 30 - 5
src/Avalonia.Controls/Primitives/Popup.cs

@@ -6,7 +6,6 @@ using System.Linq;
 using Avalonia.Input;
 using Avalonia.Input.Raw;
 using Avalonia.Interactivity;
-using Avalonia.Layout;
 using Avalonia.LogicalTree;
 using Avalonia.Metadata;
 using Avalonia.VisualTree;
@@ -270,9 +269,10 @@ namespace Avalonia.Controls.Primitives
                 _popupRoot.SnapInsideScreenEdges();
             }
 
-            _ignoreIsOpenChanged = true;
-            IsOpen = true;
-            _ignoreIsOpenChanged = false;
+            using (BeginIgnoringIsOpen())
+            {
+                IsOpen = true;
+            }
 
             Opened?.Invoke(this, EventArgs.Empty);
         }
@@ -305,7 +305,11 @@ namespace Avalonia.Controls.Primitives
                 _popupRoot.Hide();
             }
 
-            IsOpen = false;
+            using (BeginIgnoringIsOpen())
+            {
+                IsOpen = false;
+            }
+
             Closed?.Invoke(this, EventArgs.Empty);
         }
 
@@ -467,5 +471,26 @@ namespace Avalonia.Controls.Primitives
                 Close();
             }
         }
+
+        private IgnoreIsOpenScope BeginIgnoringIsOpen()
+        {
+            return new IgnoreIsOpenScope(this);
+        }
+
+        private readonly struct IgnoreIsOpenScope : IDisposable
+        {
+            private readonly Popup _owner;
+
+            public IgnoreIsOpenScope(Popup owner)
+            {
+                _owner = owner;
+                _owner._ignoreIsOpenChanged = true;
+            }
+
+            public void Dispose()
+            {
+                _owner._ignoreIsOpenChanged = false;
+            }
+        }
     }
 }

+ 36 - 18
src/Avalonia.Controls/Primitives/SelectingItemsControl.cs

@@ -222,6 +222,10 @@ namespace Avalonia.Controls.Primitives
         /// <summary>
         /// Gets or sets the selection mode.
         /// </summary>
+        /// <remarks>
+        /// Note that the selection mode only applies to selections made via user interaction.
+        /// Multiple selections can be made programatically regardless of the value of this property.
+        /// </remarks>
         protected SelectionMode SelectionMode
         {
             get { return GetValue(SelectionModeProperty); }
@@ -338,24 +342,36 @@ namespace Avalonia.Controls.Primitives
         {
             base.OnContainersMaterialized(e);
 
-            var selectedIndex = SelectedIndex;
-            var selectedContainer = e.Containers
-                .FirstOrDefault(x => (x.ContainerControl as ISelectable)?.IsSelected == true);
+            var resetSelectedItems = false;
 
-            if (selectedContainer != null)
+            foreach (var container in e.Containers)
             {
-                SelectedIndex = selectedContainer.Index;
-            }
-            else if (selectedIndex >= e.StartingIndex &&
-                     selectedIndex < e.StartingIndex + e.Containers.Count)
-            {
-                var container = e.Containers[selectedIndex - e.StartingIndex];
+                if ((container.ContainerControl as ISelectable)?.IsSelected == true)
+                {
+                    if (SelectedIndex == -1)
+                    {
+                        SelectedIndex = container.Index;
+                    }
+                    else
+                    {
+                        if (_selection.Add(container.Index))
+                        {
+                            resetSelectedItems = true;
+                        }
+                    }
 
-                if (container.ContainerControl != null)
+                    MarkContainerSelected(container.ContainerControl, true);
+                }
+                else if (_selection.Contains(container.Index))
                 {
                     MarkContainerSelected(container.ContainerControl, true);
                 }
             }
+
+            if (resetSelectedItems)
+            {
+                ResetSelectedItems();
+            }
         }
 
         /// <inheritdoc/>
@@ -469,11 +485,6 @@ namespace Avalonia.Controls.Primitives
         /// </summary>
         protected void SelectAll()
         {
-            if ((SelectionMode & (SelectionMode.Multiple | SelectionMode.Toggle)) == 0)
-            {
-                throw new NotSupportedException("Multiple selection is not enabled on this control.");
-            }
-
             UpdateSelectedItems(() =>
             {
                 _selection.Clear();
@@ -523,7 +534,14 @@ namespace Avalonia.Controls.Primitives
                     var toggle = (toggleModifier || (mode & SelectionMode.Toggle) != 0);
                     var range = multi && rangeModifier;
 
-                    if (range)
+                    if (rightButton)
+                    {
+                        if (!_selection.Contains(index))
+                        {
+                            UpdateSelectedItem(index);
+                        }
+                    }
+                    else if (range)
                     {
                         UpdateSelectedItems(() =>
                         {
@@ -582,7 +600,7 @@ namespace Avalonia.Controls.Primitives
                     }
                     else
                     {
-                        UpdateSelectedItem(index, !(rightButton && _selection.Contains(index)));
+                        UpdateSelectedItem(index);
                     }
 
                     if (Presenter?.Panel != null)

+ 0 - 9
src/Avalonia.Controls/Primitives/TabStrip.cs

@@ -13,11 +13,8 @@ namespace Avalonia.Controls.Primitives
         private static readonly FuncTemplate<IPanel> DefaultPanel =
             new FuncTemplate<IPanel>(() => new WrapPanel { Orientation = Orientation.Horizontal });
 
-        private static IMemberSelector s_MemberSelector = new FuncMemberSelector<object, object>(SelectHeader);
-
         static TabStrip()
         {
-            MemberSelectorProperty.OverrideDefaultValue<TabStrip>(s_MemberSelector);
             SelectionModeProperty.OverrideDefaultValue<TabStrip>(SelectionMode.AlwaysSelected);
             FocusableProperty.OverrideDefaultValue(typeof(TabStrip), false);
             ItemsPanelProperty.OverrideDefaultValue<TabStrip>(DefaultPanel);
@@ -52,11 +49,5 @@ namespace Avalonia.Controls.Primitives
                 e.Handled = UpdateSelectionFromEventSource(e.Source);
             }
         }
-
-        private static object SelectHeader(object o)
-        {
-            var headered = o as IHeadered;
-            return (headered != null) ? (headered.Header ?? string.Empty) : o;
-        }
     }
 }

+ 5 - 25
src/Avalonia.Controls/Primitives/TemplatedControl.cs

@@ -257,13 +257,14 @@ namespace Avalonia.Controls.Primitives
                 {
                     Logger.Verbose(LogArea.Control, this, "Creating control template");
 
-                    var child = template.Build(this);
-                    var nameScope = new NameScope();
-                    NameScope.SetNameScope((Control)child, nameScope);
+                    var (child, nameScope) = template.Build(this);
                     ApplyTemplatedParent(child);
-                    RegisterNames(child, nameScope);
                     ((ISetLogicalParent)child).SetParent(this);
                     VisualChildren.Add(child);
+                    
+                    // Existing code kinda expect to see a NameScope even if it's empty
+                    if (nameScope == null)
+                        nameScope = new NameScope();
 
                     OnTemplateApplied(new TemplateAppliedEventArgs(nameScope));
                 }
@@ -342,26 +343,5 @@ namespace Avalonia.Controls.Primitives
                 }
             }
         }
-
-        /// <summary>
-        /// Registers each control with its name scope.
-        /// </summary>
-        /// <param name="control">The control.</param>
-        /// <param name="nameScope">The name scope.</param>
-        private void RegisterNames(IControl control, INameScope nameScope)
-        {
-            if (control.Name != null)
-            {
-                nameScope.Register(control.Name, control);
-            }
-
-            if (control.TemplatedParent == this)
-            {
-                foreach (IControl child in control.GetLogicalChildren())
-                {
-                    RegisterNames(child, nameScope);
-                }
-            }
-        }
     }
 }

+ 8 - 2
src/Avalonia.Controls/Templates/FuncControlTemplate.cs

@@ -16,9 +16,15 @@ namespace Avalonia.Controls.Templates
         /// Initializes a new instance of the <see cref="FuncControlTemplate"/> class.
         /// </summary>
         /// <param name="build">The build function.</param>
-        public FuncControlTemplate(Func<ITemplatedControl, IControl> build)
+        public FuncControlTemplate(Func<ITemplatedControl, INameScope, IControl> build)
             : base(build)
         {
         }
+
+        public new ControlTemplateResult Build(ITemplatedControl param)
+        {
+            var (control, scope) = BuildWithNameScope(param);
+            return new ControlTemplateResult(control, scope);
+        }
     }
-}
+}

+ 3 - 3
src/Avalonia.Controls/Templates/FuncControlTemplate`2.cs

@@ -17,9 +17,9 @@ namespace Avalonia.Controls.Templates
         /// Initializes a new instance of the <see cref="FuncControlTemplate{T}"/> class.
         /// </summary>
         /// <param name="build">The build function.</param>
-        public FuncControlTemplate(Func<T, IControl> build)
-            : base(x => build((T)x))
+        public FuncControlTemplate(Func<T, INameScope, IControl> build)
+            : base((x, s) => build((T)x, s))
         {
         }
     }
-}
+}

+ 4 - 4
src/Avalonia.Controls/Templates/FuncDataTemplate.cs

@@ -17,7 +17,7 @@ namespace Avalonia.Controls.Templates
         /// </summary>
         public static readonly FuncDataTemplate Default =
             new FuncDataTemplate<object>(
-                data =>
+                (data, s) =>
                 {
                     if (data != null)
                     {
@@ -49,7 +49,7 @@ namespace Avalonia.Controls.Templates
         /// <param name="supportsRecycling">Whether the control can be recycled.</param>
         public FuncDataTemplate(
             Type type, 
-            Func<object, IControl> build,
+            Func<object, INameScope, IControl> build,
             bool supportsRecycling = false)
             : this(o => IsInstance(o, type), build, supportsRecycling)
         {
@@ -67,7 +67,7 @@ namespace Avalonia.Controls.Templates
         /// <param name="supportsRecycling">Whether the control can be recycled.</param>
         public FuncDataTemplate(
             Func<object, bool> match,
-            Func<object, IControl> build,
+            Func<object, INameScope, IControl> build,
             bool supportsRecycling = false)
             : base(build)
         {
@@ -105,4 +105,4 @@ namespace Avalonia.Controls.Templates
             return (o != null) && t.GetTypeInfo().IsAssignableFrom(o.GetType().GetTypeInfo());
         }
     }
-}
+}

+ 22 - 4
src/Avalonia.Controls/Templates/FuncDataTemplate`1.cs

@@ -18,7 +18,7 @@ namespace Avalonia.Controls.Templates
         /// A function which when passed an object of <typeparamref name="T"/> returns a control.
         /// </param>
         /// <param name="supportsRecycling">Whether the control can be recycled.</param>
-        public FuncDataTemplate(Func<T, IControl> build, bool supportsRecycling = false)
+        public FuncDataTemplate(Func<T, INameScope, IControl> build, bool supportsRecycling = false)
             : base(typeof(T), CastBuild(build), supportsRecycling)
         {
         }
@@ -35,12 +35,30 @@ namespace Avalonia.Controls.Templates
         /// <param name="supportsRecycling">Whether the control can be recycled.</param>
         public FuncDataTemplate(
             Func<T, bool> match,
-            Func<T, IControl> build,
+            Func<T, INameScope, IControl> build,
             bool supportsRecycling = false)
             : base(CastMatch(match), CastBuild(build), supportsRecycling)
         {
         }
 
+        /// <summary>
+        /// Initializes a new instance of the <see cref="FuncDataTemplate{T}"/> class.
+        /// </summary>
+        /// <param name="match">
+        /// A function which determines whether the data template matches the specified data.
+        /// </param>
+        /// <param name="build">
+        /// A function which when passed an object of <typeparamref name="T"/> returns a control.
+        /// </param>
+        /// <param name="supportsRecycling">Whether the control can be recycled.</param>
+        public FuncDataTemplate(
+            Func<T, bool> match,
+            Func<T, IControl> build,
+            bool supportsRecycling = false)
+            : this(match, (a, _) => build(a), supportsRecycling)
+        {
+        }
+
         /// <summary>
         /// Casts a strongly typed match function to a weakly typed one.
         /// </summary>
@@ -57,9 +75,9 @@ namespace Avalonia.Controls.Templates
         /// <typeparam name="TResult">The strong data type.</typeparam>
         /// <param name="f">The strongly typed function.</param>
         /// <returns>The weakly typed function.</returns>
-        private static Func<object, TResult> CastBuild<TResult>(Func<T, TResult> f)
+        private static Func<object, INameScope, TResult> CastBuild<TResult>(Func<T, INameScope, TResult> f)
         {
-            return o => f((T)o);
+            return (o, s) => f((T)o, s);
         }
     }
 }

+ 0 - 35
src/Avalonia.Controls/Templates/FuncMemberSelector.cs

@@ -1,35 +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.Controls.Templates
-{
-    /// <summary>
-    /// Selects a member of an object using a <see cref="Func{TObject, TMember}"/>.
-    /// </summary>
-    public class FuncMemberSelector<TObject, TMember> : IMemberSelector
-    {
-        private readonly Func<TObject, TMember> _selector;
-
-        /// <summary>
-        /// Initializes a new instance of the <see cref="FuncMemberSelector{TObject, TMember}"/>
-        /// class.
-        /// </summary>
-        /// <param name="selector">The selector.</param>
-        public FuncMemberSelector(Func<TObject, TMember> selector)
-        {
-            this._selector = selector;
-        }
-
-        /// <summary>
-        /// Selects a member of an object.
-        /// </summary>
-        /// <param name="o">The object.</param>
-        /// <returns>The selected member.</returns>
-        public object Select(object o)
-        {
-            return (o is TObject) ? _selector((TObject)o) : default(TMember);
-        }
-    }
-}

+ 14 - 0
src/Avalonia.Controls/Templates/FuncTemplateNameScopeExtensions.cs

@@ -0,0 +1,14 @@
+using System;
+
+namespace Avalonia.Controls.Templates
+{
+    public static class FuncTemplateNameScopeExtensions
+    {
+        public static T RegisterInNameScope<T>(this T control, INameScope scope)
+        where T : StyledElement
+        {
+            scope.Register(control.Name, control);
+            return control;
+        }
+    }
+}

+ 11 - 4
src/Avalonia.Controls/Templates/FuncTemplate`2.cs

@@ -13,13 +13,13 @@ namespace Avalonia.Controls.Templates
     public class FuncTemplate<TParam, TControl> : ITemplate<TParam, TControl>
         where TControl : IControl
     {
-        private readonly Func<TParam, TControl> _func;
+        private readonly Func<TParam, INameScope, TControl> _func;
 
         /// <summary>
         /// Initializes a new instance of the <see cref="FuncTemplate{TControl, TParam}"/> class.
         /// </summary>
         /// <param name="func">The function used to create the control.</param>
-        public FuncTemplate(Func<TParam, TControl> func)
+        public FuncTemplate(Func<TParam, INameScope, TControl> func)
         {
             Contract.Requires<ArgumentNullException>(func != null);
 
@@ -35,7 +35,14 @@ namespace Avalonia.Controls.Templates
         /// </returns>
         public TControl Build(TParam param)
         {
-            return _func(param);
+            return BuildWithNameScope(param).control;
+        }
+
+        protected (TControl control, INameScope nameScope) BuildWithNameScope(TParam param)
+        {
+            var scope = new NameScope();
+            var rv = _func(param, scope);
+            return (rv, scope);
         }
     }
-}
+}

+ 2 - 2
src/Avalonia.Controls/Templates/FuncTreeDataTemplate.cs

@@ -28,7 +28,7 @@ namespace Avalonia.Controls.Templates
         /// </param>
         public FuncTreeDataTemplate(
             Type type,
-            Func<object, IControl> build,
+            Func<object, INameScope, IControl> build,
             Func<object, IEnumerable> itemsSelector)
             : this(o => IsInstance(o, type), build, itemsSelector)
         {
@@ -48,7 +48,7 @@ namespace Avalonia.Controls.Templates
         /// </param>
         public FuncTreeDataTemplate(
             Func<object, bool> match,
-            Func<object, IControl> build,
+            Func<object, INameScope, IControl> build,
             Func<object, IEnumerable> itemsSelector)
             : base(match, build)
         {

+ 13 - 2
src/Avalonia.Controls/Templates/FuncTreeDataTemplate`1.cs

@@ -23,7 +23,7 @@ namespace Avalonia.Controls.Templates
         /// items.
         /// </param>
         public FuncTreeDataTemplate(
-            Func<T, Control> build,
+            Func<T, INameScope, Control> build,
             Func<T, IEnumerable> itemsSelector)
             : base(
                 typeof(T),
@@ -46,7 +46,7 @@ namespace Avalonia.Controls.Templates
         /// </param>
         public FuncTreeDataTemplate(
             Func<T, bool> match,
-            Func<T, Control> build,
+            Func<T, INameScope, Control> build,
             Func<T, IEnumerable> itemsSelector)
             : base(
                 CastMatch(match),
@@ -65,6 +65,17 @@ namespace Avalonia.Controls.Templates
             return o => (o is T) && f((T)o);
         }
 
+        /// <summary>
+        /// Casts a function with a typed parameter to an untyped function.
+        /// </summary>
+        /// <typeparam name="TResult">The result.</typeparam>
+        /// <param name="f">The typed function.</param>
+        /// <returns>The untyped function.</returns>
+        private static Func<object, INameScope, TResult> Cast<TResult>(Func<T, INameScope, TResult> f)
+        {
+            return (o, s) => f((T)o, s);
+        }
+        
         /// <summary>
         /// Casts a function with a typed parameter to an untyped function.
         /// </summary>

+ 20 - 2
src/Avalonia.Controls/Templates/IControlTemplate.cs

@@ -9,7 +9,25 @@ namespace Avalonia.Controls.Templates
     /// <summary>
     /// Interface representing a template used to build a <see cref="TemplatedControl"/>.
     /// </summary>
-    public interface IControlTemplate : ITemplate<ITemplatedControl, IControl>
+    public interface IControlTemplate : ITemplate<ITemplatedControl, ControlTemplateResult>
     {
     }
-}
+
+    public class ControlTemplateResult
+    {
+        public IControl Control { get; }
+        public INameScope NameScope { get; }
+
+        public ControlTemplateResult(IControl control, INameScope nameScope)
+        {
+            Control = control;
+            NameScope = nameScope;
+        }
+
+        public void Deconstruct(out IControl control, out INameScope scope)
+        {
+            control = Control;
+            scope = NameScope;
+        }
+    }
+}

+ 0 - 18
src/Avalonia.Controls/Templates/IMemberSelector.cs

@@ -1,18 +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.
-
-namespace Avalonia.Controls.Templates
-{
-    /// <summary>
-    /// Selects a member of an object.
-    /// </summary>
-    public interface IMemberSelector
-    {
-        /// <summary>
-        /// Selects a member of an object.
-        /// </summary>
-        /// <param name="o">The object.</param>
-        /// <returns>The selected member.</returns>
-        object Select(object o);
-    }
-}

+ 2 - 2
src/Avalonia.Controls/Templates/ITemplate`2.cs

@@ -8,7 +8,7 @@ namespace Avalonia.Controls.Templates
     /// </summary>
     /// <typeparam name="TParam">The type of the parameter.</typeparam>
     /// <typeparam name="TControl">The type of control.</typeparam>
-    public interface ITemplate<TParam, TControl> where TControl : IControl
+    public interface ITemplate<TParam, TControl>
     {
         /// <summary>
         /// Creates the control.
@@ -19,4 +19,4 @@ namespace Avalonia.Controls.Templates
         /// </returns>
         TControl Build(TParam param);
     }
-}
+}

+ 74 - 31
src/Avalonia.Controls/TreeView.cs

@@ -105,32 +105,21 @@ namespace Avalonia.Controls
             get => _selectedItem;
             set
             {
-                SetAndRaise(SelectedItemProperty, ref _selectedItem,
-                    (object val, ref object backing, Action<Action> notifyWrapper) =>
-                    {
-                        var old = backing;
-                        backing = val;
-
-                        notifyWrapper(() =>
-                            RaisePropertyChanged(
-                                SelectedItemProperty,
-                                old,
-                                val));
+                SetAndRaise(SelectedItemProperty, ref _selectedItem, value);
 
-                        if (val != null)
-                        {
-                            if (SelectedItems.Count != 1 || SelectedItems[0] != val)
-                            {
-                                _syncingSelectedItems = true;
-                                SelectSingleItem(val);
-                                _syncingSelectedItems = false;
-                            }
-                        }
-                        else if (SelectedItems.Count > 0)
-                        {
-                            SelectedItems.Clear();
-                        }
-                    }, value);
+                if (value != null)
+                {
+                    if (SelectedItems.Count != 1 || SelectedItems[0] != value)
+                    {
+                        _syncingSelectedItems = true;
+                        SelectSingleItem(value);
+                        _syncingSelectedItems = false;
+                    }
+                }
+                else if (SelectedItems.Count > 0)
+                {
+                    SelectedItems.Clear();
+                }
             }
         }
 
@@ -164,6 +153,48 @@ namespace Avalonia.Controls
             }
         }
 
+        /// <summary>
+        /// Expands the specified <see cref="TreeViewItem"/> all descendent <see cref="TreeViewItem"/>s.
+        /// </summary>
+        /// <param name="item">The item to expand.</param>
+        public void ExpandSubTree(TreeViewItem item)
+        {
+            item.IsExpanded = true;
+
+            var panel = item.Presenter.Panel;
+
+            if (panel != null)
+            {
+                foreach (var child in panel.Children)
+                {
+                    if (child is TreeViewItem treeViewItem)
+                    {
+                        ExpandSubTree(treeViewItem);
+                    }
+                }
+            }
+        }
+
+        /// <summary>
+        /// Selects all items in the <see cref="TreeView"/>.
+        /// </summary>
+        /// <remarks>
+        /// Note that this method only selects nodes currently visible due to their parent nodes
+        /// being expanded: it does not expand nodes.
+        /// </remarks>
+        public void SelectAll()
+        {
+            SynchronizeItems(SelectedItems, ItemContainerGenerator.Index.Items);
+        }
+
+        /// <summary>
+        /// Deselects all items in the <see cref="TreeView"/>.
+        /// </summary>
+        public void UnselectAll()
+        {
+            SelectedItems.Clear();
+        }
+
         /// <summary>
         /// Subscribes to the <see cref="SelectedItems"/> CollectionChanged event, if any.
         /// </summary>
@@ -409,7 +440,7 @@ namespace Avalonia.Controls
 
                 if (this.SelectionMode == SelectionMode.Multiple && Match(keymap.SelectAll))
                 {
-                    SynchronizeItems(SelectedItems, ItemContainerGenerator.Index.Items);
+                    SelectAll();
                     e.Handled = true;
                 }
             }
@@ -479,7 +510,8 @@ namespace Avalonia.Controls
                     e.Source,
                     true,
                     (e.InputModifiers & InputModifiers.Shift) != 0,
-                    (e.InputModifiers & InputModifiers.Control) != 0);
+                    (e.InputModifiers & InputModifiers.Control) != 0,
+                    e.MouseButton == MouseButton.Right);
             }
         }
 
@@ -490,11 +522,13 @@ namespace Avalonia.Controls
         /// <param name="select">Whether the item should be selected or unselected.</param>
         /// <param name="rangeModifier">Whether the range modifier is enabled (i.e. shift key).</param>
         /// <param name="toggleModifier">Whether the toggle modifier is enabled (i.e. ctrl key).</param>
+        /// <param name="rightButton">Whether the event is a right-click.</param>
         protected void UpdateSelectionFromContainer(
             IControl container,
             bool select = true,
             bool rangeModifier = false,
-            bool toggleModifier = false)
+            bool toggleModifier = false,
+            bool rightButton = false)
         {
             var item = ItemContainerGenerator.Index.ItemFromContainer(container);
 
@@ -515,7 +549,14 @@ namespace Avalonia.Controls
             var multi = (mode & SelectionMode.Multiple) != 0;
             var range = multi && selectedContainer != null && rangeModifier;
 
-            if (!toggle && !range)
+            if (rightButton)
+            {
+                if (!SelectedItems.Contains(item))
+                {
+                    SelectSingleItem(item);
+                }
+            }
+            else if (!toggle && !range)
             {
                 SelectSingleItem(item);
             }
@@ -684,6 +725,7 @@ namespace Avalonia.Controls
         /// <param name="select">Whether the container should be selected or unselected.</param>
         /// <param name="rangeModifier">Whether the range modifier is enabled (i.e. shift key).</param>
         /// <param name="toggleModifier">Whether the toggle modifier is enabled (i.e. ctrl key).</param>
+        /// <param name="rightButton">Whether the event is a right-click.</param>
         /// <returns>
         /// True if the event originated from a container that belongs to the control; otherwise
         /// false.
@@ -692,13 +734,14 @@ namespace Avalonia.Controls
             IInteractive eventSource,
             bool select = true,
             bool rangeModifier = false,
-            bool toggleModifier = false)
+            bool toggleModifier = false,
+            bool rightButton = false)
         {
             var container = GetContainerFromEventSource(eventSource);
 
             if (container != null)
             {
-                UpdateSelectionFromContainer(container, select, rangeModifier, toggleModifier);
+                UpdateSelectionFromContainer(container, select, rangeModifier, toggleModifier, rightButton);
                 return true;
             }
 

+ 1 - 33
src/Avalonia.Controls/UserControl.cs

@@ -9,40 +9,8 @@ namespace Avalonia.Controls
     /// <summary>
     /// Provides the base class for defining a new control that encapsulates related existing controls and provides its own logic.
     /// </summary>
-    public class UserControl : ContentControl, IStyleable, INameScope
+    public class UserControl : ContentControl, IStyleable
     {
-        private readonly NameScope _nameScope = new NameScope();
 
-        /// <inheritdoc/>
-        event EventHandler<NameScopeEventArgs> INameScope.Registered
-        {
-            add { _nameScope.Registered += value; }
-            remove { _nameScope.Registered -= value; }
-        }
-
-        /// <inheritdoc/>
-        event EventHandler<NameScopeEventArgs> INameScope.Unregistered
-        {
-            add { _nameScope.Unregistered += value; }
-            remove { _nameScope.Unregistered -= value; }
-        }
-
-        /// <inheritdoc/>
-        void INameScope.Register(string name, object element)
-        {
-            _nameScope.Register(name, element);
-        }
-
-        /// <inheritdoc/>
-        object INameScope.Find(string name)
-        {
-            return _nameScope.Find(name);
-        }
-
-        /// <inheritdoc/>
-        void INameScope.Unregister(string name)
-        {
-            _nameScope.Unregister(name);
-        }
     }
 }

+ 1 - 33
src/Avalonia.Controls/Window.cs

@@ -48,7 +48,7 @@ namespace Avalonia.Controls
     /// <summary>
     /// A top-level window.
     /// </summary>
-    public class Window : WindowBase, IStyleable, IFocusScope, ILayoutRoot, INameScope
+    public class Window : WindowBase, IStyleable, IFocusScope, ILayoutRoot
     {
         /// <summary>
         /// Defines the <see cref="SizeToContent"/> property.
@@ -157,20 +157,6 @@ namespace Avalonia.Controls
             _maxPlatformClientSize = PlatformImpl?.MaxClientSize ?? default(Size);
         }
 
-        /// <inheritdoc/>
-        event EventHandler<NameScopeEventArgs> INameScope.Registered
-        {
-            add { _nameScope.Registered += value; }
-            remove { _nameScope.Registered -= value; }
-        }
-
-        /// <inheritdoc/>
-        event EventHandler<NameScopeEventArgs> INameScope.Unregistered
-        {
-            add { _nameScope.Unregistered += value; }
-            remove { _nameScope.Unregistered -= value; }
-        }
-
         /// <summary>
         /// Gets the platform-specific window implementation.
         /// </summary>
@@ -494,24 +480,6 @@ namespace Avalonia.Controls
             }
         }
 
-        /// <inheritdoc/>
-        void INameScope.Register(string name, object element)
-        {
-            _nameScope.Register(name, element);
-        }
-
-        /// <inheritdoc/>
-        object INameScope.Find(string name)
-        {
-            return _nameScope.Find(name);
-        }
-
-        /// <inheritdoc/>
-        void INameScope.Unregister(string name)
-        {
-            _nameScope.Unregister(name);
-        }
-
         /// <inheritdoc/>
         protected override Size MeasureOverride(Size availableSize)
         {

+ 19 - 18
src/Avalonia.Diagnostics/DevTools.xaml

@@ -1,23 +1,24 @@
 <UserControl xmlns="https://github.com/avaloniaui"
              xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
              x:Class="Avalonia.Diagnostics.DevTools">
-  <Grid RowDefinitions="Auto,*,Auto">
-    <TabStrip SelectedIndex="{Binding SelectedTab, Mode=TwoWay}">
-      <TabStripItem Content="Logical Tree"/>
-      <TabStripItem Content="Visual Tree"/>
-      <TabStripItem Content="Events"/>
-    </TabStrip>
+    <Grid RowDefinitions="*,Auto" Margin="4">
 
-    <ContentControl Content="{Binding Content}" Grid.Row="1"/> 
-    
-    <StackPanel Spacing="4" Orientation="Horizontal" Grid.Row="2">
-      <TextBlock>Hold Ctrl+Shift over a control to inspect.</TextBlock>
-      <Separator Width="8"/>
-      <TextBlock>Focused:</TextBlock>
-      <TextBlock Text="{Binding FocusedControl}"/>
-      <Separator Width="8"/>
-      <TextBlock>Pointer Over:</TextBlock>
-      <TextBlock Text="{Binding PointerOverElement}"/>
-    </StackPanel>
-  </Grid>
+        <TabControl Grid.Row="0" Items="{Binding Tools}" SelectedItem="{Binding SelectedTool}">
+            <TabControl.ItemTemplate>
+                <DataTemplate>
+                    <TextBlock Text="{Binding Name}" />
+                </DataTemplate>
+            </TabControl.ItemTemplate>
+        </TabControl>
+
+        <StackPanel Grid.Row="1" Spacing="4" Orientation="Horizontal">
+            <TextBlock>Hold Ctrl+Shift over a control to inspect.</TextBlock>
+            <Separator Width="8" />
+            <TextBlock>Focused:</TextBlock>
+            <TextBlock Text="{Binding FocusedControl}" />
+            <Separator Width="8" />
+            <TextBlock>Pointer Over:</TextBlock>
+            <TextBlock Text="{Binding PointerOverElement}" />
+        </StackPanel>
+    </Grid>
 </UserControl>

+ 6 - 2
src/Avalonia.Diagnostics/DevTools.xaml.cs

@@ -1,10 +1,13 @@
+// Copyright (c) The Avalonia Project. All rights reserved.
+// Licensed under the MIT license. See licence.md file in the project root for full license information.
+
 using System;
 using System.Collections.Generic;
 using System.Linq;
+using System.Reactive.Disposables;
 using System.Reactive.Linq;
 using Avalonia.Controls;
 using Avalonia.Controls.Primitives;
-using Avalonia.Controls.Templates;
 using Avalonia.Diagnostics.ViewModels;
 using Avalonia.Input;
 using Avalonia.Input.Raw;
@@ -82,7 +85,8 @@ namespace Avalonia.Diagnostics
                         DataTemplates =
                         {
                             new ViewLocator<ViewModelBase>(),
-                        }
+                        },
+                        Title = "Avalonia DevTools"
                     };
 
                     devToolsWindow.Closed += devTools.DevToolsClosed;

+ 1 - 1
src/Avalonia.Diagnostics/ViewLocator.cs

@@ -31,4 +31,4 @@ namespace Avalonia.Diagnostics
             return data is TViewModel;
         }
     }
-}
+}

+ 22 - 43
src/Avalonia.Diagnostics/ViewModels/DevToolsViewModel.cs

@@ -2,7 +2,9 @@
 // Licensed under the MIT license. See licence.md file in the project root for full license information.
 
 using System;
-using System.Reactive.Linq;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Linq;
 using Avalonia.Controls;
 using Avalonia.Input;
 
@@ -10,21 +12,23 @@ namespace Avalonia.Diagnostics.ViewModels
 {
     internal class DevToolsViewModel : ViewModelBase
     {
-        private ViewModelBase _content;
-        private int _selectedTab;
-        private TreePageViewModel _logicalTree;
-        private TreePageViewModel _visualTree;
-        private EventsViewModel _eventsView;
+        private IDevToolViewModel _selectedTool;
         private string _focusedControl;
         private string _pointerOverElement;
 
         public DevToolsViewModel(IControl root)
         {
-            _logicalTree = new TreePageViewModel(LogicalTreeNode.Create(root));
-            _visualTree = new TreePageViewModel(VisualTreeNode.Create(root));
-            _eventsView = new EventsViewModel(root);
+            Tools = new ObservableCollection<IDevToolViewModel>
+            {
+                new TreePageViewModel(LogicalTreeNode.Create(root), "Logical Tree"),
+                new TreePageViewModel(VisualTreeNode.Create(root), "Visual Tree"),
+                new EventsViewModel(root)
+            };
+
+            SelectedTool = Tools.First();
 
             UpdateFocusedControl();
+
             KeyboardDevice.Instance.PropertyChanged += (s, e) =>
             {
                 if (e.PropertyName == nameof(KeyboardDevice.Instance.FocusedElement))
@@ -33,58 +37,33 @@ namespace Avalonia.Diagnostics.ViewModels
                 }
             };
 
-            SelectedTab = 0;
             root.GetObservable(TopLevel.PointerOverElementProperty)
                 .Subscribe(x => PointerOverElement = x?.GetType().Name);
         }
 
-        public ViewModelBase Content
+        public IDevToolViewModel SelectedTool
         {
-            get { return _content; }
-            private set { RaiseAndSetIfChanged(ref _content, value); }
+            get => _selectedTool;
+            set => RaiseAndSetIfChanged(ref _selectedTool, value);
         }
 
-        public int SelectedTab
-        {
-            get { return _selectedTab; }
-            set
-            {
-                _selectedTab = value;
-
-                switch (value)
-                {
-                    case 0:
-                        Content = _logicalTree;
-                        break;
-                    case 1:
-                        Content = _visualTree;
-                        break;
-                    case 2:
-                        Content = _eventsView;
-                        break;
-                }
-
-                RaisePropertyChanged();
-            }
-        }
+        public ObservableCollection<IDevToolViewModel> Tools { get; }
 
         public string FocusedControl
         {
-            get { return _focusedControl; }
-            private set { RaiseAndSetIfChanged(ref _focusedControl, value); }
+            get => _focusedControl;
+            private set => RaiseAndSetIfChanged(ref _focusedControl, value);
         }
 
         public string PointerOverElement
         {
-            get { return _pointerOverElement; }
-            private set { RaiseAndSetIfChanged(ref _pointerOverElement, value); }
+            get => _pointerOverElement;
+            private set => RaiseAndSetIfChanged(ref _pointerOverElement, value);
         }
 
         public void SelectControl(IControl control)
         {
-            var tree = Content as TreePageViewModel;
-
-            if (tree != null)
+            if (SelectedTool is TreePageViewModel tree)
             {
                 tree.SelectControl(control);
             }

+ 7 - 6
src/Avalonia.Diagnostics/ViewModels/EventsViewModel.cs

@@ -5,8 +5,6 @@ using System;
 using System.Collections.ObjectModel;
 using System.Globalization;
 using System.Linq;
-using System.Windows.Input;
-
 using Avalonia.Controls;
 using Avalonia.Data.Converters;
 using Avalonia.Interactivity;
@@ -14,21 +12,24 @@ using Avalonia.Media;
 
 namespace Avalonia.Diagnostics.ViewModels
 {
-    internal class EventsViewModel : ViewModelBase
+    internal class EventsViewModel : ViewModelBase, IDevToolViewModel
     {
         private readonly IControl _root;
         private FiredEvent _selectedEvent;
 
         public EventsViewModel(IControl root)
         {
-            this._root = root;
-            this.Nodes = RoutedEventRegistry.Instance.GetAllRegistered()
+            _root = root;
+
+            Nodes = RoutedEventRegistry.Instance.GetAllRegistered()
                 .GroupBy(e => e.OwnerType)
                 .OrderBy(e => e.Key.Name)
                 .Select(g => new EventOwnerTreeNode(g.Key, g, this))
                 .ToArray();
         }
 
+        public string Name => "Events";
+
         public EventTreeNodeBase[] Nodes { get; }
 
         public ObservableCollection<FiredEvent> RecordedEvents { get; } = new ObservableCollection<FiredEvent>();
@@ -49,7 +50,7 @@ namespace Avalonia.Diagnostics.ViewModels
     {
         public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
         {
-            return (bool)value ? Brushes.LightGreen : Brushes.Transparent;
+            return (bool)value ? Brushes.Green : Brushes.Transparent;
         }
 
         public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)

+ 16 - 0
src/Avalonia.Diagnostics/ViewModels/IDevToolViewModel.cs

@@ -0,0 +1,16 @@
+// 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.
+
+namespace Avalonia.Diagnostics.ViewModels
+{
+    /// <summary>
+    /// View model interface for tool showing up in DevTools
+    /// </summary>
+    public interface IDevToolViewModel
+    {
+        /// <summary>
+        /// Name of a tool.
+        /// </summary>
+        string Name { get; }
+    }
+}

+ 5 - 2
src/Avalonia.Diagnostics/ViewModels/TreePageViewModel.cs

@@ -6,16 +6,19 @@ using Avalonia.VisualTree;
 
 namespace Avalonia.Diagnostics.ViewModels
 {
-    internal class TreePageViewModel : ViewModelBase
+    internal class TreePageViewModel : ViewModelBase, IDevToolViewModel
     {
         private TreeNode _selected;
         private ControlDetailsViewModel _details;
 
-        public TreePageViewModel(TreeNode[] nodes)
+        public TreePageViewModel(TreeNode[] nodes, string name)
         {
             Nodes = nodes;
+            Name = name;
         }
 
+        public string Name { get; }
+
         public TreeNode[] Nodes { get; protected set; }
 
         public TreeNode SelectedNode

+ 5 - 11
src/Avalonia.Diagnostics/Views/ControlDetailsView.cs

@@ -7,7 +7,6 @@ using System.Reactive.Linq;
 using Avalonia.Controls;
 using Avalonia.Diagnostics.ViewModels;
 using Avalonia.Media;
-using Avalonia.Styling;
 
 namespace Avalonia.Diagnostics.Views
 {
@@ -42,16 +41,6 @@ namespace Avalonia.Diagnostics.Views
             {
                 Content = _grid = new SimpleGrid
                 {
-                    Styles =
-                    {
-                        new Style(x => x.Is<Control>())
-                        {
-                            Setters = new[]
-                            {
-                                new Setter(MarginProperty, new Thickness(2)),
-                            }
-                        },
-                    },
                     [GridRepeater.TemplateProperty] = pt,
                 }
             };
@@ -61,8 +50,11 @@ namespace Avalonia.Diagnostics.Views
         {
             var property = (PropertyDetails)i;
 
+            var margin = new Thickness(2);
+
             yield return new TextBlock
             {
+                Margin = margin,
                 Text = property.Name,
                 TextWrapping = TextWrapping.NoWrap,
                 [!ToolTip.TipProperty] = property.GetObservable<string>(nameof(property.Diagnostic)).ToBinding(),
@@ -70,6 +62,7 @@ namespace Avalonia.Diagnostics.Views
 
             yield return new TextBlock
             {
+                Margin = margin,
                 TextWrapping = TextWrapping.NoWrap,
                 [!TextBlock.TextProperty] = property.GetObservable<object>(nameof(property.Value))
                     .Select(v => v?.ToString())
@@ -78,6 +71,7 @@ namespace Avalonia.Diagnostics.Views
 
             yield return new TextBlock
             {
+                Margin = margin,
                 TextWrapping = TextWrapping.NoWrap,
                 [!TextBlock.TextProperty] = property.GetObservable<string>((nameof(property.Priority))).ToBinding(),
             };

+ 4 - 1
src/Avalonia.Diagnostics/Views/PropertyChangedExtenions.cs → src/Avalonia.Diagnostics/Views/PropertyChangedExtensions.cs

@@ -1,4 +1,7 @@
-using System;
+// Copyright (c) The Avalonia Project. All rights reserved.
+// Licensed under the MIT license. See licence.md file in the project root for full license information.
+
+using System;
 using System.ComponentModel;
 using System.Reactive.Linq;
 using System.Reflection;

+ 3 - 0
src/Avalonia.Diagnostics/Views/TreePage.xaml.cs

@@ -1,3 +1,6 @@
+// Copyright (c) The Avalonia Project. All rights reserved.
+// Licensed under the MIT license. See licence.md file in the project root for full license information.
+
 using Avalonia.Controls;
 using Avalonia.Controls.Generators;
 using Avalonia.Controls.Primitives;

+ 14 - 4
src/Avalonia.OpenGL/GlInterface.cs

@@ -9,12 +9,14 @@ namespace Avalonia.OpenGL
     public class GlInterface : GlInterfaceBase
     {
         public string Version { get; }
+        public string Vendor { get; }
+        public string Renderer { get; }
 
         public GlInterface(Func<string, bool, IntPtr> getProcAddress) : base(getProcAddress)
         {
-            var versionPtr = GetString(GlConsts.GL_VERSION);
-            if (versionPtr != IntPtr.Zero)
-                Version = Marshal.PtrToStringAnsi(versionPtr);
+            Version = GetString(GlConsts.GL_VERSION);
+            Renderer = GetString(GlConsts.GL_RENDERER);
+            Vendor = GetString(GlConsts.GL_VENDOR);
         }
 
         public GlInterface(Func<Utf8Buffer, IntPtr> n) : this(ConvertNative(n))
@@ -54,7 +56,15 @@ namespace Avalonia.OpenGL
 
         public delegate IntPtr GlGetString(int v);
         [GlEntryPoint("glGetString")]
-        public GlGetString GetString { get; }
+        public GlGetString GetStringNative { get; }
+
+        public string GetString(int v)
+        {
+            var ptr = GetStringNative(v);
+            if (ptr != IntPtr.Zero)
+                return Marshal.PtrToStringAnsi(ptr);
+            return null;
+        }
 
         public delegate void GlGetIntegerv(int name, out int rv);
         [GlEntryPoint("glGetIntegerv")]

+ 2 - 2
src/Avalonia.ReactiveUI/AutoDataTemplateBindingHook.cs

@@ -16,7 +16,7 @@ namespace Avalonia.ReactiveUI
     /// </summary>
     public class AutoDataTemplateBindingHook : IPropertyBindingHook
     {
-        private static FuncDataTemplate DefaultItemTemplate = new FuncDataTemplate<object>(x =>
+        private static FuncDataTemplate DefaultItemTemplate = new FuncDataTemplate<object>((x, _) =>
         {
             var control = new ViewModelViewHost();
             var context = control.GetObservable(Control.DataContextProperty);
@@ -52,4 +52,4 @@ namespace Avalonia.ReactiveUI
             return true;
         }
     }
-}
+}

+ 73 - 0
src/Avalonia.Styling/Controls/ChildNameScope.cs

@@ -0,0 +1,73 @@
+using System.Threading.Tasks;
+using Avalonia.Utilities;
+
+namespace Avalonia.Controls
+{
+    public class ChildNameScope : INameScope
+    {
+        private readonly INameScope _parentScope;
+        private readonly NameScope _inner = new NameScope();
+
+        public ChildNameScope(INameScope parentScope)
+        {
+            _parentScope = parentScope;
+        }
+        
+        public void Register(string name, object element) => _inner.Register(name, element);
+
+        public SynchronousCompletionAsyncResult<object> FindAsync(string name)
+        {
+            var found = Find(name);
+            if (found != null)
+                return new SynchronousCompletionAsyncResult<object>(found);
+            // Not found and both current and parent scope are in completed state
+            if(IsCompleted)
+                return new SynchronousCompletionAsyncResult<object>(null);
+            return DoFindAsync(name);
+        }
+
+        public SynchronousCompletionAsyncResult<object> DoFindAsync(string name)
+        {
+            var src = new SynchronousCompletionAsyncResultSource<object>();
+
+            void ParentSearch()
+            {
+                var parentSearch = _parentScope.FindAsync(name);
+                if (parentSearch.IsCompleted)
+                    src.SetResult(parentSearch.GetResult());
+                else
+                    parentSearch.OnCompleted(() => src.SetResult(parentSearch.GetResult()));
+            }
+            if (!_inner.IsCompleted)
+            {
+                // Guaranteed to be incomplete at this point
+                var innerSearch = _inner.FindAsync(name);
+                innerSearch.OnCompleted(() =>
+                {
+                    var value = innerSearch.GetResult();
+                    if (value != null)
+                        src.SetResult(value);
+                    else ParentSearch();
+                });
+            }
+            else
+                ParentSearch();
+
+            return src.AsyncResult;
+        }
+
+        public object Find(string name)
+        {
+            var found = _inner.Find(name);
+            if (found != null)
+                return found;
+            if (_inner.IsCompleted)
+                return _parentScope.Find(name);
+            return null;
+        }
+
+        public void Complete() => _inner.Complete();
+
+        public bool IsCompleted => _inner.IsCompleted && _parentScope.IsCompleted;
+    }
+}

+ 20 - 14
src/Avalonia.Styling/Controls/INameScope.cs

@@ -2,6 +2,8 @@
 // Licensed under the MIT license. See licence.md file in the project root for full license information.
 
 using System;
+using System.Threading.Tasks;
+using Avalonia.Utilities;
 
 namespace Avalonia.Controls
 {
@@ -10,16 +12,6 @@ namespace Avalonia.Controls
     /// </summary>
     public interface INameScope
     {
-        /// <summary>
-        /// Raised when an element is registered with the name scope.
-        /// </summary>
-        event EventHandler<NameScopeEventArgs> Registered;
-
-        /// <summary>
-        /// Raised when an element is unregistered with the name scope.
-        /// </summary>
-        event EventHandler<NameScopeEventArgs> Unregistered;
-
         /// <summary>
         /// Registers an element in the name scope.
         /// </summary>
@@ -28,16 +20,30 @@ namespace Avalonia.Controls
         void Register(string name, object element);
 
         /// <summary>
-        /// Finds a named element in the name scope.
+        /// Finds a named element in the name scope, waits for the scope to be completely populated before returning null
+        /// Returned task is configured to run any continuations synchronously.
+        /// </summary>
+        /// <param name="name">The name.</param>
+        /// <returns>The element, or null if the name was not found.</returns>
+        SynchronousCompletionAsyncResult<object> FindAsync(string name);
+        
+        /// <summary>
+        /// Finds a named element in the name scope, returns immediately, doesn't traverse the name scope stack
         /// </summary>
         /// <param name="name">The name.</param>
         /// <returns>The element, or null if the name was not found.</returns>
         object Find(string name);
 
         /// <summary>
-        /// Unregisters an element with the name scope.
+        /// Marks the name scope as completed, no further registrations will be allowed
         /// </summary>
-        /// <param name="name">The name.</param>
-        void Unregister(string name);
+        void Complete();
+        
+        /// <summary>
+        /// Returns whether further registrations are allowed on the scope
+        /// </summary>
+        bool IsCompleted { get; }
+
+
     }
 }

+ 38 - 61
src/Avalonia.Styling/Controls/NameScope.cs

@@ -3,7 +3,9 @@
 
 using System;
 using System.Collections.Generic;
+using System.Threading.Tasks;
 using Avalonia.LogicalTree;
+using Avalonia.Utilities;
 
 namespace Avalonia.Controls
 {
@@ -18,44 +20,14 @@ namespace Avalonia.Controls
         public static readonly AttachedProperty<INameScope> NameScopeProperty =
             AvaloniaProperty.RegisterAttached<NameScope, StyledElement, INameScope>("NameScope");
 
+        /// <inheritdoc/>
+        public bool IsCompleted { get; private set; }
+        
         private readonly Dictionary<string, object> _inner = new Dictionary<string, object>();
 
-        /// <summary>
-        /// Raised when an element is registered with the name scope.
-        /// </summary>
-        public event EventHandler<NameScopeEventArgs> Registered;
-
-        /// <summary>
-        /// Raised when an element is unregistered with the name scope.
-        /// </summary>
-        public event EventHandler<NameScopeEventArgs> Unregistered;
-
-        /// <summary>
-        /// Finds the containing name scope for a styled element.
-        /// </summary>
-        /// <param name="styled">The styled element.</param>
-        /// <returns>The containing name scope.</returns>
-        public static INameScope FindNameScope(StyledElement styled)
-        {
-            Contract.Requires<ArgumentNullException>(styled != null);
-
-            INameScope result;
-
-            while (styled != null)
-            {
-                result = styled as INameScope ?? GetNameScope(styled);
-
-                if (result != null)
-                {
-                    return result;
-                }
-
-                styled = (styled as ILogical)?.LogicalParent as StyledElement;
-            }
-
-            return null;
-        }
-
+        private readonly Dictionary<string, SynchronousCompletionAsyncResultSource<object>> _pendingSearches =
+            new Dictionary<string, SynchronousCompletionAsyncResultSource<object>>();
+        
         /// <summary>
         /// Gets the value of the attached <see cref="NameScopeProperty"/> on a styled element.
         /// </summary>
@@ -80,13 +52,11 @@ namespace Avalonia.Controls
             styled.SetValue(NameScopeProperty, value);
         }
 
-        /// <summary>
-        /// Registers an element with the name scope.
-        /// </summary>
-        /// <param name="name">The element name.</param>
-        /// <param name="element">The element.</param>
+        /// <inheritdoc />
         public void Register(string name, object element)
         {
+            if (IsCompleted)
+                throw new InvalidOperationException("NameScope is completed, no further registrations are allowed");
             Contract.Requires<ArgumentNullException>(name != null);
             Contract.Requires<ArgumentNullException>(element != null);
 
@@ -102,15 +72,29 @@ namespace Avalonia.Controls
             else
             {
                 _inner.Add(name, element);
-                Registered?.Invoke(this, new NameScopeEventArgs(name, element));
+                if (_pendingSearches.TryGetValue(name, out var tcs))
+                {
+                    _pendingSearches.Remove(name);
+                    tcs.SetResult(element);
+                }
             }
         }
 
-        /// <summary>
-        /// Finds a named element in the name scope.
-        /// </summary>
-        /// <param name="name">The name.</param>
-        /// <returns>The element, or null if the name was not found.</returns>
+        public SynchronousCompletionAsyncResult<object> FindAsync(string name)
+        {
+            var found = Find(name);
+            if (found != null)
+                return new SynchronousCompletionAsyncResult<object>(found);
+            if (IsCompleted)
+                return new SynchronousCompletionAsyncResult<object>((object)null);
+            if (!_pendingSearches.TryGetValue(name, out var tcs))
+                // We are intentionally running continuations synchronously here
+                _pendingSearches[name] = tcs = new SynchronousCompletionAsyncResultSource<object>();
+
+            return tcs.AsyncResult;
+        }
+
+        /// <inheritdoc />
         public object Find(string name)
         {
             Contract.Requires<ArgumentNullException>(name != null);
@@ -120,21 +104,14 @@ namespace Avalonia.Controls
             return result;
         }
 
-        /// <summary>
-        /// Unregisters an element with the name scope.
-        /// </summary>
-        /// <param name="name">The name.</param>
-        public void Unregister(string name)
+        public void Complete()
         {
-            Contract.Requires<ArgumentNullException>(name != null);
-
-            object element;
-
-            if (_inner.TryGetValue(name, out element))
-            {
-                _inner.Remove(name);
-                Unregistered?.Invoke(this, new NameScopeEventArgs(name, element));
-            }
+            IsCompleted = true;
+            foreach (var kp in _pendingSearches)
+                kp.Value.TrySetResult(null);
+            _pendingSearches.Clear();
         }
+
+        
     }
 }

+ 41 - 0
src/Avalonia.Styling/Controls/NameScopeExtensions.cs

@@ -37,6 +37,25 @@ namespace Avalonia.Controls
             return (T)result;
         }
 
+        /// <summary>
+        /// Finds a named element in an <see cref="INameScope"/>.
+        /// </summary>
+        /// <typeparam name="T">The element type.</typeparam>
+        /// <param name="anchor">The control to take the name scope from.</param>
+        /// <param name="name">The name.</param>
+        /// <returns>The named element or null if not found.</returns>
+        public static T Find<T>(this ILogical anchor, string name)
+            where T : class
+        {
+            Contract.Requires<ArgumentNullException>(anchor != null);
+            Contract.Requires<ArgumentNullException>(name != null);
+            var styledAnchor = anchor as StyledElement;
+            if (styledAnchor == null)
+                return null;
+            var nameScope = (anchor as INameScope) ?? NameScope.GetNameScope(styledAnchor);
+            return nameScope?.Find<T>(name);
+        }
+
         /// <summary>
         /// Gets a named element from an <see cref="INameScope"/> or throws if no element of the
         /// requested name was found.
@@ -67,6 +86,28 @@ namespace Avalonia.Controls
             return (T)result;
         }
 
+        /// <summary>
+        /// Gets a named element from an <see cref="INameScope"/> or throws if no element of the
+        /// requested name was found.
+        /// </summary>
+        /// <typeparam name="T">The element type.</typeparam>
+        /// <param name="anchor">The control to take the name scope from.</param>
+        /// <param name="name">The name.</param>
+        /// <returns>The named element.</returns>
+        public static T Get<T>(this ILogical anchor, string name)
+            where T : class
+        {
+            Contract.Requires<ArgumentNullException>(anchor != null);
+            Contract.Requires<ArgumentNullException>(name != null);
+               
+            var nameScope = (anchor as INameScope) ?? NameScope.GetNameScope((StyledElement)anchor);
+            if (nameScope == null)
+                throw new InvalidOperationException(
+                    "The control doesn't have an associated name scope, probably no registrations has been done yet");
+            
+            return nameScope.Get<T>(name);
+        }
+        
         public static INameScope FindNameScope(this ILogical control)
         {
             Contract.Requires<ArgumentNullException>(control != null);

+ 62 - 0
src/Avalonia.Styling/Controls/NameScopeLocator.cs

@@ -0,0 +1,62 @@
+using System;
+using System.Linq;
+using System.Reactive.Disposables;
+using System.Reactive.Threading.Tasks;
+using System.Reflection;
+using System.Threading.Tasks;
+using Avalonia.LogicalTree;
+using Avalonia.Reactive;
+using Avalonia.Utilities;
+
+namespace Avalonia.Controls
+{
+    public class NameScopeLocator
+    {
+        /// <summary>
+        /// Tracks a named control relative to another control.
+        /// </summary>
+        /// <param name="scope">The scope relative from which the object should be resolved.</param>
+        /// <param name="name">The name of the object to find.</param>
+        public static IObservable<object> Track(INameScope scope, string name)
+        {
+            return new NeverEndingSynchronousCompletionAsyncResultObservable<object>(scope.FindAsync(name));
+        }
+        
+        // This class is implemented in such weird way because for some reason
+        // our binding system doesn't expect OnCompleted to be ever called and
+        // seems to treat it as binding cancellation or something 
+
+        private class NeverEndingSynchronousCompletionAsyncResultObservable<T> : IObservable<T>
+        {
+            private T _value;
+            private SynchronousCompletionAsyncResult<T>? _asyncResult;
+
+            public NeverEndingSynchronousCompletionAsyncResultObservable(SynchronousCompletionAsyncResult<T> task)
+            {
+                if (task.IsCompleted)
+                    _value = task.GetResult();
+                else
+                    _asyncResult = task;
+            }
+            
+            public IDisposable Subscribe(IObserver<T> observer)
+            {
+                if (_asyncResult?.IsCompleted == true)
+                {
+                    _value = _asyncResult.Value.GetResult();
+                    _asyncResult = null;
+                }
+
+                if (_asyncResult != null)
+                    _asyncResult.Value.OnCompleted(() =>
+                    {
+                        observer.OnNext(_asyncResult.Value.GetResult());
+                    });
+                else
+                    observer.OnNext(_value);
+                
+                return Disposable.Empty;
+            }
+        }
+    }
+}

+ 3 - 71
src/Avalonia.Styling/LogicalTree/ControlLocator.cs

@@ -15,18 +15,6 @@ namespace Avalonia.LogicalTree
     /// </summary>
     public static class ControlLocator
     {
-        /// <summary>
-        /// Tracks a named control relative to another control.
-        /// </summary>
-        /// <param name="relativeTo">
-        /// The control relative from which the other control should be found.
-        /// </param>
-        /// <param name="name">The name of the control to find.</param>
-        public static IObservable<ILogical> Track(ILogical relativeTo, string name)
-        {
-            return new ControlTracker(relativeTo, name);
-        }
-
         public static IObservable<ILogical> Track(ILogical relativeTo, int ancestorLevel, Type ancestorType = null)
         {
             return new ControlTracker(relativeTo, ancestorLevel, ancestorType);
@@ -35,18 +23,10 @@ namespace Avalonia.LogicalTree
         private class ControlTracker : LightweightObservableBase<ILogical>
         {
             private readonly ILogical _relativeTo;
-            private readonly string _name;
             private readonly int _ancestorLevel;
             private readonly Type _ancestorType;
-            INameScope _nameScope;
             ILogical _value;
 
-            public ControlTracker(ILogical relativeTo, string name)
-            {
-                _relativeTo = relativeTo;
-                _name = name;
-            }
-
             public ControlTracker(ILogical relativeTo, int ancestorLevel, Type ancestorType)
             {
                 _relativeTo = relativeTo;
@@ -66,12 +46,6 @@ namespace Avalonia.LogicalTree
                 _relativeTo.AttachedToLogicalTree -= Attached;
                 _relativeTo.DetachedFromLogicalTree -= Detached;
 
-                if (_nameScope != null)
-                {
-                    _nameScope.Registered -= Registered;
-                    _nameScope.Unregistered -= Unregistered;
-                }
-
                 _value = null;
             }
 
@@ -88,57 +62,15 @@ namespace Avalonia.LogicalTree
 
             private void Detached(object sender, LogicalTreeAttachmentEventArgs e)
             {
-                if (_nameScope != null)
-                {
-                    _nameScope.Registered -= Registered;
-                    _nameScope.Unregistered -= Unregistered;
-                }
-
                 _value = null;
                 PublishNext(null);
             }
 
-            private void Registered(object sender, NameScopeEventArgs e)
-            {
-                if (e.Name == _name && e.Element is ILogical logical)
-                {
-                    _value = logical;
-                    PublishNext(logical);
-                }
-            }
-
-            private void Unregistered(object sender, NameScopeEventArgs e)
-            {
-                if (e.Name == _name)
-                {
-                    _value = null;
-                    PublishNext(null);
-                }
-            }
-
             private void Update()
             {
-                if (_name != null)
-                {
-                    _nameScope = _relativeTo.FindNameScope();
-
-                    if (_nameScope != null)
-                    {
-                        _nameScope.Registered += Registered;
-                        _nameScope.Unregistered += Unregistered;
-                        _value = _nameScope.Find<ILogical>(_name);
-                    }
-                    else
-                    {
-                        _value = null;
-                    }
-                }
-                else
-                {
-                    _value = _relativeTo.GetLogicalAncestors()
-                        .Where(x => _ancestorType?.GetTypeInfo().IsAssignableFrom(x.GetType().GetTypeInfo()) ?? true)
-                        .ElementAtOrDefault(_ancestorLevel);
-                }
+                _value = _relativeTo.GetLogicalAncestors()
+                    .Where(x => _ancestorType?.GetTypeInfo().IsAssignableFrom(x.GetType().GetTypeInfo()) ?? true)
+                    .ElementAtOrDefault(_ancestorLevel);
             }
         }
     }

+ 0 - 21
src/Avalonia.Styling/StyledElement.cs

@@ -61,7 +61,6 @@ namespace Avalonia
         private readonly Classes _classes = new Classes();
         private bool _isAttachedToLogicalTree;
         private IAvaloniaList<ILogical> _logicalChildren;
-        private INameScope _nameScope;
         private IResourceDictionary _resources;
         private Styles _styles;
         private bool _styled;
@@ -82,7 +81,6 @@ namespace Avalonia
         /// </summary>
         public StyledElement()
         {
-            _nameScope = this as INameScope;
             _isAttachedToLogicalTree = this is IStyleRoot;
         }
 
@@ -381,7 +379,6 @@ namespace Avalonia
         {
             if (_initCount == 0 && (!_styled || force))
             {
-                RegisterWithNameScope();
                 ApplyStyling();
                 _styled = true;
             }
@@ -675,19 +672,6 @@ namespace Avalonia
             AvaloniaLocator.Current.GetService<IStyler>()?.ApplyStyles(this);
         }
 
-        private void RegisterWithNameScope()
-        {
-            if (_nameScope == null)
-            {
-                _nameScope = NameScope.GetNameScope(this) ?? ((StyledElement)Parent)?._nameScope;
-            }
-
-            if (Name != null)
-            {
-                _nameScope?.Register(Name, this);
-            }
-        }
-
         private static void ValidateLogicalChild(ILogical c)
         {
             if (c == null)
@@ -724,11 +708,6 @@ namespace Avalonia
         {
             if (_isAttachedToLogicalTree)
             {
-                if (Name != null)
-                {
-                    _nameScope?.Unregister(Name);
-                }
-
                 _isAttachedToLogicalTree = false;
                 _styleDetach.OnNext(this);
                 OnDetachedFromLogicalTree(e);

+ 0 - 1
src/Avalonia.Styling/Styling/Setter.cs

@@ -99,7 +99,6 @@ namespace Avalonia.Styling
                 if (template != null && !isPropertyOfTypeITemplate)
                 {
                     var materialized = template.Build();
-                    NameScope.SetNameScope((StyledElement)materialized, new NameScope());
                     value = materialized;
                 }
 

+ 1 - 1
src/Avalonia.Styling/Styling/Styles.cs

@@ -180,7 +180,7 @@ namespace Avalonia.Styling
         /// <inheritdoc/>
         public bool TryGetResource(object key, out object value)
         {
-            if (_resources != null && _resources.TryGetValue(key, out value))
+            if (_resources != null && _resources.TryGetResource(key, out value))
             {
                 return true;
             }

+ 0 - 1
src/Avalonia.Themes.Default/AutoCompleteBox.xaml

@@ -27,7 +27,6 @@
                        Background="{TemplateBinding Background}"
                        Foreground="{TemplateBinding Foreground}"
                        ItemTemplate="{TemplateBinding ItemTemplate}"
-                       MemberSelector="{TemplateBinding ValueMemberSelector}"
                        ScrollViewer.HorizontalScrollBarVisibility="Auto"
                        ScrollViewer.VerticalScrollBarVisibility="Auto" />
             </Border>

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

@@ -18,6 +18,5 @@
     <EmbeddedResource Include="**/*.xaml"/>
   </ItemGroup>
   <Import Project="..\..\build\BuildTargets.targets"/>
-  <Import Project="..\..\build\BuildTargets.targets" />
   <Import Project="..\..\build\Rx.props" />
 </Project>

+ 1 - 2
src/Avalonia.Themes.Default/Carousel.xaml

@@ -8,10 +8,9 @@
                            Items="{TemplateBinding Items}"
                            ItemsPanel="{TemplateBinding ItemsPanel}"
                            Margin="{TemplateBinding Padding}"
-                           MemberSelector="{TemplateBinding MemberSelector}"
                            SelectedIndex="{TemplateBinding SelectedIndex}"
                            PageTransition="{TemplateBinding PageTransition}"/>
       </Border>
     </ControlTemplate>
   </Setter>
-</Style>
+</Style>

+ 0 - 1
src/Avalonia.Themes.Default/ComboBox.xaml

@@ -45,7 +45,6 @@
                                           Items="{TemplateBinding Items}"
                                           ItemsPanel="{TemplateBinding ItemsPanel}"
                                           ItemTemplate="{TemplateBinding ItemTemplate}"
-                                          MemberSelector="{TemplateBinding MemberSelector}"
                                           VirtualizationMode="{TemplateBinding VirtualizationMode}"
                                   />
                       </ScrollViewer>

+ 1 - 1
src/Avalonia.Themes.Default/DataValidationErrors.xaml

@@ -29,7 +29,7 @@
           </Style>
         </Canvas.Styles>
         <ToolTip.Tip>
-          <ItemsControl Items="{Binding}" MemberSelector="Message"/>
+          <ItemsControl Items="{Binding}"/>
         </ToolTip.Tip>
         <Path Data="M14,7 A7,7 0 0,0 0,7 M0,7 A7,7 0 1,0 14,7 M7,3l0,5 M7,9l0,2" Stroke="{DynamicResource ErrorBrush}" StrokeThickness="2"/>
       </Canvas>

+ 2 - 3
src/Avalonia.Themes.Default/ItemsControl.xaml

@@ -4,8 +4,7 @@
       <ItemsPresenter Name="PART_ItemsPresenter"
                       Items="{TemplateBinding Items}"
                       ItemsPanel="{TemplateBinding ItemsPanel}"
-                      ItemTemplate="{TemplateBinding ItemTemplate}"
-                      MemberSelector="{TemplateBinding MemberSelector}"/>
+                      ItemTemplate="{TemplateBinding ItemTemplate}"/>
     </ControlTemplate>
   </Setter>
-</Style>
+</Style>

+ 1 - 2
src/Avalonia.Themes.Default/ListBox.xaml

@@ -18,10 +18,9 @@
                           ItemsPanel="{TemplateBinding ItemsPanel}"
                           ItemTemplate="{TemplateBinding ItemTemplate}"
                           Margin="{TemplateBinding Padding}"
-                          MemberSelector="{TemplateBinding MemberSelector}"
                           VirtualizationMode="{TemplateBinding VirtualizationMode}"/>
         </ScrollViewer>
       </Border>
     </ControlTemplate>
   </Setter>
-</Style>
+</Style>

+ 2 - 4
src/Avalonia.Themes.Default/MenuItem.xaml

@@ -56,8 +56,7 @@
                                     Items="{TemplateBinding Items}"
                                     ItemsPanel="{TemplateBinding ItemsPanel}"
                                     ItemTemplate="{TemplateBinding ItemTemplate}"
-                                    Margin="2"
-                                    MemberSelector="{TemplateBinding MemberSelector}"/>
+                                    Margin="2"/>
                     <Rectangle Name="iconSeparator"
                                Fill="{DynamicResource ThemeControlMidBrush}"
                                HorizontalAlignment="Left"
@@ -114,8 +113,7 @@
                                     Items="{TemplateBinding Items}"
                                     ItemsPanel="{TemplateBinding ItemsPanel}"
                                     ItemTemplate="{TemplateBinding ItemTemplate}"
-                                    Margin="2"
-                                    MemberSelector="{TemplateBinding MemberSelector}"/>
+                                    Margin="2"/>
                     <Rectangle Name="iconSeparator"
                                Fill="{DynamicResource ThemeControlMidBrush}"
                                HorizontalAlignment="Left"

+ 1 - 2
src/Avalonia.Themes.Default/TabControl.xaml

@@ -14,8 +14,7 @@
                             Name="PART_ItemsPresenter"
                             Items="{TemplateBinding Items}"
                             ItemsPanel="{TemplateBinding ItemsPanel}"
-                            ItemTemplate="{TemplateBinding ItemTemplate}"
-                            MemberSelector="{TemplateBinding MemberSelector}"                     >
+                            ItemTemplate="{TemplateBinding ItemTemplate}">
                         </ItemsPresenter>
                         <ContentPresenter
                             Name="PART_SelectedContentHost"

+ 1 - 2
src/Avalonia.Themes.Default/TabStrip.xaml

@@ -3,7 +3,6 @@
     <Setter Property="Template">
       <ControlTemplate>
         <ItemsPresenter Name="PART_ItemsPresenter"
-                        MemberSelector="{TemplateBinding MemberSelector}"
                         Items="{TemplateBinding Items}"
                         ItemsPanel="{TemplateBinding ItemsPanel}"
                         ItemTemplate="{TemplateBinding ItemTemplate}"/>
@@ -18,4 +17,4 @@
   <Style Selector="TabStrip > TabStripItem">
     <Setter Property="Margin" Value="16"/>
   </Style>
-</Styles>
+</Styles>

+ 1 - 2
src/Avalonia.Themes.Default/TreeView.xaml

@@ -15,8 +15,7 @@
           <ItemsPresenter Name="PART_ItemsPresenter"
                           Items="{TemplateBinding Items}"
                           ItemsPanel="{TemplateBinding ItemsPanel}"
-                          Margin="{TemplateBinding Padding}"
-                          MemberSelector="{TemplateBinding MemberSelector}"/>
+                          Margin="{TemplateBinding Padding}"/>
         </ScrollViewer>
       </Border>
     </ControlTemplate>

+ 1 - 2
src/Avalonia.Themes.Default/TreeViewItem.xaml

@@ -32,8 +32,7 @@
                     <ItemsPresenter Name="PART_ItemsPresenter"
                                     IsVisible="{TemplateBinding IsExpanded}"
                                     Items="{TemplateBinding Items}"
-                                    ItemsPanel="{TemplateBinding ItemsPanel}"
-                                    MemberSelector="{TemplateBinding MemberSelector}"/>
+                                    ItemsPanel="{TemplateBinding ItemsPanel}"/>
                 </StackPanel>
             </ControlTemplate>
         </Setter>

+ 3 - 2
src/Avalonia.Visuals/Rendering/RenderLayers.cs

@@ -8,8 +8,8 @@ namespace Avalonia.Rendering
 {
     public class RenderLayers : IEnumerable<RenderLayer>
     {
-        private List<RenderLayer> _inner = new List<RenderLayer>();
-        private Dictionary<IVisual, RenderLayer> _index = new Dictionary<IVisual, RenderLayer>();
+        private readonly List<RenderLayer> _inner = new List<RenderLayer>();
+        private readonly Dictionary<IVisual, RenderLayer> _index = new Dictionary<IVisual, RenderLayer>();
         
         public int Count => _inner.Count;
         public RenderLayer this[IVisual layerRoot] => _index[layerRoot];
@@ -56,6 +56,7 @@ namespace Avalonia.Rendering
             }
 
             _index.Clear();
+            _inner.Clear();
         }
 
         public bool TryGetValue(IVisual layerRoot, out RenderLayer value)

+ 13 - 0
src/Avalonia.X11/Glx/GlxDisplay.cs

@@ -90,6 +90,19 @@ namespace Avalonia.X11.Glx
             GlInterface = new GlInterface(GlxInterface.GlxGetProcAddress);
             if (GlInterface.Version == null)
                 throw new OpenGlException("GL version string is null, aborting");
+            if (GlInterface.Renderer == null)
+                throw new OpenGlException("GL renderer string is null, aborting");
+
+            if (Environment.GetEnvironmentVariable("AVALONIA_GLX_IGNORE_RENDERER_BLACKLIST") != "1")
+            {
+                var blacklist = AvaloniaLocator.Current.GetService<X11PlatformOptions>()
+                    ?.GlxRendererBlacklist;
+                if (blacklist != null)
+                    foreach(var item in blacklist)
+                        if (GlInterface.Renderer.Contains(item))
+                            throw new OpenGlException($"Renderer '{GlInterface.Renderer}' is blacklisted by '{item}'");
+            }
+            
         }
 
         public void ClearContext() => Glx.MakeContextCurrent(_x11.Display,

+ 8 - 0
src/Avalonia.X11/X11Platform.cs

@@ -96,6 +96,14 @@ namespace Avalonia
     {
         public bool UseEGL { get; set; }
         public bool UseGpu { get; set; } = true;
+
+        public List<string> GlxRendererBlacklist { get; set; } = new List<string>
+        {
+            // llvmpipe is a software GL rasterizer. If it's returned by glGetString,
+            // that usually means that something in the system is horribly misconfigured
+            // and sometimes attempts to use GLX might cause a segfault
+            "llvmpipe"
+        };
         public string WmClass { get; set; } = Assembly.GetEntryAssembly()?.GetName()?.Name ?? "AvaloniaApplication";
         public bool? EnableMultiTouch { get; set; }
     }

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

@@ -12,7 +12,6 @@
         <Compile Include="AvaloniaXamlLoader.cs" />
         <Compile Include="Converters\AvaloniaUriTypeConverter.cs" />
         <Compile Include="Converters\FontFamilyTypeConverter.cs" />
-        <Compile Include="Converters\MemberSelectorTypeConverter.cs" />
         <Compile Include="Converters\TimeSpanTypeConverter.cs" />
         <Compile Include="Extensions.cs" />
         <Compile Include="MarkupExtension.cs" />
@@ -33,7 +32,6 @@
         <Compile Include="Templates\DataTemplate.cs" />
         <Compile Include="Templates\FocusAdornerTemplate.cs" />
         <Compile Include="Templates\ItemsPanelTemplate.cs" />
-        <Compile Include="Templates\MemberSelector.cs" />
         <Compile Include="Templates\Template.cs" />
         <Compile Include="Templates\TemplateContent.cs" />
         <Compile Include="Templates\TreeDataTemplate.cs" />

+ 0 - 24
src/Markup/Avalonia.Markup.Xaml/Converters/MemberSelectorTypeConverter.cs

@@ -1,24 +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;
-using System.Globalization;
-using Avalonia.Markup.Xaml.Templates;
-
-namespace Avalonia.Markup.Xaml.Converters
-{
-	using System.ComponentModel;
-
-    public class MemberSelectorTypeConverter : TypeConverter
-    {
-        public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
-        {
-            return sourceType == typeof(string);
-        }
-
-        public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
-        {
-            return MemberSelector.Parse((string)value);
-        }
-    }
-}

+ 2 - 1
src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/BindingExtension.cs

@@ -40,7 +40,8 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions
                 Source = Source,
                 StringFormat = StringFormat,
                 RelativeSource = RelativeSource,
-                DefaultAnchor = new WeakReference(GetDefaultAnchor(descriptorContext))
+                DefaultAnchor = new WeakReference(GetDefaultAnchor(descriptorContext)),
+                NameScope = new WeakReference<INameScope>(serviceProvider.GetService<INameScope>())
             };
         }
 

+ 2 - 2
src/Markup/Avalonia.Markup.Xaml/Templates/ControlTemplate.cs

@@ -17,6 +17,6 @@ namespace Avalonia.Markup.Xaml.Templates
 
         public Type TargetType { get; set; }
 
-        public IControl Build(ITemplatedControl control) => TemplateContent.Load(Content);
+        public ControlTemplateResult Build(ITemplatedControl control) => TemplateContent.Load(Content);
     }
-}
+}

+ 2 - 2
src/Markup/Avalonia.Markup.Xaml/Templates/DataTemplate.cs

@@ -32,6 +32,6 @@ namespace Avalonia.Markup.Xaml.Templates
             }
         }
 
-        public IControl Build(object data) => TemplateContent.Load(Content);
+        public IControl Build(object data) => TemplateContent.Load(Content).Control;
     }
-}
+}

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