From 433c70a6c826af2bf65fad745a4ff160502deab5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Pedro?= Date: Sun, 9 Jun 2019 21:26:08 +0100 Subject: [PATCH 01/45] Added BindingOperations.DoNothing. Added tests. --- src/Avalonia.Base/Data/BindingOperations.cs | 2 + .../Data/Core/BindingExpression.cs | 31 ++++++++ .../Avalonia.Markup/Data/MultiBinding.cs | 7 +- .../Converters/ValueConverterTests.cs | 71 +++++++++++++++++++ .../Data/BindingTests.cs | 20 +++--- 5 files changed, 118 insertions(+), 13 deletions(-) create mode 100644 tests/Avalonia.Markup.Xaml.UnitTests/Converters/ValueConverterTests.cs diff --git a/src/Avalonia.Base/Data/BindingOperations.cs b/src/Avalonia.Base/Data/BindingOperations.cs index 15de6c4d0d..44b47329ac 100644 --- a/src/Avalonia.Base/Data/BindingOperations.cs +++ b/src/Avalonia.Base/Data/BindingOperations.cs @@ -10,6 +10,8 @@ namespace Avalonia.Data { public static class BindingOperations { + public static readonly object DoNothing = new object(); + /// /// Applies an a property on an . /// diff --git a/src/Avalonia.Base/Data/Core/BindingExpression.cs b/src/Avalonia.Base/Data/Core/BindingExpression.cs index f1717bde3b..7f8396cdfa 100644 --- a/src/Avalonia.Base/Data/Core/BindingExpression.cs +++ b/src/Avalonia.Base/Data/Core/BindingExpression.cs @@ -114,6 +114,11 @@ namespace Avalonia.Data.Core /// public void OnNext(object value) { + if (value == BindingOperations.DoNothing) + { + return; + } + using (_inner.Subscribe(_ => { })) { var type = _inner.ResultType; @@ -126,6 +131,11 @@ namespace Avalonia.Data.Core ConverterParameter, CultureInfo.CurrentCulture); + if (converted == BindingOperations.DoNothing) + { + return; + } + if (converted == AvaloniaProperty.UnsetValue) { converted = TypeUtilities.Default(type); @@ -186,6 +196,11 @@ namespace Avalonia.Data.Core /// private object ConvertValue(object value) { + if (value == BindingOperations.DoNothing) + { + return value; + } + var notification = value as BindingNotification; if (notification == null) @@ -196,6 +211,11 @@ namespace Avalonia.Data.Core ConverterParameter, CultureInfo.CurrentCulture); + if (converted == BindingOperations.DoNothing) + { + return converted; + } + notification = converted as BindingNotification; if (notification?.ErrorType == BindingErrorType.None) @@ -327,7 +347,18 @@ namespace Avalonia.Data.Core public void OnNext(object value) { + if (value == BindingOperations.DoNothing) + { + return; + } + var converted = _owner.ConvertValue(value); + + if (converted == BindingOperations.DoNothing) + { + return; + } + _owner._value = new WeakReference(converted); _owner.PublishNext(converted); } diff --git a/src/Markup/Avalonia.Markup/Data/MultiBinding.cs b/src/Markup/Avalonia.Markup/Data/MultiBinding.cs index eb2a8df2eb..d1e3765dfa 100644 --- a/src/Markup/Avalonia.Markup/Data/MultiBinding.cs +++ b/src/Markup/Avalonia.Markup/Data/MultiBinding.cs @@ -92,7 +92,12 @@ namespace Avalonia.Data var culture = CultureInfo.CurrentCulture; var converted = Converter.Convert(values, targetType, ConverterParameter, culture); - if (converted == AvaloniaProperty.UnsetValue && FallbackValue != null) + if (converted == BindingOperations.DoNothing) + { + return converted; + } + + if (converted == AvaloniaProperty.UnsetValue) { converted = FallbackValue; } diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/Converters/ValueConverterTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/Converters/ValueConverterTests.cs new file mode 100644 index 0000000000..6f2c4363e2 --- /dev/null +++ b/tests/Avalonia.Markup.Xaml.UnitTests/Converters/ValueConverterTests.cs @@ -0,0 +1,71 @@ +using System; +using System.Globalization; +using Avalonia.Controls; +using Avalonia.Data; +using Avalonia.Data.Converters; +using Avalonia.UnitTests; +using Xunit; + +namespace Avalonia.Markup.Xaml.UnitTests.Converters +{ + public class ValueConverterTests + { + [Fact] + public void ValueConverter_Special_Values_Work() + { + using (UnitTestApplication.Start(TestServices.StyledWindow)) + { + var xaml = @" + + +"; + var loader = new AvaloniaXamlLoader(); + var window = (Window)loader.Load(xaml); + var textBlock = window.FindControl("textBlock"); + + window.ApplyTemplate(); + + window.DataContext = 2; + Assert.Equal("foo", textBlock.Text); + + window.DataContext = -3; + Assert.Equal("foo", textBlock.Text); + + window.DataContext = 0; + Assert.Equal("bar", textBlock.Text); + } + } + } + + public class TestConverter : IValueConverter + { + public static readonly TestConverter Instance = new TestConverter(); + + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + if (value is int i) + { + if (i > 0) + { + return "foo"; + } + + if (i == 0) + { + return AvaloniaProperty.UnsetValue; + } + + return BindingOperations.DoNothing; + } + + return "(default)"; + } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + throw new NotImplementedException(); + } + } +} diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/Data/BindingTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/Data/BindingTests.cs index 0a00b9d39d..9813c1f222 100644 --- a/tests/Avalonia.Markup.Xaml.UnitTests/Data/BindingTests.cs +++ b/tests/Avalonia.Markup.Xaml.UnitTests/Data/BindingTests.cs @@ -1,20 +1,10 @@ // Copyright (c) The Avalonia Project. All rights reserved. // Licensed under the MIT license. See licence.md file in the project root for full license information. -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reactive.Linq; using Avalonia.Controls; using Avalonia.Data; -using Avalonia.Markup.Data; -using Moq; -using Xunit; -using System.ComponentModel; -using System.Runtime.CompilerServices; using Avalonia.UnitTests; -using Avalonia.Data.Converters; -using Avalonia.Data.Core; +using Xunit; namespace Avalonia.Markup.Xaml.UnitTests.Data { @@ -34,10 +24,16 @@ namespace Avalonia.Markup.Xaml.UnitTests.Data var window = (Window)loader.Load(xaml); var textBlock = window.FindControl("textBlock"); - window.DataContext = "foo"; window.ApplyTemplate(); + window.DataContext = "foo"; Assert.Equal("foo", textBlock.Text); + + window.DataContext = BindingOperations.DoNothing; + Assert.Equal("foo", textBlock.Text); + + window.DataContext = "bar"; + Assert.Equal("bar", textBlock.Text); } } } From 8515e027ef2074c1378061efde89b939be03bc68 Mon Sep 17 00:00:00 2001 From: artyom Date: Thu, 20 Jun 2019 11:43:04 +0300 Subject: [PATCH 02/45] Add AutoSuspendHelper implementation --- src/Avalonia.ReactiveUI/AutoSuspendHelper.cs | 44 ++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 src/Avalonia.ReactiveUI/AutoSuspendHelper.cs diff --git a/src/Avalonia.ReactiveUI/AutoSuspendHelper.cs b/src/Avalonia.ReactiveUI/AutoSuspendHelper.cs new file mode 100644 index 0000000000..e39ecb4a0f --- /dev/null +++ b/src/Avalonia.ReactiveUI/AutoSuspendHelper.cs @@ -0,0 +1,44 @@ +// 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; +using Avalonia.VisualTree; +using Avalonia.Controls; +using System.Threading; +using System.Reactive.Disposables; +using System.Reactive.Subjects; +using System.Reactive.Linq; +using System.Reactive; +using ReactiveUI; +using System; + +namespace Avalonia.ReactiveUI +{ + /// + /// A ReactiveUI AutoSuspendHelper which initializes suspension hooks for + /// Avalonia applications. Call its constructor in your app's composition root, + /// before calling the RxApp.SuspensionHost.SetupDefaultSuspendResume method. + /// + public sealed class AutoSuspendHelper + { + public AutoSuspendHelper(Application app) + { + RxApp.SuspensionHost.IsResuming = Observable.Never(); + RxApp.SuspensionHost.IsLaunchingNew = Observable.Return(Unit.Default); + + var exiting = new Subject(); + app.Exit += (o, e) => + { + // This is required to prevent the app from shutting down too early. + var manual = new ManualResetEvent(false); + exiting.OnNext(Disposable.Create(() => manual.Set())); + manual.WaitOne(); + }; + RxApp.SuspensionHost.ShouldPersistState = exiting; + + var errored = new Subject(); + AppDomain.CurrentDomain.UnhandledException += (o, e) => errored.OnNext(Unit.Default); + RxApp.SuspensionHost.ShouldInvalidateState = errored; + } + } +} From 5230e465a663fee27dc9d05d562230073bcca669 Mon Sep 17 00:00:00 2001 From: artyom Date: Thu, 20 Jun 2019 12:03:33 +0300 Subject: [PATCH 03/45] Correct wrong variable name --- .../AvaloniaActivationForViewFetcherTest.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/Avalonia.ReactiveUI.UnitTests/AvaloniaActivationForViewFetcherTest.cs b/tests/Avalonia.ReactiveUI.UnitTests/AvaloniaActivationForViewFetcherTest.cs index 2c81e8fea3..1d85312b1a 100644 --- a/tests/Avalonia.ReactiveUI.UnitTests/AvaloniaActivationForViewFetcherTest.cs +++ b/tests/Avalonia.ReactiveUI.UnitTests/AvaloniaActivationForViewFetcherTest.cs @@ -171,7 +171,7 @@ namespace Avalonia.ReactiveUI.UnitTests [Fact] public void Activation_For_View_Fetcher_Should_Support_Windows() { - using (var application = UnitTestApplication.Start(TestServices.MockWindowingPlatform)) + using (UnitTestApplication.Start(TestServices.MockWindowingPlatform)) { var window = new TestWindowWithWhenActivated(); Assert.False(window.Active); @@ -187,7 +187,7 @@ namespace Avalonia.ReactiveUI.UnitTests [Fact] public void Activatable_Window_View_Model_Is_Activated_And_Deactivated() { - using (var application = UnitTestApplication.Start(TestServices.MockWindowingPlatform)) + using (UnitTestApplication.Start(TestServices.MockWindowingPlatform)) { var viewModel = new ActivatableViewModel(); var window = new ActivatableWindow { ViewModel = viewModel }; From 6c16ac48032730a7b8ef1fd5bb701f251878028f Mon Sep 17 00:00:00 2001 From: artyom Date: Thu, 20 Jun 2019 12:38:59 +0300 Subject: [PATCH 04/45] Unit test AutoSuspendHelper --- .../AutoSuspendHelperTest.cs | 60 +++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 tests/Avalonia.ReactiveUI.UnitTests/AutoSuspendHelperTest.cs diff --git a/tests/Avalonia.ReactiveUI.UnitTests/AutoSuspendHelperTest.cs b/tests/Avalonia.ReactiveUI.UnitTests/AutoSuspendHelperTest.cs new file mode 100644 index 0000000000..a3464878e6 --- /dev/null +++ b/tests/Avalonia.ReactiveUI.UnitTests/AutoSuspendHelperTest.cs @@ -0,0 +1,60 @@ +// 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.Reactive.Concurrency; +using System.Reactive.Disposables; +using Avalonia.Controls; +using Avalonia.Rendering; +using Avalonia.Platform; +using Avalonia.UnitTests; +using Avalonia; +using ReactiveUI; +using DynamicData; +using Xunit; +using Splat; +using Avalonia.Markup.Xaml; +using System.ComponentModel; +using System.Threading.Tasks; +using System.Reactive; +using Avalonia.ReactiveUI; +using System.Reactive.Subjects; +using System.Reactive.Linq; +using System.Collections.Generic; + +namespace Avalonia.ReactiveUI.UnitTests +{ + public class AutoSuspendHelperTest + { + [Fact] + public void AutoSuspendHelper_Should_Immediately_Fire_IsLaunchingNew() + { + using (UnitTestApplication.Start(TestServices.MockWindowingPlatform)) + { + var isLaunchingReceived = false; + var application = AvaloniaLocator.Current.GetService(); + var suspension = new AutoSuspendHelper(application); + + RxApp.SuspensionHost.IsLaunchingNew.Subscribe(_ => isLaunchingReceived = true); + Assert.True(isLaunchingReceived); + } + } + + [Fact] + public void ShouldPersistState_Should_Fire_On_App_Exit_When_SuspensionDriver_Is_Initialized() + { + using (UnitTestApplication.Start(TestServices.MockWindowingPlatform)) + { + var shouldPersistReceived = false; + var application = AvaloniaLocator.Current.GetService(); + var suspension = new AutoSuspendHelper(application); + + RxApp.SuspensionHost.ShouldPersistState.Subscribe(_ => shouldPersistReceived = true); + RxApp.SuspensionHost.SetupDefaultSuspendResume(new DummySuspensionDriver()); + + application.Shutdown(); + Assert.True(shouldPersistReceived); + } + } + } +} \ No newline at end of file From 67bd1b8d86744ac65f4427b0b85239856c6b71ef Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Tue, 25 Jun 2019 21:28:56 +0200 Subject: [PATCH 05/45] Added failing tests for #1701. --- .../AvaloniaObjectTests_DataValidation.cs | 78 +++++++++++++++---- .../TextBoxTests.cs | 23 ++++++ 2 files changed, 87 insertions(+), 14 deletions(-) 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/TextBoxTests.cs b/tests/Avalonia.Controls.UnitTests/TextBoxTests.cs index 932aada64e..cef5fe61fc 100644 --- a/tests/Avalonia.Controls.UnitTests/TextBoxTests.cs +++ b/tests/Avalonia.Controls.UnitTests/TextBoxTests.cs @@ -444,6 +444,22 @@ namespace Avalonia.Controls.UnitTests } } + [Fact] + public void Setting_Bound_Text_To_Null_Works() + { + using (UnitTestApplication.Start(Services)) + { + var source = new Class1 { Bar = "bar" }; + var target = new TextBox { DataContext = source }; + + target.Bind(TextBox.TextProperty, new Binding("Bar")); + + Assert.Equal("bar", target.Text); + source.Bar = null; + Assert.Null(target.Text); + } + } + private static TestServices FocusServices => TestServices.MockThreadingInterface.With( focusManager: new FocusManager(), keyboardDevice: () => new KeyboardDevice(), @@ -492,12 +508,19 @@ namespace Avalonia.Controls.UnitTests private class Class1 : NotifyingBase { private int _foo; + private string _bar; public int Foo { get { return _foo; } set { _foo = value; RaisePropertyChanged(); } } + + public string Bar + { + get { return _bar; } + set { _bar = value; RaisePropertyChanged(); } + } } } } From ecabeba4d15540168507822472f917c58bd768ae Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Tue, 25 Jun 2019 22:49:02 +0200 Subject: [PATCH 06/45] Fix conversion of null in DefaultValueConverter. `null` is a valid value for a non-value type so don't convert it to `{unset}`. Fixes #1701. --- src/Avalonia.Base/Data/Converters/DefaultValueConverter.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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) From 5adf7c3a4cda4f598dd5ab93e04b7bb8a5f7bb91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Pedro?= Date: Wed, 26 Jun 2019 17:48:03 +0100 Subject: [PATCH 07/45] Moved test to separate method. --- .../Data/BindingTests.cs | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/Data/BindingTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/Data/BindingTests.cs index 9813c1f222..e412657711 100644 --- a/tests/Avalonia.Markup.Xaml.UnitTests/Data/BindingTests.cs +++ b/tests/Avalonia.Markup.Xaml.UnitTests/Data/BindingTests.cs @@ -24,6 +24,27 @@ namespace Avalonia.Markup.Xaml.UnitTests.Data var window = (Window)loader.Load(xaml); var textBlock = window.FindControl("textBlock"); + window.DataContext = "foo"; + window.ApplyTemplate(); + + Assert.Equal("foo", textBlock.Text); + } + } + + [Fact] + public void Binding_To_DoNothing_Works() + { + using (UnitTestApplication.Start(TestServices.StyledWindow)) + { + var xaml = @" + + +"; + var loader = new AvaloniaXamlLoader(); + var window = (Window)loader.Load(xaml); + var textBlock = window.FindControl("textBlock"); + window.ApplyTemplate(); window.DataContext = "foo"; From 11a3f0c0934204410f7e1eb2848a8cf627161564 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Thu, 27 Jun 2019 01:07:37 +0200 Subject: [PATCH 08/45] Added failing test for #2565. Along with a passing test I wrote looking for the solution, and some documentation about SelectionMode. --- src/Avalonia.Controls/ListBox.cs | 8 +++- .../Primitives/SelectingItemsControl.cs | 4 ++ .../SelectingItemsControlTests_Multiple.cs | 44 ++++++++++++++++++- 3 files changed, 54 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Controls/ListBox.cs b/src/Avalonia.Controls/ListBox.cs index 3150b6be91..f26cd47bcb 100644 --- a/src/Avalonia.Controls/ListBox.cs +++ b/src/Avalonia.Controls/ListBox.cs @@ -68,7 +68,13 @@ namespace Avalonia.Controls /// public new IList SelectedItems => base.SelectedItems; - /// + /// + /// Gets or sets the selection mode. + /// + /// + /// Note that the selection mode only applies to selections made via user interaction. + /// Multiple selections can be made programatically regardless of the value of this property. + /// public new SelectionMode SelectionMode { get { return base.SelectionMode; } diff --git a/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs b/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs index 91a9fa7e40..6f7bf4eb3f 100644 --- a/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs +++ b/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs @@ -222,6 +222,10 @@ namespace Avalonia.Controls.Primitives /// /// Gets or sets the selection mode. /// + /// + /// Note that the selection mode only applies to selections made via user interaction. + /// Multiple selections can be made programatically regardless of the value of this property. + /// protected SelectionMode SelectionMode { get { return GetValue(SelectionModeProperty); } diff --git a/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests_Multiple.cs b/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests_Multiple.cs index a33d97779e..a44e23096f 100644 --- a/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests_Multiple.cs +++ b/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests_Multiple.cs @@ -53,7 +53,7 @@ namespace Avalonia.Controls.UnitTests.Primitives } [Fact] - public void Assigning_SelectedItems_Should_Set_SelectedIndex() + public void Assigning_Single_SelectedItems_Should_Set_SelectedIndex() { var target = new TestSelector { @@ -62,9 +62,51 @@ namespace Avalonia.Controls.UnitTests.Primitives }; target.ApplyTemplate(); + target.Presenter.ApplyTemplate(); target.SelectedItems = new AvaloniaList("bar"); Assert.Equal(1, target.SelectedIndex); + Assert.Equal(new[] { "bar" }, target.SelectedItems); + Assert.Equal(new[] { 1 }, SelectedContainers(target)); + } + + [Fact] + public void Assigning_Multiple_SelectedItems_Should_Set_SelectedIndex() + { + // Note that we don't need SelectionMode = Multiple here. Multiple selections can always + // be made in code. + var target = new TestSelector + { + Items = new[] { "foo", "bar", "baz" }, + Template = Template(), + }; + + target.ApplyTemplate(); + target.Presenter.ApplyTemplate(); + target.SelectedItems = new AvaloniaList("foo", "bar", "baz"); + + Assert.Equal(0, target.SelectedIndex); + Assert.Equal(new[] { "foo", "bar", "baz" }, target.SelectedItems); + Assert.Equal(new[] { 0, 1, 2 }, SelectedContainers(target)); + } + + [Fact] + public void Selected_Items_Should_Be_Marked_When_Panel_Created_After_SelectedItems_Is_Set() + { + // Issue #2565. + var target = new TestSelector + { + Items = new[] { "foo", "bar", "baz" }, + Template = Template(), + }; + + target.ApplyTemplate(); + target.SelectedItems = new AvaloniaList("foo", "bar", "baz"); + target.Presenter.ApplyTemplate(); + + Assert.Equal(0, target.SelectedIndex); + Assert.Equal(new[] { "foo", "bar", "baz" }, target.SelectedItems); + Assert.Equal(new[] { 0, 1, 2 }, SelectedContainers(target)); } [Fact] From fc9a8db010acd04949de0814461aed10a636ff27 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Thu, 27 Jun 2019 01:08:29 +0200 Subject: [PATCH 09/45] Correctly select materialized containers. And add another test. --- .../Primitives/SelectingItemsControl.cs | 34 +++++++++++++------ .../SelectingItemsControlTests_Multiple.cs | 31 +++++++++++++++++ 2 files changed, 54 insertions(+), 11 deletions(-) diff --git a/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs b/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs index 6f7bf4eb3f..4f01f5467b 100644 --- a/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs +++ b/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs @@ -342,24 +342,36 @@ namespace Avalonia.Controls.Primitives { base.OnContainersMaterialized(e); - var selectedIndex = SelectedIndex; - var selectedContainer = e.Containers - .FirstOrDefault(x => (x.ContainerControl as ISelectable)?.IsSelected == true); + var resetSelectedItems = false; - if (selectedContainer != null) + foreach (var container in e.Containers) { - SelectedIndex = selectedContainer.Index; - } - else if (selectedIndex >= e.StartingIndex && - selectedIndex < e.StartingIndex + e.Containers.Count) - { - var container = e.Containers[selectedIndex - e.StartingIndex]; + if ((container.ContainerControl as ISelectable)?.IsSelected == true) + { + if (SelectedIndex == -1) + { + SelectedIndex = container.Index; + } + else + { + if (_selection.Add(container.Index)) + { + resetSelectedItems = true; + } + } - if (container.ContainerControl != null) + MarkContainerSelected(container.ContainerControl, true); + } + else if (_selection.Contains(container.Index)) { MarkContainerSelected(container.ContainerControl, true); } } + + if (resetSelectedItems) + { + ResetSelectedItems(); + } } /// diff --git a/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests_Multiple.cs b/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests_Multiple.cs index a44e23096f..2faced358b 100644 --- a/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests_Multiple.cs +++ b/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests_Multiple.cs @@ -1068,6 +1068,31 @@ namespace Avalonia.Controls.UnitTests.Primitives Assert.Equal(1, target.SelectedItems.Count); } + [Fact] + public void Adding_Selected_ItemContainers_Should_Update_Selection() + { + var items = new AvaloniaList(new[] + { + new ItemContainer(), + new ItemContainer(), + }); + + var target = new TestSelector + { + Items = items, + Template = Template(), + }; + + target.ApplyTemplate(); + target.Presenter.ApplyTemplate(); + items.Add(new ItemContainer { IsSelected = true }); + items.Add(new ItemContainer { IsSelected = true }); + + Assert.Equal(2, target.SelectedIndex); + Assert.Equal(items[2], target.SelectedItem); + Assert.Equal(new[] { items[2], items[3] }, target.SelectedItems); + } + private IEnumerable SelectedContainers(SelectingItemsControl target) { return target.Presenter.Panel.Children @@ -1120,5 +1145,11 @@ namespace Avalonia.Controls.UnitTests.Primitives public List Items { get; } public List SelectedItems { get; } } + + private class ItemContainer : Control, ISelectable + { + public string Value { get; set; } + public bool IsSelected { get; set; } + } } } From 9c56a4ba8412cca250a5574a717be54164cd9351 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Fri, 28 Jun 2019 15:21:05 +0300 Subject: [PATCH 10/45] [X11] Fixed NumLock --- src/Avalonia.X11/X11KeyTransform.cs | 10 +++------- src/Avalonia.X11/X11Window.cs | 15 +++++++++++++-- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/src/Avalonia.X11/X11KeyTransform.cs b/src/Avalonia.X11/X11KeyTransform.cs index 26495111d1..c68cb04733 100644 --- a/src/Avalonia.X11/X11KeyTransform.cs +++ b/src/Avalonia.X11/X11KeyTransform.cs @@ -221,12 +221,8 @@ namespace Avalonia.X11 //{ X11Key.?, Key.DeadCharProcessed } }; - public static Key ConvertKey(IntPtr key) - { - var ikey = key.ToInt32(); - Key result; - return KeyDic.TryGetValue((X11Key)ikey, out result) ? result : Key.None; - } -} + public static Key ConvertKey(X11Key key) + => KeyDic.TryGetValue(key, out var result) ? result : Key.None; + } } diff --git a/src/Avalonia.X11/X11Window.cs b/src/Avalonia.X11/X11Window.cs index 01beebfff1..18c23aa31e 100644 --- a/src/Avalonia.X11/X11Window.cs +++ b/src/Avalonia.X11/X11Window.cs @@ -419,10 +419,21 @@ namespace Avalonia.X11 return; var buffer = stackalloc byte[40]; - var latinKeysym = XKeycodeToKeysym(_x11.Display, ev.KeyEvent.keycode, 0); + var index = ev.KeyEvent.state.HasFlag(XModifierMask.ShiftMask); + + // We need the latin key, since it's mainly used for hotkeys, we use a different API for text anyway + var key = (X11Key)XKeycodeToKeysym(_x11.Display, ev.KeyEvent.keycode, index ? 1 : 0).ToInt32(); + + // Manually switch the Shift index for the keypad, + // there should be a proper way to do this + if (ev.KeyEvent.state.HasFlag(XModifierMask.Mod2Mask) + && key > X11Key.Num_Lock && key <= X11Key.KP_9) + key = (X11Key)XKeycodeToKeysym(_x11.Display, ev.KeyEvent.keycode, index ? 0 : 1).ToInt32(); + + ScheduleInput(new RawKeyEventArgs(_keyboard, (ulong)ev.KeyEvent.time.ToInt64(), ev.type == XEventName.KeyPress ? RawKeyEventType.KeyDown : RawKeyEventType.KeyUp, - X11KeyTransform.ConvertKey(latinKeysym), TranslateModifiers(ev.KeyEvent.state)), ref ev); + X11KeyTransform.ConvertKey(key), TranslateModifiers(ev.KeyEvent.state)), ref ev); if (ev.type == XEventName.KeyPress) { From 009f39ae743c72da1c058d2a31251b6b12c47379 Mon Sep 17 00:00:00 2001 From: artyom Date: Sun, 30 Jun 2019 19:43:19 +0300 Subject: [PATCH 11/45] Update AutoSuspendHelper to use IControlledApplicationLifetime --- src/Avalonia.ReactiveUI/AutoSuspendHelper.cs | 65 +++++++++++++++---- .../AutoSuspendHelperTest.cs | 14 ++-- 2 files changed, 64 insertions(+), 15 deletions(-) diff --git a/src/Avalonia.ReactiveUI/AutoSuspendHelper.cs b/src/Avalonia.ReactiveUI/AutoSuspendHelper.cs index e39ecb4a0f..a4f14a4138 100644 --- a/src/Avalonia.ReactiveUI/AutoSuspendHelper.cs +++ b/src/Avalonia.ReactiveUI/AutoSuspendHelper.cs @@ -11,6 +11,8 @@ using System.Reactive.Linq; using System.Reactive; using ReactiveUI; using System; +using Avalonia.Controls.ApplicationLifetimes; +using Splat; namespace Avalonia.ReactiveUI { @@ -19,26 +21,67 @@ namespace Avalonia.ReactiveUI /// Avalonia applications. Call its constructor in your app's composition root, /// before calling the RxApp.SuspensionHost.SetupDefaultSuspendResume method. /// - public sealed class AutoSuspendHelper + public sealed class AutoSuspendHelper : IEnableLogger, IDisposable { - public AutoSuspendHelper(Application app) + private readonly Subject _shouldPersistState = new Subject(); + private readonly Subject _isLaunchingNew = new Subject(); + + /// + /// Initializes a new instance of the class. + /// + /// Pass in the Application.ApplicationLifetime property. + public AutoSuspendHelper(IApplicationLifetime lifetime) { RxApp.SuspensionHost.IsResuming = Observable.Never(); - RxApp.SuspensionHost.IsLaunchingNew = Observable.Return(Unit.Default); + RxApp.SuspensionHost.IsLaunchingNew = _isLaunchingNew; - var exiting = new Subject(); - app.Exit += (o, e) => + if (lifetime is IControlledApplicationLifetime controlled) { - // This is required to prevent the app from shutting down too early. - var manual = new ManualResetEvent(false); - exiting.OnNext(Disposable.Create(() => manual.Set())); - manual.WaitOne(); - }; - RxApp.SuspensionHost.ShouldPersistState = exiting; + this.Log().Debug("Using IControlledApplicationLifetime events to handle app exit."); + controlled.Exit += (sender, args) => OnControlledApplicationLifetimeExit(); + RxApp.SuspensionHost.ShouldPersistState = _shouldPersistState; + } + else if (lifetime != null) + { + var type = lifetime.GetType().FullName; + var message = $"Don't know how to detect app exit event for {type}."; + throw new NotSupportedException(message); + } + else + { + var message = "ApplicationLifetime is null. " + + "Ensure you are initializing AutoSuspendHelper " + + "when Avalonia application initialization is completed."; + throw new ArgumentNullException(message); + } var errored = new Subject(); AppDomain.CurrentDomain.UnhandledException += (o, e) => errored.OnNext(Unit.Default); RxApp.SuspensionHost.ShouldInvalidateState = errored; } + + /// + /// Call this method in your App.OnFrameworkInitializationCompleted method. + /// + public void OnFrameworkInitializationCompleted() => _isLaunchingNew.OnNext(Unit.Default); + + /// + /// Disposes internally stored observers. + /// + public void Dispose() + { + _shouldPersistState.Dispose(); + _isLaunchingNew.Dispose(); + } + + private void OnControlledApplicationLifetimeExit() + { + this.Log().Debug("Received IControlledApplicationLifetime exit event."); + var manual = new ManualResetEvent(false); + _shouldPersistState.OnNext(Disposable.Create(() => manual.Set())); + + manual.WaitOne(); + this.Log().Debug("Completed actions on IControlledApplicationLifetime exit event."); + } } } diff --git a/tests/Avalonia.ReactiveUI.UnitTests/AutoSuspendHelperTest.cs b/tests/Avalonia.ReactiveUI.UnitTests/AutoSuspendHelperTest.cs index a3464878e6..d370454656 100644 --- a/tests/Avalonia.ReactiveUI.UnitTests/AutoSuspendHelperTest.cs +++ b/tests/Avalonia.ReactiveUI.UnitTests/AutoSuspendHelperTest.cs @@ -21,6 +21,7 @@ using Avalonia.ReactiveUI; using System.Reactive.Subjects; using System.Reactive.Linq; using System.Collections.Generic; +using System.Threading; namespace Avalonia.ReactiveUI.UnitTests { @@ -33,9 +34,10 @@ namespace Avalonia.ReactiveUI.UnitTests { var isLaunchingReceived = false; var application = AvaloniaLocator.Current.GetService(); - var suspension = new AutoSuspendHelper(application); + var suspension = new AutoSuspendHelper(application.ApplicationLifetime); RxApp.SuspensionHost.IsLaunchingNew.Subscribe(_ => isLaunchingReceived = true); + suspension.OnFrameworkInitializationCompleted(); Assert.True(isLaunchingReceived); } } @@ -47,12 +49,16 @@ namespace Avalonia.ReactiveUI.UnitTests { var shouldPersistReceived = false; var application = AvaloniaLocator.Current.GetService(); - var suspension = new AutoSuspendHelper(application); + var suspension = new AutoSuspendHelper(application.ApplicationLifetime); - RxApp.SuspensionHost.ShouldPersistState.Subscribe(_ => shouldPersistReceived = true); RxApp.SuspensionHost.SetupDefaultSuspendResume(new DummySuspensionDriver()); + RxApp.SuspensionHost.ShouldPersistState.Subscribe(_ => shouldPersistReceived = true); + suspension.OnFrameworkInitializationCompleted(); - application.Shutdown(); + var source = new CancellationTokenSource(); + source.CancelAfter(TimeSpan.FromMilliseconds(100)); + application.Run(source.Token); + Assert.True(shouldPersistReceived); } } From da467dd647432679b894be948b613f2f931fc3c7 Mon Sep 17 00:00:00 2001 From: artyom Date: Sun, 30 Jun 2019 21:00:18 +0300 Subject: [PATCH 12/45] Fix failing unit tests --- .../AutoSuspendHelperTest.cs | 49 ++++++++++++------- 1 file changed, 32 insertions(+), 17 deletions(-) diff --git a/tests/Avalonia.ReactiveUI.UnitTests/AutoSuspendHelperTest.cs b/tests/Avalonia.ReactiveUI.UnitTests/AutoSuspendHelperTest.cs index d370454656..142eba41d5 100644 --- a/tests/Avalonia.ReactiveUI.UnitTests/AutoSuspendHelperTest.cs +++ b/tests/Avalonia.ReactiveUI.UnitTests/AutoSuspendHelperTest.cs @@ -4,40 +4,53 @@ using System; using System.Reactive.Concurrency; using System.Reactive.Disposables; +using System.ComponentModel; +using System.Threading.Tasks; +using System.Reactive; +using System.Reactive.Subjects; +using System.Reactive.Linq; +using System.Collections.Generic; +using System.Runtime.Serialization; +using System.Threading; +using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Controls; using Avalonia.Rendering; using Avalonia.Platform; using Avalonia.UnitTests; +using Avalonia.Markup.Xaml; +using Avalonia.ReactiveUI; using Avalonia; using ReactiveUI; using DynamicData; using Xunit; using Splat; -using Avalonia.Markup.Xaml; -using System.ComponentModel; -using System.Threading.Tasks; -using System.Reactive; -using Avalonia.ReactiveUI; -using System.Reactive.Subjects; -using System.Reactive.Linq; -using System.Collections.Generic; -using System.Threading; namespace Avalonia.ReactiveUI.UnitTests { public class AutoSuspendHelperTest { + [DataContract] + public class AppState + { + [DataMember] + public string Example { get; set; } + } + [Fact] public void AutoSuspendHelper_Should_Immediately_Fire_IsLaunchingNew() { using (UnitTestApplication.Start(TestServices.MockWindowingPlatform)) + using (var lifetime = new ClassicDesktopStyleApplicationLifetime(Application.Current)) { var isLaunchingReceived = false; var application = AvaloniaLocator.Current.GetService(); - var suspension = new AutoSuspendHelper(application.ApplicationLifetime); + application.ApplicationLifetime = lifetime; + // Initialize ReactiveUI Suspension as in real-world scenario. + var suspension = new AutoSuspendHelper(application.ApplicationLifetime); RxApp.SuspensionHost.IsLaunchingNew.Subscribe(_ => isLaunchingReceived = true); suspension.OnFrameworkInitializationCompleted(); + Assert.True(isLaunchingReceived); } } @@ -45,21 +58,23 @@ namespace Avalonia.ReactiveUI.UnitTests [Fact] public void ShouldPersistState_Should_Fire_On_App_Exit_When_SuspensionDriver_Is_Initialized() { - using (UnitTestApplication.Start(TestServices.MockWindowingPlatform)) + using (UnitTestApplication.Start(TestServices.MockWindowingPlatform)) + using (var lifetime = new ClassicDesktopStyleApplicationLifetime(Application.Current)) { var shouldPersistReceived = false; var application = AvaloniaLocator.Current.GetService(); - var suspension = new AutoSuspendHelper(application.ApplicationLifetime); + application.ApplicationLifetime = lifetime; - RxApp.SuspensionHost.SetupDefaultSuspendResume(new DummySuspensionDriver()); + // Initialize ReactiveUI Suspension as in real-world scenario. + var suspension = new AutoSuspendHelper(application.ApplicationLifetime); + RxApp.SuspensionHost.CreateNewAppState = () => new AppState { Example = "Foo" }; RxApp.SuspensionHost.ShouldPersistState.Subscribe(_ => shouldPersistReceived = true); + RxApp.SuspensionHost.SetupDefaultSuspendResume(new DummySuspensionDriver()); suspension.OnFrameworkInitializationCompleted(); - var source = new CancellationTokenSource(); - source.CancelAfter(TimeSpan.FromMilliseconds(100)); - application.Run(source.Token); - + lifetime.Shutdown(); Assert.True(shouldPersistReceived); + Assert.Equal("Foo", RxApp.SuspensionHost.GetAppState().Example); } } } From 6eed5187468b46f416837afc718bf1ff2d5bc450 Mon Sep 17 00:00:00 2001 From: artyom Date: Sun, 30 Jun 2019 21:08:12 +0300 Subject: [PATCH 13/45] Indicate exotic lifetimes are not supported --- .../AutoSuspendHelperTest.cs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/tests/Avalonia.ReactiveUI.UnitTests/AutoSuspendHelperTest.cs b/tests/Avalonia.ReactiveUI.UnitTests/AutoSuspendHelperTest.cs index 142eba41d5..876f37cc9e 100644 --- a/tests/Avalonia.ReactiveUI.UnitTests/AutoSuspendHelperTest.cs +++ b/tests/Avalonia.ReactiveUI.UnitTests/AutoSuspendHelperTest.cs @@ -36,6 +36,11 @@ namespace Avalonia.ReactiveUI.UnitTests public string Example { get; set; } } + public class ExoticApplicationLifetimeWithoutLifecycleEvents : IDisposable, IApplicationLifetime + { + public void Dispose() { } + } + [Fact] public void AutoSuspendHelper_Should_Immediately_Fire_IsLaunchingNew() { @@ -77,5 +82,17 @@ namespace Avalonia.ReactiveUI.UnitTests Assert.Equal("Foo", RxApp.SuspensionHost.GetAppState().Example); } } + + [Fact] + public void AutoSuspendHelper_Should_Throw_For_Not_Supported_Lifetimes() + { + using (UnitTestApplication.Start(TestServices.MockWindowingPlatform)) + using (var lifetime = new ExoticApplicationLifetimeWithoutLifecycleEvents()) + { + var application = AvaloniaLocator.Current.GetService(); + application.ApplicationLifetime = lifetime; + Assert.Throws(() => new AutoSuspendHelper(application.ApplicationLifetime)); + } + } } } \ No newline at end of file From ae0feb21e07497bbd5811cbfe00b865a23941f4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Pedro?= Date: Sun, 30 Jun 2019 23:42:45 +0100 Subject: [PATCH 14/45] Fixed XML comment references. --- .../ControlledApplicationLifetimeExitEventArgs.cs | 2 +- .../ApplicationLifetimes/StartupEventArgs.cs | 2 +- src/Avalonia.Controls/ShutdownMode.cs | 4 +++- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/Avalonia.Controls/ApplicationLifetimes/ControlledApplicationLifetimeExitEventArgs.cs b/src/Avalonia.Controls/ApplicationLifetimes/ControlledApplicationLifetimeExitEventArgs.cs index d4c3b27f7a..2963c019cc 100644 --- a/src/Avalonia.Controls/ApplicationLifetimes/ControlledApplicationLifetimeExitEventArgs.cs +++ b/src/Avalonia.Controls/ApplicationLifetimes/ControlledApplicationLifetimeExitEventArgs.cs @@ -6,7 +6,7 @@ using System; namespace Avalonia.Controls.ApplicationLifetimes { /// - /// Contains the arguments for the event. + /// Contains the arguments for the event. /// public class ControlledApplicationLifetimeExitEventArgs : EventArgs { diff --git a/src/Avalonia.Controls/ApplicationLifetimes/StartupEventArgs.cs b/src/Avalonia.Controls/ApplicationLifetimes/StartupEventArgs.cs index 4c08712707..423832793e 100644 --- a/src/Avalonia.Controls/ApplicationLifetimes/StartupEventArgs.cs +++ b/src/Avalonia.Controls/ApplicationLifetimes/StartupEventArgs.cs @@ -8,7 +8,7 @@ using System.Linq; namespace Avalonia.Controls.ApplicationLifetimes { /// - /// Contains the arguments for the event. + /// Contains the arguments for the event. /// public class ControlledApplicationLifetimeStartupEventArgs : EventArgs { diff --git a/src/Avalonia.Controls/ShutdownMode.cs b/src/Avalonia.Controls/ShutdownMode.cs index 46e27ff4e1..ee593c28a8 100644 --- a/src/Avalonia.Controls/ShutdownMode.cs +++ b/src/Avalonia.Controls/ShutdownMode.cs @@ -1,10 +1,12 @@ // 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.ApplicationLifetimes; + namespace Avalonia.Controls { /// - /// Describes the possible values for . + /// Describes the possible values for . /// public enum ShutdownMode { From fa55755b71015567d25c4e183a29ec9f3c53354c Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Wed, 3 Jul 2019 23:29:19 +0300 Subject: [PATCH 15/45] Manually handle name scope registrations --- src/Avalonia.Controls/AutoCompleteBox.cs | 2 +- .../Embedding/EmbeddableControlRoot.cs | 21 +---- .../Embedding/Offscreen/OffscreenTopLevel.cs | 19 ----- .../Generators/TreeItemContainerGenerator.cs | 14 +++- .../Presenters/ContentPresenter.cs | 6 -- .../Primitives/TemplatedControl.cs | 30 ++----- .../Templates/FuncControlTemplate.cs | 4 +- .../Templates/FuncControlTemplate`2.cs | 6 +- .../Templates/FuncDataTemplate.cs | 8 +- .../Templates/FuncDataTemplate`1.cs | 26 +++++- .../FuncTemplateNameScopeExtensions.cs | 24 ++++++ .../Templates/FuncTemplate`2.cs | 13 ++- .../Templates/FuncTreeDataTemplate.cs | 4 +- .../Templates/FuncTreeDataTemplate`1.cs | 15 +++- src/Avalonia.Controls/UserControl.cs | 34 +------- src/Avalonia.Controls/Window.cs | 34 +------- .../AutoDataTemplateBindingHook.cs | 4 +- src/Avalonia.Styling/Controls/NameScope.cs | 15 ++++ .../Controls/NameScopeExtensions.cs | 41 ++++++++++ src/Avalonia.Styling/StyledElement.cs | 21 ----- src/Avalonia.Styling/Styling/Setter.cs | 1 - .../AvaloniaXamlIlLanguage.cs | 1 + .../Transformers/AddNameScopeRegistration.cs | 69 ++++++++-------- .../AvaloniaXamlIlWellKnownTypes.cs | 12 +++ .../XamlIl/Runtime/XamlIlRuntimeHelpers.cs | 1 + .../Avalonia.Markup.Xaml/XamlIl/xamlil.github | 2 +- src/Markup/Avalonia.Markup.Xaml/XamlTypes.cs | 8 ++ .../AutoCompleteBoxTests.cs | 10 +-- .../CarouselTests.cs | 4 +- .../ComboBoxTests.cs | 10 +-- .../ContentControlTests.cs | 12 +-- .../DatePickerTests.cs | 12 +-- .../GridSplitterTests.cs | 21 +++-- .../HeaderedItemsControlTests .cs | 6 +- .../ItemsControlTests.cs | 10 +-- .../ListBoxTests.cs | 30 +++---- .../ListBoxTests_Single.cs | 13 +-- .../Mixins/ContentControlMixinTests.cs | 17 ++-- .../ContentPresenterTests_InTemplate.cs | 18 ++-- .../ContentPresenterTests_Standalone.cs | 16 ++-- .../ContentPresenterTests_Unrooted.cs | 4 +- .../Presenters/ItemsPresenterTests.cs | 2 +- .../ItemsPresenterTests_Virtualization.cs | 2 +- ...emsPresenterTests_Virtualization_Simple.cs | 8 +- .../Primitives/PopupRootTests.cs | 6 +- .../Primitives/PopupTests.cs | 8 +- .../Primitives/RangeBaseTests.cs | 6 +- .../Primitives/ScrollBarTests.cs | 10 +-- .../Primitives/SelectingItemsControlTests.cs | 4 +- .../SelectingItemsControlTests_AutoSelect.cs | 4 +- .../SelectingItemsControlTests_Multiple.cs | 10 +-- .../Primitives/TabStripTests.cs | 4 +- .../Primitives/TemplatedControlTests.cs | 58 ++++++------- .../ScrollViewerTests.cs | 12 +-- .../TabControlTests.cs | 18 ++-- .../TextBoxTests.cs | 4 +- .../TextBoxTests_DataValidation.cs | 4 +- .../TopLevelTests.cs | 4 +- .../TreeViewTests.cs | 18 ++-- .../UserControlTests.cs | 6 +- .../Utils/HotKeyManagerTests.cs | 4 +- .../WindowBaseTests.cs | 4 +- .../Data/BindingTests_ElementName.cs | 10 ++- .../Data/MultiBindingTests_Converters.cs | 5 +- .../Data/TemplateBindingTests.cs | 6 +- .../MarkupExtensions/BindingExtensionTests.cs | 4 +- .../DynamicResourceExtensionTests.cs | 4 +- .../StaticResourceExtensionTests.cs | 4 +- .../AutoDataTemplateBindingHookTest.cs | 8 +- .../TransitioningContentControlTest.cs | 8 +- .../ControlLocatorTests.cs | 82 +++++++++++++++++-- .../SelectorTests_Multiple.cs | 12 +-- .../Avalonia.Styling.UnitTests/SetterTests.cs | 13 --- .../StyledElementTests.cs | 15 ---- .../StyledElementTests_NameScope.cs | 69 ---------------- .../StyledElementTests_Resources.cs | 4 +- tests/Avalonia.UnitTests/TestRoot.cs | 48 +++++------ tests/Avalonia.UnitTests/TestTemplatedRoot.cs | 33 +------- 78 files changed, 537 insertions(+), 602 deletions(-) create mode 100644 src/Avalonia.Controls/Templates/FuncTemplateNameScopeExtensions.cs delete mode 100644 tests/Avalonia.Styling.UnitTests/StyledElementTests_NameScope.cs diff --git a/src/Avalonia.Controls/AutoCompleteBox.cs b/src/Avalonia.Controls/AutoCompleteBox.cs index b87e10d284..473c4fe21b 100644 --- a/src/Avalonia.Controls/AutoCompleteBox.cs +++ b/src/Avalonia.Controls/AutoCompleteBox.cs @@ -795,7 +795,7 @@ namespace Avalonia.Controls var template = new FuncDataTemplate( typeof(object), - o => + (o, _) => { var control = new ContentControl(); control.Bind(ContentControl.ContentProperty, value); 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/TreeItemContainerGenerator.cs b/src/Avalonia.Controls/Generators/TreeItemContainerGenerator.cs index 304c86dbf7..43d1108fb9 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; @@ -123,11 +122,20 @@ namespace Avalonia.Controls.Generators return false; } + class WrapperTreeDataTemplate : ITreeDataTemplate + { + 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/Presenters/ContentPresenter.cs b/src/Avalonia.Controls/Presenters/ContentPresenter.cs index 49f268c128..c2690d503d 100644 --- a/src/Avalonia.Controls/Presenters/ContentPresenter.cs +++ b/src/Avalonia.Controls/Presenters/ContentPresenter.cs @@ -325,12 +325,6 @@ namespace Avalonia.Controls.Presenters { _dataTemplate = dataTemplate; newChild = _dataTemplate.Build(content); - - // Give the new control its own name scope. - if (newChild is Control controlResult) - { - NameScope.SetNameScope(controlResult, new NameScope()); - } } } else diff --git a/src/Avalonia.Controls/Primitives/TemplatedControl.cs b/src/Avalonia.Controls/Primitives/TemplatedControl.cs index 32e220b789..2927a3c7b3 100644 --- a/src/Avalonia.Controls/Primitives/TemplatedControl.cs +++ b/src/Avalonia.Controls/Primitives/TemplatedControl.cs @@ -258,13 +258,16 @@ namespace Avalonia.Controls.Primitives Logger.Verbose(LogArea.Control, this, "Creating control template"); var child = template.Build(this); - var nameScope = new NameScope(); - NameScope.SetNameScope((Control)child, nameScope); ApplyTemplatedParent(child); - RegisterNames(child, nameScope); ((ISetLogicalParent)child).SetParent(this); VisualChildren.Add(child); + var nameScope = (child is StyledElement styledChild) ? NameScope.GetNameScope(styledChild) : null; + + // Existing code kinda expect to see a NameScope even if it's empty + if (nameScope == null) + nameScope = new NameScope(); + OnTemplateApplied(new TemplateAppliedEventArgs(nameScope)); } @@ -342,26 +345,5 @@ namespace Avalonia.Controls.Primitives } } } - - /// - /// Registers each control with its name scope. - /// - /// The control. - /// The name scope. - private void RegisterNames(IControl control, INameScope nameScope) - { - if (control.Name != null) - { - nameScope.Register(control.Name, control); - } - - if (control.TemplatedParent == this) - { - foreach (IControl child in control.GetLogicalChildren()) - { - RegisterNames(child, nameScope); - } - } - } } } diff --git a/src/Avalonia.Controls/Templates/FuncControlTemplate.cs b/src/Avalonia.Controls/Templates/FuncControlTemplate.cs index 4c3c92309e..8a588b4089 100644 --- a/src/Avalonia.Controls/Templates/FuncControlTemplate.cs +++ b/src/Avalonia.Controls/Templates/FuncControlTemplate.cs @@ -16,9 +16,9 @@ namespace Avalonia.Controls.Templates /// Initializes a new instance of the class. /// /// The build function. - public FuncControlTemplate(Func build) + public FuncControlTemplate(Func build) : base(build) { } } -} \ 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/FuncTemplateNameScopeExtensions.cs b/src/Avalonia.Controls/Templates/FuncTemplateNameScopeExtensions.cs new file mode 100644 index 0000000000..c9c083f9c7 --- /dev/null +++ b/src/Avalonia.Controls/Templates/FuncTemplateNameScopeExtensions.cs @@ -0,0 +1,24 @@ +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; + } + + public static T WithNameScope(this T control, INameScope scope) + where T : StyledElement + { + var existingScope = NameScope.GetNameScope(control); + if (existingScope != null && existingScope != scope) + throw new InvalidOperationException("Control already has a name scope"); + NameScope.SetNameScope(control, scope); + return control; + } + } +} diff --git a/src/Avalonia.Controls/Templates/FuncTemplate`2.cs b/src/Avalonia.Controls/Templates/FuncTemplate`2.cs index b1ce63fdf1..a779f796e7 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,12 @@ namespace Avalonia.Controls.Templates /// public TControl Build(TParam param) { - return _func(param); + var scope = new NameScope(); + var rv = _func(param, scope); + // TODO: May be return the name scope alongside with the control instead? + if (rv is StyledElement sl) + NameScope.SetNameScope(sl, scope); + return rv; } } -} \ 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/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.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/NameScope.cs b/src/Avalonia.Styling/Controls/NameScope.cs index e3a29af541..aeafa0bb0c 100644 --- a/src/Avalonia.Styling/Controls/NameScope.cs +++ b/src/Avalonia.Styling/Controls/NameScope.cs @@ -106,6 +106,21 @@ namespace Avalonia.Controls } } + /// + /// Registers an element in the name scope associated with the scopeElement. + /// Creates the scope if one isn't associated with the element yet + /// + /// + /// + /// + public static void Register(StyledElement scopeElement, string name, object element) + { + var scope = scopeElement as INameScope ?? GetNameScope(scopeElement); + if(scope == null) + SetNameScope(scopeElement, scope = new NameScope()); + scope.Register(name, element); + } + /// /// Finds a named element in the name scope. /// 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/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/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/AvaloniaXamlIlLanguage.cs b/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/AvaloniaXamlIlLanguage.cs index c25e1186d0..b4d090e278 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"), 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..05548808af 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,7 +41,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers Children = { pa, - new ScopeRegistrationNode(value) + new AvaloniaNameScopeRegistrationXamlIlNode(value, context.GetAvaloniaTypes()) } }; } @@ -49,46 +49,41 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers return node; } - class ScopeRegistrationNode : XamlIlAstNode, IXamlIlAstManipulationNode, IXamlIlAstEmitableNode + + } + + class AvaloniaNameScopeRegistrationXamlIlNode : XamlIlAstNode, IXamlIlAstManipulationNode, IXamlIlAstEmitableNode + { + private readonly AvaloniaXamlIlWellKnownTypes _types; + public IXamlIlAstValueNode Name { get; set; } + + public AvaloniaNameScopeRegistrationXamlIlNode(IXamlIlAstValueNode name, AvaloniaXamlIlWellKnownTypes types) : base(name) { - public IXamlIlAstValueNode Value { get; set; } - public ScopeRegistrationNode(IXamlIlAstValueNode value) : base(value) - { - Value = value; - } + _types = types; + Name = name; + } - public override void VisitChildren(IXamlIlAstVisitor visitor) - => Value = (IXamlIlAstValueNode)Value.Visit(visitor); + public override void VisitChildren(IXamlIlAstVisitor visitor) + => Name = (IXamlIlAstValueNode)Name.Visit(visitor); - public XamlIlNodeEmitResult Emit(XamlIlEmitContext context, IXamlIlEmitter codeGen) + public XamlIlNodeEmitResult Emit(XamlIlEmitContext context, IXamlIlEmitter codeGen) + { + using (var targetLoc = context.GetLocal(context.Configuration.WellKnownTypes.Object)) { - 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 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); - } - return XamlIlNodeEmitResult.Void(1); + codeGen + // var target = {pop} + .Stloc(targetLoc.Local) + // NameScope.Register(context.IntermediateRoot, Name, target) + .Ldloc(context.ContextLocal) + .Ldfld(context.RuntimeContext.IntermediateRootObjectField) + .Castclass(_types.StyledElement); + context.Emit(Name, codeGen, Name.Type.GetClrType()); + codeGen + .Ldloc(targetLoc.Local) + .EmitCall(_types.NameScopeStaticRegister, 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..81bbc80851 100644 --- a/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs +++ b/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs @@ -19,6 +19,9 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers public IXamlIlType Transitions { get; } public IXamlIlType AssignBindingAttribute { get; } public IXamlIlType UnsetValueType { get; } + public IXamlIlType StyledElement { get; } + public IXamlIlType NameScope { get; } + public IXamlIlMethod NameScopeStaticRegister { get; } public AvaloniaXamlIlWellKnownTypes(XamlIlAstTransformationContext ctx) { @@ -37,6 +40,15 @@ 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"); + NameScope = ctx.Configuration.TypeSystem.GetType("Avalonia.Controls.NameScope"); + NameScopeStaticRegister = NameScope.FindMethod( + new FindMethodMethodSignature("Register", XamlIlTypes.Void, + StyledElement, XamlIlTypes.String, XamlIlTypes.Object) + { + IsStatic = true, DeclaringOnly = true, IsExactMatch = 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..c6d2ecee72 100644 --- a/src/Markup/Avalonia.Markup.Xaml/XamlIl/Runtime/XamlIlRuntimeHelpers.cs +++ b/src/Markup/Avalonia.Markup.Xaml/XamlIl/Runtime/XamlIlRuntimeHelpers.cs @@ -56,6 +56,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.Runtime } public object RootObject { get; } + public object IntermediateRootObject => RootObject; } diff --git a/src/Markup/Avalonia.Markup.Xaml/XamlIl/xamlil.github b/src/Markup/Avalonia.Markup.Xaml/XamlIl/xamlil.github index 894b2c0282..fc52295ea6 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 fc52295ea6c45f709bf63975026a4a18ce230be9 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/tests/Avalonia.Controls.UnitTests/AutoCompleteBoxTests.cs b/tests/Avalonia.Controls.UnitTests/AutoCompleteBoxTests.cs index b10929cbdc..b302237285 100644 --- a/tests/Avalonia.Controls.UnitTests/AutoCompleteBoxTests.cs +++ b/tests/Avalonia.Controls.UnitTests/AutoCompleteBoxTests.cs @@ -1012,25 +1012,25 @@ 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(); + var panel = new Panel().WithNameScope(scope); panel.Children.Add(textBox); panel.Children.Add(popup); panel.Children.Add(listbox); diff --git a/tests/Avalonia.Controls.UnitTests/CarouselTests.cs b/tests/Avalonia.Controls.UnitTests/CarouselTests.cs index 515e18434d..f70a61f43a 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).WithNameScope(scope); } } } diff --git a/tests/Avalonia.Controls.UnitTests/ComboBoxTests.cs b/tests/Avalonia.Controls.UnitTests/ComboBoxTests.cs index 70ec6c1408..879f32087d 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,10 +102,10 @@ namespace Avalonia.Controls.UnitTests { Name = "PART_ItemsPresenter", [!ItemsPresenter.ItemsProperty] = parent[!ComboBox.ItemsProperty], - } - } + }.RegisterInNameScope(scope) + }.RegisterInNameScope(scope) } - }; + }.WithNameScope(scope); }); } } diff --git a/tests/Avalonia.Controls.UnitTests/ContentControlTests.cs b/tests/Avalonia.Controls.UnitTests/ContentControlTests.cs index c17893604c..abddbe16ab 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,8 +343,8 @@ namespace Avalonia.Controls.UnitTests Name = "PART_ContentPresenter", [~ContentPresenter.ContentProperty] = parent[~ContentControl.ContentProperty], [~ContentPresenter.ContentTemplateProperty] = parent[~ContentControl.ContentTemplateProperty], - } - }; + }.RegisterInNameScope(scope) + }.WithNameScope(scope); }); } } diff --git a/tests/Avalonia.Controls.UnitTests/DatePickerTests.cs b/tests/Avalonia.Controls.UnitTests/DatePickerTests.cs index 936d700ad0..ac30daf505 100644 --- a/tests/Avalonia.Controls.UnitTests/DatePickerTests.cs +++ b/tests/Avalonia.Controls.UnitTests/DatePickerTests.cs @@ -93,30 +93,30 @@ 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(); + var panel = new Panel().WithNameScope(scope); panel.Children.Add(textBox); panel.Children.Add(button); panel.Children.Add(popup); 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..832a19c02d 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,8 +71,8 @@ namespace Avalonia.Controls.UnitTests { Name = "PART_HeaderPresenter", [~ContentPresenter.ContentProperty] = parent[~HeaderedItemsControl.HeaderProperty], - } - }; + }.RegisterInNameScope(scope) + }.WithNameScope(scope); }); } } diff --git a/tests/Avalonia.Controls.UnitTests/ItemsControlTests.cs b/tests/Avalonia.Controls.UnitTests/ItemsControlTests.cs index 3cf886ade4..6680766ffa 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, }; @@ -578,7 +578,7 @@ namespace Avalonia.Controls.UnitTests private FuncControlTemplate GetTemplate() { - return new FuncControlTemplate(parent => + return new FuncControlTemplate((parent, scope) => { return new Border { @@ -588,8 +588,8 @@ namespace Avalonia.Controls.UnitTests Name = "PART_ItemsPresenter", MemberSelector = parent.MemberSelector, [~ItemsPresenter.ItemsProperty] = parent[~ItemsControl.ItemsProperty], - } - }; + }.RegisterInNameScope(scope) + }.WithNameScope(scope); }); } diff --git a/tests/Avalonia.Controls.UnitTests/ListBoxTests.cs b/tests/Avalonia.Controls.UnitTests/ListBoxTests.cs index 238e214a5d..ef61d34ee1 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).WithNameScope(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).WithNameScope(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).WithNameScope(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..ece9e27fb3 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) + }.WithNameScope(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).WithNameScope(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..06679b2399 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) } - }) + }.WithNameScope(scope)) }; + var ex = Record.Exception(() => target.ApplyTemplate()); @@ -43,14 +44,14 @@ 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) } - }) + }.WithNameScope(scope)) }; target.ApplyTemplate(); diff --git a/tests/Avalonia.Controls.UnitTests/Presenters/ContentPresenterTests_InTemplate.cs b/tests/Avalonia.Controls.UnitTests/Presenters/ContentPresenterTests_InTemplate.cs index 708e934214..b8b329099a 100644 --- a/tests/Avalonia.Controls.UnitTests/Presenters/ContentPresenterTests_InTemplate.cs +++ b/tests/Avalonia.Controls.UnitTests/Presenters/ContentPresenterTests_InTemplate.cs @@ -170,7 +170,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(target.Child); @@ -184,7 +184,7 @@ namespace Avalonia.Controls.UnitTests.Presenters target.Content = "Foo"; Assert.IsType(target.Child); - target.ContentTemplate = new FuncDataTemplate(_ => new Canvas()); + target.ContentTemplate = new FuncDataTemplate((_, __) => new Canvas()); Assert.IsType(target.Child); target.ContentTemplate = null; @@ -209,7 +209,7 @@ namespace Avalonia.Controls.UnitTests.Presenters public void Recycles_DataTemplate() { var (target, _) = CreateTarget(); - target.DataTemplates.Add(new FuncDataTemplate(_ => new Border(), true)); + target.DataTemplates.Add(new FuncDataTemplate((_, __) => new Border(), true)); target.Content = "foo"; @@ -239,7 +239,7 @@ namespace Avalonia.Controls.UnitTests.Presenters public void Detects_DataTemplate_Doesnt_Support_Recycling() { var (target, _) = CreateTarget(); - target.DataTemplates.Add(new FuncDataTemplate(_ => new Border(), false)); + target.DataTemplates.Add(new FuncDataTemplate((_, __) => new Border(), false)); target.Content = "foo"; @@ -256,7 +256,7 @@ namespace Avalonia.Controls.UnitTests.Presenters var (target, _) = CreateTarget(); target.DataTemplates.Add(new FuncDataTemplate(x => x == "bar", _ => new Canvas(), true)); - target.DataTemplates.Add(new FuncDataTemplate(_ => new Border(), true)); + target.DataTemplates.Add(new FuncDataTemplate((_, __) => new Border(), true)); target.Content = "foo"; @@ -278,8 +278,8 @@ namespace Avalonia.Controls.UnitTests.Presenters }; var (target, host) = CreateTarget(); - host.DataTemplates.Add(new FuncDataTemplate(x => textBlock)); - host.DataTemplates.Add(new FuncDataTemplate(x => new Canvas())); + host.DataTemplates.Add(new FuncDataTemplate((_, __) => textBlock)); + host.DataTemplates.Add(new FuncDataTemplate((_, __) => new Canvas())); target.Content = "foo"; Assert.Same(textBlock, target.Child); @@ -296,11 +296,11 @@ namespace Avalonia.Controls.UnitTests.Presenters { var templatedParent = new ContentControl { - Template = new FuncControlTemplate(x => + Template = new FuncControlTemplate((_, s) => new ContentPresenter { Name = "PART_ContentPresenter", - }), + }.RegisterInNameScope(s).WithNameScope(s)), }; var root = new TestRoot { Child = templatedParent }; diff --git a/tests/Avalonia.Controls.UnitTests/Presenters/ContentPresenterTests_Standalone.cs b/tests/Avalonia.Controls.UnitTests/Presenters/ContentPresenterTests_Standalone.cs index 2facee16b7..5dd4deabf6 100644 --- a/tests/Avalonia.Controls.UnitTests/Presenters/ContentPresenterTests_Standalone.cs +++ b/tests/Avalonia.Controls.UnitTests/Presenters/ContentPresenterTests_Standalone.cs @@ -54,7 +54,7 @@ namespace Avalonia.Controls.UnitTests.Presenters var target = new ContentPresenter { ContentTemplate = - new FuncDataTemplate(t => new ContentControl() { Content = t }, false) + new FuncDataTemplate((t, _) => new ContentControl() { Content = t }, false) }; var parentMock = new Mock(); @@ -93,14 +93,14 @@ namespace Avalonia.Controls.UnitTests.Presenters { var contentControl = new ContentControl { - Template = new FuncControlTemplate(c => new ContentPresenter() + Template = new FuncControlTemplate((c, scope) => new ContentPresenter() { Name = "PART_ContentPresenter", [~ContentPresenter.ContentProperty] = c[~ContentControl.ContentProperty], [~ContentPresenter.ContentTemplateProperty] = c[~ContentControl.ContentTemplateProperty] - }), + }.RegisterInNameScope(scope).WithNameScope(scope)), ContentTemplate = - new FuncDataTemplate(t => new ContentControl() { Content = t }, false) + new FuncDataTemplate((t, _) => new ContentControl() { Content = t }, false) }; var parentMock = new Mock(); @@ -144,7 +144,7 @@ namespace Avalonia.Controls.UnitTests.Presenters var target = new ContentPresenter { ContentTemplate = - new FuncDataTemplate(t => new ContentControl() { Content = t }, false) + new FuncDataTemplate((t, _) => new ContentControl() { Content = t }, false) }; var parentMock = new Mock(); @@ -179,7 +179,7 @@ namespace Avalonia.Controls.UnitTests.Presenters var target = new ContentPresenter { ContentTemplate = - new FuncDataTemplate(t => new ContentControl() { Content = t }, false) + new FuncDataTemplate((t, _) => new ContentControl() { Content = t }, false) }; target.Content = "foo"; @@ -235,8 +235,8 @@ namespace Avalonia.Controls.UnitTests.Presenters { DataTemplates = { - new FuncDataTemplate(x => textBlock), - new FuncDataTemplate(x => new Canvas()), + new FuncDataTemplate((x, _) => textBlock), + new FuncDataTemplate((x, _) => new Canvas()), }, }; diff --git a/tests/Avalonia.Controls.UnitTests/Presenters/ContentPresenterTests_Unrooted.cs b/tests/Avalonia.Controls.UnitTests/Presenters/ContentPresenterTests_Unrooted.cs index 5268c9ac0d..09970926fa 100644 --- a/tests/Avalonia.Controls.UnitTests/Presenters/ContentPresenterTests_Unrooted.cs +++ b/tests/Avalonia.Controls.UnitTests/Presenters/ContentPresenterTests_Unrooted.cs @@ -90,7 +90,7 @@ namespace Avalonia.Controls.UnitTests.Presenters { DataTemplates = { - new FuncDataTemplate(x => new Decorator()), + new FuncDataTemplate((x, _) => new Decorator()), }, }; @@ -99,4 +99,4 @@ namespace Avalonia.Controls.UnitTests.Presenters Assert.IsType(target.Child); } } -} \ No newline at end of file +} diff --git a/tests/Avalonia.Controls.UnitTests/Presenters/ItemsPresenterTests.cs b/tests/Avalonia.Controls.UnitTests/Presenters/ItemsPresenterTests.cs index 3d13e4c32f..7ca11d9bed 100644 --- a/tests/Avalonia.Controls.UnitTests/Presenters/ItemsPresenterTests.cs +++ b/tests/Avalonia.Controls.UnitTests/Presenters/ItemsPresenterTests.cs @@ -220,7 +220,7 @@ namespace Avalonia.Controls.UnitTests.Presenters { VirtualizationMode = ItemVirtualizationMode.None, Items = items, - ItemTemplate = new FuncDataTemplate(x => new TextBlock { Height = 10 }), + ItemTemplate = new FuncDataTemplate((x, _) => new TextBlock { Height = 10 }), }; target.ApplyTemplate(); diff --git a/tests/Avalonia.Controls.UnitTests/Presenters/ItemsPresenterTests_Virtualization.cs b/tests/Avalonia.Controls.UnitTests/Presenters/ItemsPresenterTests_Virtualization.cs index 3ea32ed719..269da884ba 100644 --- a/tests/Avalonia.Controls.UnitTests/Presenters/ItemsPresenterTests_Virtualization.cs +++ b/tests/Avalonia.Controls.UnitTests/Presenters/ItemsPresenterTests_Virtualization.cs @@ -309,7 +309,7 @@ namespace Avalonia.Controls.UnitTests.Presenters private static IDataTemplate ItemTemplate() { - return new FuncDataTemplate(x => new Canvas + return new FuncDataTemplate((x, _) => new Canvas { Width = 10, Height = 10, diff --git a/tests/Avalonia.Controls.UnitTests/Presenters/ItemsPresenterTests_Virtualization_Simple.cs b/tests/Avalonia.Controls.UnitTests/Presenters/ItemsPresenterTests_Virtualization_Simple.cs index 97d57e9eb6..dcbe8b4e0a 100644 --- a/tests/Avalonia.Controls.UnitTests/Presenters/ItemsPresenterTests_Virtualization_Simple.cs +++ b/tests/Avalonia.Controls.UnitTests/Presenters/ItemsPresenterTests_Virtualization_Simple.cs @@ -470,7 +470,7 @@ namespace Avalonia.Controls.UnitTests.Presenters { VirtualizationMode = ItemVirtualizationMode.None, Items = items, - ItemTemplate = new FuncDataTemplate(x => new TextBlock { Height = 10 }), + ItemTemplate = new FuncDataTemplate((x, _) => new TextBlock { Height = 10 }), }; target.ApplyTemplate(); @@ -1046,7 +1046,7 @@ namespace Avalonia.Controls.UnitTests.Presenters private static IDataTemplate StringDataTemplate() { - return new FuncDataTemplate(x => new Canvas + return new FuncDataTemplate((x, _) => new Canvas { Width = 10, Height = 10, @@ -1095,12 +1095,12 @@ namespace Avalonia.Controls.UnitTests.Presenters { public TestContainer() { - Template = new FuncControlTemplate(parent => new ContentPresenter + Template = new FuncControlTemplate((parent, scope) => new ContentPresenter { Name = "PART_ContentPresenter", [~ContentPresenter.ContentProperty] = parent[~ContentControl.ContentProperty], [~ContentPresenter.ContentTemplateProperty] = parent[~ContentControl.ContentTemplateProperty], - }); + }.RegisterInNameScope(scope).WithNameScope(scope)); } } } diff --git a/tests/Avalonia.Controls.UnitTests/Primitives/PopupRootTests.cs b/tests/Avalonia.Controls.UnitTests/Primitives/PopupRootTests.cs index d4b5d01a6b..25dcea32ab 100644 --- a/tests/Avalonia.Controls.UnitTests/Primitives/PopupRootTests.cs +++ b/tests/Avalonia.Controls.UnitTests/Primitives/PopupRootTests.cs @@ -134,12 +134,12 @@ namespace Avalonia.Controls.UnitTests.Primitives { var result = new PopupRoot { - Template = new FuncControlTemplate(parent => + Template = new FuncControlTemplate((parent, scope) => new ContentPresenter { Name = "PART_ContentPresenter", [!ContentPresenter.ContentProperty] = parent[!PopupRoot.ContentProperty], - }), + }.RegisterInNameScope(scope).WithNameScope(scope)), }; result.ApplyTemplate(); @@ -154,7 +154,7 @@ namespace Avalonia.Controls.UnitTests.Primitives public TemplatedControlWithPopup() { - Template = new FuncControlTemplate(parent => + Template = new FuncControlTemplate((parent, _) => new Popup { [!Popup.ChildProperty] = parent[!TemplatedControlWithPopup.PopupContentProperty], diff --git a/tests/Avalonia.Controls.UnitTests/Primitives/PopupTests.cs b/tests/Avalonia.Controls.UnitTests/Primitives/PopupTests.cs index bd11cf2e3c..4db542c4e1 100644 --- a/tests/Avalonia.Controls.UnitTests/Primitives/PopupTests.cs +++ b/tests/Avalonia.Controls.UnitTests/Primitives/PopupTests.cs @@ -315,16 +315,16 @@ namespace Avalonia.Controls.UnitTests.Primitives return result; } - private static IControl PopupRootTemplate(PopupRoot control) + private static IControl PopupRootTemplate(PopupRoot control, INameScope scope) { return new ContentPresenter { Name = "PART_ContentPresenter", [~ContentPresenter.ContentProperty] = control[~ContentControl.ContentProperty], - }; + }.RegisterInNameScope(scope).WithNameScope(scope); } - private static IControl PopupContentControlTemplate(PopupContentControl control) + private static IControl PopupContentControlTemplate(PopupContentControl control, INameScope scope) { return new Popup { @@ -333,7 +333,7 @@ namespace Avalonia.Controls.UnitTests.Primitives { [~ContentPresenter.ContentProperty] = control[~ContentControl.ContentProperty], } - }; + }.RegisterInNameScope(scope).WithNameScope(scope); } private class PopupContentControl : ContentControl diff --git a/tests/Avalonia.Controls.UnitTests/Primitives/RangeBaseTests.cs b/tests/Avalonia.Controls.UnitTests/Primitives/RangeBaseTests.cs index d913e3e54f..5fe7e270d9 100644 --- a/tests/Avalonia.Controls.UnitTests/Primitives/RangeBaseTests.cs +++ b/tests/Avalonia.Controls.UnitTests/Primitives/RangeBaseTests.cs @@ -111,7 +111,7 @@ namespace Avalonia.Controls.UnitTests.Primitives var target = new TestRange() { - Template = new FuncControlTemplate(c => + Template = new FuncControlTemplate((c, scope) => { track = new Track() { @@ -122,7 +122,7 @@ namespace Avalonia.Controls.UnitTests.Primitives Name = "PART_Track", Thumb = new Thumb() - }; + }.RegisterInNameScope(scope); if (useXamlBinding) { @@ -138,7 +138,7 @@ namespace Avalonia.Controls.UnitTests.Primitives track[~~Track.ValueProperty] = c[~~RangeBase.ValueProperty]; } - return track; + return track.WithNameScope(scope); }), Minimum = 0, Maximum = 100, diff --git a/tests/Avalonia.Controls.UnitTests/Primitives/ScrollBarTests.cs b/tests/Avalonia.Controls.UnitTests/Primitives/ScrollBarTests.cs index 672b5c608b..cdb6aebb25 100644 --- a/tests/Avalonia.Controls.UnitTests/Primitives/ScrollBarTests.cs +++ b/tests/Avalonia.Controls.UnitTests/Primitives/ScrollBarTests.cs @@ -169,7 +169,7 @@ namespace Avalonia.Controls.UnitTests.Primitives Assert.False(target.IsVisible); } - private static Control Template(ScrollBar control) + private static Control Template(ScrollBar control, INameScope scope) { return new Border { @@ -185,11 +185,11 @@ namespace Avalonia.Controls.UnitTests.Primitives { Template = new FuncControlTemplate(ThumbTemplate), }, - }, - }; + }.RegisterInNameScope(scope), + }.WithNameScope(scope); } - private static Control ThumbTemplate(Thumb control) + private static Control ThumbTemplate(Thumb control, INameScope scope) { return new Border { @@ -197,4 +197,4 @@ namespace Avalonia.Controls.UnitTests.Primitives }; } } -} \ No newline at end of file +} diff --git a/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests.cs b/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests.cs index 75cababf54..1036730575 100644 --- a/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests.cs +++ b/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests.cs @@ -896,13 +896,13 @@ namespace Avalonia.Controls.UnitTests.Primitives private FuncControlTemplate Template() { - return new FuncControlTemplate(control => + return new FuncControlTemplate((control, scope) => new ItemsPresenter { Name = "itemsPresenter", [~ItemsPresenter.ItemsProperty] = control[~ItemsControl.ItemsProperty], [~ItemsPresenter.ItemsPanelProperty] = control[~ItemsControl.ItemsPanelProperty], - }); + }.RegisterInNameScope(scope).WithNameScope(scope)); } private class Item : Control, ISelectable diff --git a/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests_AutoSelect.cs b/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests_AutoSelect.cs index 88cb726469..e2d306dd67 100644 --- a/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests_AutoSelect.cs +++ b/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests_AutoSelect.cs @@ -84,13 +84,13 @@ namespace Avalonia.Controls.UnitTests.Primitives private FuncControlTemplate Template() { - return new FuncControlTemplate(control => + return new FuncControlTemplate((control, scope) => new ItemsPresenter { Name = "itemsPresenter", [~ItemsPresenter.ItemsProperty] = control[~ItemsControl.ItemsProperty], [~ItemsPresenter.ItemsPanelProperty] = control[~ItemsControl.ItemsPanelProperty], - }); + }.RegisterInNameScope(scope).WithNameScope(scope)); } private class TestSelector : SelectingItemsControl diff --git a/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests_Multiple.cs b/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests_Multiple.cs index a33d97779e..68884a6a4c 100644 --- a/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests_Multiple.cs +++ b/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests_Multiple.cs @@ -964,7 +964,7 @@ namespace Avalonia.Controls.UnitTests.Primitives { Template = Template(), Items = new[] { "Foo", "Bar", "Baz" }, - ItemTemplate = new FuncDataTemplate(x => new TextBlock { Width = 20, Height = 10 }), + ItemTemplate = new FuncDataTemplate((x, _) => new TextBlock { Width = 20, Height = 10 }), SelectionMode = SelectionMode.Multiple, }; @@ -988,7 +988,7 @@ namespace Avalonia.Controls.UnitTests.Primitives { Template = Template(), Items = new[] { "Foo", "Bar", "Baz" }, - ItemTemplate = new FuncDataTemplate(x => new TextBlock { Width = 20, Height = 10 }), + ItemTemplate = new FuncDataTemplate((x, _) => new TextBlock { Width = 20, Height = 10 }), SelectionMode = SelectionMode.Multiple, }; @@ -1010,7 +1010,7 @@ namespace Avalonia.Controls.UnitTests.Primitives { Template = Template(), Items = new[] { "Foo", "Bar", "Baz" }, - ItemTemplate = new FuncDataTemplate(x => new TextBlock { Width = 20, Height = 10 }), + ItemTemplate = new FuncDataTemplate((x, _) => new TextBlock { Width = 20, Height = 10 }), SelectionMode = SelectionMode.Multiple, }; @@ -1035,13 +1035,13 @@ namespace Avalonia.Controls.UnitTests.Primitives private FuncControlTemplate Template() { - return new FuncControlTemplate(control => + return new FuncControlTemplate((control, scope) => new ItemsPresenter { Name = "PART_ItemsPresenter", [~ItemsPresenter.ItemsProperty] = control[~ItemsControl.ItemsProperty], [~ItemsPresenter.ItemsPanelProperty] = control[~ItemsControl.ItemsPanelProperty], - }); + }.RegisterInNameScope(scope).WithNameScope(scope)); } private class TestSelector : SelectingItemsControl diff --git a/tests/Avalonia.Controls.UnitTests/Primitives/TabStripTests.cs b/tests/Avalonia.Controls.UnitTests/Primitives/TabStripTests.cs index 55b3d6f756..d7e78fdd0d 100644 --- a/tests/Avalonia.Controls.UnitTests/Primitives/TabStripTests.cs +++ b/tests/Avalonia.Controls.UnitTests/Primitives/TabStripTests.cs @@ -159,14 +159,14 @@ namespace Avalonia.Controls.UnitTests.Primitives Assert.Same("3rd", ((TabItem)target.SelectedItem).Name); } - private Control CreateTabStripTemplate(TabStrip parent) + private Control CreateTabStripTemplate(TabStrip parent, INameScope scope) { return new ItemsPresenter { Name = "itemsPresenter", [!ItemsPresenter.ItemsProperty] = parent[!ItemsControl.ItemsProperty], [!ItemsPresenter.MemberSelectorProperty] = parent[!ItemsControl.MemberSelectorProperty], - }; + }.RegisterInNameScope(scope).WithNameScope(scope); } } } diff --git a/tests/Avalonia.Controls.UnitTests/Primitives/TemplatedControlTests.cs b/tests/Avalonia.Controls.UnitTests/Primitives/TemplatedControlTests.cs index 166586ace1..db0f2b6fb3 100644 --- a/tests/Avalonia.Controls.UnitTests/Primitives/TemplatedControlTests.cs +++ b/tests/Avalonia.Controls.UnitTests/Primitives/TemplatedControlTests.cs @@ -23,7 +23,7 @@ namespace Avalonia.Controls.UnitTests.Primitives { bool executed = false; - var template = new FuncControlTemplate(_ => + var template = new FuncControlTemplate((_, __) => { executed = true; return new Control(); @@ -42,7 +42,7 @@ namespace Avalonia.Controls.UnitTests.Primitives { bool executed = false; - var template = new FuncControlTemplate(_ => + var template = new FuncControlTemplate((_, __) => { executed = true; return new Control(); @@ -63,7 +63,7 @@ namespace Avalonia.Controls.UnitTests.Primitives { var target = new TemplatedControl { - Template = new FuncControlTemplate(_ => new Decorator + Template = new FuncControlTemplate((_, __) => new Decorator { Child = new Panel { @@ -97,7 +97,7 @@ namespace Avalonia.Controls.UnitTests.Primitives { var target = new TemplatedControl { - Template = new FuncControlTemplate(_ => new Decorator + Template = new FuncControlTemplate((_, __) => new Decorator { Child = new Panel { @@ -120,7 +120,7 @@ namespace Avalonia.Controls.UnitTests.Primitives { var target = new TemplatedControl { - Template = new FuncControlTemplate(_ => new Decorator + Template = new FuncControlTemplate((_, __) => new Decorator { Child = new Panel { @@ -149,7 +149,7 @@ namespace Avalonia.Controls.UnitTests.Primitives { var target = new TemplatedControl { - Template = new FuncControlTemplate(_ => new Decorator()) + Template = new FuncControlTemplate((_, __) => new Decorator()) }; target.ApplyTemplate(); @@ -165,14 +165,14 @@ namespace Avalonia.Controls.UnitTests.Primitives { var target = new TemplatedControl { - Template = new FuncControlTemplate(_ => new Decorator()) + Template = new FuncControlTemplate((_, __) => new Decorator()) }; target.ApplyTemplate(); var child = (Decorator)target.GetVisualChildren().Single(); - target.Template = new FuncControlTemplate(_ => new Canvas()); + target.Template = new FuncControlTemplate((_, __) => new Canvas()); target.ApplyTemplate(); Assert.Null(child.Parent); @@ -183,7 +183,7 @@ namespace Avalonia.Controls.UnitTests.Primitives { var target = new TemplatedControl { - Template = new FuncControlTemplate(_ => new ScrollViewer()) + Template = new FuncControlTemplate((_, __) => new ScrollViewer()) }; target.ApplyTemplate(); @@ -203,7 +203,7 @@ namespace Avalonia.Controls.UnitTests.Primitives { Child = target = new TestTemplatedControl { - Template = new FuncControlTemplate(_ => + Template = new FuncControlTemplate((_, __) => { return new StackPanel { @@ -232,11 +232,11 @@ namespace Avalonia.Controls.UnitTests.Primitives { var target = new TestTemplatedControl { - Template = new FuncControlTemplate(_ => + Template = new FuncControlTemplate((_, __) => { return new ContentControl { - Template = new FuncControlTemplate(parent => + Template = new FuncControlTemplate((parent, ___) => { return new Border { @@ -311,7 +311,7 @@ namespace Avalonia.Controls.UnitTests.Primitives { var target = new TestTemplatedControl { - Template = new FuncControlTemplate(_ => new Decorator()) + Template = new FuncControlTemplate((_, __) => new Decorator()) }; var raised = false; @@ -334,7 +334,7 @@ namespace Avalonia.Controls.UnitTests.Primitives { var target = new TestTemplatedControl { - Template = new FuncControlTemplate(_ => new Decorator + Template = new FuncControlTemplate((_, __) => new Decorator { Child = new Border(), }) @@ -348,7 +348,7 @@ namespace Avalonia.Controls.UnitTests.Primitives Assert.Equal(target, decorator.TemplatedParent); Assert.Equal(target, border.TemplatedParent); - target.Template = new FuncControlTemplate(_ => new Canvas()); + target.Template = new FuncControlTemplate((_, __) => new Canvas()); // Templated children should not be removed here: the control may be re-added // somewhere with the same template, so they could still be of use. @@ -370,7 +370,7 @@ namespace Avalonia.Controls.UnitTests.Primitives { Child = new TestTemplatedControl { - Template = new FuncControlTemplate(_ => new Decorator + Template = new FuncControlTemplate((_, __) => new Decorator { Child = templateChild, }) @@ -392,7 +392,7 @@ namespace Avalonia.Controls.UnitTests.Primitives { Child = new TestTemplatedControl { - Template = new FuncControlTemplate(_ => new Decorator + Template = new FuncControlTemplate((_, __) => new Decorator { Child = templateChild, }) @@ -425,7 +425,7 @@ namespace Avalonia.Controls.UnitTests.Primitives { new Setter( TemplatedControl.TemplateProperty, - new FuncControlTemplate(_ => new Decorator + new FuncControlTemplate((_, __) => new Decorator { Child = new Border(), })) @@ -461,7 +461,7 @@ namespace Avalonia.Controls.UnitTests.Primitives { new Setter( TemplatedControl.TemplateProperty, - new FuncControlTemplate(_ => new Decorator + new FuncControlTemplate((_, __) => new Decorator { Child = new Border(), })) @@ -500,7 +500,7 @@ namespace Avalonia.Controls.UnitTests.Primitives { new Setter( TemplatedControl.TemplateProperty, - new FuncControlTemplate(_ => new Decorator + new FuncControlTemplate((_, __) => new Decorator { Child = new Border(), })) @@ -520,7 +520,7 @@ namespace Avalonia.Controls.UnitTests.Primitives { new Setter( TemplatedControl.TemplateProperty, - new FuncControlTemplate(_ => new Decorator + new FuncControlTemplate((_, __) => new Decorator { Child = new Border(), })) @@ -555,7 +555,7 @@ namespace Avalonia.Controls.UnitTests.Primitives { Child = target = new TestTemplatedControl { - Template = new FuncControlTemplate(_ => new Decorator()), + Template = new FuncControlTemplate((_, __) => new Decorator()), } }; @@ -573,7 +573,7 @@ namespace Avalonia.Controls.UnitTests.Primitives } } - private static IControl ScrollingContentControlTemplate(ContentControl control) + private static IControl ScrollingContentControlTemplate(ContentControl control, INameScope scope) { return new Border { @@ -585,20 +585,20 @@ namespace Avalonia.Controls.UnitTests.Primitives { Name = "PART_ContentPresenter", [!ContentPresenter.ContentProperty] = control[!ContentControl.ContentProperty], - } - } - }; + }.RegisterInNameScope(scope) + }.RegisterInNameScope(scope) + }.WithNameScope(scope); } - private static Control ScrollViewerTemplate(ScrollViewer control) + private static Control ScrollViewerTemplate(ScrollViewer control, INameScope scope) { var result = new ScrollContentPresenter { Name = "PART_ContentPresenter", [~ContentPresenter.ContentProperty] = control[~ContentControl.ContentProperty], - }; + }.RegisterInNameScope(scope).WithNameScope(scope); return result; } } -} \ No newline at end of file +} diff --git a/tests/Avalonia.Controls.UnitTests/ScrollViewerTests.cs b/tests/Avalonia.Controls.UnitTests/ScrollViewerTests.cs index d1385176c5..9076d8019c 100644 --- a/tests/Avalonia.Controls.UnitTests/ScrollViewerTests.cs +++ b/tests/Avalonia.Controls.UnitTests/ScrollViewerTests.cs @@ -64,7 +64,7 @@ namespace Avalonia.Controls.UnitTests Assert.Equal(new Vector(10, 10), target.Offset); } - private Control CreateTemplate(ScrollViewer control) + private Control CreateTemplate(ScrollViewer control, INameScope scope) { return new Grid { @@ -88,7 +88,7 @@ namespace Avalonia.Controls.UnitTests [~~ScrollContentPresenter.OffsetProperty] = control[~~ScrollViewer.OffsetProperty], [~~ScrollContentPresenter.ViewportProperty] = control[~~ScrollViewer.ViewportProperty], [~ScrollContentPresenter.CanHorizontallyScrollProperty] = control[~ScrollViewer.CanHorizontallyScrollProperty], - }, + }.RegisterInNameScope(scope), new ScrollBar { Name = "horizontalScrollBar", @@ -98,7 +98,7 @@ namespace Avalonia.Controls.UnitTests [~ScrollBar.ViewportSizeProperty] = control[~ScrollViewer.HorizontalScrollBarViewportSizeProperty], [~ScrollBar.VisibilityProperty] = control[~ScrollViewer.HorizontalScrollBarVisibilityProperty], [Grid.RowProperty] = 1, - }, + }.RegisterInNameScope(scope), new ScrollBar { Name = "verticalScrollBar", @@ -108,9 +108,9 @@ namespace Avalonia.Controls.UnitTests [~ScrollBar.ViewportSizeProperty] = control[~ScrollViewer.VerticalScrollBarViewportSizeProperty], [~ScrollBar.VisibilityProperty] = control[~ScrollViewer.VerticalScrollBarVisibilityProperty], [Grid.ColumnProperty] = 1, - }, + }.RegisterInNameScope(scope), }, - }; + }.WithNameScope(scope); } } -} \ No newline at end of file +} diff --git a/tests/Avalonia.Controls.UnitTests/TabControlTests.cs b/tests/Avalonia.Controls.UnitTests/TabControlTests.cs index e00084eba4..35e1ed9e1c 100644 --- a/tests/Avalonia.Controls.UnitTests/TabControlTests.cs +++ b/tests/Avalonia.Controls.UnitTests/TabControlTests.cs @@ -129,7 +129,7 @@ namespace Avalonia.Controls.UnitTests }, }; - var template = new FuncControlTemplate(x => new Decorator()); + var template = new FuncControlTemplate((x, __) => new Decorator()); using (UnitTestApplication.Start(TestServices.RealStyler)) { @@ -176,7 +176,7 @@ namespace Avalonia.Controls.UnitTests DataContext = "Base", DataTemplates = { - new FuncDataTemplate(x => new Button { Content = x }) + new FuncDataTemplate((x, __) => new Button { Content = x }) }, Items = items, }; @@ -273,7 +273,7 @@ namespace Avalonia.Controls.UnitTests TabControl target = new TabControl { Template = TabControlTemplate(), - ContentTemplate = new FuncDataTemplate(x => + ContentTemplate = new FuncDataTemplate((x, _) => new TextBlock { Tag = "bar", Text = x }), Items = new[] { "Foo" }, }; @@ -289,7 +289,7 @@ namespace Avalonia.Controls.UnitTests private IControlTemplate TabControlTemplate() { - return new FuncControlTemplate(parent => + return new FuncControlTemplate((parent, scope) => new StackPanel { Children = @@ -299,26 +299,26 @@ namespace Avalonia.Controls.UnitTests Name = "PART_ItemsPresenter", [!TabStrip.ItemsProperty] = parent[!TabControl.ItemsProperty], [!TabStrip.ItemTemplateProperty] = parent[!TabControl.ItemTemplateProperty], - }, + }.RegisterInNameScope(scope), new ContentPresenter { Name = "PART_SelectedContentHost", [!ContentPresenter.ContentProperty] = parent[!TabControl.SelectedContentProperty], [!ContentPresenter.ContentTemplateProperty] = parent[!TabControl.SelectedContentTemplateProperty], - } + }.RegisterInNameScope(scope) } - }); + }.WithNameScope(scope)); } private IControlTemplate TabItemTemplate() { - return new FuncControlTemplate(parent => + return new FuncControlTemplate((parent, scope) => new ContentPresenter { Name = "PART_ContentPresenter", [!ContentPresenter.ContentProperty] = parent[!TabItem.HeaderProperty], [!ContentPresenter.ContentTemplateProperty] = parent[!TabItem.HeaderTemplateProperty] - }); + }.RegisterInNameScope(scope).WithNameScope(scope)); } private void ApplyTemplate(TabControl target) diff --git a/tests/Avalonia.Controls.UnitTests/TextBoxTests.cs b/tests/Avalonia.Controls.UnitTests/TextBoxTests.cs index 932aada64e..7b08b4811f 100644 --- a/tests/Avalonia.Controls.UnitTests/TextBoxTests.cs +++ b/tests/Avalonia.Controls.UnitTests/TextBoxTests.cs @@ -456,7 +456,7 @@ namespace Avalonia.Controls.UnitTests private IControlTemplate CreateTemplate() { - return new FuncControlTemplate(control => + return new FuncControlTemplate((control, scope) => new TextPresenter { Name = "PART_TextPresenter", @@ -467,7 +467,7 @@ namespace Avalonia.Controls.UnitTests Priority = BindingPriority.TemplatedParent, RelativeSource = new RelativeSource(RelativeSourceMode.TemplatedParent), }, - }); + }.RegisterInNameScope(scope).WithNameScope(scope)); } private void RaiseKeyEvent(TextBox textBox, Key key, InputModifiers inputModifiers) diff --git a/tests/Avalonia.Controls.UnitTests/TextBoxTests_DataValidation.cs b/tests/Avalonia.Controls.UnitTests/TextBoxTests_DataValidation.cs index 4d79dd557e..d9e60ffbcf 100644 --- a/tests/Avalonia.Controls.UnitTests/TextBoxTests_DataValidation.cs +++ b/tests/Avalonia.Controls.UnitTests/TextBoxTests_DataValidation.cs @@ -93,7 +93,7 @@ namespace Avalonia.Controls.UnitTests private IControlTemplate CreateTemplate() { - return new FuncControlTemplate(control => + return new FuncControlTemplate((control, scope) => new TextPresenter { Name = "PART_TextPresenter", @@ -104,7 +104,7 @@ namespace Avalonia.Controls.UnitTests Priority = BindingPriority.TemplatedParent, RelativeSource = new RelativeSource(RelativeSourceMode.TemplatedParent), }, - }); + }.RegisterInNameScope(scope).WithNameScope(scope)); } private class ExceptionTest diff --git a/tests/Avalonia.Controls.UnitTests/TopLevelTests.cs b/tests/Avalonia.Controls.UnitTests/TopLevelTests.cs index f112289460..99d55c4c5c 100644 --- a/tests/Avalonia.Controls.UnitTests/TopLevelTests.cs +++ b/tests/Avalonia.Controls.UnitTests/TopLevelTests.cs @@ -226,12 +226,12 @@ namespace Avalonia.Controls.UnitTests private FuncControlTemplate CreateTemplate() { - return new FuncControlTemplate(x => + return new FuncControlTemplate((x, scope) => new ContentPresenter { Name = "PART_ContentPresenter", [!ContentPresenter.ContentProperty] = x[!ContentControl.ContentProperty], - }); + }.RegisterInNameScope(scope).WithNameScope(scope)); } private class TestTopLevel : TopLevel diff --git a/tests/Avalonia.Controls.UnitTests/TreeViewTests.cs b/tests/Avalonia.Controls.UnitTests/TreeViewTests.cs index b66d6ed11c..21a7f4e6db 100644 --- a/tests/Avalonia.Controls.UnitTests/TreeViewTests.cs +++ b/tests/Avalonia.Controls.UnitTests/TreeViewTests.cs @@ -50,7 +50,7 @@ namespace Avalonia.Controls.UnitTests Template = CreateTreeViewTemplate(), Items = CreateTestTreeData(), ItemTemplate = new FuncTreeDataTemplate( - _ => new Canvas(), + (_, __) => new Canvas(), x => x.Children), } }; @@ -475,7 +475,7 @@ namespace Avalonia.Controls.UnitTests DataContext = "Base", DataTemplates = { - new FuncDataTemplate(x => new Button { Content = x }) + new FuncDataTemplate((x, _) => new Button { Content = x }) }, Items = items, }; @@ -513,7 +513,7 @@ namespace Avalonia.Controls.UnitTests Assert.Null(NameScope.GetNameScope((TreeViewItem)item)); } - [Fact] + [Fact(Skip = "Is this behavior needed anymore?")] public void DataTemplate_Created_Item_Should_Be_NameScope() { var items = new object[] @@ -799,16 +799,16 @@ namespace Avalonia.Controls.UnitTests private IControlTemplate CreateTreeViewTemplate() { - return new FuncControlTemplate(parent => new ItemsPresenter + return new FuncControlTemplate((parent, scope) => new ItemsPresenter { Name = "PART_ItemsPresenter", [~ItemsPresenter.ItemsProperty] = parent[~ItemsControl.ItemsProperty], - }); + }.RegisterInNameScope(scope).WithNameScope(scope)); } private IControlTemplate CreateTreeViewItemTemplate() { - return new FuncControlTemplate(parent => new Panel + return new FuncControlTemplate((parent, scope) => new Panel { Children = { @@ -816,14 +816,14 @@ namespace Avalonia.Controls.UnitTests { Name = "PART_HeaderPresenter", [~ContentPresenter.ContentProperty] = parent[~TreeViewItem.HeaderProperty], - }, + }.RegisterInNameScope(scope), new ItemsPresenter { Name = "PART_ItemsPresenter", [~ItemsPresenter.ItemsProperty] = parent[~ItemsControl.ItemsProperty], - } + }.RegisterInNameScope(scope) } - }); + }.WithNameScope(scope)); } private List ExtractItemHeader(TreeView tree, int level) diff --git a/tests/Avalonia.Controls.UnitTests/UserControlTests.cs b/tests/Avalonia.Controls.UnitTests/UserControlTests.cs index 6da771217c..7bcf8fe4f2 100644 --- a/tests/Avalonia.Controls.UnitTests/UserControlTests.cs +++ b/tests/Avalonia.Controls.UnitTests/UserControlTests.cs @@ -40,7 +40,7 @@ namespace Avalonia.Controls.UnitTests private FuncControlTemplate GetTemplate() { - return new FuncControlTemplate(parent => + return new FuncControlTemplate((parent, scope) => { return new Border { @@ -49,8 +49,8 @@ namespace Avalonia.Controls.UnitTests { Name = "PART_ContentPresenter", [~ContentPresenter.ContentProperty] = parent[~ContentControl.ContentProperty], - } - }; + }.RegisterInNameScope(scope) + }.WithNameScope(scope); }); } } diff --git a/tests/Avalonia.Controls.UnitTests/Utils/HotKeyManagerTests.cs b/tests/Avalonia.Controls.UnitTests/Utils/HotKeyManagerTests.cs index 641979638a..427c91d989 100644 --- a/tests/Avalonia.Controls.UnitTests/Utils/HotKeyManagerTests.cs +++ b/tests/Avalonia.Controls.UnitTests/Utils/HotKeyManagerTests.cs @@ -59,13 +59,13 @@ namespace Avalonia.Controls.UnitTests.Utils private FuncControlTemplate CreateWindowTemplate() { - return new FuncControlTemplate(parent => + return new FuncControlTemplate((parent, scope) => { return new ContentPresenter { Name = "PART_ContentPresenter", [~ContentPresenter.ContentProperty] = parent[~ContentControl.ContentProperty], - }; + }.RegisterInNameScope(scope).WithNameScope(scope); }); } } diff --git a/tests/Avalonia.Controls.UnitTests/WindowBaseTests.cs b/tests/Avalonia.Controls.UnitTests/WindowBaseTests.cs index 12d29f2e5b..f4b3149e31 100644 --- a/tests/Avalonia.Controls.UnitTests/WindowBaseTests.cs +++ b/tests/Avalonia.Controls.UnitTests/WindowBaseTests.cs @@ -253,12 +253,12 @@ namespace Avalonia.Controls.UnitTests private FuncControlTemplate CreateTemplate() { - return new FuncControlTemplate(x => + return new FuncControlTemplate((x, scope) => new ContentPresenter { Name = "PART_ContentPresenter", [!ContentPresenter.ContentProperty] = x[!ContentControl.ContentProperty], - }); + }.RegisterInNameScope(scope).WithNameScope(scope)); } private class TestWindowBase : WindowBase diff --git a/tests/Avalonia.Markup.UnitTests/Data/BindingTests_ElementName.cs b/tests/Avalonia.Markup.UnitTests/Data/BindingTests_ElementName.cs index 61df0bffdf..e2a5824102 100644 --- a/tests/Avalonia.Markup.UnitTests/Data/BindingTests_ElementName.cs +++ b/tests/Avalonia.Markup.UnitTests/Data/BindingTests_ElementName.cs @@ -34,6 +34,8 @@ namespace Avalonia.Markup.UnitTests.Data } }; + root.RegisterChildrenNames(); + var binding = new Binding { ElementName = "source", @@ -69,6 +71,7 @@ namespace Avalonia.Markup.UnitTests.Data } } }; + root.RegisterChildrenNames(); var binding = new Binding { @@ -99,7 +102,8 @@ namespace Avalonia.Markup.UnitTests.Data } } }; - + root.RegisterChildrenNames(); + var binding = new Binding { ElementName = "source", @@ -113,7 +117,7 @@ namespace Avalonia.Markup.UnitTests.Data Name = "source", Text = "foo", }); - + root.RegisterChildrenNames(); Assert.Equal("foo", target.Text); } @@ -136,6 +140,7 @@ namespace Avalonia.Markup.UnitTests.Data } } }; + root.RegisterChildrenNames(); var binding = new Binding { @@ -151,6 +156,7 @@ namespace Avalonia.Markup.UnitTests.Data }; stackPanel.Children.Add(source); + root.RegisterChildrenNames(); Assert.Same(source, target.Content); } diff --git a/tests/Avalonia.Markup.UnitTests/Data/MultiBindingTests_Converters.cs b/tests/Avalonia.Markup.UnitTests/Data/MultiBindingTests_Converters.cs index d3e3ce5507..ee95463f1f 100644 --- a/tests/Avalonia.Markup.UnitTests/Data/MultiBindingTests_Converters.cs +++ b/tests/Avalonia.Markup.UnitTests/Data/MultiBindingTests_Converters.cs @@ -23,9 +23,10 @@ namespace Avalonia.Markup.UnitTests.Data DataContext = new Class1(), }; + var format = "{0:0.0} + {1:00}"; var target = new MultiBinding { - StringFormat = "{0:0.0} + {1:00}", + StringFormat = format, Bindings = { new Binding(nameof(Class1.Foo)), @@ -35,7 +36,7 @@ namespace Avalonia.Markup.UnitTests.Data textBlock.Bind(TextBlock.TextProperty, target); - Assert.Equal("1.0 + 02", textBlock.Text); + Assert.Equal(string.Format(format, 1, 2), textBlock.Text); } [Fact] diff --git a/tests/Avalonia.Markup.UnitTests/Data/TemplateBindingTests.cs b/tests/Avalonia.Markup.UnitTests/Data/TemplateBindingTests.cs index ef3864abd7..51cb75a867 100644 --- a/tests/Avalonia.Markup.UnitTests/Data/TemplateBindingTests.cs +++ b/tests/Avalonia.Markup.UnitTests/Data/TemplateBindingTests.cs @@ -18,7 +18,7 @@ namespace Avalonia.Markup.UnitTests.Data { var source = new Button { - Template = new FuncControlTemplate