diff --git a/.gitignore b/.gitignore
index 971c945246..b5a46e16f4 100644
--- a/.gitignore
+++ b/.gitignore
@@ -198,3 +198,11 @@ info.plist
build-intermediate
obj-Direct2D1/
obj-Skia/
+
+##################
+# Vim
+##################
+.vim
+coc-settings.json
+.ccls-cache
+.ccls
diff --git a/azure-pipelines.yml b/azure-pipelines.yml
index 3f4fbb0d50..7e3532ee23 100644
--- a/azure-pipelines.yml
+++ b/azure-pipelines.yml
@@ -102,7 +102,7 @@ jobs:
- job: Windows
pool:
- vmImage: 'vs2017-win2016'
+ vmImage: 'windows-2019'
steps:
- task: CmdLine@2
displayName: 'Install Nuke'
diff --git a/build-native.sh b/build-native.sh
old mode 100644
new mode 100755
diff --git a/build/Base.props b/build/Base.props
index e565ab1664..a60373ebb3 100644
--- a/build/Base.props
+++ b/build/Base.props
@@ -2,4 +2,4 @@
-
\ No newline at end of file
+
diff --git a/build/SharedVersion.props b/build/SharedVersion.props
index 4f0b1f0a5b..76abcf6912 100644
--- a/build/SharedVersion.props
+++ b/build/SharedVersion.props
@@ -2,7 +2,7 @@
xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
Avalonia
- 0.8.1
+ 0.8.999
Copyright 2019 © The AvaloniaUI Project
https://github.com/AvaloniaUI/Avalonia/blob/master/licence.md
https://github.com/AvaloniaUI/Avalonia/
diff --git a/native/Avalonia.Native/src/OSX/KeyTransform.mm b/native/Avalonia.Native/src/OSX/KeyTransform.mm
index 7486aaad69..971bcf24f8 100644
--- a/native/Avalonia.Native/src/OSX/KeyTransform.mm
+++ b/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 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},
};
diff --git a/nukebuild/Build.cs b/nukebuild/Build.cs
index a99ac4d026..dd2f27116d 100644
--- a/nukebuild/Build.cs
+++ b/nukebuild/Build.cs
@@ -89,6 +89,29 @@ partial class Build : NukeBuild
}
+ IReadOnlyCollection
diff --git a/samples/ControlCatalog/MainView.xaml b/samples/ControlCatalog/MainView.xaml
index 1cddb9d295..8699508320 100644
--- a/samples/ControlCatalog/MainView.xaml
+++ b/samples/ControlCatalog/MainView.xaml
@@ -36,6 +36,7 @@
+
diff --git a/samples/ControlCatalog/Pages/AutoCompleteBoxPage.xaml b/samples/ControlCatalog/Pages/AutoCompleteBoxPage.xaml
index 0ca3567970..f90a0c4658 100644
--- a/samples/ControlCatalog/Pages/AutoCompleteBoxPage.xaml
+++ b/samples/ControlCatalog/Pages/AutoCompleteBoxPage.xaml
@@ -37,10 +37,6 @@
-
-
+
+ TabStrip
+ A control which displays a selectable strip of tabs
+
+
+
+ Defined in XAML
+
+ Item 1
+ Item 2
+ Disabled
+
+
+
+
+ Dynamically generated
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/ControlCatalog/Pages/TabStripPage.xaml.cs b/samples/ControlCatalog/Pages/TabStripPage.xaml.cs
new file mode 100644
index 0000000000..f0630cf534
--- /dev/null
+++ b/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;
+ }
+ }
+}
diff --git a/samples/ControlCatalog/SideBar.xaml b/samples/ControlCatalog/SideBar.xaml
index 3047b1e519..3513e94107 100644
--- a/samples/ControlCatalog/SideBar.xaml
+++ b/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}">
+ ItemTemplate="{TemplateBinding ItemTemplate}">
-
\ No newline at end of file
+
diff --git a/src/Android/Avalonia.Android/Avalonia.Android.csproj b/src/Android/Avalonia.Android/Avalonia.Android.csproj
index 9c3d4fb3a1..0089ea3b8d 100644
--- a/src/Android/Avalonia.Android/Avalonia.Android.csproj
+++ b/src/Android/Avalonia.Android/Avalonia.Android.csproj
@@ -1,6 +1,6 @@
- monoandroid44
+ monoandroid80
true
diff --git a/src/Android/Avalonia.AndroidTestApplication/Avalonia.AndroidTestApplication.csproj b/src/Android/Avalonia.AndroidTestApplication/Avalonia.AndroidTestApplication.csproj
index c2c0cd4301..1b2b205d45 100644
--- a/src/Android/Avalonia.AndroidTestApplication/Avalonia.AndroidTestApplication.csproj
+++ b/src/Android/Avalonia.AndroidTestApplication/Avalonia.AndroidTestApplication.csproj
@@ -16,7 +16,7 @@
Resources\Resource.Designer.cs
Off
False
- v4.4
+ v8.0
Properties\AndroidManifest.xml
@@ -150,6 +150,7 @@
+
diff --git a/src/Avalonia.Base/Data/Converters/DefaultValueConverter.cs b/src/Avalonia.Base/Data/Converters/DefaultValueConverter.cs
index 990a4b04f2..0ffd6a9539 100644
--- a/src/Avalonia.Base/Data/Converters/DefaultValueConverter.cs
+++ b/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)
diff --git a/src/Avalonia.Base/Utilities/SynchronousCompletionAsyncResult.cs b/src/Avalonia.Base/Utilities/SynchronousCompletionAsyncResult.cs
new file mode 100644
index 0000000000..f321b2b2f1
--- /dev/null
+++ b/src/Avalonia.Base/Utilities/SynchronousCompletionAsyncResult.cs
@@ -0,0 +1,106 @@
+using System;
+using System.Collections.Generic;
+using System.Runtime.CompilerServices;
+
+namespace Avalonia.Utilities
+{
+ ///
+ /// A task-like operation that is guaranteed to finish continuations synchronously,
+ /// can be used for parametrized one-shot events
+ ///
+ public struct SynchronousCompletionAsyncResult : INotifyCompletion
+ {
+ private readonly SynchronousCompletionAsyncResultSource _source;
+ private readonly T _result;
+ private readonly bool _isValid;
+ internal SynchronousCompletionAsyncResult(SynchronousCompletionAsyncResultSource 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 GetAwaiter() => this;
+ }
+
+ ///
+ /// Source for incomplete SynchronousCompletionAsyncResult
+ ///
+ ///
+ public class SynchronousCompletionAsyncResultSource
+ {
+ private T _result;
+ internal bool IsCompleted { get; private set; }
+ public SynchronousCompletionAsyncResult AsyncResult => new SynchronousCompletionAsyncResult(this);
+
+ internal T Result => IsCompleted ?
+ _result :
+ throw new InvalidOperationException("Asynchronous operation is not yet completed");
+
+ private List _continuations;
+
+ internal void OnCompleted(Action continuation)
+ {
+ if(_continuations==null)
+ _continuations = new List();
+ _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);
+ }
+ }
+}
diff --git a/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs b/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs
index c54d8e19a1..0dd52c9b48 100644
--- a/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs
+++ b/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));
}
diff --git a/src/Avalonia.Controls/AutoCompleteBox.cs b/src/Avalonia.Controls/AutoCompleteBox.cs
index b87e10d284..1e2fc9f9d0 100644
--- a/src/Avalonia.Controls/AutoCompleteBox.cs
+++ b/src/Avalonia.Controls/AutoCompleteBox.cs
@@ -345,7 +345,6 @@ namespace Avalonia.Controls
///
private IDisposable _collectionChangeSubscription;
- private IMemberSelector _valueMemberSelector;
private Func>> _asyncPopulator;
private CancellationTokenSource _populationCancellationTokenSource;
@@ -541,12 +540,6 @@ namespace Avalonia.Controls
o => o.Items,
(o, v) => o.Items = v);
- public static readonly DirectProperty ValueMemberSelectorProperty =
- AvaloniaProperty.RegisterDirect(
- nameof(ValueMemberSelector),
- o => o.ValueMemberSelector,
- (o, v) => o.ValueMemberSelector = v);
-
public static readonly DirectProperty>>> AsyncPopulatorProperty =
AvaloniaProperty.RegisterDirect>>>(
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
}
}
- ///
- /// Gets or sets the MemberSelector that is used to get values for
- /// display in the text portion of the
- /// control.
- ///
- /// The MemberSelector that is used to get values for display in
- /// the text portion of the
- /// control.
- public IMemberSelector ValueMemberSelector
- {
- get { return _valueMemberSelector; }
- set { SetAndRaise(ValueMemberSelectorProperty, ref _valueMemberSelector, value); }
- }
-
///
/// Gets or sets the selected item in the drop-down.
///
@@ -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();
}
diff --git a/src/Avalonia.Controls/ComboBox.cs b/src/Avalonia.Controls/ComboBox.cs
index 5d427df5a6..f32b8fabc6 100644
--- a/src/Avalonia.Controls/ComboBox.cs
+++ b/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;
}
}
diff --git a/src/Avalonia.Controls/ContextMenu.cs b/src/Avalonia.Controls/ContextMenu.cs
index 92293a32d6..58b4324a3e 100644
--- a/src/Avalonia.Controls/ContextMenu.cs
+++ b/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.LogicalTree;
namespace Avalonia.Controls
@@ -90,9 +90,14 @@ namespace Avalonia.Controls
/// The control.
public void Open(Control control)
{
+ if (IsOpen)
+ {
+ return;
+ }
+
if (_popup == null)
{
- _popup = new Popup()
+ _popup = new Popup
{
PlacementMode = PlacementMode.Pointer,
PlacementTarget = control,
@@ -107,7 +112,14 @@ namespace Avalonia.Controls
((ISetLogicalParent)_popup).SetParent(control);
_popup.Child = this;
_popup.IsOpen = true;
+
IsOpen = true;
+
+ RaiseEvent(new RoutedEventArgs
+ {
+ RoutedEvent = MenuOpenedEvent,
+ Source = this,
+ });
}
///
@@ -115,13 +127,15 @@ namespace Avalonia.Controls
///
public override void Close()
{
+ if (!IsOpen)
+ {
+ return;
+ }
+
if (_popup != null && _popup.IsVisible)
{
_popup.IsOpen = false;
}
-
- SelectedIndex = -1;
- IsOpen = false;
}
protected override IItemContainerGenerator CreateItemContainerGenerator()
@@ -129,6 +143,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();
@@ -145,8 +171,7 @@ namespace Avalonia.Controls
i.IsSubMenuOpen = false;
}
- contextMenu.IsOpen = false;
- contextMenu.SelectedIndex = -1;
+ contextMenu.CloseCore();
}
}
diff --git a/src/Avalonia.Controls/Embedding/EmbeddableControlRoot.cs b/src/Avalonia.Controls/Embedding/EmbeddableControlRoot.cs
index 83d1c4aae3..2d48a7d33b 100644
--- a/src/Avalonia.Controls/Embedding/EmbeddableControlRoot.cs
+++ b/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 Registered
- {
- add { _nameScope.Registered += value; }
- remove { _nameScope.Registered -= value; }
- }
-
- public event EventHandler 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();
}
diff --git a/src/Avalonia.Controls/Embedding/Offscreen/OffscreenTopLevel.cs b/src/Avalonia.Controls/Embedding/Offscreen/OffscreenTopLevel.cs
index c4f83ffd54..d326ab5734 100644
--- a/src/Avalonia.Controls/Embedding/Offscreen/OffscreenTopLevel.cs
+++ b/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 Registered
- {
- add { _nameScope.Registered += value; }
- remove { _nameScope.Registered -= value; }
- }
-
- public event EventHandler 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()
diff --git a/src/Avalonia.Controls/Generators/IItemContainerGenerator.cs b/src/Avalonia.Controls/Generators/IItemContainerGenerator.cs
index 653a4f5dcb..2d6757219d 100644
--- a/src/Avalonia.Controls/Generators/IItemContainerGenerator.cs
+++ b/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.
///
/// The item.
- /// An optional member selector.
/// The created controls.
- ItemContainerInfo Materialize(
- int index,
- object item,
- IMemberSelector selector);
+ ItemContainerInfo Materialize(int index, object item);
///
/// Removes a set of created containers.
@@ -84,11 +80,7 @@ namespace Avalonia.Controls.Generators
/// The removed containers.
IEnumerable RemoveRange(int startingIndex, int count);
- bool TryRecycle(
- int oldIndex,
- int newIndex,
- object item,
- IMemberSelector selector);
+ bool TryRecycle(int oldIndex, int newIndex, object item);
///
/// Clears all created containers and returns the removed controls.
diff --git a/src/Avalonia.Controls/Generators/ItemContainerGenerator.cs b/src/Avalonia.Controls/Generators/ItemContainerGenerator.cs
index f1a1f94a01..4fd6f4135c 100644
--- a/src/Avalonia.Controls/Generators/ItemContainerGenerator.cs
+++ b/src/Avalonia.Controls/Generators/ItemContainerGenerator.cs
@@ -54,13 +54,9 @@ namespace Avalonia.Controls.Generators
public virtual Type ContainerType => null;
///
- 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
}
///
- 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;
///
public virtual IEnumerable Clear()
diff --git a/src/Avalonia.Controls/Generators/ItemContainerGenerator`1.cs b/src/Avalonia.Controls/Generators/ItemContainerGenerator`1.cs
index 320d6c8faf..d1d1c2b172 100644
--- a/src/Avalonia.Controls/Generators/ItemContainerGenerator`1.cs
+++ b/src/Avalonia.Controls/Generators/ItemContainerGenerator`1.cs
@@ -79,11 +79,7 @@ namespace Avalonia.Controls.Generators
}
///
- 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;
diff --git a/src/Avalonia.Controls/Generators/TreeItemContainerGenerator.cs b/src/Avalonia.Controls/Generators/TreeItemContainerGenerator.cs
index 304c86dbf7..c06a64443c 100644
--- a/src/Avalonia.Controls/Generators/TreeItemContainerGenerator.cs
+++ b/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;
}
}
diff --git a/src/Avalonia.Controls/ItemsControl.cs b/src/Avalonia.Controls/ItemsControl.cs
index a292ff7d0a..902e55bde6 100644
--- a/src/Avalonia.Controls/ItemsControl.cs
+++ b/src/Avalonia.Controls/ItemsControl.cs
@@ -54,12 +54,6 @@ namespace Avalonia.Controls
public static readonly StyledProperty ItemTemplateProperty =
AvaloniaProperty.Register(nameof(ItemTemplate));
- ///
- /// Defines the property.
- ///
- public static readonly StyledProperty MemberSelectorProperty =
- AvaloniaProperty.Register(nameof(MemberSelector));
-
private IEnumerable _items = new AvaloniaList
public static readonly RoutedEvent MenuOpenedEvent =
- RoutedEvent.Register
/// The build function.
- public FuncControlTemplate(Func build)
+ public FuncControlTemplate(Func build)
: base(build)
{
}
+
+ public new ControlTemplateResult Build(ITemplatedControl param)
+ {
+ var (control, scope) = BuildWithNameScope(param);
+ return new ControlTemplateResult(control, scope);
+ }
}
-}
\ No newline at end of file
+}
diff --git a/src/Avalonia.Controls/Templates/FuncControlTemplate`2.cs b/src/Avalonia.Controls/Templates/FuncControlTemplate`2.cs
index 8e49b51cb8..eec7a6030f 100644
--- a/src/Avalonia.Controls/Templates/FuncControlTemplate`2.cs
+++ b/src/Avalonia.Controls/Templates/FuncControlTemplate`2.cs
@@ -17,9 +17,9 @@ namespace Avalonia.Controls.Templates
/// Initializes a new instance of the class.
///
/// The build function.
- public FuncControlTemplate(Func build)
- : base(x => build((T)x))
+ public FuncControlTemplate(Func build)
+ : base((x, s) => build((T)x, s))
{
}
}
-}
\ No newline at end of file
+}
diff --git a/src/Avalonia.Controls/Templates/FuncDataTemplate.cs b/src/Avalonia.Controls/Templates/FuncDataTemplate.cs
index 1d90fcd01e..204540e431 100644
--- a/src/Avalonia.Controls/Templates/FuncDataTemplate.cs
+++ b/src/Avalonia.Controls/Templates/FuncDataTemplate.cs
@@ -17,7 +17,7 @@ namespace Avalonia.Controls.Templates
///
public static readonly FuncDataTemplate Default =
new FuncDataTemplate(
- data =>
+ (data, s) =>
{
if (data != null)
{
@@ -49,7 +49,7 @@ namespace Avalonia.Controls.Templates
/// Whether the control can be recycled.
public FuncDataTemplate(
Type type,
- Func build,
+ Func build,
bool supportsRecycling = false)
: this(o => IsInstance(o, type), build, supportsRecycling)
{
@@ -67,7 +67,7 @@ namespace Avalonia.Controls.Templates
/// Whether the control can be recycled.
public FuncDataTemplate(
Func match,
- Func build,
+ Func build,
bool supportsRecycling = false)
: base(build)
{
@@ -105,4 +105,4 @@ namespace Avalonia.Controls.Templates
return (o != null) && t.GetTypeInfo().IsAssignableFrom(o.GetType().GetTypeInfo());
}
}
-}
\ No newline at end of file
+}
diff --git a/src/Avalonia.Controls/Templates/FuncDataTemplate`1.cs b/src/Avalonia.Controls/Templates/FuncDataTemplate`1.cs
index 9339aa6924..68b737928d 100644
--- a/src/Avalonia.Controls/Templates/FuncDataTemplate`1.cs
+++ b/src/Avalonia.Controls/Templates/FuncDataTemplate`1.cs
@@ -18,7 +18,7 @@ namespace Avalonia.Controls.Templates
/// A function which when passed an object of returns a control.
///
/// Whether the control can be recycled.
- public FuncDataTemplate(Func build, bool supportsRecycling = false)
+ public FuncDataTemplate(Func build, bool supportsRecycling = false)
: base(typeof(T), CastBuild(build), supportsRecycling)
{
}
@@ -35,12 +35,30 @@ namespace Avalonia.Controls.Templates
/// Whether the control can be recycled.
public FuncDataTemplate(
Func match,
- Func build,
+ Func build,
bool supportsRecycling = false)
: base(CastMatch(match), CastBuild(build), supportsRecycling)
{
}
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ /// A function which determines whether the data template matches the specified data.
+ ///
+ ///
+ /// A function which when passed an object of returns a control.
+ ///
+ /// Whether the control can be recycled.
+ public FuncDataTemplate(
+ Func match,
+ Func build,
+ bool supportsRecycling = false)
+ : this(match, (a, _) => build(a), supportsRecycling)
+ {
+ }
+
///
/// Casts a strongly typed match function to a weakly typed one.
///
@@ -57,9 +75,9 @@ namespace Avalonia.Controls.Templates
/// The strong data type.
/// The strongly typed function.
/// The weakly typed function.
- private static Func CastBuild(Func f)
+ private static Func CastBuild(Func f)
{
- return o => f((T)o);
+ return (o, s) => f((T)o, s);
}
}
}
diff --git a/src/Avalonia.Controls/Templates/FuncMemberSelector.cs b/src/Avalonia.Controls/Templates/FuncMemberSelector.cs
deleted file mode 100644
index 5ab186261e..0000000000
--- a/src/Avalonia.Controls/Templates/FuncMemberSelector.cs
+++ /dev/null
@@ -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
-{
- ///
- /// Selects a member of an object using a .
- ///
- public class FuncMemberSelector : IMemberSelector
- {
- private readonly Func _selector;
-
- ///
- /// Initializes a new instance of the
- /// class.
- ///
- /// The selector.
- public FuncMemberSelector(Func selector)
- {
- this._selector = selector;
- }
-
- ///
- /// Selects a member of an object.
- ///
- /// The object.
- /// The selected member.
- public object Select(object o)
- {
- return (o is TObject) ? _selector((TObject)o) : default(TMember);
- }
- }
-}
diff --git a/src/Avalonia.Controls/Templates/FuncTemplateNameScopeExtensions.cs b/src/Avalonia.Controls/Templates/FuncTemplateNameScopeExtensions.cs
new file mode 100644
index 0000000000..76bc298e30
--- /dev/null
+++ b/src/Avalonia.Controls/Templates/FuncTemplateNameScopeExtensions.cs
@@ -0,0 +1,14 @@
+using System;
+
+namespace Avalonia.Controls.Templates
+{
+ public static class FuncTemplateNameScopeExtensions
+ {
+ public static T RegisterInNameScope(this T control, INameScope scope)
+ where T : StyledElement
+ {
+ scope.Register(control.Name, control);
+ return control;
+ }
+ }
+}
diff --git a/src/Avalonia.Controls/Templates/FuncTemplate`2.cs b/src/Avalonia.Controls/Templates/FuncTemplate`2.cs
index b1ce63fdf1..e0608d0471 100644
--- a/src/Avalonia.Controls/Templates/FuncTemplate`2.cs
+++ b/src/Avalonia.Controls/Templates/FuncTemplate`2.cs
@@ -13,13 +13,13 @@ namespace Avalonia.Controls.Templates
public class FuncTemplate : ITemplate
where TControl : IControl
{
- private readonly Func _func;
+ private readonly Func _func;
///
/// Initializes a new instance of the class.
///
/// The function used to create the control.
- public FuncTemplate(Func func)
+ public FuncTemplate(Func func)
{
Contract.Requires(func != null);
@@ -35,7 +35,14 @@ namespace Avalonia.Controls.Templates
///
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);
}
}
-}
\ No newline at end of file
+}
diff --git a/src/Avalonia.Controls/Templates/FuncTreeDataTemplate.cs b/src/Avalonia.Controls/Templates/FuncTreeDataTemplate.cs
index e7c9cf8608..998ef0e9f2 100644
--- a/src/Avalonia.Controls/Templates/FuncTreeDataTemplate.cs
+++ b/src/Avalonia.Controls/Templates/FuncTreeDataTemplate.cs
@@ -28,7 +28,7 @@ namespace Avalonia.Controls.Templates
///
public FuncTreeDataTemplate(
Type type,
- Func build,
+ Func build,
Func itemsSelector)
: this(o => IsInstance(o, type), build, itemsSelector)
{
@@ -48,7 +48,7 @@ namespace Avalonia.Controls.Templates
///
public FuncTreeDataTemplate(
Func match,
- Func build,
+ Func build,
Func itemsSelector)
: base(match, build)
{
diff --git a/src/Avalonia.Controls/Templates/FuncTreeDataTemplate`1.cs b/src/Avalonia.Controls/Templates/FuncTreeDataTemplate`1.cs
index 4ca96f60bd..41f870ab42 100644
--- a/src/Avalonia.Controls/Templates/FuncTreeDataTemplate`1.cs
+++ b/src/Avalonia.Controls/Templates/FuncTreeDataTemplate`1.cs
@@ -23,7 +23,7 @@ namespace Avalonia.Controls.Templates
/// items.
///
public FuncTreeDataTemplate(
- Func build,
+ Func build,
Func itemsSelector)
: base(
typeof(T),
@@ -46,7 +46,7 @@ namespace Avalonia.Controls.Templates
///
public FuncTreeDataTemplate(
Func match,
- Func build,
+ Func build,
Func itemsSelector)
: base(
CastMatch(match),
@@ -65,6 +65,17 @@ namespace Avalonia.Controls.Templates
return o => (o is T) && f((T)o);
}
+ ///
+ /// Casts a function with a typed parameter to an untyped function.
+ ///
+ /// The result.
+ /// The typed function.
+ /// The untyped function.
+ private static Func Cast(Func f)
+ {
+ return (o, s) => f((T)o, s);
+ }
+
///
/// Casts a function with a typed parameter to an untyped function.
///
diff --git a/src/Avalonia.Controls/Templates/IControlTemplate.cs b/src/Avalonia.Controls/Templates/IControlTemplate.cs
index 408329c450..a3ef0fa954 100644
--- a/src/Avalonia.Controls/Templates/IControlTemplate.cs
+++ b/src/Avalonia.Controls/Templates/IControlTemplate.cs
@@ -9,7 +9,25 @@ namespace Avalonia.Controls.Templates
///
/// Interface representing a template used to build a .
///
- public interface IControlTemplate : ITemplate
+ public interface IControlTemplate : ITemplate
{
}
-}
\ No newline at end of file
+
+ 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;
+ }
+ }
+}
diff --git a/src/Avalonia.Controls/Templates/IMemberSelector.cs b/src/Avalonia.Controls/Templates/IMemberSelector.cs
deleted file mode 100644
index e1ec42a849..0000000000
--- a/src/Avalonia.Controls/Templates/IMemberSelector.cs
+++ /dev/null
@@ -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
-{
- ///
- /// Selects a member of an object.
- ///
- public interface IMemberSelector
- {
- ///
- /// Selects a member of an object.
- ///
- /// The object.
- /// The selected member.
- object Select(object o);
- }
-}
diff --git a/src/Avalonia.Controls/Templates/ITemplate`2.cs b/src/Avalonia.Controls/Templates/ITemplate`2.cs
index f61292c64b..a0c0e88eca 100644
--- a/src/Avalonia.Controls/Templates/ITemplate`2.cs
+++ b/src/Avalonia.Controls/Templates/ITemplate`2.cs
@@ -8,7 +8,7 @@ namespace Avalonia.Controls.Templates
///
/// The type of the parameter.
/// The type of control.
- public interface ITemplate where TControl : IControl
+ public interface ITemplate
{
///
/// Creates the control.
@@ -19,4 +19,4 @@ namespace Avalonia.Controls.Templates
///
TControl Build(TParam param);
}
-}
\ No newline at end of file
+}
diff --git a/src/Avalonia.Controls/UserControl.cs b/src/Avalonia.Controls/UserControl.cs
index e42ca5e0e6..4f15839821 100644
--- a/src/Avalonia.Controls/UserControl.cs
+++ b/src/Avalonia.Controls/UserControl.cs
@@ -9,40 +9,8 @@ namespace Avalonia.Controls
///
/// Provides the base class for defining a new control that encapsulates related existing controls and provides its own logic.
///
- public class UserControl : ContentControl, IStyleable, INameScope
+ public class UserControl : ContentControl, IStyleable
{
- private readonly NameScope _nameScope = new NameScope();
- ///
- event EventHandler INameScope.Registered
- {
- add { _nameScope.Registered += value; }
- remove { _nameScope.Registered -= value; }
- }
-
- ///
- event EventHandler INameScope.Unregistered
- {
- add { _nameScope.Unregistered += value; }
- remove { _nameScope.Unregistered -= value; }
- }
-
- ///
- void INameScope.Register(string name, object element)
- {
- _nameScope.Register(name, element);
- }
-
- ///
- object INameScope.Find(string name)
- {
- return _nameScope.Find(name);
- }
-
- ///
- void INameScope.Unregister(string name)
- {
- _nameScope.Unregister(name);
- }
}
}
diff --git a/src/Avalonia.Controls/Window.cs b/src/Avalonia.Controls/Window.cs
index 5c117f508b..d2793fe0dd 100644
--- a/src/Avalonia.Controls/Window.cs
+++ b/src/Avalonia.Controls/Window.cs
@@ -48,7 +48,7 @@ namespace Avalonia.Controls
///
/// A top-level window.
///
- public class Window : WindowBase, IStyleable, IFocusScope, ILayoutRoot, INameScope
+ public class Window : WindowBase, IStyleable, IFocusScope, ILayoutRoot
{
///
/// Defines the property.
@@ -157,20 +157,6 @@ namespace Avalonia.Controls
_maxPlatformClientSize = PlatformImpl?.MaxClientSize ?? default(Size);
}
- ///
- event EventHandler INameScope.Registered
- {
- add { _nameScope.Registered += value; }
- remove { _nameScope.Registered -= value; }
- }
-
- ///
- event EventHandler INameScope.Unregistered
- {
- add { _nameScope.Unregistered += value; }
- remove { _nameScope.Unregistered -= value; }
- }
-
///
/// Gets the platform-specific window implementation.
///
@@ -494,24 +480,6 @@ namespace Avalonia.Controls
}
}
- ///
- void INameScope.Register(string name, object element)
- {
- _nameScope.Register(name, element);
- }
-
- ///
- object INameScope.Find(string name)
- {
- return _nameScope.Find(name);
- }
-
- ///
- void INameScope.Unregister(string name)
- {
- _nameScope.Unregister(name);
- }
-
///
protected override Size MeasureOverride(Size availableSize)
{
diff --git a/src/Avalonia.OpenGL/GlInterface.cs b/src/Avalonia.OpenGL/GlInterface.cs
index 718afc4a94..f556949cfa 100644
--- a/src/Avalonia.OpenGL/GlInterface.cs
+++ b/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 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 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")]
diff --git a/src/Avalonia.ReactiveUI/AutoDataTemplateBindingHook.cs b/src/Avalonia.ReactiveUI/AutoDataTemplateBindingHook.cs
index 3f41f54363..4881c77034 100644
--- a/src/Avalonia.ReactiveUI/AutoDataTemplateBindingHook.cs
+++ b/src/Avalonia.ReactiveUI/AutoDataTemplateBindingHook.cs
@@ -16,7 +16,7 @@ namespace Avalonia.ReactiveUI
///
public class AutoDataTemplateBindingHook : IPropertyBindingHook
{
- private static FuncDataTemplate DefaultItemTemplate = new FuncDataTemplate(x =>
+ private static FuncDataTemplate DefaultItemTemplate = new FuncDataTemplate((x, _) =>
{
var control = new ViewModelViewHost();
var context = control.GetObservable(Control.DataContextProperty);
@@ -52,4 +52,4 @@ namespace Avalonia.ReactiveUI
return true;
}
}
-}
\ No newline at end of file
+}
diff --git a/src/Avalonia.Styling/Controls/ChildNameScope.cs b/src/Avalonia.Styling/Controls/ChildNameScope.cs
new file mode 100644
index 0000000000..e6707e71db
--- /dev/null
+++ b/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 FindAsync(string name)
+ {
+ var found = Find(name);
+ if (found != null)
+ return new SynchronousCompletionAsyncResult(found);
+ // Not found and both current and parent scope are in completed state
+ if(IsCompleted)
+ return new SynchronousCompletionAsyncResult(null);
+ return DoFindAsync(name);
+ }
+
+ public SynchronousCompletionAsyncResult DoFindAsync(string name)
+ {
+ var src = new SynchronousCompletionAsyncResultSource();
+
+ 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;
+ }
+}
diff --git a/src/Avalonia.Styling/Controls/INameScope.cs b/src/Avalonia.Styling/Controls/INameScope.cs
index e4ff90c573..b4f4c8e57b 100644
--- a/src/Avalonia.Styling/Controls/INameScope.cs
+++ b/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
///
public interface INameScope
{
- ///
- /// Raised when an element is registered with the name scope.
- ///
- event EventHandler Registered;
-
- ///
- /// Raised when an element is unregistered with the name scope.
- ///
- event EventHandler Unregistered;
-
///
/// Registers an element in the name scope.
///
@@ -28,16 +20,30 @@ namespace Avalonia.Controls
void Register(string name, object element);
///
- /// 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.
+ ///
+ /// The name.
+ /// The element, or null if the name was not found.
+ SynchronousCompletionAsyncResult FindAsync(string name);
+
+ ///
+ /// Finds a named element in the name scope, returns immediately, doesn't traverse the name scope stack
///
/// The name.
/// The element, or null if the name was not found.
object Find(string name);
///
- /// Unregisters an element with the name scope.
+ /// Marks the name scope as completed, no further registrations will be allowed
///
- /// The name.
- void Unregister(string name);
+ void Complete();
+
+ ///
+ /// Returns whether further registrations are allowed on the scope
+ ///
+ bool IsCompleted { get; }
+
+
}
}
diff --git a/src/Avalonia.Styling/Controls/NameScope.cs b/src/Avalonia.Styling/Controls/NameScope.cs
index e3a29af541..ec01a53cfd 100644
--- a/src/Avalonia.Styling/Controls/NameScope.cs
+++ b/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 NameScopeProperty =
AvaloniaProperty.RegisterAttached("NameScope");
+ ///
+ public bool IsCompleted { get; private set; }
+
private readonly Dictionary _inner = new Dictionary();
- ///
- /// Raised when an element is registered with the name scope.
- ///
- public event EventHandler Registered;
-
- ///
- /// Raised when an element is unregistered with the name scope.
- ///
- public event EventHandler Unregistered;
-
- ///
- /// Finds the containing name scope for a styled element.
- ///
- /// The styled element.
- /// The containing name scope.
- public static INameScope FindNameScope(StyledElement styled)
- {
- Contract.Requires(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> _pendingSearches =
+ new Dictionary>();
+
///
/// Gets the value of the attached on a styled element.
///
@@ -80,13 +52,11 @@ namespace Avalonia.Controls
styled.SetValue(NameScopeProperty, value);
}
- ///
- /// Registers an element with the name scope.
- ///
- /// The element name.
- /// The element.
+ ///
public void Register(string name, object element)
{
+ if (IsCompleted)
+ throw new InvalidOperationException("NameScope is completed, no further registrations are allowed");
Contract.Requires(name != null);
Contract.Requires(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);
+ }
}
}
- ///
- /// Finds a named element in the name scope.
- ///
- /// The name.
- /// The element, or null if the name was not found.
+ public SynchronousCompletionAsyncResult FindAsync(string name)
+ {
+ var found = Find(name);
+ if (found != null)
+ return new SynchronousCompletionAsyncResult(found);
+ if (IsCompleted)
+ return new SynchronousCompletionAsyncResult((object)null);
+ if (!_pendingSearches.TryGetValue(name, out var tcs))
+ // We are intentionally running continuations synchronously here
+ _pendingSearches[name] = tcs = new SynchronousCompletionAsyncResultSource();
+
+ return tcs.AsyncResult;
+ }
+
+ ///
public object Find(string name)
{
Contract.Requires(name != null);
@@ -120,21 +104,14 @@ namespace Avalonia.Controls
return result;
}
- ///
- /// Unregisters an element with the name scope.
- ///
- /// The name.
- public void Unregister(string name)
+ public void Complete()
{
- Contract.Requires(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();
}
+
+
}
}
diff --git a/src/Avalonia.Styling/Controls/NameScopeExtensions.cs b/src/Avalonia.Styling/Controls/NameScopeExtensions.cs
index 991a97a614..5921acd3b6 100644
--- a/src/Avalonia.Styling/Controls/NameScopeExtensions.cs
+++ b/src/Avalonia.Styling/Controls/NameScopeExtensions.cs
@@ -37,6 +37,25 @@ namespace Avalonia.Controls
return (T)result;
}
+ ///
+ /// Finds a named element in an .
+ ///
+ /// The element type.
+ /// The control to take the name scope from.
+ /// The name.
+ /// The named element or null if not found.
+ public static T Find(this ILogical anchor, string name)
+ where T : class
+ {
+ Contract.Requires(anchor != null);
+ Contract.Requires(name != null);
+ var styledAnchor = anchor as StyledElement;
+ if (styledAnchor == null)
+ return null;
+ var nameScope = (anchor as INameScope) ?? NameScope.GetNameScope(styledAnchor);
+ return nameScope?.Find(name);
+ }
+
///
/// Gets a named element from an or throws if no element of the
/// requested name was found.
@@ -67,6 +86,28 @@ namespace Avalonia.Controls
return (T)result;
}
+ ///
+ /// Gets a named element from an or throws if no element of the
+ /// requested name was found.
+ ///
+ /// The element type.
+ /// The control to take the name scope from.
+ /// The name.
+ /// The named element.
+ public static T Get(this ILogical anchor, string name)
+ where T : class
+ {
+ Contract.Requires(anchor != null);
+ Contract.Requires(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(name);
+ }
+
public static INameScope FindNameScope(this ILogical control)
{
Contract.Requires(control != null);
diff --git a/src/Avalonia.Styling/Controls/NameScopeLocator.cs b/src/Avalonia.Styling/Controls/NameScopeLocator.cs
new file mode 100644
index 0000000000..719cf9344b
--- /dev/null
+++ b/src/Avalonia.Styling/Controls/NameScopeLocator.cs
@@ -0,0 +1,64 @@
+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
+ {
+ ///
+ /// Tracks a named control relative to another control.
+ ///
+ ///
+ /// The control relative from which the other control should be found.
+ ///
+ /// The name of the control to find.
+ public static IObservable Track(INameScope scope, string name)
+ {
+ return new NeverEndingSynchronousCompletionAsyncResultObservable(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 : IObservable
+ {
+ private T _value;
+ private SynchronousCompletionAsyncResult? _asyncResult;
+
+ public NeverEndingSynchronousCompletionAsyncResultObservable(SynchronousCompletionAsyncResult task)
+ {
+ if (task.IsCompleted)
+ _value = task.GetResult();
+ else
+ _asyncResult = task;
+ }
+
+ public IDisposable Subscribe(IObserver 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;
+ }
+ }
+ }
+}
diff --git a/src/Avalonia.Styling/LogicalTree/ControlLocator.cs b/src/Avalonia.Styling/LogicalTree/ControlLocator.cs
index 7186143bf9..491f38c153 100644
--- a/src/Avalonia.Styling/LogicalTree/ControlLocator.cs
+++ b/src/Avalonia.Styling/LogicalTree/ControlLocator.cs
@@ -15,18 +15,6 @@ namespace Avalonia.LogicalTree
///
public static class ControlLocator
{
- ///
- /// Tracks a named control relative to another control.
- ///
- ///
- /// The control relative from which the other control should be found.
- ///
- /// The name of the control to find.
- public static IObservable Track(ILogical relativeTo, string name)
- {
- return new ControlTracker(relativeTo, name);
- }
-
public static IObservable 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
{
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(_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);
}
}
}
diff --git a/src/Avalonia.Styling/StyledElement.cs b/src/Avalonia.Styling/StyledElement.cs
index 146a4c75e7..b4aa89d5bf 100644
--- a/src/Avalonia.Styling/StyledElement.cs
+++ b/src/Avalonia.Styling/StyledElement.cs
@@ -61,7 +61,6 @@ namespace Avalonia
private readonly Classes _classes = new Classes();
private bool _isAttachedToLogicalTree;
private IAvaloniaList _logicalChildren;
- private INameScope _nameScope;
private IResourceDictionary _resources;
private Styles _styles;
private bool _styled;
@@ -82,7 +81,6 @@ namespace Avalonia
///
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()?.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);
diff --git a/src/Avalonia.Styling/Styling/Setter.cs b/src/Avalonia.Styling/Styling/Setter.cs
index 3702259f35..9312d38c51 100644
--- a/src/Avalonia.Styling/Styling/Setter.cs
+++ b/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;
}
diff --git a/src/Avalonia.Styling/Styling/Styles.cs b/src/Avalonia.Styling/Styling/Styles.cs
index 789bb6ffd3..a4563110a9 100644
--- a/src/Avalonia.Styling/Styling/Styles.cs
+++ b/src/Avalonia.Styling/Styling/Styles.cs
@@ -180,7 +180,7 @@ namespace Avalonia.Styling
///
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;
}
diff --git a/src/Avalonia.Themes.Default/AutoCompleteBox.xaml b/src/Avalonia.Themes.Default/AutoCompleteBox.xaml
index 11d8a344d9..788b60892b 100644
--- a/src/Avalonia.Themes.Default/AutoCompleteBox.xaml
+++ b/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" />
diff --git a/src/Avalonia.Themes.Default/Avalonia.Themes.Default.csproj b/src/Avalonia.Themes.Default/Avalonia.Themes.Default.csproj
index eb2b6789e1..3d525955b4 100644
--- a/src/Avalonia.Themes.Default/Avalonia.Themes.Default.csproj
+++ b/src/Avalonia.Themes.Default/Avalonia.Themes.Default.csproj
@@ -18,6 +18,5 @@
-
diff --git a/src/Avalonia.Themes.Default/Carousel.xaml b/src/Avalonia.Themes.Default/Carousel.xaml
index efe12c4333..955a49a974 100644
--- a/src/Avalonia.Themes.Default/Carousel.xaml
+++ b/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}"/>
-
\ No newline at end of file
+
diff --git a/src/Avalonia.Themes.Default/ComboBox.xaml b/src/Avalonia.Themes.Default/ComboBox.xaml
index ca6c2e372e..6227962a48 100644
--- a/src/Avalonia.Themes.Default/ComboBox.xaml
+++ b/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}"
/>
diff --git a/src/Avalonia.Themes.Default/DataValidationErrors.xaml b/src/Avalonia.Themes.Default/DataValidationErrors.xaml
index 0c40a7eb25..f4145a51f5 100644
--- a/src/Avalonia.Themes.Default/DataValidationErrors.xaml
+++ b/src/Avalonia.Themes.Default/DataValidationErrors.xaml
@@ -29,7 +29,7 @@
-
+
diff --git a/src/Avalonia.Themes.Default/ItemsControl.xaml b/src/Avalonia.Themes.Default/ItemsControl.xaml
index 7b6671b42c..f3def542fc 100644
--- a/src/Avalonia.Themes.Default/ItemsControl.xaml
+++ b/src/Avalonia.Themes.Default/ItemsControl.xaml
@@ -4,8 +4,7 @@
+ ItemTemplate="{TemplateBinding ItemTemplate}"/>
-
\ No newline at end of file
+
diff --git a/src/Avalonia.Themes.Default/ListBox.xaml b/src/Avalonia.Themes.Default/ListBox.xaml
index 57b0c541b8..59c596bcaa 100644
--- a/src/Avalonia.Themes.Default/ListBox.xaml
+++ b/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}"/>
-
\ No newline at end of file
+
diff --git a/src/Avalonia.Themes.Default/MenuItem.xaml b/src/Avalonia.Themes.Default/MenuItem.xaml
index be86e8b14c..a794d15577 100644
--- a/src/Avalonia.Themes.Default/MenuItem.xaml
+++ b/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"/>
+ Margin="2"/>
+ ItemTemplate="{TemplateBinding ItemTemplate}">
@@ -18,4 +17,4 @@
-
\ No newline at end of file
+
diff --git a/src/Avalonia.Themes.Default/TreeView.xaml b/src/Avalonia.Themes.Default/TreeView.xaml
index 4e38c6db3a..6ed2fd17b8 100644
--- a/src/Avalonia.Themes.Default/TreeView.xaml
+++ b/src/Avalonia.Themes.Default/TreeView.xaml
@@ -15,8 +15,7 @@
+ Margin="{TemplateBinding Padding}"/>
diff --git a/src/Avalonia.Themes.Default/TreeViewItem.xaml b/src/Avalonia.Themes.Default/TreeViewItem.xaml
index b5e0e7a005..5dd082cf7a 100644
--- a/src/Avalonia.Themes.Default/TreeViewItem.xaml
+++ b/src/Avalonia.Themes.Default/TreeViewItem.xaml
@@ -32,8 +32,7 @@
+ ItemsPanel="{TemplateBinding ItemsPanel}"/>
diff --git a/src/Avalonia.Visuals/Rendering/RenderLayers.cs b/src/Avalonia.Visuals/Rendering/RenderLayers.cs
index 0ff7862ab6..e82934fbad 100644
--- a/src/Avalonia.Visuals/Rendering/RenderLayers.cs
+++ b/src/Avalonia.Visuals/Rendering/RenderLayers.cs
@@ -8,8 +8,8 @@ namespace Avalonia.Rendering
{
public class RenderLayers : IEnumerable
{
- private List _inner = new List();
- private Dictionary _index = new Dictionary();
+ private readonly List _inner = new List();
+ private readonly Dictionary _index = new Dictionary();
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)
diff --git a/src/Avalonia.X11/Glx/GlxDisplay.cs b/src/Avalonia.X11/Glx/GlxDisplay.cs
index 5602b33280..04f2a7137c 100644
--- a/src/Avalonia.X11/Glx/GlxDisplay.cs
+++ b/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()
+ ?.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,
diff --git a/src/Avalonia.X11/X11Platform.cs b/src/Avalonia.X11/X11Platform.cs
index cf5902eff7..7bdc61eb28 100644
--- a/src/Avalonia.X11/X11Platform.cs
+++ b/src/Avalonia.X11/X11Platform.cs
@@ -96,6 +96,14 @@ namespace Avalonia
{
public bool UseEGL { get; set; }
public bool UseGpu { get; set; } = true;
+
+ public List GlxRendererBlacklist { get; set; } = new List
+ {
+ // 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; }
}
diff --git a/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj b/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj
index 6f3dabd568..06c5375520 100644
--- a/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj
+++ b/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj
@@ -12,7 +12,6 @@
-
@@ -33,7 +32,6 @@
-
diff --git a/src/Markup/Avalonia.Markup.Xaml/Converters/MemberSelectorTypeConverter.cs b/src/Markup/Avalonia.Markup.Xaml/Converters/MemberSelectorTypeConverter.cs
deleted file mode 100644
index 8dc052fe63..0000000000
--- a/src/Markup/Avalonia.Markup.Xaml/Converters/MemberSelectorTypeConverter.cs
+++ /dev/null
@@ -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);
- }
- }
-}
\ No newline at end of file
diff --git a/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/BindingExtension.cs b/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/BindingExtension.cs
index 726f4221f8..a466714136 100644
--- a/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/BindingExtension.cs
+++ b/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(serviceProvider.GetService())
};
}
diff --git a/src/Markup/Avalonia.Markup.Xaml/Templates/ControlTemplate.cs b/src/Markup/Avalonia.Markup.Xaml/Templates/ControlTemplate.cs
index e28828625f..f46ee8787b 100644
--- a/src/Markup/Avalonia.Markup.Xaml/Templates/ControlTemplate.cs
+++ b/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);
}
-}
\ No newline at end of file
+}
diff --git a/src/Markup/Avalonia.Markup.Xaml/Templates/DataTemplate.cs b/src/Markup/Avalonia.Markup.Xaml/Templates/DataTemplate.cs
index ec5de8a059..c81a3718a7 100644
--- a/src/Markup/Avalonia.Markup.Xaml/Templates/DataTemplate.cs
+++ b/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;
}
-}
\ No newline at end of file
+}
diff --git a/src/Markup/Avalonia.Markup.Xaml/Templates/ItemsPanelTemplate.cs b/src/Markup/Avalonia.Markup.Xaml/Templates/ItemsPanelTemplate.cs
index 6e4d2a3980..0dd4e57a7f 100644
--- a/src/Markup/Avalonia.Markup.Xaml/Templates/ItemsPanelTemplate.cs
+++ b/src/Markup/Avalonia.Markup.Xaml/Templates/ItemsPanelTemplate.cs
@@ -14,8 +14,8 @@ namespace Avalonia.Markup.Xaml.Templates
public object Content { get; set; }
public IPanel Build()
- => (IPanel)TemplateContent.Load(Content);
+ => (IPanel)TemplateContent.Load(Content).Control;
object ITemplate.Build() => Build();
}
-}
\ No newline at end of file
+}
diff --git a/src/Markup/Avalonia.Markup.Xaml/Templates/MemberSelector.cs b/src/Markup/Avalonia.Markup.Xaml/Templates/MemberSelector.cs
deleted file mode 100644
index fa91ab60ff..0000000000
--- a/src/Markup/Avalonia.Markup.Xaml/Templates/MemberSelector.cs
+++ /dev/null
@@ -1,48 +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 Avalonia.Controls.Templates;
-using Avalonia.Data;
-using Avalonia.Data.Core;
-using Avalonia.Markup.Parsers;
-using System;
-using System.Reactive.Linq;
-
-namespace Avalonia.Markup.Xaml.Templates
-{
- public class MemberSelector : IMemberSelector
- {
- private string _memberName;
-
- public string MemberName
- {
- get { return _memberName; }
- set
- {
- if (_memberName != value)
- {
- _memberName = value;
- }
- }
- }
-
- public static MemberSelector Parse(string s)
- {
- return new MemberSelector { MemberName = s };
- }
-
- public object Select(object o)
- {
- if (string.IsNullOrEmpty(MemberName))
- {
- return o;
- }
-
- var expression = ExpressionObserverBuilder.Build(o, MemberName);
- object result = AvaloniaProperty.UnsetValue;
-
- expression.Subscribe(x => result = x);
- return (result == AvaloniaProperty.UnsetValue || result is BindingNotification) ? null : result;
- }
- }
-}
\ No newline at end of file
diff --git a/src/Markup/Avalonia.Markup.Xaml/Templates/Template.cs b/src/Markup/Avalonia.Markup.Xaml/Templates/Template.cs
index b8fec4e8d0..5f36cfe0ac 100644
--- a/src/Markup/Avalonia.Markup.Xaml/Templates/Template.cs
+++ b/src/Markup/Avalonia.Markup.Xaml/Templates/Template.cs
@@ -13,8 +13,8 @@ namespace Avalonia.Markup.Xaml.Templates
[TemplateContent]
public object Content { get; set; }
- public IControl Build() => TemplateContent.Load(Content);
+ public IControl Build() => TemplateContent.Load(Content).Control;
object ITemplate.Build() => Build();
}
-}
\ No newline at end of file
+}
diff --git a/src/Markup/Avalonia.Markup.Xaml/Templates/TemplateContent.cs b/src/Markup/Avalonia.Markup.Xaml/Templates/TemplateContent.cs
index ead373d380..6a30c3861a 100644
--- a/src/Markup/Avalonia.Markup.Xaml/Templates/TemplateContent.cs
+++ b/src/Markup/Avalonia.Markup.Xaml/Templates/TemplateContent.cs
@@ -4,17 +4,18 @@
using System;
using Avalonia.Controls;
using System.Collections.Generic;
+using Avalonia.Controls.Templates;
namespace Avalonia.Markup.Xaml.Templates
{
public static class TemplateContent
{
- public static IControl Load(object templateContent)
+ public static ControlTemplateResult Load(object templateContent)
{
if (templateContent is Func direct)
{
- return (IControl)direct(null);
+ return (ControlTemplateResult)direct(null);
}
throw new ArgumentException(nameof(templateContent));
}
diff --git a/src/Markup/Avalonia.Markup.Xaml/Templates/TreeDataTemplate.cs b/src/Markup/Avalonia.Markup.Xaml/Templates/TreeDataTemplate.cs
index bd2b9d2efd..0e2a131afd 100644
--- a/src/Markup/Avalonia.Markup.Xaml/Templates/TreeDataTemplate.cs
+++ b/src/Markup/Avalonia.Markup.Xaml/Templates/TreeDataTemplate.cs
@@ -51,9 +51,9 @@ namespace Avalonia.Markup.Xaml.Templates
public IControl Build(object data)
{
- var visualTreeForItem = TemplateContent.Load(Content);
+ var visualTreeForItem = TemplateContent.Load(Content).Control;
visualTreeForItem.DataContext = data;
return visualTreeForItem;
}
}
-}
\ No newline at end of file
+}
diff --git a/src/Markup/Avalonia.Markup.Xaml/XamlIl/AvaloniaXamlIlRuntimeCompiler.cs b/src/Markup/Avalonia.Markup.Xaml/XamlIl/AvaloniaXamlIlRuntimeCompiler.cs
index 167b75603f..b91d679fba 100644
--- a/src/Markup/Avalonia.Markup.Xaml/XamlIl/AvaloniaXamlIlRuntimeCompiler.cs
+++ b/src/Markup/Avalonia.Markup.Xaml/XamlIl/AvaloniaXamlIlRuntimeCompiler.cs
@@ -156,7 +156,7 @@ namespace Avalonia.Markup.Xaml.XamlIl
{
overrideField.SetValue(null,
new Action(
- target => { populateCb(XamlIlRuntimeHelpers.RootServiceProviderV1, target); }));
+ target => { populateCb(XamlIlRuntimeHelpers.CreateRootServiceProviderV2(), target); }));
try
{
return Activator.CreateInstance(targetType);
@@ -170,11 +170,11 @@ namespace Avalonia.Markup.Xaml.XamlIl
var createCb = Expression.Lambda>(
Expression.Convert(Expression.Call(
created.GetMethod(AvaloniaXamlIlCompiler.BuildName), isp), typeof(object)), isp).Compile();
- return createCb(XamlIlRuntimeHelpers.RootServiceProviderV1);
+ return createCb(XamlIlRuntimeHelpers.CreateRootServiceProviderV2());
}
else
{
- populateCb(XamlIlRuntimeHelpers.RootServiceProviderV1, rootInstance);
+ populateCb(XamlIlRuntimeHelpers.CreateRootServiceProviderV2(), rootInstance);
return rootInstance;
}
}
diff --git a/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/AvaloniaXamlIlLanguage.cs b/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/AvaloniaXamlIlLanguage.cs
index c25e1186d0..63c8b1c074 100644
--- a/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/AvaloniaXamlIlLanguage.cs
+++ b/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/AvaloniaXamlIlLanguage.cs
@@ -35,6 +35,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
},
ProvideValueTarget = typeSystem.GetType("Avalonia.Markup.Xaml.IProvideValueTarget"),
RootObjectProvider = typeSystem.GetType("Avalonia.Markup.Xaml.IRootObjectProvider"),
+ RootObjectProviderIntermediateRootPropertyName = "IntermediateRootObject",
UriContextProvider = typeSystem.GetType("Avalonia.Markup.Xaml.IUriContext"),
ParentStackProvider =
typeSystem.GetType("Avalonia.Markup.Xaml.XamlIl.Runtime.IAvaloniaXamlIlParentStackProvider"),
@@ -53,9 +54,30 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
ProvideValueTargetPropertyEmitter = XamlIlAvaloniaPropertyHelper.Emit,
};
rv.CustomAttributeResolver = new AttributeResolver(typeSystem, rv);
+ rv.ContextTypeBuilderCallback = (b, c) => EmitNameScopeField(rv, typeSystem, b, c);
return rv;
}
+ public const string ContextNameScopeFieldName = "AvaloniaNameScope";
+
+ private static void EmitNameScopeField(XamlIlLanguageTypeMappings mappings,
+ IXamlIlTypeSystem typeSystem,
+ IXamlIlTypeBuilder typebuilder, IXamlIlEmitter constructor)
+ {
+
+ var nameScopeType = typeSystem.FindType("Avalonia.Controls.INameScope");
+ var field = typebuilder.DefineField(nameScopeType,
+ ContextNameScopeFieldName, true, false);
+ constructor
+ .Ldarg_0()
+ .Ldarg(1)
+ .Ldtype(nameScopeType)
+ .EmitCall(mappings.ServiceProvider.GetMethod(new FindMethodMethodSignature("GetService",
+ typeSystem.FindType("System.Object"), typeSystem.FindType("System.Type"))))
+ .Stfld(field);
+ }
+
+
class AttributeResolver : IXamlIlCustomAttributeResolver
{
private readonly IXamlIlType _typeConverterAttribute;
@@ -81,8 +103,6 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
var ilist = typeSystem.GetType("System.Collections.Generic.IList`1");
AddType(ilist.MakeGenericType(typeSystem.GetType("Avalonia.Point")),
typeSystem.GetType("Avalonia.Markup.Xaml.Converters.PointsListTypeConverter"));
- Add("Avalonia.Controls.Templates.IMemberSelector",
- "Avalonia.Markup.Xaml.Converters.MemberSelectorTypeConverter");
Add("Avalonia.Controls.WindowIcon","Avalonia.Markup.Xaml.Converters.IconTypeConverter");
Add("System.Globalization.CultureInfo", "System.ComponentModel.CultureInfoConverter");
Add("System.Uri", "Avalonia.Markup.Xaml.Converters.AvaloniaUriTypeConverter");
diff --git a/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AddNameScopeRegistration.cs b/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AddNameScopeRegistration.cs
index 33056fa3e8..805b733feb 100644
--- a/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AddNameScopeRegistration.cs
+++ b/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AddNameScopeRegistration.cs
@@ -15,7 +15,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
&& pa.Property.DeclaringType.FullName == "Avalonia.StyledElement")
{
if (context.ParentNodes().FirstOrDefault() is XamlIlManipulationGroupNode mg
- && mg.Children.OfType().Any())
+ && mg.Children.OfType().Any())
return node;
IXamlIlAstValueNode value = null;
@@ -41,54 +41,99 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
Children =
{
pa,
- new ScopeRegistrationNode(value)
+ new AvaloniaNameScopeRegistrationXamlIlNode(value, context.GetAvaloniaTypes())
}
};
}
+ if (!context.ParentNodes().Any()
+ && node is XamlIlValueWithManipulationNode mnode)
+ {
+ mnode.Manipulation = new XamlIlManipulationGroupNode(mnode,
+ new[]
+ {
+ mnode.Manipulation,
+ new HandleRootObjectScopeNode(mnode, context.GetAvaloniaTypes())
+ });
+ }
return node;
}
- class ScopeRegistrationNode : XamlIlAstNode, IXamlIlAstManipulationNode, IXamlIlAstEmitableNode
+ class HandleRootObjectScopeNode : XamlIlAstNode, IXamlIlAstManipulationNode, IXamlIlAstEmitableNode
{
- public IXamlIlAstValueNode Value { get; set; }
- public ScopeRegistrationNode(IXamlIlAstValueNode value) : base(value)
+ private readonly AvaloniaXamlIlWellKnownTypes _types;
+
+ public HandleRootObjectScopeNode(IXamlIlLineInfo lineInfo,
+ AvaloniaXamlIlWellKnownTypes types) : base(lineInfo)
{
- Value = value;
+ _types = types;
}
- public override void VisitChildren(IXamlIlAstVisitor visitor)
- => Value = (IXamlIlAstValueNode)Value.Visit(visitor);
-
public XamlIlNodeEmitResult Emit(XamlIlEmitContext context, IXamlIlEmitter codeGen)
{
- var exts = context.Configuration.TypeSystem.GetType("Avalonia.Controls.NameScopeExtensions");
- var findNameScope = exts.FindMethod(m => m.Name == "FindNameScope");
- var registerMethod = findNameScope.ReturnType.FindMethod(m => m.Name == "Register");
- using (var targetLoc = context.GetLocal(context.Configuration.WellKnownTypes.Object))
- using (var nameScopeLoc = context.GetLocal(findNameScope.ReturnType))
+ var next = codeGen.DefineLabel();
+ var scopeField = context.RuntimeContext.ContextType.Fields.First(f =>
+ f.Name == AvaloniaXamlIlLanguage.ContextNameScopeFieldName);
+ using (var local = codeGen.LocalsPool.GetLocal(_types.StyledElement))
{
- var exit = codeGen.DefineLabel();
- codeGen
- // var target = {pop}
- .Stloc(targetLoc.Local)
- // var scope = target.FindNameScope()
- .Ldloc(targetLoc.Local)
- .Castclass(findNameScope.Parameters[0])
- .EmitCall(findNameScope)
- .Stloc(nameScopeLoc.Local)
- // if({scope} != null) goto call;
- .Ldloc(nameScopeLoc.Local)
- .Brfalse(exit)
- .Ldloc(nameScopeLoc.Local);
- context.Emit(Value, codeGen, Value.Type.GetClrType());
codeGen
- .Ldloc(targetLoc.Local)
- .EmitCall(registerMethod)
- .MarkLabel(exit);
+ .Isinst(_types.StyledElement)
+ .Dup()
+ .Stloc(local.Local)
+ .Brfalse(next)
+ .Ldloc(local.Local)
+ .Ldloc(context.ContextLocal)
+ .Ldfld(scopeField)
+ .EmitCall(_types.NameScopeSetNameScope, true)
+ .MarkLabel(next)
+ .Ldloc(context.ContextLocal)
+ .Ldfld(scopeField)
+ .EmitCall(_types.INameScopeComplete, true);
}
+
return XamlIlNodeEmitResult.Void(1);
+
}
}
}
+
+ class AvaloniaNameScopeRegistrationXamlIlNode : XamlIlAstNode, IXamlIlAstManipulationNode, IXamlIlAstEmitableNode
+ {
+ private readonly AvaloniaXamlIlWellKnownTypes _types;
+ public IXamlIlAstValueNode Name { get; set; }
+
+ public AvaloniaNameScopeRegistrationXamlIlNode(IXamlIlAstValueNode name, AvaloniaXamlIlWellKnownTypes types) : base(name)
+ {
+ _types = types;
+ Name = name;
+ }
+
+ public override void VisitChildren(IXamlIlAstVisitor visitor)
+ => Name = (IXamlIlAstValueNode)Name.Visit(visitor);
+
+ public XamlIlNodeEmitResult Emit(XamlIlEmitContext context, IXamlIlEmitter codeGen)
+ {
+ var scopeField = context.RuntimeContext.ContextType.Fields.First(f =>
+ f.Name == AvaloniaXamlIlLanguage.ContextNameScopeFieldName);
+
+ using (var targetLoc = context.GetLocal(context.Configuration.WellKnownTypes.Object))
+ {
+
+ codeGen
+ // var target = {pop}
+ .Stloc(targetLoc.Local)
+ // _context.NameScope.Register(Name, target)
+ .Ldloc(context.ContextLocal)
+ .Ldfld(scopeField);
+
+ context.Emit(Name, codeGen, Name.Type.GetClrType());
+
+ codeGen
+ .Ldloc(targetLoc.Local)
+ .EmitCall(_types.INameScopeRegister, true);
+ }
+
+ return XamlIlNodeEmitResult.Void(1);
+ }
+ }
}
diff --git a/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs b/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs
index c054e57380..1efae902c6 100644
--- a/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs
+++ b/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs
@@ -16,13 +16,21 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
public IXamlIlMethod AvaloniaObjectSetValueMethod { get; }
public IXamlIlType IDisposable { get; }
public XamlIlTypeWellKnownTypes XamlIlTypes { get; }
+ public XamlIlLanguageTypeMappings XamlIlMappings { get; }
public IXamlIlType Transitions { get; }
public IXamlIlType AssignBindingAttribute { get; }
public IXamlIlType UnsetValueType { get; }
+ public IXamlIlType StyledElement { get; }
+ public IXamlIlType NameScope { get; }
+ public IXamlIlMethod NameScopeSetNameScope { get; }
+ public IXamlIlType INameScope { get; }
+ public IXamlIlMethod INameScopeRegister { get; }
+ public IXamlIlMethod INameScopeComplete { get; }
public AvaloniaXamlIlWellKnownTypes(XamlIlAstTransformationContext ctx)
{
XamlIlTypes = ctx.Configuration.WellKnownTypes;
+ XamlIlMappings = ctx.Configuration.TypeMappings;
AvaloniaObject = ctx.Configuration.TypeSystem.GetType("Avalonia.AvaloniaObject");
IAvaloniaObject = ctx.Configuration.TypeSystem.GetType("Avalonia.IAvaloniaObject");
AvaloniaObjectExtensions = ctx.Configuration.TypeSystem.GetType("Avalonia.AvaloniaObjectExtensions");
@@ -37,8 +45,26 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
AvaloniaProperty,
IBinding, ctx.Configuration.WellKnownTypes.Object);
UnsetValueType = ctx.Configuration.TypeSystem.GetType("Avalonia.UnsetValueType");
+ StyledElement = ctx.Configuration.TypeSystem.GetType("Avalonia.StyledElement");
+ INameScope = ctx.Configuration.TypeSystem.GetType("Avalonia.Controls.INameScope");
+ INameScopeRegister = INameScope.GetMethod(
+ new FindMethodMethodSignature("Register", XamlIlTypes.Void,
+ XamlIlTypes.String, XamlIlTypes.Object)
+ {
+ IsStatic = false, DeclaringOnly = true, IsExactMatch = true
+ });
+ INameScopeComplete = INameScope.GetMethod(
+ new FindMethodMethodSignature("Complete", XamlIlTypes.Void)
+ {
+ IsStatic = false, DeclaringOnly = true, IsExactMatch = true
+ });
+ NameScope = ctx.Configuration.TypeSystem.GetType("Avalonia.Controls.NameScope");
+ NameScopeSetNameScope = NameScope.GetMethod(new FindMethodMethodSignature("SetNameScope",
+ XamlIlTypes.Void, StyledElement, INameScope) {IsStatic = true});
+
AvaloniaObjectSetValueMethod = AvaloniaObject.FindMethod("SetValue", XamlIlTypes.Void,
false, AvaloniaProperty, XamlIlTypes.Object, BindingPriority);
+
}
}
diff --git a/src/Markup/Avalonia.Markup.Xaml/XamlIl/Runtime/XamlIlRuntimeHelpers.cs b/src/Markup/Avalonia.Markup.Xaml/XamlIl/Runtime/XamlIlRuntimeHelpers.cs
index 2d8ea643ac..1c2fa17643 100644
--- a/src/Markup/Avalonia.Markup.Xaml/XamlIl/Runtime/XamlIlRuntimeHelpers.cs
+++ b/src/Markup/Avalonia.Markup.Xaml/XamlIl/Runtime/XamlIlRuntimeHelpers.cs
@@ -1,8 +1,10 @@
using System;
using System.Collections.Generic;
+using System.Diagnostics;
using System.Linq;
using System.Reflection;
using Avalonia.Controls;
+using Avalonia.Controls.Templates;
using Avalonia.Data;
// ReSharper disable UnusedMember.Global
// ReSharper disable UnusedParameter.Global
@@ -17,7 +19,14 @@ namespace Avalonia.Markup.Xaml.XamlIl.Runtime
var resourceNodes = provider.GetService().Parents
.OfType().ToList();
var rootObject = provider.GetService().RootObject;
- return sp => builder(new DeferredParentServiceProvider(sp, resourceNodes, rootObject));
+ var parentScope = provider.GetService();
+ return sp =>
+ {
+ var scope = parentScope != null ? new ChildNameScope(parentScope) : (INameScope)new NameScope();
+ var obj = builder(new DeferredParentServiceProvider(sp, resourceNodes, rootObject, scope));
+ scope.Complete();
+ return new ControlTemplateResult((IControl)obj, scope);
+ };
}
class DeferredParentServiceProvider :
@@ -27,12 +36,14 @@ namespace Avalonia.Markup.Xaml.XamlIl.Runtime
{
private readonly IServiceProvider _parentProvider;
private readonly List _parentResourceNodes;
+ private readonly INameScope _nameScope;
public DeferredParentServiceProvider(IServiceProvider parentProvider, List parentResourceNodes,
- object rootObject)
+ object rootObject, INameScope nameScope)
{
_parentProvider = parentProvider;
_parentResourceNodes = parentResourceNodes;
+ _nameScope = nameScope;
RootObject = rootObject;
}
@@ -48,6 +59,8 @@ namespace Avalonia.Markup.Xaml.XamlIl.Runtime
public object GetService(Type serviceType)
{
+ if (serviceType == typeof(INameScope))
+ return _nameScope;
if (serviceType == typeof(IAvaloniaXamlIlParentStackProvider))
return this;
if (serviceType == typeof(IRootObjectProvider))
@@ -56,6 +69,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.Runtime
}
public object RootObject { get; }
+ public object IntermediateRootObject => RootObject;
}
@@ -132,12 +146,28 @@ namespace Avalonia.Markup.Xaml.XamlIl.Runtime
}
}
- public static readonly IServiceProvider RootServiceProviderV1 = new RootServiceProvider();
+ [Obsolete("Don't use", true)]
+ public static readonly IServiceProvider RootServiceProviderV1 = new RootServiceProvider(null);
+ [DebuggerStepThrough]
+ public static IServiceProvider CreateRootServiceProviderV2()
+ {
+ return new RootServiceProvider(new NameScope());
+ }
+
class RootServiceProvider : IServiceProvider, IAvaloniaXamlIlParentStackProvider
{
+ private readonly INameScope _nameScope;
+
+ public RootServiceProvider(INameScope nameScope)
+ {
+ _nameScope = nameScope;
+ }
+
public object GetService(Type serviceType)
{
+ if (serviceType == typeof(INameScope))
+ return _nameScope;
if (serviceType == typeof(IAvaloniaXamlIlParentStackProvider))
return this;
return null;
diff --git a/src/Markup/Avalonia.Markup.Xaml/XamlIl/xamlil.github b/src/Markup/Avalonia.Markup.Xaml/XamlIl/xamlil.github
index 894b2c0282..c2ec091f79 160000
--- a/src/Markup/Avalonia.Markup.Xaml/XamlIl/xamlil.github
+++ b/src/Markup/Avalonia.Markup.Xaml/XamlIl/xamlil.github
@@ -1 +1 @@
-Subproject commit 894b2c02827fd5eb16a338de5d5b6c9fbc60fef5
+Subproject commit c2ec091f79fb4e1eea629bc823c9c24da7050022
diff --git a/src/Markup/Avalonia.Markup.Xaml/XamlTypes.cs b/src/Markup/Avalonia.Markup.Xaml/XamlTypes.cs
index 06cc85101a..5ef3dc9753 100644
--- a/src/Markup/Avalonia.Markup.Xaml/XamlTypes.cs
+++ b/src/Markup/Avalonia.Markup.Xaml/XamlTypes.cs
@@ -10,7 +10,15 @@ namespace Avalonia.Markup.Xaml
public interface IRootObjectProvider
{
+ ///
+ /// The root object of the xaml file
+ ///
object RootObject { get; }
+ ///
+ /// The "current" root object, contains either the root of the xaml file
+ /// or the root object of the control/data template
+ ///
+ object IntermediateRootObject { get; }
}
public interface IUriContext
diff --git a/src/Markup/Avalonia.Markup/Data/Binding.cs b/src/Markup/Avalonia.Markup/Data/Binding.cs
index 0b85e3224d..dbe5800e55 100644
--- a/src/Markup/Avalonia.Markup/Data/Binding.cs
+++ b/src/Markup/Avalonia.Markup/Data/Binding.cs
@@ -5,6 +5,7 @@ using System;
using System.Linq;
using System.Reactive;
using System.Reactive.Linq;
+using Avalonia.Controls;
using Avalonia.Data.Converters;
using Avalonia.Data.Core;
using Avalonia.LogicalTree;
@@ -90,6 +91,8 @@ namespace Avalonia.Data
public string StringFormat { get; set; }
public WeakReference DefaultAnchor { get; set; }
+
+ public WeakReference NameScope { get; set; }
///
/// Gets or sets a function used to resolve types from names in the binding path.
@@ -110,7 +113,9 @@ namespace Avalonia.Data
ExpressionObserver observer;
- var (node, mode) = ExpressionObserverBuilder.Parse(Path, enableDataValidation, TypeResolver);
+ INameScope nameScope = null;
+ NameScope?.TryGetTarget(out nameScope);
+ var (node, mode) = ExpressionObserverBuilder.Parse(Path, enableDataValidation, TypeResolver, nameScope);
if (ElementName != null)
{
@@ -254,9 +259,12 @@ namespace Avalonia.Data
ExpressionNode node)
{
Contract.Requires(target != null);
-
+
+ NameScope.TryGetTarget(out var scope);
+ if (scope == null)
+ throw new InvalidOperationException("Name scope is null or was already collected");
var result = new ExpressionObserver(
- ControlLocator.Track(target, elementName),
+ NameScopeLocator.Track(scope, elementName),
node,
null);
return result;
diff --git a/src/Markup/Avalonia.Markup/Markup/Parsers/ExpressionObserverBuilder.cs b/src/Markup/Avalonia.Markup/Markup/Parsers/ExpressionObserverBuilder.cs
index 8cb14277ff..f957bcab1e 100644
--- a/src/Markup/Avalonia.Markup/Markup/Parsers/ExpressionObserverBuilder.cs
+++ b/src/Markup/Avalonia.Markup/Markup/Parsers/ExpressionObserverBuilder.cs
@@ -1,5 +1,6 @@
using System;
using System.Reactive;
+using Avalonia.Controls;
using Avalonia.Data.Core;
using Avalonia.Utilities;
@@ -7,7 +8,8 @@ namespace Avalonia.Markup.Parsers
{
public static class ExpressionObserverBuilder
{
- internal static (ExpressionNode Node, SourceMode Mode) Parse(string expression, bool enableValidation = false, Func typeResolver = null)
+ internal static (ExpressionNode Node, SourceMode Mode) Parse(string expression, bool enableValidation = false, Func typeResolver = null,
+ INameScope nameScope = null)
{
if (string.IsNullOrWhiteSpace(expression))
{
@@ -15,7 +17,7 @@ namespace Avalonia.Markup.Parsers
}
var reader = new CharacterReader(expression.AsSpan());
- var parser = new ExpressionParser(enableValidation, typeResolver);
+ var parser = new ExpressionParser(enableValidation, typeResolver, nameScope);
var node = parser.Parse(ref reader);
if (!reader.End)
diff --git a/src/Markup/Avalonia.Markup/Markup/Parsers/ExpressionParser.cs b/src/Markup/Avalonia.Markup/Markup/Parsers/ExpressionParser.cs
index a1350a8393..ed3e65feda 100644
--- a/src/Markup/Avalonia.Markup/Markup/Parsers/ExpressionParser.cs
+++ b/src/Markup/Avalonia.Markup/Markup/Parsers/ExpressionParser.cs
@@ -7,6 +7,7 @@ using Avalonia.Utilities;
using System;
using System.Collections.Generic;
using System.Linq;
+using Avalonia.Controls;
namespace Avalonia.Markup.Parsers
{
@@ -20,10 +21,12 @@ namespace Avalonia.Markup.Parsers
{
private readonly bool _enableValidation;
private readonly Func _typeResolver;
+ private readonly INameScope _nameScope;
- public ExpressionParser(bool enableValidation, Func typeResolver)
+ public ExpressionParser(bool enableValidation, Func typeResolver, INameScope nameScope)
{
_typeResolver = typeResolver;
+ _nameScope = nameScope;
_enableValidation = enableValidation;
}
@@ -213,7 +216,7 @@ namespace Avalonia.Markup.Parsers
throw new ExpressionParseException(r.Position, "Element name expected after '#'.");
}
- nodes.Add(new ElementNameNode(name.ToString()));
+ nodes.Add(new ElementNameNode(_nameScope, name.ToString()));
return State.AfterMember;
}
diff --git a/src/Markup/Avalonia.Markup/Markup/Parsers/Nodes/ElementNameNode.cs b/src/Markup/Avalonia.Markup/Markup/Parsers/Nodes/ElementNameNode.cs
index f09efca7d0..981e93c534 100644
--- a/src/Markup/Avalonia.Markup/Markup/Parsers/Nodes/ElementNameNode.cs
+++ b/src/Markup/Avalonia.Markup/Markup/Parsers/Nodes/ElementNameNode.cs
@@ -1,4 +1,5 @@
using System;
+using Avalonia.Controls;
using Avalonia.Data.Core;
using Avalonia.LogicalTree;
@@ -6,11 +7,13 @@ namespace Avalonia.Markup.Parsers.Nodes
{
internal class ElementNameNode : ExpressionNode
{
+ private readonly WeakReference _nameScope;
private readonly string _name;
private IDisposable _subscription;
- public ElementNameNode(string name)
+ public ElementNameNode(INameScope nameScope, string name)
{
+ _nameScope = new WeakReference(nameScope);
_name = name;
}
@@ -18,14 +21,10 @@ namespace Avalonia.Markup.Parsers.Nodes
protected override void StartListeningCore(WeakReference reference)
{
- if (reference.Target is ILogical logical)
- {
- _subscription = ControlLocator.Track(logical, _name).Subscribe(ValueChanged);
- }
+ if (_nameScope.TryGetTarget(out var scope))
+ _subscription = NameScopeLocator.Track(scope, _name).Subscribe(ValueChanged);
else
- {
_subscription = null;
- }
}
protected override void StopListeningCore()
diff --git a/src/Skia/Avalonia.Skia/DrawingContextImpl.cs b/src/Skia/Avalonia.Skia/DrawingContextImpl.cs
index ee4564ff35..262d87d8b6 100644
--- a/src/Skia/Avalonia.Skia/DrawingContextImpl.cs
+++ b/src/Skia/Avalonia.Skia/DrawingContextImpl.cs
@@ -101,6 +101,7 @@ namespace Avalonia.Skia
public SKCanvas Canvas { get; }
SKCanvas ISkiaDrawingContextImpl.SkCanvas => Canvas;
+ GRContext ISkiaDrawingContextImpl.GrContext => _grContext;
///
public void Clear(Color color)
diff --git a/src/Skia/Avalonia.Skia/ISkiaDrawingContextImpl.cs b/src/Skia/Avalonia.Skia/ISkiaDrawingContextImpl.cs
index ff82990c47..a9b91384d7 100644
--- a/src/Skia/Avalonia.Skia/ISkiaDrawingContextImpl.cs
+++ b/src/Skia/Avalonia.Skia/ISkiaDrawingContextImpl.cs
@@ -6,5 +6,6 @@ namespace Avalonia.Skia
public interface ISkiaDrawingContextImpl : IDrawingContextImpl
{
SKCanvas SkCanvas { get; }
+ GRContext GrContext { get; }
}
}
diff --git a/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs b/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs
index bc7fc1c9fa..097aba6dc9 100644
--- a/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs
+++ b/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs
@@ -608,6 +608,14 @@ namespace Avalonia.Win32.Interop
GWL_USERDATA = -21
}
+ public enum MenuCharParam
+ {
+ MNC_IGNORE = 0,
+ MNC_CLOSE = 1,
+ MNC_EXECUTE = 2,
+ MNC_SELECT = 3
+ }
+
[StructLayout(LayoutKind.Sequential)]
public struct RGBQUAD
{
diff --git a/src/Windows/Avalonia.Win32/WindowImpl.cs b/src/Windows/Avalonia.Win32/WindowImpl.cs
index 5cc148fa0d..2f7805884d 100644
--- a/src/Windows/Avalonia.Win32/WindowImpl.cs
+++ b/src/Windows/Avalonia.Win32/WindowImpl.cs
@@ -514,6 +514,10 @@ namespace Avalonia.Win32
KeyInterop.KeyFromVirtualKey(ToInt32(wParam)), WindowsKeyboardDevice.Instance.Modifiers);
break;
+ case UnmanagedMethods.WindowsMessage.WM_MENUCHAR:
+ // mute the system beep
+ return (IntPtr)((Int32)UnmanagedMethods.MenuCharParam.MNC_CLOSE << 16);
+
case UnmanagedMethods.WindowsMessage.WM_KEYUP:
case UnmanagedMethods.WindowsMessage.WM_SYSKEYUP:
e = new RawKeyEventArgs(
diff --git a/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_DataValidation.cs b/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_DataValidation.cs
index b12b2e3c31..428f878945 100644
--- a/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_DataValidation.cs
+++ b/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_DataValidation.cs
@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Reactive.Subjects;
using Avalonia.Data;
+using Avalonia.UnitTests;
using Xunit;
namespace Avalonia.Base.UnitTests
@@ -34,10 +35,10 @@ namespace Avalonia.Base.UnitTests
{
var target = new Class1();
- target.SetValue(Class1.ValidatedDirectProperty, new BindingNotification(6));
- target.SetValue(Class1.ValidatedDirectProperty, new BindingNotification(new Exception(), BindingErrorType.Error));
- target.SetValue(Class1.ValidatedDirectProperty, new BindingNotification(new Exception(), BindingErrorType.DataValidationError));
- target.SetValue(Class1.ValidatedDirectProperty, new BindingNotification(7));
+ target.SetValue(Class1.ValidatedDirectIntProperty, new BindingNotification(6));
+ target.SetValue(Class1.ValidatedDirectIntProperty, new BindingNotification(new Exception(), BindingErrorType.Error));
+ target.SetValue(Class1.ValidatedDirectIntProperty, new BindingNotification(new Exception(), BindingErrorType.DataValidationError));
+ target.SetValue(Class1.ValidatedDirectIntProperty, new BindingNotification(7));
Assert.Equal(
new[]
@@ -73,7 +74,7 @@ namespace Avalonia.Base.UnitTests
var source = new Subject();
var target = new Class1
{
- [!Class1.ValidatedDirectProperty] = source.ToBinding(),
+ [!Class1.ValidatedDirectIntProperty] = source.ToBinding(),
};
source.OnNext(new BindingNotification(6));
@@ -92,6 +93,30 @@ namespace Avalonia.Base.UnitTests
target.Notifications.AsEnumerable());
}
+ [Fact]
+ public void Bound_Validated_Direct_String_Property_Can_Be_Set_To_Null()
+ {
+ var source = new ViewModel
+ {
+ StringValue = "foo",
+ };
+
+ var target = new Class1
+ {
+ [!Class1.ValidatedDirectStringProperty] = new Binding
+ {
+ Path = nameof(ViewModel.StringValue),
+ Source = source,
+ },
+ };
+
+ Assert.Equal("foo", target.ValidatedDirectString);
+
+ source.StringValue = null;
+
+ Assert.Null(target.ValidatedDirectString);
+ }
+
private class Class1 : AvaloniaObject
{
public static readonly StyledProperty NonValidatedProperty =
@@ -104,15 +129,23 @@ namespace Avalonia.Base.UnitTests
o => o.NonValidatedDirect,
(o, v) => o.NonValidatedDirect = v);
- public static readonly DirectProperty ValidatedDirectProperty =
+ public static readonly DirectProperty ValidatedDirectIntProperty =
AvaloniaProperty.RegisterDirect(
- nameof(ValidatedDirect),
- o => o.ValidatedDirect,
- (o, v) => o.ValidatedDirect = v,
+ nameof(ValidatedDirectInt),
+ o => o.ValidatedDirectInt,
+ (o, v) => o.ValidatedDirectInt = v,
+ enableDataValidation: true);
+
+ public static readonly DirectProperty ValidatedDirectStringProperty =
+ AvaloniaProperty.RegisterDirect(
+ nameof(ValidatedDirectString),
+ o => o.ValidatedDirectString,
+ (o, v) => o.ValidatedDirectString = v,
enableDataValidation: true);
private int _nonValidatedDirect;
- private int _direct;
+ private int _directInt;
+ private string _directString;
public int NonValidated
{
@@ -122,14 +155,20 @@ namespace Avalonia.Base.UnitTests
public int NonValidatedDirect
{
- get { return _direct; }
+ get { return _directInt; }
set { SetAndRaise(NonValidatedDirectProperty, ref _nonValidatedDirect, value); }
}
- public int ValidatedDirect
+ public int ValidatedDirectInt
+ {
+ get { return _directInt; }
+ set { SetAndRaise(ValidatedDirectIntProperty, ref _directInt, value); }
+ }
+
+ public string ValidatedDirectString
{
- get { return _direct; }
- set { SetAndRaise(ValidatedDirectProperty, ref _direct, value); }
+ get { return _directString; }
+ set { SetAndRaise(ValidatedDirectStringProperty, ref _directString, value); }
}
public IList Notifications { get; } = new List();
@@ -139,5 +178,16 @@ namespace Avalonia.Base.UnitTests
Notifications.Add(notification);
}
}
+
+ public class ViewModel : NotifyingBase
+ {
+ private string _stringValue;
+
+ public string StringValue
+ {
+ get { return _stringValue; }
+ set { _stringValue = value; RaisePropertyChanged(); }
+ }
+ }
}
}
diff --git a/tests/Avalonia.Controls.UnitTests/AutoCompleteBoxTests.cs b/tests/Avalonia.Controls.UnitTests/AutoCompleteBoxTests.cs
index b10929cbdc..015a122677 100644
--- a/tests/Avalonia.Controls.UnitTests/AutoCompleteBoxTests.cs
+++ b/tests/Avalonia.Controls.UnitTests/AutoCompleteBoxTests.cs
@@ -1012,23 +1012,23 @@ namespace Avalonia.Controls.UnitTests
}
private IControlTemplate CreateTemplate()
{
- return new FuncControlTemplate(control =>
+ return new FuncControlTemplate((control, scope) =>
{
var textBox =
new TextBox
{
Name = "PART_TextBox"
- };
+ }.RegisterInNameScope(scope);
var listbox =
new ListBox
{
Name = "PART_SelectingItemsControl"
- };
+ }.RegisterInNameScope(scope);
var popup =
new Popup
{
Name = "PART_Popup"
- };
+ }.RegisterInNameScope(scope);
var panel = new Panel();
panel.Children.Add(textBox);
diff --git a/tests/Avalonia.Controls.UnitTests/CarouselTests.cs b/tests/Avalonia.Controls.UnitTests/CarouselTests.cs
index 515e18434d..b16ac6bb8e 100644
--- a/tests/Avalonia.Controls.UnitTests/CarouselTests.cs
+++ b/tests/Avalonia.Controls.UnitTests/CarouselTests.cs
@@ -302,7 +302,7 @@ namespace Avalonia.Controls.UnitTests
Assert.Equal("FooBar", target.SelectedItem);
}
- private Control CreateTemplate(Carousel control)
+ private Control CreateTemplate(Carousel control, INameScope scope)
{
return new CarouselPresenter
{
@@ -312,7 +312,7 @@ namespace Avalonia.Controls.UnitTests
[~CarouselPresenter.ItemsPanelProperty] = control[~Carousel.ItemsPanelProperty],
[~CarouselPresenter.SelectedIndexProperty] = control[~Carousel.SelectedIndexProperty],
[~CarouselPresenter.PageTransitionProperty] = control[~Carousel.PageTransitionProperty],
- };
+ }.RegisterInNameScope(scope);
}
}
}
diff --git a/tests/Avalonia.Controls.UnitTests/ComboBoxTests.cs b/tests/Avalonia.Controls.UnitTests/ComboBoxTests.cs
index 70ec6c1408..599e214b31 100644
--- a/tests/Avalonia.Controls.UnitTests/ComboBoxTests.cs
+++ b/tests/Avalonia.Controls.UnitTests/ComboBoxTests.cs
@@ -80,7 +80,7 @@ namespace Avalonia.Controls.UnitTests
private FuncControlTemplate GetTemplate()
{
- return new FuncControlTemplate(parent =>
+ return new FuncControlTemplate((parent, scope) =>
{
return new Panel
{
@@ -94,7 +94,7 @@ namespace Avalonia.Controls.UnitTests
new ToggleButton
{
Name = "toggle",
- },
+ }.RegisterInNameScope(scope),
new Popup
{
Name = "PART_Popup",
@@ -102,8 +102,8 @@ namespace Avalonia.Controls.UnitTests
{
Name = "PART_ItemsPresenter",
[!ItemsPresenter.ItemsProperty] = parent[!ComboBox.ItemsProperty],
- }
- }
+ }.RegisterInNameScope(scope)
+ }.RegisterInNameScope(scope)
}
};
});
diff --git a/tests/Avalonia.Controls.UnitTests/ContentControlTests.cs b/tests/Avalonia.Controls.UnitTests/ContentControlTests.cs
index c17893604c..93355a22f2 100644
--- a/tests/Avalonia.Controls.UnitTests/ContentControlTests.cs
+++ b/tests/Avalonia.Controls.UnitTests/ContentControlTests.cs
@@ -126,7 +126,7 @@ namespace Avalonia.Controls.UnitTests
var target = new ContentControl
{
Template = GetTemplate(),
- ContentTemplate = new FuncDataTemplate(_ => new Canvas()),
+ ContentTemplate = new FuncDataTemplate((_, __) => new Canvas()),
};
target.Content = "Foo";
@@ -302,8 +302,8 @@ namespace Avalonia.Controls.UnitTests
var target = new ContentControl
{
- Template = new FuncControlTemplate(_ => presenter),
- ContentTemplate = new FuncDataTemplate(x => new Canvas()),
+ Template = new FuncControlTemplate((_, __) => presenter),
+ ContentTemplate = new FuncDataTemplate((_, __) => new Canvas()),
Content = "foo",
};
@@ -333,7 +333,7 @@ namespace Avalonia.Controls.UnitTests
private FuncControlTemplate GetTemplate()
{
- return new FuncControlTemplate(parent =>
+ return new FuncControlTemplate((parent, scope) =>
{
return new Border
{
@@ -343,7 +343,7 @@ namespace Avalonia.Controls.UnitTests
Name = "PART_ContentPresenter",
[~ContentPresenter.ContentProperty] = parent[~ContentControl.ContentProperty],
[~ContentPresenter.ContentTemplateProperty] = parent[~ContentControl.ContentTemplateProperty],
- }
+ }.RegisterInNameScope(scope)
};
});
}
diff --git a/tests/Avalonia.Controls.UnitTests/ContextMenuTests.cs b/tests/Avalonia.Controls.UnitTests/ContextMenuTests.cs
index 6482fcb4da..58d205deaa 100644
--- a/tests/Avalonia.Controls.UnitTests/ContextMenuTests.cs
+++ b/tests/Avalonia.Controls.UnitTests/ContextMenuTests.cs
@@ -16,6 +16,60 @@ namespace Avalonia.Controls.UnitTests
private Mock popupImpl;
private MouseTestHelper _mouse = new MouseTestHelper();
+ [Fact]
+ public void Opening_Raises_Single_Opened_Event()
+ {
+ using (Application())
+ {
+ var sut = new ContextMenu();
+ var target = new Panel
+ {
+ ContextMenu = sut
+ };
+
+ new Window { Content = target };
+
+ int openedCount = 0;
+
+ sut.MenuOpened += (sender, args) =>
+ {
+ openedCount++;
+ };
+
+ sut.Open(null);
+
+ Assert.Equal(1, openedCount);
+ }
+ }
+
+ [Fact]
+ public void Closing_Raises_Single_Closed_Event()
+ {
+ using (Application())
+ {
+ var sut = new ContextMenu();
+ var target = new Panel
+ {
+ ContextMenu = sut
+ };
+
+ new Window { Content = target };
+
+ sut.Open(null);
+
+ int closedCount = 0;
+
+ sut.MenuClosed += (sender, args) =>
+ {
+ closedCount++;
+ };
+
+ sut.Close();
+
+ Assert.Equal(1, closedCount);
+ }
+ }
+
[Fact]
public void Clicking_On_Control_Toggles_ContextMenu()
{
diff --git a/tests/Avalonia.Controls.UnitTests/DatePickerTests.cs b/tests/Avalonia.Controls.UnitTests/DatePickerTests.cs
index 936d700ad0..7de80f72cc 100644
--- a/tests/Avalonia.Controls.UnitTests/DatePickerTests.cs
+++ b/tests/Avalonia.Controls.UnitTests/DatePickerTests.cs
@@ -93,28 +93,28 @@ namespace Avalonia.Controls.UnitTests
private IControlTemplate CreateTemplate()
{
- return new FuncControlTemplate(control =>
+ return new FuncControlTemplate((control, scope) =>
{
var textBox =
new TextBox
{
Name = "PART_TextBox"
- };
+ }.RegisterInNameScope(scope);
var button =
new Button
{
Name = "PART_Button"
- };
+ }.RegisterInNameScope(scope);
var calendar =
new Calendar
{
Name = "PART_Calendar"
- };
+ }.RegisterInNameScope(scope);
var popup =
new Popup
{
Name = "PART_Popup"
- };
+ }.RegisterInNameScope(scope);
var panel = new Panel();
panel.Children.Add(textBox);
diff --git a/tests/Avalonia.Controls.UnitTests/Generators/ItemContainerGeneratorTests.cs b/tests/Avalonia.Controls.UnitTests/Generators/ItemContainerGeneratorTests.cs
index 9b4be59647..70410dff0d 100644
--- a/tests/Avalonia.Controls.UnitTests/Generators/ItemContainerGeneratorTests.cs
+++ b/tests/Avalonia.Controls.UnitTests/Generators/ItemContainerGeneratorTests.cs
@@ -118,7 +118,7 @@ namespace Avalonia.Controls.UnitTests.Generators
{
var owner = new Decorator();
var target = new ItemContainerGenerator(owner);
- var container = (ContentPresenter)target.Materialize(0, "foo", null).ContainerControl;
+ var container = (ContentPresenter)target.Materialize(0, "foo").ContainerControl;
Assert.Equal("foo", container.Content);
@@ -135,7 +135,7 @@ namespace Avalonia.Controls.UnitTests.Generators
{
var owner = new Decorator();
var target = new ItemContainerGenerator(owner, ListBoxItem.ContentProperty, null);
- var container = (ListBoxItem)target.Materialize(0, "foo", null).ContainerControl;
+ var container = (ListBoxItem)target.Materialize(0, "foo").ContainerControl;
Assert.Equal("foo", container.Content);
@@ -156,7 +156,7 @@ namespace Avalonia.Controls.UnitTests.Generators
foreach (var item in items)
{
- var container = generator.Materialize(index++, item, null);
+ var container = generator.Materialize(index++, item);
result.Add(container);
}
diff --git a/tests/Avalonia.Controls.UnitTests/Generators/ItemContainerGeneratorTypedTests.cs b/tests/Avalonia.Controls.UnitTests/Generators/ItemContainerGeneratorTypedTests.cs
index f63c0efbf9..05954cbcd2 100644
--- a/tests/Avalonia.Controls.UnitTests/Generators/ItemContainerGeneratorTypedTests.cs
+++ b/tests/Avalonia.Controls.UnitTests/Generators/ItemContainerGeneratorTypedTests.cs
@@ -35,7 +35,7 @@ namespace Avalonia.Controls.UnitTests.Generators
foreach (var item in items)
{
- var container = generator.Materialize(index++, item, null);
+ var container = generator.Materialize(index++, item);
result.Add(container);
}
diff --git a/tests/Avalonia.Controls.UnitTests/GridSplitterTests.cs b/tests/Avalonia.Controls.UnitTests/GridSplitterTests.cs
index a1ba608ec6..a790d2fca1 100644
--- a/tests/Avalonia.Controls.UnitTests/GridSplitterTests.cs
+++ b/tests/Avalonia.Controls.UnitTests/GridSplitterTests.cs
@@ -20,6 +20,7 @@ namespace Avalonia.Controls.UnitTests
[Fact]
public void Detects_Horizontal_Orientation()
{
+ GridSplitter splitter;
var grid = new Grid()
{
RowDefinitions = new RowDefinitions("*,Auto,*"),
@@ -27,7 +28,7 @@ namespace Avalonia.Controls.UnitTests
Children =
{
new Border { [Grid.RowProperty] = 0 },
- new GridSplitter { [Grid.RowProperty] = 1, Name = "splitter" },
+ (splitter = new GridSplitter { [Grid.RowProperty] = 1 }),
new Border { [Grid.RowProperty] = 2 }
}
};
@@ -35,12 +36,13 @@ namespace Avalonia.Controls.UnitTests
var root = new TestRoot { Child = grid };
root.Measure(new Size(100, 300));
root.Arrange(new Rect(0, 0, 100, 300));
- Assert.Contains(grid.FindControl("splitter").Classes, ":horizontal".Equals);
+ Assert.Contains(splitter.Classes, ":horizontal".Equals);
}
[Fact]
public void Detects_Vertical_Orientation()
{
+ GridSplitter splitter;
var grid = new Grid()
{
ColumnDefinitions = new ColumnDefinitions("*,Auto,*"),
@@ -48,7 +50,7 @@ namespace Avalonia.Controls.UnitTests
Children =
{
new Border { [Grid.ColumnProperty] = 0 },
- new GridSplitter { [Grid.ColumnProperty] = 1, Name = "splitter" },
+ (splitter = new GridSplitter { [Grid.ColumnProperty] = 1}),
new Border { [Grid.ColumnProperty] = 2 },
}
};
@@ -56,12 +58,13 @@ namespace Avalonia.Controls.UnitTests
var root = new TestRoot { Child = grid };
root.Measure(new Size(100, 300));
root.Arrange(new Rect(0, 0, 100, 300));
- Assert.Contains(grid.FindControl("splitter").Classes, ":vertical".Equals);
+ Assert.Contains(splitter.Classes, ":vertical".Equals);
}
[Fact]
public void Detects_With_Both_Auto()
{
+ GridSplitter splitter;
var grid = new Grid()
{
ColumnDefinitions = new ColumnDefinitions("Auto,Auto,Auto"),
@@ -69,7 +72,7 @@ namespace Avalonia.Controls.UnitTests
Children =
{
new Border { [Grid.ColumnProperty] = 0 },
- new GridSplitter { [Grid.ColumnProperty] = 1, Name = "splitter" },
+ (splitter = new GridSplitter { [Grid.ColumnProperty] = 1}),
new Border { [Grid.ColumnProperty] = 2 },
}
};
@@ -77,7 +80,7 @@ namespace Avalonia.Controls.UnitTests
var root = new TestRoot { Child = grid };
root.Measure(new Size(100, 300));
root.Arrange(new Rect(0, 0, 100, 300));
- Assert.Contains(grid.FindControl("splitter").Classes, ":vertical".Equals);
+ Assert.Contains(splitter.Classes, ":vertical".Equals);
}
[Fact]
@@ -129,13 +132,14 @@ namespace Avalonia.Controls.UnitTests
[Fact]
public void In_First_Position_Doesnt_Throw_Exception()
{
+ GridSplitter splitter;
var grid = new Grid()
{
ColumnDefinitions = new ColumnDefinitions("Auto,*,*"),
RowDefinitions = new RowDefinitions("*,*"),
Children =
{
- new GridSplitter { [Grid.ColumnProperty] = 0, Name = "splitter" },
+ (splitter = new GridSplitter { [Grid.ColumnProperty] = 0} ),
new Border { [Grid.ColumnProperty] = 1 },
new Border { [Grid.ColumnProperty] = 2 },
}
@@ -144,7 +148,6 @@ namespace Avalonia.Controls.UnitTests
var root = new TestRoot { Child = grid };
root.Measure(new Size(100, 300));
root.Arrange(new Rect(0, 0, 100, 300));
- var splitter = grid.FindControl("splitter");
splitter.RaiseEvent(new VectorEventArgs
{
RoutedEvent = Thumb.DragDeltaEvent,
@@ -199,4 +202,4 @@ namespace Avalonia.Controls.UnitTests
Assert.Equal(columnDefinitions[2].Width, new GridLength(80, GridUnitType.Star));
}
}
-}
\ No newline at end of file
+}
diff --git a/tests/Avalonia.Controls.UnitTests/HeaderedItemsControlTests .cs b/tests/Avalonia.Controls.UnitTests/HeaderedItemsControlTests .cs
index 66789ef874..fcf7bf3c8c 100644
--- a/tests/Avalonia.Controls.UnitTests/HeaderedItemsControlTests .cs
+++ b/tests/Avalonia.Controls.UnitTests/HeaderedItemsControlTests .cs
@@ -63,7 +63,7 @@ namespace Avalonia.Controls.UnitTests
private FuncControlTemplate GetTemplate()
{
- return new FuncControlTemplate(parent =>
+ return new FuncControlTemplate((parent, scope) =>
{
return new Border
{
@@ -71,7 +71,7 @@ namespace Avalonia.Controls.UnitTests
{
Name = "PART_HeaderPresenter",
[~ContentPresenter.ContentProperty] = parent[~HeaderedItemsControl.HeaderProperty],
- }
+ }.RegisterInNameScope(scope)
};
});
}
diff --git a/tests/Avalonia.Controls.UnitTests/ItemsControlTests.cs b/tests/Avalonia.Controls.UnitTests/ItemsControlTests.cs
index 3cf886ade4..b2839360ee 100644
--- a/tests/Avalonia.Controls.UnitTests/ItemsControlTests.cs
+++ b/tests/Avalonia.Controls.UnitTests/ItemsControlTests.cs
@@ -23,7 +23,7 @@ namespace Avalonia.Controls.UnitTests
var target = new ItemsControl
{
Template = GetTemplate(),
- ItemTemplate = new FuncDataTemplate(_ => new Canvas()),
+ ItemTemplate = new FuncDataTemplate((_, __) => new Canvas()),
};
target.Items = new[] { "Foo" };
@@ -411,7 +411,7 @@ namespace Avalonia.Controls.UnitTests
DataContext = "Base",
DataTemplates =
{
- new FuncDataTemplate- (x => new Button { Content = x })
+ new FuncDataTemplate
- ((x, __) => new Button { Content = x })
},
Items = items,
};
@@ -430,27 +430,6 @@ namespace Avalonia.Controls.UnitTests
dataContexts);
}
- [Fact]
- public void MemberSelector_Should_Select_Member()
- {
- var target = new ItemsControl
- {
- Template = GetTemplate(),
- Items = new[] { new Item("Foo"), new Item("Bar") },
- MemberSelector = new FuncMemberSelector
- (x => x.Value),
- };
-
- target.ApplyTemplate();
- target.Presenter.ApplyTemplate();
-
- var text = target.Presenter.Panel.Children
- .Cast()
- .Select(x => x.Content)
- .ToList();
-
- Assert.Equal(new[] { "Foo", "Bar" }, text);
- }
-
[Fact]
public void Control_Item_Should_Not_Be_NameScope()
{
@@ -472,29 +451,6 @@ namespace Avalonia.Controls.UnitTests
Assert.Null(NameScope.GetNameScope((TextBlock)item));
}
- [Fact]
- public void DataTemplate_Created_Content_Should_Be_NameScope()
- {
- var items = new object[]
- {
- "foo",
- };
-
- var target = new ItemsControl
- {
- Template = GetTemplate(),
- Items = items,
- };
-
- target.ApplyTemplate();
- target.Presenter.ApplyTemplate();
-
- var container = (ContentPresenter)target.Presenter.Panel.LogicalChildren[0];
- container.UpdateChild();
-
- Assert.NotNull(NameScope.GetNameScope((TextBlock)container.Child));
- }
-
[Fact]
public void Focuses_Next_Item_On_Key_Down()
{
@@ -578,7 +534,7 @@ namespace Avalonia.Controls.UnitTests
private FuncControlTemplate GetTemplate()
{
- return new FuncControlTemplate(parent =>
+ return new FuncControlTemplate((parent, scope) =>
{
return new Border
{
@@ -586,9 +542,8 @@ namespace Avalonia.Controls.UnitTests
Child = new ItemsPresenter
{
Name = "PART_ItemsPresenter",
- MemberSelector = parent.MemberSelector,
[~ItemsPresenter.ItemsProperty] = parent[~ItemsControl.ItemsProperty],
- }
+ }.RegisterInNameScope(scope)
};
});
}
diff --git a/tests/Avalonia.Controls.UnitTests/ListBoxTests.cs b/tests/Avalonia.Controls.UnitTests/ListBoxTests.cs
index 238e214a5d..9a459328aa 100644
--- a/tests/Avalonia.Controls.UnitTests/ListBoxTests.cs
+++ b/tests/Avalonia.Controls.UnitTests/ListBoxTests.cs
@@ -25,7 +25,7 @@ namespace Avalonia.Controls.UnitTests
{
Template = ListBoxTemplate(),
Items = new[] { "Foo" },
- ItemTemplate = new FuncDataTemplate(_ => new Canvas()),
+ ItemTemplate = new FuncDataTemplate((_, __) => new Canvas()),
};
Prepare(target);
@@ -113,7 +113,7 @@ namespace Avalonia.Controls.UnitTests
DataContext = "Base",
DataTemplates =
{
- new FuncDataTemplate
- (x => new Button { Content = x })
+ new FuncDataTemplate
- ((x, _) => new Button { Content = x })
},
Items = items,
};
@@ -138,7 +138,7 @@ namespace Avalonia.Controls.UnitTests
{
Template = ListBoxTemplate(),
Items = Enumerable.Range(0, 20).Select(x => $"Item {x}").ToList(),
- ItemTemplate = new FuncDataTemplate(x => new TextBlock { Height = 10 }),
+ ItemTemplate = new FuncDataTemplate((x, _) => new TextBlock { Height = 10 }),
SelectedIndex = 0,
};
@@ -162,7 +162,7 @@ namespace Avalonia.Controls.UnitTests
{
Template = ListBoxTemplate(),
Items = Enumerable.Range(0, 20).Select(x => $"Item {x}").ToList(),
- ItemTemplate = new FuncDataTemplate(x => new TextBlock { Width = 20, Height = 10 }),
+ ItemTemplate = new FuncDataTemplate((x, _) => new TextBlock { Width = 20, Height = 10 }),
SelectedIndex = 0,
};
@@ -181,7 +181,7 @@ namespace Avalonia.Controls.UnitTests
{
Template = ListBoxTemplate(),
Items = items,
- ItemTemplate = new FuncDataTemplate(x => new TextBlock { Width = 20, Height = 10 }),
+ ItemTemplate = new FuncDataTemplate((x, _) => new TextBlock { Width = 20, Height = 10 }),
SelectedIndex = 0,
};
@@ -205,7 +205,7 @@ namespace Avalonia.Controls.UnitTests
Template = ListBoxTemplate(),
Items = items,
SelectionMode = SelectionMode.Toggle,
- ItemTemplate = new FuncDataTemplate(x => new TextBlock { Height = 10 })
+ ItemTemplate = new FuncDataTemplate((x, _) => new TextBlock { Height = 10 })
};
Prepare(target);
@@ -239,7 +239,7 @@ namespace Avalonia.Controls.UnitTests
{
Template = ListBoxTemplate(),
Items = items,
- ItemTemplate = new FuncDataTemplate(x => new TextBlock { Height = 11 })
+ ItemTemplate = new FuncDataTemplate((x, _) => new TextBlock { Height = 11 })
};
Prepare(target);
@@ -287,7 +287,7 @@ namespace Avalonia.Controls.UnitTests
target.DataContext = items;
target.VirtualizationMode = virtualizationMode;
- target.ItemTemplate = new FuncDataTemplate(c =>
+ target.ItemTemplate = new FuncDataTemplate((c, _) =>
{
var tb = new TextBlock() { Height = 10, Width = 30 };
tb.Bind(TextBlock.TextProperty, new Data.Binding());
@@ -334,7 +334,7 @@ namespace Avalonia.Controls.UnitTests
private FuncControlTemplate ListBoxTemplate()
{
- return new FuncControlTemplate(parent =>
+ return new FuncControlTemplate((parent, scope) =>
new ScrollViewer
{
Name = "PART_ScrollViewer",
@@ -345,24 +345,24 @@ namespace Avalonia.Controls.UnitTests
[~ItemsPresenter.ItemsProperty] = parent.GetObservable(ItemsControl.ItemsProperty).ToBinding(),
[~ItemsPresenter.ItemsPanelProperty] = parent.GetObservable(ItemsControl.ItemsPanelProperty).ToBinding(),
[~ItemsPresenter.VirtualizationModeProperty] = parent.GetObservable(ListBox.VirtualizationModeProperty).ToBinding(),
- }
- });
+ }.RegisterInNameScope(scope)
+ }.RegisterInNameScope(scope));
}
private FuncControlTemplate ListBoxItemTemplate()
{
- return new FuncControlTemplate(parent =>
+ return new FuncControlTemplate((parent, scope) =>
new ContentPresenter
{
Name = "PART_ContentPresenter",
[!ContentPresenter.ContentProperty] = parent[!ListBoxItem.ContentProperty],
[!ContentPresenter.ContentTemplateProperty] = parent[!ListBoxItem.ContentTemplateProperty],
- });
+ }.RegisterInNameScope(scope));
}
private FuncControlTemplate ScrollViewerTemplate()
{
- return new FuncControlTemplate(parent =>
+ return new FuncControlTemplate((parent, scope) =>
new ScrollContentPresenter
{
Name = "PART_ContentPresenter",
@@ -370,7 +370,7 @@ namespace Avalonia.Controls.UnitTests
[~~ScrollContentPresenter.ExtentProperty] = parent[~~ScrollViewer.ExtentProperty],
[~~ScrollContentPresenter.OffsetProperty] = parent[~~ScrollViewer.OffsetProperty],
[~~ScrollContentPresenter.ViewportProperty] = parent[~~ScrollViewer.ViewportProperty],
- });
+ }.RegisterInNameScope(scope));
}
private void Prepare(ListBox target)
diff --git a/tests/Avalonia.Controls.UnitTests/ListBoxTests_Single.cs b/tests/Avalonia.Controls.UnitTests/ListBoxTests_Single.cs
index de34558ad1..2a61ff1566 100644
--- a/tests/Avalonia.Controls.UnitTests/ListBoxTests_Single.cs
+++ b/tests/Avalonia.Controls.UnitTests/ListBoxTests_Single.cs
@@ -245,7 +245,7 @@ namespace Avalonia.Controls.UnitTests
}
}
- private Control CreateListBoxTemplate(ITemplatedControl parent)
+ private Control CreateListBoxTemplate(ITemplatedControl parent, INameScope scope)
{
return new ScrollViewer
{
@@ -254,17 +254,18 @@ namespace Avalonia.Controls.UnitTests
{
Name = "PART_ItemsPresenter",
[~ItemsPresenter.ItemsProperty] = parent.GetObservable(ItemsControl.ItemsProperty).ToBinding(),
- }
+ }.RegisterInNameScope(scope)
};
}
- private Control CreateScrollViewerTemplate(ITemplatedControl parent)
+ private Control CreateScrollViewerTemplate(ITemplatedControl parent, INameScope scope)
{
return new ScrollContentPresenter
{
Name = "PART_ContentPresenter",
- [~ContentPresenter.ContentProperty] = parent.GetObservable(ContentControl.ContentProperty).ToBinding(),
- };
+ [~ContentPresenter.ContentProperty] =
+ parent.GetObservable(ContentControl.ContentProperty).ToBinding(),
+ }.RegisterInNameScope(scope);
}
private void ApplyTemplate(ListBox target)
diff --git a/tests/Avalonia.Controls.UnitTests/Mixins/ContentControlMixinTests.cs b/tests/Avalonia.Controls.UnitTests/Mixins/ContentControlMixinTests.cs
index f06553411c..638443e17f 100644
--- a/tests/Avalonia.Controls.UnitTests/Mixins/ContentControlMixinTests.cs
+++ b/tests/Avalonia.Controls.UnitTests/Mixins/ContentControlMixinTests.cs
@@ -21,15 +21,16 @@ namespace Avalonia.Controls.UnitTests.Mixins
{
var target = new TestControl()
{
- Template = new FuncControlTemplate(_ => new Panel
+ Template = new FuncControlTemplate((_, scope) => new Panel
{
Children =
{
- new ContentPresenter { Name = "Content_1_Presenter" },
- new ContentPresenter { Name = "Content_2_Presenter" }
+ new ContentPresenter {Name = "Content_1_Presenter"}.RegisterInNameScope(scope),
+ new ContentPresenter {Name = "Content_2_Presenter"}.RegisterInNameScope(scope)
}
})
};
+
var ex = Record.Exception(() => target.ApplyTemplate());
@@ -43,12 +44,12 @@ namespace Avalonia.Controls.UnitTests.Mixins
var p2 = new ContentPresenter { Name = "Content_2_Presenter" };
var target = new TestControl
{
- Template = new FuncControlTemplate(_ => new Panel
+ Template = new FuncControlTemplate((_, scope) => new Panel
{
Children =
{
- p1,
- p2
+ p1.RegisterInNameScope(scope),
+ p2.RegisterInNameScope(scope)
}
})
};
diff --git a/tests/Avalonia.Controls.UnitTests/NameScopeTests.cs b/tests/Avalonia.Controls.UnitTests/NameScopeTests.cs
index 802f7717e8..0fdcd3fd34 100644
--- a/tests/Avalonia.Controls.UnitTests/NameScopeTests.cs
+++ b/tests/Avalonia.Controls.UnitTests/NameScopeTests.cs
@@ -20,36 +20,146 @@ namespace Avalonia.Controls.UnitTests
}
[Fact]
- public void Unregister_Unregisters_Element()
+ public void Cannot_Register_New_Element_With_Existing_Name()
+ {
+ var target = new NameScope();
+
+ target.Register("foo", new object());
+ Assert.Throws(() => target.Register("foo", new object()));
+ }
+
+ [Fact]
+ public void Can_Register_Same_Element_More_Than_Once()
{
var target = new NameScope();
var element = new object();
target.Register("foo", element);
- target.Unregister("foo");
+ target.Register("foo", element);
- Assert.Null(target.Find("foo"));
+ Assert.Same(element, target.Find("foo"));
}
[Fact]
- public void Cannot_Register_New_Element_With_Existing_Name()
+ public void Cannot_Register_New_Element_For_Completed_Scope()
{
var target = new NameScope();
+ var element = new object();
- target.Register("foo", new object());
- Assert.Throws(() => target.Register("foo", new object()));
+ target.Register("foo", element);
+ target.Complete();
+ Assert.Throws(() => target.Register("bar", element));
}
+
+ /*
+ `async void` here is intentional since we expect the continuation to be
+ executed *synchronously* and behave more like an event handler to make sure that
+ that the object graph is completely ready to use after it's built
+ rather than have pending continuations queued by SynchronizationContext.
+ */
+ object _found = null;
+ async void FindAsync(INameScope scope, string name)
+ {
+ _found = await scope.FindAsync(name);
+ }
+
[Fact]
- public void Can_Register_Same_Element_More_Than_Once()
+ public void FindAsync_Should_Find_Controls_Added_Earlier()
{
- var target = new NameScope();
+ var scope = new NameScope();
+ var element = new object();
+ scope.Register("foo", element);
+ FindAsync(scope, "foo");
+ Assert.Same(_found, element);
+ }
+
+ [Fact]
+ public void FindAsync_Should_Find_Controls_Added_Later()
+ {
+ var scope = new NameScope();
var element = new object();
+
+ FindAsync(scope, "foo");
+ Assert.Null(_found);
+ scope.Register("foo", element);
+ Assert.Same(_found, element);
+ }
+
+ [Fact]
+ public void FindAsync_Should_Return_Null_After_Scope_Completion()
+ {
+ var scope = new NameScope();
+ var element = new object();
+ bool finished = false;
+ async void Find(string name)
+ {
+ Assert.Null(await scope.FindAsync(name));
+ finished = true;
+ }
+ Find("foo");
+ Assert.False(finished);
+ scope.Register("bar", element);
+ Assert.False(finished);
+ scope.Complete();
+ Assert.True(finished);
+ }
- target.Register("foo", element);
- target.Register("foo", element);
+ [Fact]
+ public void Child_Scope_Should_Not_Find_Control_In_Parent_Scope_Unless_Completed()
+ {
+ var scope = new NameScope();
+ var childScope = new ChildNameScope(scope);
+ var element = new object();
+ scope.Register("foo", element);
+ Assert.Null(childScope.Find("foo"));
+ childScope.Complete();
+ Assert.Same(element, childScope.Find("foo"));
+ }
+
+ [Fact]
+ public void Child_Scope_Should_Prefer_Own_Elements()
+ {
+ var scope = new NameScope();
+ var childScope = new ChildNameScope(scope);
+ var element = new object();
+ var childElement = new object();
+ scope.Register("foo", element);
+ childScope.Register("foo", childElement);
+ childScope.Complete();
+ Assert.Same(childElement, childScope.Find("foo"));
+ }
- Assert.Same(element, target.Find("foo"));
+ [Fact]
+ public void Child_Scope_FindAsync_Should_Find_Elements_In_Parent_Scope_When_Child_Is_Completed()
+ {
+ var scope = new NameScope();
+ var childScope = new ChildNameScope(scope);
+ var element = new object();
+ scope.Register("foo", element);
+ FindAsync(childScope, "foo");
+ Assert.Null(_found);
+ childScope.Complete();
+ Assert.Same(element, _found);
+ }
+
+
+ [Fact]
+ public void Child_Scope_FindAsync_Should_Prefer_Own_Elements()
+ {
+ var scope = new NameScope();
+ var childScope = new ChildNameScope(scope);
+ var element = new object();
+ var childElement = new object();
+ FindAsync(childScope, "foo");
+ scope.Register("foo", element);
+ Assert.Null(_found);
+ childScope.Register("foo", childElement);
+ Assert.Same(childElement, childScope.Find("foo"));
+ childScope.Complete();
+ FindAsync(childScope, "foo");
+ Assert.Same(childElement, childScope.Find("foo"));
}
+
}
}
diff --git a/tests/Avalonia.Controls.UnitTests/Presenters/ContentPresenterTests_InTemplate.cs b/tests/Avalonia.Controls.UnitTests/Presenters/ContentPresenterTests_InTemplate.cs
index 708e934214..7d05547799 100644
--- a/tests/Avalonia.Controls.UnitTests/Presenters/ContentPresenterTests_InTemplate.cs
+++ b/tests/Avalonia.Controls.UnitTests/Presenters/ContentPresenterTests_InTemplate.cs
@@ -136,17 +136,6 @@ namespace Avalonia.Controls.UnitTests.Presenters
Assert.Null(NameScope.GetNameScope((Control)target.Child));
}
- [Fact]
- public void DataTemplate_Created_Control_Should_Be_NameScope()
- {
- var (target, _) = CreateTarget();
-
- target.Content = "Foo";
-
- Assert.IsType(target.Child);
- Assert.NotNull(NameScope.GetNameScope((Control)target.Child));
- }
-
[Fact]
public void Assigning_Control_To_Content_Should_Not_Set_DataContext()
{
@@ -170,7 +159,7 @@ namespace Avalonia.Controls.UnitTests.Presenters
{
var (target, _) = CreateTarget();
- target.ContentTemplate = new FuncDataTemplate(_ => new Canvas());
+ target.ContentTemplate = new FuncDataTemplate((_, __) => new Canvas());
target.Content = "Foo";
Assert.IsType