diff --git a/build/PackageDiffIgnore.xml b/build/PackageDiffIgnore.xml index cde7c0efdf04..0113851ecedc 100644 --- a/build/PackageDiffIgnore.xml +++ b/build/PackageDiffIgnore.xml @@ -1910,11 +1910,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/SourceGenerators/System.Xaml.Tests/Test/System.Xaml/XamlXmlReaderTest.cs b/src/SourceGenerators/System.Xaml.Tests/Test/System.Xaml/XamlXmlReaderTest.cs index f24ae58f0a09..e2a7db8e47ae 100644 --- a/src/SourceGenerators/System.Xaml.Tests/Test/System.Xaml/XamlXmlReaderTest.cs +++ b/src/SourceGenerators/System.Xaml.Tests/Test/System.Xaml/XamlXmlReaderTest.cs @@ -360,6 +360,7 @@ public void Read_GenericSimple() [Test] public void Read_GenericWithProperty() { + const string not_win = "{http://uno.ui/not_win}"; var sequence = new SequenceItem[] { new SequenceItem { NodeType = XamlNodeType.NamespaceDeclaration, }, new SequenceItem { NodeType = XamlNodeType.NamespaceDeclaration, }, @@ -382,16 +383,18 @@ public void Read_GenericWithProperty() new SequenceItem { NodeType = XamlNodeType.EndMember, }, new SequenceItem { NodeType = XamlNodeType.StartMember, MemberType = "{http://schemas.microsoft.com/winfx/2006/xaml}_UnknownContent", }, - new SequenceItem { NodeType = XamlNodeType.StartObject, TypeName = "{http://schemas.microsoft.com/winfx/2006/xaml/presentation}Style"}, + new SequenceItem { NodeType = XamlNodeType.StartObject, TypeName = $"{not_win}Style"}, new SequenceItem { NodeType = XamlNodeType.StartMember, MemberType = "{http://schemas.microsoft.com/winfx/2006/xaml}Key", }, new SequenceItem { NodeType = XamlNodeType.Value, Value = "DefaultComboBoxItemStyle", }, new SequenceItem { NodeType = XamlNodeType.EndMember, }, - new SequenceItem { NodeType = XamlNodeType.StartMember, MemberType = "{http://schemas.microsoft.com/winfx/2006/xaml/presentation}Style.OtherProperty", }, + new SequenceItem { NodeType = XamlNodeType.StartMember, MemberType = $"{not_win}Style.OtherProperty", }, new SequenceItem { NodeType = XamlNodeType.Value, Value = "test", }, new SequenceItem { NodeType = XamlNodeType.EndMember, }, +#if false // XamlXmlReader fails to read any xmlns'd node/attribute with invalid uri (such as a guid or 'using:System.Somewhere') new SequenceItem { NodeType = XamlNodeType.StartMember, MemberType = "{http://schemas.microsoft.com/winfx/2006/xaml/presentation}Style.TargetType", }, new SequenceItem { NodeType = XamlNodeType.Value, Value = "SelectorItem", }, new SequenceItem { NodeType = XamlNodeType.EndMember, }, +#endif new SequenceItem { NodeType = XamlNodeType.EndObject, }, new SequenceItem { NodeType = XamlNodeType.EndMember, }, new SequenceItem { NodeType = XamlNodeType.EndObject, }, @@ -2507,9 +2510,30 @@ public void Bug680385 () } #endregion + private string GetXml(string fileName) + { + var directory = Path.GetDirectoryName(new Uri(GetType().Assembly.CodeBase).LocalPath); + var path = Path.Combine(directory, "Test/XmlFiles", fileName); + + return File.ReadAllText(path).Replace("System.Xaml_test_net_4_0", "Uno.Xaml.Tests"); + } + + private IEnumerable<(XamlNodeType, string, string, object, string)> ReadAllTokens(string fileName) + { + var r = GetReader(fileName); + int i = 0; + while (r.Read()) + { + yield return (r.NodeType, r.Member?.ToString(), r.Member?.Name, r.Value, r.Type?.ToString()); + } + } + private void ReadSequence(string fileName, IEnumerable sequence) { - //ReadTest(fileName); +#if DEBUG && false + var xml = GetXml(fileName); + var tokens = ReadAllTokens(fileName).ToArray(); +#endif var r = GetReader(fileName); diff --git a/src/SourceGenerators/Uno.UI.SourceGenerators.Tests/DependencyObjectGeneratorTests/Given_DependencyObjectGenerator.cs b/src/SourceGenerators/Uno.UI.SourceGenerators.Tests/DependencyObjectGeneratorTests/Given_DependencyObjectGenerator.cs index 50d5372af071..731110a9ed7e 100644 --- a/src/SourceGenerators/Uno.UI.SourceGenerators.Tests/DependencyObjectGeneratorTests/Given_DependencyObjectGenerator.cs +++ b/src/SourceGenerators/Uno.UI.SourceGenerators.Tests/DependencyObjectGeneratorTests/Given_DependencyObjectGenerator.cs @@ -143,6 +143,7 @@ internal partial class Inner : DependencyObject using System.Linq; using System.Collections.Generic; using System.Collections; + using System.ComponentModel; using System.Diagnostics.CodeAnalysis; using Uno.Disposables; using System.Runtime.CompilerServices; @@ -158,7 +159,7 @@ internal partial class Inner : DependencyObject partial class OuterClass { [global::Microsoft.UI.Xaml.Data.Bindable] - partial class Inner : IDependencyObjectStoreProvider, IWeakReferenceProvider + partial class Inner : IDependencyObjectStoreProvider, ITemplatedParentProvider, IWeakReferenceProvider { private DependencyObjectStore __storeBackingField; public global::Windows.UI.Core.CoreDispatcher Dispatcher => global::Windows.ApplicationModel.Core.CoreApplication.MainView.Dispatcher; @@ -169,7 +170,7 @@ private DependencyObjectStore __Store { if(__storeBackingField == null) { - __storeBackingField = new DependencyObjectStore(this, DataContextProperty, TemplatedParentProperty); + __storeBackingField = new DependencyObjectStore(this, DataContextProperty); __InitializeBinder(); } return __storeBackingField; @@ -184,6 +185,28 @@ private DependencyObjectStore __Store public object GetAnimationBaseValue(DependencyProperty dp) => __Store.GetAnimationBaseValue(dp); public long RegisterPropertyChangedCallback(DependencyProperty dp, DependencyPropertyChangedCallback callback) => __Store.RegisterPropertyChangedCallback(dp, callback); public void UnregisterPropertyChangedCallback(DependencyProperty dp, long token) => __Store.UnregisterPropertyChangedCallback(dp, token); + + [EditorBrowsable(EditorBrowsableState.Never)]private ManagedWeakReference _templatedParentWeakRef; + [EditorBrowsable(EditorBrowsableState.Never)]public ManagedWeakReference GetTemplatedParentWeakRef() => _templatedParentWeakRef; + + [EditorBrowsable(EditorBrowsableState.Never)]public DependencyObject GetTemplatedParent() => _templatedParentWeakRef?.Target as DependencyObject; + [EditorBrowsable(EditorBrowsableState.Never)]public void SetTemplatedParent(DependencyObject parent) + { + //if (parent != null) + //{ + // global::System.Diagnostics.Debug.Assert(parent + // is global::Windows.UI.Xaml.Controls.Control + // or global::Windows.UI.Xaml.Controls.ContentPresenter + // or global::Windows.UI.Xaml.Controls.ItemsPresenter); + // global::System.Diagnostics.Debug.Assert(GetTemplatedParent() == null); + //} + + SetTemplatedParentImpl(parent); + } + [EditorBrowsable(EditorBrowsableState.Never)]private protected virtual void SetTemplatedParentImpl(DependencyObject parent) + { + _templatedParentWeakRef = (parent as IWeakReferenceProvider)?.WeakReference; + } private readonly static IEventProvider _binderTrace = Tracing.Get(DependencyObjectStore.TraceProvider.Id); private BinderReferenceHolder _refHolder; @@ -266,15 +289,16 @@ internal protected virtual void OnDataContextChanged(DependencyPropertyChangedEv #endregion - #region TemplatedParent DependencyProperty + #region TemplatedParent DependencyProperty // legacy api, should no longer to be used. - public DependencyObject TemplatedParent + [EditorBrowsable(EditorBrowsableState.Never)]public DependencyObject TemplatedParent { get => (DependencyObject)GetValue(TemplatedParentProperty); set => SetValue(TemplatedParentProperty, value); } // Using a DependencyProperty as the backing store for TemplatedParent. This enables animation, styling, binding, etc... + [EditorBrowsable(EditorBrowsableState.Never)] public static DependencyProperty TemplatedParentProperty { get ; } = DependencyProperty.Register( name: nameof(TemplatedParent), @@ -282,15 +306,15 @@ public DependencyObject TemplatedParent ownerType: typeof(Inner), typeMetadata: new FrameworkPropertyMetadata( defaultValue: null, - options: FrameworkPropertyMetadataOptions.Inherits | FrameworkPropertyMetadataOptions.ValueDoesNotInheritDataContext | FrameworkPropertyMetadataOptions.WeakStorage, + options: /*FrameworkPropertyMetadataOptions.Inherits | */FrameworkPropertyMetadataOptions.ValueDoesNotInheritDataContext | FrameworkPropertyMetadataOptions.WeakStorage, propertyChangedCallback: (s, e) => ((Inner)s).OnTemplatedParentChanged(e) ) ); + [EditorBrowsable(EditorBrowsableState.Never)] internal protected virtual void OnTemplatedParentChanged(DependencyPropertyChangedEventArgs e) { - __Store.SetTemplatedParent(e.NewValue as FrameworkElement); OnTemplatedParentChangedPartial(e); } @@ -321,6 +345,7 @@ public void SetBindingValue(object value, [CallerMemberName] string propertyName partial void OnDataContextChangedPartial(DependencyPropertyChangedEventArgs e); + [EditorBrowsable(EditorBrowsableState.Never)] partial void OnTemplatedParentChangedPartial(DependencyPropertyChangedEventArgs e); public global::Microsoft.UI.Xaml.Data.BindingExpression GetBindingExpression(DependencyProperty dependencyProperty) diff --git a/src/SourceGenerators/Uno.UI.SourceGenerators.Tests/XamlCodeGeneratorTests/Out/SOSLIIOFDOTAFE/XamlCodeGenerator_MainPage_d6cd66944958ced0c513e0a04797b51d.cs b/src/SourceGenerators/Uno.UI.SourceGenerators.Tests/XamlCodeGeneratorTests/Out/SOSLIIOFDOTAFE/XamlCodeGenerator_MainPage_d6cd66944958ced0c513e0a04797b51d.cs index dd263452a3de..d1e437382bdd 100644 --- a/src/SourceGenerators/Uno.UI.SourceGenerators.Tests/XamlCodeGeneratorTests/Out/SOSLIIOFDOTAFE/XamlCodeGenerator_MainPage_d6cd66944958ced0c513e0a04797b51d.cs +++ b/src/SourceGenerators/Uno.UI.SourceGenerators.Tests/XamlCodeGeneratorTests/Out/SOSLIIOFDOTAFE/XamlCodeGenerator_MainPage_d6cd66944958ced0c513e0a04797b51d.cs @@ -111,7 +111,7 @@ private void InitializeComponent() "MyItemTemplate" ] = new global::Uno.UI.Xaml.WeakResourceInitializer(this, __ResourceOwner_1 => - new global::Microsoft.UI.Xaml.DataTemplate(__ResourceOwner_1 , __owner => ((I_MainPage_d6cd66944958ced0c513e0a04797b51d_TestReproMainPageSC0)global::Uno.UI.Helpers.TypeMappings.CreateInstance<_MainPage_d6cd66944958ced0c513e0a04797b51d_TestReproMainPageSC0>()).Build(__owner) + new global::Microsoft.UI.Xaml.DataTemplate(__ResourceOwner_1, (__owner) => ((I_MainPage_d6cd66944958ced0c513e0a04797b51d_TestReproMainPageSC0)global::Uno.UI.Helpers.TypeMappings.CreateInstance<_MainPage_d6cd66944958ced0c513e0a04797b51d_TestReproMainPageSC0>()).Build(__owner) ) .GenericApply(__that, __nameScope, ((c6, __that, __nameScope) => { global::Uno.UI.Helpers.MarkupHelper.SetElementProperty(c6, "OriginalSourceLocation", "file:///C:/Project/0/MainPage.xaml#L13:6"); @@ -125,7 +125,7 @@ private void InitializeComponent() { IsParsing = true, Name = "TheListView", - HeaderTemplate = new global::Microsoft.UI.Xaml.DataTemplate(this , __owner => ((I_MainPage_d6cd66944958ced0c513e0a04797b51d_TestReproMainPageSC1)global::Uno.UI.Helpers.TypeMappings.CreateInstance<_MainPage_d6cd66944958ced0c513e0a04797b51d_TestReproMainPageSC1>()).Build(__owner) + HeaderTemplate = new global::Microsoft.UI.Xaml.DataTemplate(this, (__owner) => ((I_MainPage_d6cd66944958ced0c513e0a04797b51d_TestReproMainPageSC1)global::Uno.UI.Helpers.TypeMappings.CreateInstance<_MainPage_d6cd66944958ced0c513e0a04797b51d_TestReproMainPageSC1>()).Build(__owner) ) .GenericApply(__that, __nameScope, ((c7, __that, __nameScope) => { global::Uno.UI.Helpers.MarkupHelper.SetElementProperty(c7, "OriginalSourceLocation", "file:///C:/Project/0/MainPage.xaml#L42:8"); diff --git a/src/SourceGenerators/Uno.UI.SourceGenerators.Tests/XamlCodeGeneratorTests/Out/SOSLIOFPLR/XamlCodeGenerator_MainPage_d6cd66944958ced0c513e0a04797b51d.cs b/src/SourceGenerators/Uno.UI.SourceGenerators.Tests/XamlCodeGeneratorTests/Out/SOSLIOFPLR/XamlCodeGenerator_MainPage_d6cd66944958ced0c513e0a04797b51d.cs index f0729493fa25..abb1c12d4419 100644 --- a/src/SourceGenerators/Uno.UI.SourceGenerators.Tests/XamlCodeGeneratorTests/Out/SOSLIOFPLR/XamlCodeGenerator_MainPage_d6cd66944958ced0c513e0a04797b51d.cs +++ b/src/SourceGenerators/Uno.UI.SourceGenerators.Tests/XamlCodeGeneratorTests/Out/SOSLIOFPLR/XamlCodeGenerator_MainPage_d6cd66944958ced0c513e0a04797b51d.cs @@ -111,7 +111,7 @@ private void InitializeComponent() "MyItemTemplate" ] = new global::Uno.UI.Xaml.WeakResourceInitializer(this, __ResourceOwner_1 => - new global::Microsoft.UI.Xaml.DataTemplate(__ResourceOwner_1 , __owner => ((I_MainPage_d6cd66944958ced0c513e0a04797b51d_TestReproMainPageSC0)global::Uno.UI.Helpers.TypeMappings.CreateInstance<_MainPage_d6cd66944958ced0c513e0a04797b51d_TestReproMainPageSC0>()).Build(__owner) + new global::Microsoft.UI.Xaml.DataTemplate(__ResourceOwner_1, (__owner) => ((I_MainPage_d6cd66944958ced0c513e0a04797b51d_TestReproMainPageSC0)global::Uno.UI.Helpers.TypeMappings.CreateInstance<_MainPage_d6cd66944958ced0c513e0a04797b51d_TestReproMainPageSC0>()).Build(__owner) ) .GenericApply(__that, __nameScope, ((c6, __that, __nameScope) => { global::Uno.UI.Helpers.MarkupHelper.SetElementProperty(c6, "OriginalSourceLocation", "file:///C:/Project/0/MainPage.xaml#L13:6"); diff --git a/src/SourceGenerators/Uno.UI.SourceGenerators.Tests/XamlCodeGeneratorTests/Out/TDBMIDTIRD/XamlCodeGenerator_MyResourceDictionary_92716e07ff456818f6d4125e055d4d57.cs b/src/SourceGenerators/Uno.UI.SourceGenerators.Tests/XamlCodeGeneratorTests/Out/TDBMIDTIRD/XamlCodeGenerator_MyResourceDictionary_92716e07ff456818f6d4125e055d4d57.cs index f6ba8f64b66c..a6e3e15cbe04 100644 --- a/src/SourceGenerators/Uno.UI.SourceGenerators.Tests/XamlCodeGeneratorTests/Out/TDBMIDTIRD/XamlCodeGenerator_MyResourceDictionary_92716e07ff456818f6d4125e055d4d57.cs +++ b/src/SourceGenerators/Uno.UI.SourceGenerators.Tests/XamlCodeGeneratorTests/Out/TDBMIDTIRD/XamlCodeGenerator_MyResourceDictionary_92716e07ff456818f6d4125e055d4d57.cs @@ -46,7 +46,7 @@ public void InitializeComponent() "myTemplate" ] = new global::Uno.UI.Xaml.WeakResourceInitializer(this, __ResourceOwner_1 => - new global::Microsoft.UI.Xaml.DataTemplate(__ResourceOwner_1 , __owner => new _MyResourceDictionary_92716e07ff456818f6d4125e055d4d57_TestReproMyResourceDictionarySC0().Build(__owner) + new global::Microsoft.UI.Xaml.DataTemplate(__ResourceOwner_1, (__owner) => new _MyResourceDictionary_92716e07ff456818f6d4125e055d4d57_TestReproMyResourceDictionarySC0().Build(__owner) ) ) ; } @@ -178,7 +178,7 @@ public ResourceDictionarySingleton__MyResourceDictionary_92716e07ff456818f6d4125 // Method for resource myTemplate private object Get_1(object __ResourceOwner_1) => - new global::Microsoft.UI.Xaml.DataTemplate(__ResourceOwner_1 , __owner => new __Resources._MyResourceDictionary_92716e07ff456818f6d4125e055d4d57_MyResourceDictionaryRDSC1().Build(__owner) + new global::Microsoft.UI.Xaml.DataTemplate(__ResourceOwner_1, (__owner) => new __Resources._MyResourceDictionary_92716e07ff456818f6d4125e055d4d57_MyResourceDictionaryRDSC1().Build(__owner) ) ; private global::Microsoft.UI.Xaml.ResourceDictionary _MyResourceDictionary_92716e07ff456818f6d4125e055d4d57_ResourceDictionary; @@ -197,7 +197,7 @@ private object Get_1(object __ResourceOwner_1) => "myTemplate" ] = new global::Uno.UI.Xaml.WeakResourceInitializer(this, __ResourceOwner_1 => - new global::Microsoft.UI.Xaml.DataTemplate(__ResourceOwner_1 , __owner => new __Resources._MyResourceDictionary_92716e07ff456818f6d4125e055d4d57_MyResourceDictionaryRDSC2().Build(__owner) + new global::Microsoft.UI.Xaml.DataTemplate(__ResourceOwner_1, (__owner) => new __Resources._MyResourceDictionary_92716e07ff456818f6d4125e055d4d57_MyResourceDictionaryRDSC2().Build(__owner) ) ) , } diff --git a/src/SourceGenerators/Uno.UI.SourceGenerators.Tests/XamlCodeGeneratorTests/Out/TTIXLE/XamlCodeGenerator_MainPage_d6cd66944958ced0c513e0a04797b51d.cs b/src/SourceGenerators/Uno.UI.SourceGenerators.Tests/XamlCodeGeneratorTests/Out/TTIXLE/XamlCodeGenerator_MainPage_d6cd66944958ced0c513e0a04797b51d.cs index 3c3a8c59ae5b..85a7f0b01019 100644 --- a/src/SourceGenerators/Uno.UI.SourceGenerators.Tests/XamlCodeGeneratorTests/Out/TTIXLE/XamlCodeGenerator_MainPage_d6cd66944958ced0c513e0a04797b51d.cs +++ b/src/SourceGenerators/Uno.UI.SourceGenerators.Tests/XamlCodeGeneratorTests/Out/TTIXLE/XamlCodeGenerator_MainPage_d6cd66944958ced0c513e0a04797b51d.cs @@ -99,7 +99,7 @@ private void InitializeComponent() { IsParsing = true, Name = "inner2", - Template = new global::Microsoft.UI.Xaml.Controls.ControlTemplate(this , __owner => new _MainPage_d6cd66944958ced0c513e0a04797b51d_TestReproMainPageSC0().Build(__owner) + Template = new global::Microsoft.UI.Xaml.Controls.ControlTemplate(this, (__owner) => new _MainPage_d6cd66944958ced0c513e0a04797b51d_TestReproMainPageSC0().Build(__owner) ) , // Source 0\MainPage.xaml (Line 15:5) } diff --git a/src/SourceGenerators/Uno.UI.SourceGenerators.Tests/XamlCodeGeneratorTests/Out/WBENIT/XamlCodeGenerator_Binding_ElementName_In_Template_66bf0a54f1801c397a6fa4930a237eca.cs b/src/SourceGenerators/Uno.UI.SourceGenerators.Tests/XamlCodeGeneratorTests/Out/WBENIT/XamlCodeGenerator_Binding_ElementName_In_Template_66bf0a54f1801c397a6fa4930a237eca.cs index 2bfcbd8de737..df6fde489dbb 100644 --- a/src/SourceGenerators/Uno.UI.SourceGenerators.Tests/XamlCodeGeneratorTests/Out/WBENIT/XamlCodeGenerator_Binding_ElementName_In_Template_66bf0a54f1801c397a6fa4930a237eca.cs +++ b/src/SourceGenerators/Uno.UI.SourceGenerators.Tests/XamlCodeGeneratorTests/Out/WBENIT/XamlCodeGenerator_Binding_ElementName_In_Template_66bf0a54f1801c397a6fa4930a237eca.cs @@ -59,7 +59,7 @@ private void InitializeComponent() IsParsing = true, Name = "topLevel", Tag = @"42", - ContentTemplate = new global::Microsoft.UI.Xaml.DataTemplate(this , __owner => new _Binding_ElementName_In_Template_66bf0a54f1801c397a6fa4930a237eca_UnoUITestsWindows_UI_Xaml_DataBindingTestsControlsBinding_ElementName_In_TemplateSC0().Build(__owner) + ContentTemplate = new global::Microsoft.UI.Xaml.DataTemplate(this, (__owner) => new _Binding_ElementName_In_Template_66bf0a54f1801c397a6fa4930a237eca_UnoUITestsWindows_UI_Xaml_DataBindingTestsControlsBinding_ElementName_In_TemplateSC0().Build(__owner) ) , // Source 0\Binding_ElementName_In_Template.xaml (Line 11:4) } diff --git a/src/SourceGenerators/Uno.UI.SourceGenerators/DependencyObject/DependencyObjectGenerator.cs b/src/SourceGenerators/Uno.UI.SourceGenerators/DependencyObject/DependencyObjectGenerator.cs index 84eaa4bba263..059614e4d766 100644 --- a/src/SourceGenerators/Uno.UI.SourceGenerators/DependencyObject/DependencyObjectGenerator.cs +++ b/src/SourceGenerators/Uno.UI.SourceGenerators/DependencyObject/DependencyObjectGenerator.cs @@ -153,6 +153,7 @@ private void ProcessType(INamedTypeSymbol typeSymbol) using System.Linq; using System.Collections.Generic; using System.Collections; +using System.ComponentModel; using System.Diagnostics.CodeAnalysis; using Uno.Disposables; using System.Runtime.CompilerServices; @@ -174,10 +175,18 @@ private void ProcessType(INamedTypeSymbol typeSymbol) } }; - var internalDependencyObject = _isUnoSolution && !typeSymbol.IsSealed ? ", IDependencyObjectInternal" : ""; - using (typeSymbol.AddToIndentedStringBuilder(builder, beforeClassHeaderAction, afterClassHeader: $" : IDependencyObjectStoreProvider, IWeakReferenceProvider{internalDependencyObject}")) + var canBeTpProvider = !typeSymbol.Interfaces.Any(x => x.Name == "INotTemplatedParentProvider"); + + var implementations = new string?[] + { + "IDependencyObjectStoreProvider", + _isUnoSolution && !typeSymbol.IsSealed ? "IDependencyObjectInternal" : null, + canBeTpProvider ? "ITemplatedParentProvider" : null, + "IWeakReferenceProvider", + }.Where(x => x is not null); + using (typeSymbol.AddToIndentedStringBuilder(builder, beforeClassHeaderAction, afterClassHeader: " : " + string.Join(", ", implementations))) { - GenerateDependencyObjectImplementation(typeSymbol, builder, hasDispatcherQueue: _dependencyObjectSymbol!.GetMembers("DispatcherQueue").Any()); + GenerateDependencyObjectImplementation(typeSymbol, builder, hasDispatcherQueue: _dependencyObjectSymbol!.GetMembers("DispatcherQueue").Any(), canBeTpProvider); GenerateIBinderImplementation(typeSymbol, builder); } @@ -236,19 +245,9 @@ public override void WillMoveToSuperview(UIKit.UIView newsuper) base.WillMoveToSuperview(newsuper); WillMoveToSuperviewPartial(newsuper); - - SyncBinder(newsuper, Window); } partial void WillMoveToSuperviewPartial(UIKit.UIView newsuper); - -private void SyncBinder(UIKit.UIView superview, UIKit.UIWindow window) -{ - if(superview == null && window == null) - { - TemplatedParent = null; - } -} "); } else @@ -274,19 +273,9 @@ public override void ViewWillMoveToSuperview(AppKit.NSView newsuper) base.ViewWillMoveToSuperview(newsuper); WillMoveToSuperviewPartial(newsuper); - - SyncBinder(newsuper, Window); } partial void WillMoveToSuperviewPartial(AppKit.NSView newsuper); - -private void SyncBinder(AppKit.NSView superview, AppKit.NSWindow window) -{ - if(superview == null && window == null) - { - TemplatedParent = null; - } -} "); } else @@ -625,6 +614,8 @@ private void WriteBinderImplementation(INamedTypeSymbol typeSymbol, IndentedStri { var virtualModifier = typeSymbol.IsSealed ? "" : "virtual"; var protectedModifier = typeSymbol.IsSealed ? "private" : "internal protected"; + var legacyNonBrowsable = "[EditorBrowsable(EditorBrowsableState.Never)]"; + string dataContextChangedInvokeArgument; if (typeSymbol.Is(_frameworkElementSymbol)) { @@ -676,15 +667,16 @@ public object DataContext #endregion -#region TemplatedParent DependencyProperty +#region TemplatedParent DependencyProperty // legacy api, should no longer to be used. -public DependencyObject TemplatedParent +{legacyNonBrowsable}public DependencyObject TemplatedParent {{ get => (DependencyObject)GetValue(TemplatedParentProperty); set => SetValue(TemplatedParentProperty, value); }} // Using a DependencyProperty as the backing store for TemplatedParent. This enables animation, styling, binding, etc... +{legacyNonBrowsable} public static DependencyProperty TemplatedParentProperty {{ get ; }} = DependencyProperty.Register( name: nameof(TemplatedParent), @@ -692,15 +684,15 @@ public DependencyObject TemplatedParent ownerType: typeof({typeSymbol.Name}), typeMetadata: new FrameworkPropertyMetadata( defaultValue: null, - options: FrameworkPropertyMetadataOptions.Inherits | FrameworkPropertyMetadataOptions.ValueDoesNotInheritDataContext | FrameworkPropertyMetadataOptions.WeakStorage, + options: /*FrameworkPropertyMetadataOptions.Inherits | */FrameworkPropertyMetadataOptions.ValueDoesNotInheritDataContext | FrameworkPropertyMetadataOptions.WeakStorage, propertyChangedCallback: (s, e) => (({typeSymbol.Name})s).OnTemplatedParentChanged(e) ) ); +{legacyNonBrowsable} {protectedModifier} {virtualModifier} void OnTemplatedParentChanged(DependencyPropertyChangedEventArgs e) {{ - __Store.SetTemplatedParent(e.NewValue as FrameworkElement); OnTemplatedParentChangedPartial(e); }} @@ -731,6 +723,7 @@ public void SetBindingValue(object value, [CallerMemberName] string propertyName partial void OnDataContextChangedPartial(DependencyPropertyChangedEventArgs e); +{legacyNonBrowsable} partial void OnTemplatedParentChangedPartial(DependencyPropertyChangedEventArgs e); public global::Microsoft.UI.Xaml.Data.BindingExpression GetBindingExpression(DependencyProperty dependencyProperty) @@ -775,7 +768,7 @@ public override bool Equals(object other) } } - private void GenerateDependencyObjectImplementation(INamedTypeSymbol typeSymbol, IndentedStringBuilder builder, bool hasDispatcherQueue) + private void GenerateDependencyObjectImplementation(INamedTypeSymbol typeSymbol, IndentedStringBuilder builder, bool hasDispatcherQueue, bool implTpProvider) { builder.AppendLineIndented(@"private DependencyObjectStore __storeBackingField;"); builder.AppendLineIndented(@"public global::Windows.UI.Core.CoreDispatcher Dispatcher => global::Windows.ApplicationModel.Core.CoreApplication.MainView.Dispatcher;"); @@ -791,7 +784,7 @@ private void GenerateDependencyObjectImplementation(INamedTypeSymbol typeSymbol, { using (builder.BlockInvariant($"if(__storeBackingField == null)")) { - builder.AppendLineIndented("__storeBackingField = new DependencyObjectStore(this, DataContextProperty, TemplatedParentProperty);"); + builder.AppendLineIndented("__storeBackingField = new DependencyObjectStore(this, DataContextProperty);"); builder.AppendLineIndented("__InitializeBinder();"); } @@ -825,6 +818,37 @@ private void GenerateDependencyObjectImplementation(INamedTypeSymbol typeSymbol, builder.AppendLineIndented("internal virtual void OnPropertyChanged2(global::Microsoft.UI.Xaml.DependencyPropertyChangedEventArgs args) { }"); } } + + if (implTpProvider) + { + var unoBrowsableOnly = _isUnoSolution ? null : "[EditorBrowsable(EditorBrowsableState.Never)]"; + + builder.AppendLine(); + builder.AppendMultiLineIndented($$""" + {{unoBrowsableOnly}}private ManagedWeakReference _templatedParentWeakRef; + {{unoBrowsableOnly}}public ManagedWeakReference GetTemplatedParentWeakRef() => _templatedParentWeakRef; + + {{unoBrowsableOnly}}public DependencyObject GetTemplatedParent() => _templatedParentWeakRef?.Target as DependencyObject; + {{unoBrowsableOnly}}public void SetTemplatedParent(DependencyObject parent) + { + //if (parent != null) + //{ + // global::System.Diagnostics.Debug.Assert(parent + // is global::Windows.UI.Xaml.Controls.Control + // or global::Windows.UI.Xaml.Controls.ContentPresenter + // or global::Windows.UI.Xaml.Controls.ItemsPresenter); + // global::System.Diagnostics.Debug.Assert(GetTemplatedParent() == null); + //} + + SetTemplatedParentImpl(parent); + } + {{unoBrowsableOnly}}{{(typeSymbol.IsSealed ? "private" : "private protected virtual")}} void SetTemplatedParentImpl(DependencyObject parent) + { + _templatedParentWeakRef = (parent as IWeakReferenceProvider)?.WeakReference; + } + """ + ); + } } } } diff --git a/src/SourceGenerators/Uno.UI.SourceGenerators/XamlGenerator/XamlFileGenerator.cs b/src/SourceGenerators/Uno.UI.SourceGenerators/XamlGenerator/XamlFileGenerator.cs index 0dc4fa793265..9a762c4b2a5c 100644 --- a/src/SourceGenerators/Uno.UI.SourceGenerators/XamlGenerator/XamlFileGenerator.cs +++ b/src/SourceGenerators/Uno.UI.SourceGenerators/XamlGenerator/XamlFileGenerator.cs @@ -922,7 +922,11 @@ private void BuildChildSubclasses(IIndentedStringBuilder writer, bool isTopLevel // implementation of a type during hot reload. using (writer.BlockInvariant($"internal interface {hrInterfaceName}")) { +#if USE_NEW_TP_CODEGEN + writer.AppendLineIndented($"{kvp.Value.ReturnType} Build(object owner, global::Microsoft.UI.Xaml.TemplateMaterializationSettings __settings);"); +#else writer.AppendLineIndented($"{kvp.Value.ReturnType} Build(object owner);"); +#endif } } @@ -938,7 +942,11 @@ private void BuildChildSubclasses(IIndentedStringBuilder writer, bool isTopLevel { writer.AppendLineIndented("global::Microsoft.UI.Xaml.NameScope __nameScope = new global::Microsoft.UI.Xaml.NameScope();"); +#if USE_NEW_TP_CODEGEN + using (writer.BlockInvariant($"public {kvp.Value.ReturnType} Build(object {CurrentResourceOwner}, global::Microsoft.UI.Xaml.TemplateMaterializationSettings __settings)")) +#else using (writer.BlockInvariant($"public {kvp.Value.ReturnType} Build(object {CurrentResourceOwner})")) +#endif { writer.AppendLineIndented($"{kvp.Value.ReturnType} __rootInstance = null;"); writer.AppendLineIndented($"var __that = this;"); @@ -951,9 +959,9 @@ private void BuildChildSubclasses(IIndentedStringBuilder writer, bool isTopLevel BuildCompiledBindingsInitializerForTemplate(writer); - using (writer.BlockInvariant("if (__rootInstance is DependencyObject d)", kvp.Value.ReturnType)) + using (writer.BlockInvariant("if (__rootInstance is DependencyObject d)")) { - using (writer.BlockInvariant("if (global::Microsoft.UI.Xaml.NameScope.GetNameScope(d) == null)", kvp.Value.ReturnType)) + using (writer.BlockInvariant("if (global::Microsoft.UI.Xaml.NameScope.GetNameScope(d) == null)")) { writer.AppendLineIndented("global::Microsoft.UI.Xaml.NameScope.SetNameScope(d, __nameScope);"); writer.AppendLineIndented("__nameScope.Owner = d;"); @@ -3064,13 +3072,22 @@ private void BuildExtendedProperties(IIndentedStringBuilder outerwriter, XamlObj writer.AppendIndented(GenerateRootPhases(objectDefinition, closureName) ?? ""); } + var isInsideFrameworkTemplate = IsMemberInsideFrameworkTemplate(objectDefinition).isInside; +#if USE_NEW_TP_CODEGEN + var isDependencyObject = IsType(objectDefinitionType, Generation.DependencyObjectSymbol.Value); + if (isInsideFrameworkTemplate && isDependencyObject) + { + writer.AppendLineIndented($"{closureName}.SetTemplatedParent(__settings?.TemplatedParent);"); + writer.AppendLineIndented($"__settings?.TemplateMemberCreatedCallback?.Invoke({closureName});"); + } +#endif + componentDefinition = CurrentScope.Components.FirstOrDefault(x => x.XamlObject == objectDefinition); if (componentDefinition is { } || // element can also be register for component by a descendant DO as its resource provider HasXBindMarkupExtension(objectDefinition) || HasMarkupExtensionNeedingComponent(objectDefinition)) { writer.AppendLineIndented($"/* _isTopLevelDictionary:{_isTopLevelDictionary} */"); - var isInsideFrameworkTemplate = IsMemberInsideFrameworkTemplate(objectDefinition).isInside; if (!_isTopLevelDictionary || isInsideFrameworkTemplate) { componentDefinition ??= AddComponentForCurrentScope(objectDefinition); @@ -5899,13 +5916,14 @@ private IEnumerable GetExtendedProperties(XamlObjectDefini private static bool IsNewScope(XamlObjectDefinition xamlObjectDefinition) { - var typeName = xamlObjectDefinition.Type.Name; - return typeName == "DataTemplate" || - typeName == "ItemsPanelTemplate" || - typeName == "ControlTemplate" || + return xamlObjectDefinition.Type.Name + is "DataTemplate" + or "ItemsPanelTemplate" + or "ControlTemplate" + // This case is specific the custom ListView for iOS. Should be removed // when the list rebuilt to be compatible. - typeName == "ListViewBaseLayoutTemplate"; + or "ListViewBaseLayoutTemplate"; } private void BuildChild(IIndentedStringBuilder writer, XamlMemberDefinition? owner, XamlObjectDefinition xamlObjectDefinition, string? outerClosure = null) @@ -5945,11 +5963,15 @@ private void BuildChild(IIndentedStringBuilder writer, XamlMemberDefinition? own { var resourceOwner = CurrentResourceOwnerName; - writer.Append($"{resourceOwner} , __owner => "); +#if USE_NEW_TP_CODEGEN + writer.Append($"{resourceOwner}, (__owner, __settings) => "); +#else + writer.Append($"{resourceOwner}, (__owner) => "); +#endif + // This case is to support the layout switching for the ListViewBaseLayout, which is not // a FrameworkTemplate. This will need to be removed when this custom list view is removed. var returnType = typeName == "ListViewBaseLayoutTemplate" ? "global::Uno.UI.Controls.Legacy.ListViewBaseLayout" : "_View"; - BuildChildThroughSubclass(writer, contentOwner, returnType); writer.AppendIndented(")"); @@ -6597,8 +6619,11 @@ private void BuildChildThroughSubclass(IIndentedStringBuilder writer, XamlMember var activator = _isHotReloadEnabled ? $"(({namespacePrefix}I{subclassName})global::Uno.UI.Helpers.TypeMappings.CreateInstance<{namespacePrefix}{subclassName}>())" : $"new {namespacePrefix}{subclassName}()"; - +#if USE_NEW_TP_CODEGEN + writer.AppendLineIndented($"{activator}.Build(__owner, __settings)"); +#else writer.AppendLineIndented($"{activator}.Build(__owner)"); +#endif } private string GenerateConstructorParameters(INamedTypeSymbol? type) diff --git a/src/Uno.UI.Maps/MapPresenter.cs b/src/Uno.UI.Maps/MapPresenter.cs index 2094b25dbeff..9834068782a9 100644 --- a/src/Uno.UI.Maps/MapPresenter.cs +++ b/src/Uno.UI.Maps/MapPresenter.cs @@ -25,7 +25,7 @@ private void UpdateOwnerSubscriptions() { _ownerSubscription.Disposable = null; - _owner = TemplatedParent as MapControl; + _owner = GetTemplatedParent() as MapControl; if (_owner != null) { diff --git a/src/Uno.UI.RuntimeTests/Helpers/ImageAssert.cs b/src/Uno.UI.RuntimeTests/Helpers/ImageAssert.cs index c43455d90406..8456d31fc678 100644 --- a/src/Uno.UI.RuntimeTests/Helpers/ImageAssert.cs +++ b/src/Uno.UI.RuntimeTests/Helpers/ImageAssert.cs @@ -106,8 +106,9 @@ private static void HasColorAtImpl(RawBitmap screenshot, int x, int y, Color exp .AppendLine($"Color at ({x},{y}) is not expected") .AppendLine($"expected: {ToArgbCode(expectedColor)} {expectedColor}") .AppendLine($"actual : {ToArgbCode(pixel)} {pixel}") - .AppendLine($"tolerance: {tolerance}") + .AppendLine($"tolerance : {tolerance}") .AppendLine($"difference: {difference}") + .AppendLine($"screenshot size: {bitmap.Width}x{bitmap.Height}") )); } @@ -173,8 +174,10 @@ public static void HasPixels(RawBitmap actual, params ExpectedPixels[] expectati var x = expectation.Location.X; var y = expectation.Location.Y; - Assert.IsTrue(bitmap.Width >= x); - Assert.IsTrue(bitmap.Height >= y); + Assert.IsTrue( + bitmap.Width >= x && bitmap.Height >= y, + $"Expectation '{expectation.Name}'@{x},{y} is outside of the provided bitmap ({bitmap.Width}x{bitmap.Height})" + ); var result = new StringBuilder(); result.AppendLine(expectation.Name); diff --git a/src/Uno.UI.RuntimeTests/Helpers/RelativeCoords.cs b/src/Uno.UI.RuntimeTests/Helpers/RelativeCoords.cs index 1ab564a45900..7fb5a5cb81fe 100644 --- a/src/Uno.UI.RuntimeTests/Helpers/RelativeCoords.cs +++ b/src/Uno.UI.RuntimeTests/Helpers/RelativeCoords.cs @@ -1,21 +1,23 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.IO; -using System.Text; -using Microsoft.UI.Xaml.Media.Imaging; using System.Runtime.InteropServices.WindowsRuntime; +using System.Text; using System.Threading.Tasks; -using Microsoft.UI.Xaml.Controls; +using Windows.Foundation; using Windows.UI; using Microsoft.UI.Xaml; -using Windows.Foundation; +using Microsoft.UI.Xaml.Controls; using Microsoft.UI.Xaml.Media; +using Microsoft.UI.Xaml.Media.Imaging; namespace Uno.UI.RuntimeTests.Helpers { /// /// Class that make it possible to find a specific child position in a parent FrameworkElement /// + [DebuggerDisplay("{DebugDisplay,nq}")] public class RelativeCoords// renamed to RelativeCoords { private readonly GeneralTransform _transform; @@ -33,6 +35,8 @@ public static RelativeCoords From(FrameworkElement parent, FrameworkElement chil return new RelativeCoords(child.TransformToVisual(parent), child.Width, child.Height); } + internal string DebugDisplay => $"{Width:0.#}x{Height:0.#}@{Left:0.#},{Top:0.#}"; + // note: the transformed (0, 0) position is based the top-left. public float X => (float)_transform.TransformPoint(new Windows.Foundation.Point(0, 0)).X; diff --git a/src/Uno.UI.RuntimeTests/Helpers/UITestHelper.cs b/src/Uno.UI.RuntimeTests/Helpers/UITestHelper.cs index 42ebadf92f8a..c6351e42ba42 100644 --- a/src/Uno.UI.RuntimeTests/Helpers/UITestHelper.cs +++ b/src/Uno.UI.RuntimeTests/Helpers/UITestHelper.cs @@ -237,6 +237,18 @@ void DumpState(Point phyLoc) await popup.ShowAsync(ContentDialogPlacement.Popup); } + + public static void CloseAllPopups() +#if HAS_UNO + => VisualTreeHelper.CloseAllPopups(TestServices.WindowHelper.XamlRoot); +#else + { + foreach (var popup in VisualTreeHelper.GetOpenPopupsForXamlRoot(TestServices.WindowHelper.XamlRoot)) + { + popup.IsOpen = false; + } + } +#endif } public class DynamicDataTemplate : IDisposable diff --git a/src/Uno.UI.RuntimeTests/IntegrationTests/dxaml/controls/commandbar/CommandBarIntegrationTests.cs b/src/Uno.UI.RuntimeTests/IntegrationTests/dxaml/controls/commandbar/CommandBarIntegrationTests.cs index ab3c4001c3e2..03d4891aec76 100644 --- a/src/Uno.UI.RuntimeTests/IntegrationTests/dxaml/controls/commandbar/CommandBarIntegrationTests.cs +++ b/src/Uno.UI.RuntimeTests/IntegrationTests/dxaml/controls/commandbar/CommandBarIntegrationTests.cs @@ -304,6 +304,9 @@ public async Task DoesCloseOnSecondaryCommandSelection() Func openFunc = async (cmdBar) => await RunOnUIThread(() => cmdBar.IsOpen = true); Func closeFunc = async (cmdBar) => { + await Task.Delay(2000); + await WindowHelper.WaitForIdle(); + FrameworkElement tapTarget = null; await RunOnUIThread(() => tapTarget = (FrameworkElement)cmdBar.SecondaryCommands[0]); @@ -1142,6 +1145,16 @@ await RunOnUIThread(() => }); await WindowHelper.WaitForIdle(); +#if true + // workaround for initial focus already on MoreButton on certain platforms, + // causing the next step unable to re-focus MoreButton again. + await RunOnUIThread(() => + { + (cmdBar.PrimaryCommands[0] as AppBarButton)?.Focus(FocusState.Programmatic); + }); + await WindowHelper.WaitForIdle(); +#endif + focusSequence = ""; // Start focus with more button diff --git a/src/Uno.UI.RuntimeTests/MUX/Microsoft_UI_Xaml_Controls/TabView/TabViewTests.Uno.cs b/src/Uno.UI.RuntimeTests/MUX/Microsoft_UI_Xaml_Controls/TabView/TabViewTests.Uno.cs index 11f3650d1aa0..37aac3896ff1 100644 --- a/src/Uno.UI.RuntimeTests/MUX/Microsoft_UI_Xaml_Controls/TabView/TabViewTests.Uno.cs +++ b/src/Uno.UI.RuntimeTests/MUX/Microsoft_UI_Xaml_Controls/TabView/TabViewTests.Uno.cs @@ -5,6 +5,7 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using MUXControlsTestApp.Utilities; using Private.Infrastructure; +using Uno.UI.RuntimeTests.Helpers; namespace Microsoft.UI.Xaml.Tests.MUXControls.ApiTests { @@ -24,7 +25,7 @@ public async Task VerifyItemsAreCreatedOnlyOnce() await RunOnUIThread.ExecuteAsync(async () => { tabView = new TabView(); - TestServices.WindowHelper.WindowContent = tabView; + await UITestHelper.Load(tabView); var items = new ObservableCollection() { diff --git a/src/Uno.UI.RuntimeTests/Tests/Microsoft_UI_Xaml_Controls/Given_NumberBox.cs b/src/Uno.UI.RuntimeTests/Tests/Microsoft_UI_Xaml_Controls/Given_NumberBox.cs index 3bfd02870df8..9a802ddb49e1 100644 --- a/src/Uno.UI.RuntimeTests/Tests/Microsoft_UI_Xaml_Controls/Given_NumberBox.cs +++ b/src/Uno.UI.RuntimeTests/Tests/Microsoft_UI_Xaml_Controls/Given_NumberBox.cs @@ -29,7 +29,7 @@ namespace Uno.UI.RuntimeTests.Tests.Microsoft_UI_Xaml_Controls; public class Given_NumberBox { [TestMethod] - public async Task When_Fluent_And_Theme_Changed() + public async Task When_NB_Fluent_And_Theme_Changed() { var textBox = new NumberBox { diff --git a/src/Uno.UI.RuntimeTests/Tests/Microsoft_UI_Xaml_Controls/Given_TreeView.cs b/src/Uno.UI.RuntimeTests/Tests/Microsoft_UI_Xaml_Controls/Given_TreeView.cs index 3f35f8b3d4cd..3b07fabbe7d5 100644 --- a/src/Uno.UI.RuntimeTests/Tests/Microsoft_UI_Xaml_Controls/Given_TreeView.cs +++ b/src/Uno.UI.RuntimeTests/Tests/Microsoft_UI_Xaml_Controls/Given_TreeView.cs @@ -935,6 +935,16 @@ public async Task When_IsSelectedItem_ParentCollapsed() Assert.AreEqual(TreeNodeSelectionState.Selected, SUT.NodeFromContainer(SUT.ContainerFromItem(targetItem))?.SelectionState, "[step2]target node is not selected"); #endif } + + [TestMethod] + public async Task When_Simple_ItemsSource() + { + var SUT = new TreeView() + { + ItemsSource = new int[] { 1, 2 } + }; + await UITestHelper.Load(SUT); + } } public partial class Given_TreeView // helper methods, view-models { diff --git a/src/Uno.UI.RuntimeTests/Tests/TemplatedParent/Setup/BehaviorSetup.xaml b/src/Uno.UI.RuntimeTests/Tests/TemplatedParent/Setup/BehaviorSetup.xaml new file mode 100644 index 000000000000..bfe83efffdbf --- /dev/null +++ b/src/Uno.UI.RuntimeTests/Tests/TemplatedParent/Setup/BehaviorSetup.xaml @@ -0,0 +1,19 @@ + + + + + diff --git a/src/Uno.UI.RuntimeTests/Tests/TemplatedParent/Setup/BehaviorSetup.xaml.cs b/src/Uno.UI.RuntimeTests/Tests/TemplatedParent/Setup/BehaviorSetup.xaml.cs new file mode 100644 index 000000000000..4c515ab34c65 --- /dev/null +++ b/src/Uno.UI.RuntimeTests/Tests/TemplatedParent/Setup/BehaviorSetup.xaml.cs @@ -0,0 +1,106 @@ +using System; +using System.Diagnostics.CodeAnalysis; +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Controls; +#if HAS_UNO +using Uno.UI.DataBinding; +#endif + +namespace Uno.UI.RuntimeTests.Tests.TemplatedParent.Setup; + +public sealed partial class BehaviorSetup : Page +{ + public BehaviorSetup() + { + this.InitializeComponent(); + } +} + +public sealed class Interaction +{ + #region DependencyProperty: Behaviors + + public static DependencyProperty BehaviorsProperty { get; } = DependencyProperty.RegisterAttached( + "Behaviors", + typeof(BehaviorCollection), + typeof(Interaction), + new PropertyMetadata(null, OnBehaviorsChanged)); + + public static BehaviorCollection GetBehaviors(DependencyObject obj) => GetBehaviorsOverride(obj); + public static void SetBehaviors(DependencyObject obj, BehaviorCollection value) => obj.SetValue(BehaviorsProperty, value); + + #endregion + + private static BehaviorCollection GetBehaviorsOverride(DependencyObject obj) + { + var value = (BehaviorCollection)obj.GetValue(BehaviorsProperty); + if (value is null) + { + obj.SetValue(BehaviorsProperty, value = new BehaviorCollection()); + } + + return value; + } + + private static void OnBehaviorsChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e) + { + if (e.NewValue is BehaviorCollection collection) + { + collection.AssociatedObject = sender; + } + } +} +public sealed class BehaviorCollection : DependencyObjectCollection +{ + public DependencyObject AssociatedObject { get; set; } +} + +public interface IBehavior { } + +public partial class LegacyDOBehavior : DependencyObject, IBehavior +#if HAS_UNO + , INotTemplatedParentProvider +#endif +{ + #region DependencyProperty: TestValue + + public static DependencyProperty TestValueProperty { get; } = DependencyProperty.Register( + nameof(TestValue), + typeof(object), + typeof(LegacyDOBehavior), + new PropertyMetadata(default(object), OnTestValueChanged)); + + public object TestValue + { + get => (object)GetValue(TestValueProperty); + set => SetValue(TestValueProperty, value); + } + + #endregion + + private static void OnTestValueChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e) + { + } +} +public partial class NonLegacyDOBehavior : DependencyObject, IBehavior +{ + #region DependencyProperty: TestValue + + public static DependencyProperty TestValueProperty { get; } = DependencyProperty.Register( + nameof(TestValue), + typeof(object), + typeof(NonLegacyDOBehavior), + new PropertyMetadata(default(object), OnTestValueChanged)); + + public object TestValue + { + get => (object)GetValue(TestValueProperty); + set => SetValue(TestValueProperty, value); + } + + #endregion + + private static void OnTestValueChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e) + { + } +} diff --git a/src/Uno.UI.RuntimeTests/Tests/TemplatedParent/Setup/ContentControl_Content.xaml b/src/Uno.UI.RuntimeTests/Tests/TemplatedParent/Setup/ContentControl_Content.xaml new file mode 100644 index 000000000000..cb6df912ef41 --- /dev/null +++ b/src/Uno.UI.RuntimeTests/Tests/TemplatedParent/Setup/ContentControl_Content.xaml @@ -0,0 +1,15 @@ + + + + + + + + diff --git a/src/Uno.UI.RuntimeTests/Tests/TemplatedParent/Setup/ContentControl_Content.xaml.cs b/src/Uno.UI.RuntimeTests/Tests/TemplatedParent/Setup/ContentControl_Content.xaml.cs new file mode 100644 index 000000000000..44d61fc674b7 --- /dev/null +++ b/src/Uno.UI.RuntimeTests/Tests/TemplatedParent/Setup/ContentControl_Content.xaml.cs @@ -0,0 +1,11 @@ +using Microsoft.UI.Xaml.Controls; + +namespace Uno.UI.RuntimeTests.Tests.TemplatedParent.Setup; + +public sealed partial class ContentControl_Content : Page +{ + public ContentControl_Content() + { + this.InitializeComponent(); + } +} diff --git a/src/Uno.UI.RuntimeTests/Tests/TemplatedParent/Setup/ContentControl_ContentTemplate.xaml b/src/Uno.UI.RuntimeTests/Tests/TemplatedParent/Setup/ContentControl_ContentTemplate.xaml new file mode 100644 index 000000000000..2a5c029f3d34 --- /dev/null +++ b/src/Uno.UI.RuntimeTests/Tests/TemplatedParent/Setup/ContentControl_ContentTemplate.xaml @@ -0,0 +1,17 @@ + + + + + + + + + + + + diff --git a/src/Uno.UI.RuntimeTests/Tests/TemplatedParent/Setup/ContentControl_ContentTemplate.xaml.cs b/src/Uno.UI.RuntimeTests/Tests/TemplatedParent/Setup/ContentControl_ContentTemplate.xaml.cs new file mode 100644 index 000000000000..c925f2422175 --- /dev/null +++ b/src/Uno.UI.RuntimeTests/Tests/TemplatedParent/Setup/ContentControl_ContentTemplate.xaml.cs @@ -0,0 +1,11 @@ +using Microsoft.UI.Xaml.Controls; + +namespace Uno.UI.RuntimeTests.Tests.TemplatedParent.Setup; + +public sealed partial class ContentControl_ContentTemplate : Page +{ + public ContentControl_ContentTemplate() + { + this.InitializeComponent(); + } +} diff --git a/src/Uno.UI.RuntimeTests/Tests/TemplatedParent/Setup/ItemsControl_HeaderFooter.xaml b/src/Uno.UI.RuntimeTests/Tests/TemplatedParent/Setup/ItemsControl_HeaderFooter.xaml new file mode 100644 index 000000000000..8b217d5ba4a2 --- /dev/null +++ b/src/Uno.UI.RuntimeTests/Tests/TemplatedParent/Setup/ItemsControl_HeaderFooter.xaml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + diff --git a/src/Uno.UI.RuntimeTests/Tests/TemplatedParent/Setup/ItemsControl_HeaderFooter.xaml.cs b/src/Uno.UI.RuntimeTests/Tests/TemplatedParent/Setup/ItemsControl_HeaderFooter.xaml.cs new file mode 100644 index 000000000000..07831f47b037 --- /dev/null +++ b/src/Uno.UI.RuntimeTests/Tests/TemplatedParent/Setup/ItemsControl_HeaderFooter.xaml.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices.WindowsRuntime; +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Controls; +using Microsoft.UI.Xaml.Controls.Primitives; +using Microsoft.UI.Xaml.Data; +using Microsoft.UI.Xaml.Input; +using Microsoft.UI.Xaml.Media; +using Microsoft.UI.Xaml.Navigation; +using Windows.Foundation; +using Windows.Foundation.Collections; + +// The Blank Page item template is documented at https://go.microsoft.com/fwlink/?LinkId=234238 + +namespace Uno.UI.RuntimeTests.Tests.TemplatedParent.Setup; +/// +/// An empty page that can be used on its own or navigated to within a Frame. +/// +public sealed partial class ItemsControl_HeaderFooter : Page +{ + public ItemsControl_HeaderFooter() + { + this.InitializeComponent(); + } +} diff --git a/src/Uno.UI.RuntimeTests/Tests/TemplatedParent/Setup/ItemsControl_ItemTemplate.xaml b/src/Uno.UI.RuntimeTests/Tests/TemplatedParent/Setup/ItemsControl_ItemTemplate.xaml new file mode 100644 index 000000000000..830ca6559677 --- /dev/null +++ b/src/Uno.UI.RuntimeTests/Tests/TemplatedParent/Setup/ItemsControl_ItemTemplate.xaml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/src/Uno.UI.RuntimeTests/Tests/TemplatedParent/Setup/ItemsControl_ItemTemplate.xaml.cs b/src/Uno.UI.RuntimeTests/Tests/TemplatedParent/Setup/ItemsControl_ItemTemplate.xaml.cs new file mode 100644 index 000000000000..216e1c1bd591 --- /dev/null +++ b/src/Uno.UI.RuntimeTests/Tests/TemplatedParent/Setup/ItemsControl_ItemTemplate.xaml.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices.WindowsRuntime; +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Controls; +using Microsoft.UI.Xaml.Controls.Primitives; +using Microsoft.UI.Xaml.Data; +using Microsoft.UI.Xaml.Input; +using Microsoft.UI.Xaml.Media; +using Microsoft.UI.Xaml.Navigation; +using Windows.Foundation; +using Windows.Foundation.Collections; + +// The Blank Page item template is documented at https://go.microsoft.com/fwlink/?LinkId=234238 + +namespace Uno.UI.RuntimeTests.Tests.TemplatedParent.Setup; +/// +/// An empty page that can be used on its own or navigated to within a Frame. +/// +public sealed partial class ItemsControl_ItemTemplate : Page +{ + public ItemsControl_ItemTemplate() + { + this.InitializeComponent(); + } +} diff --git a/src/Uno.UI.RuntimeTests/Tests/TemplatedParent/Setup/TestDP.cs b/src/Uno.UI.RuntimeTests/Tests/TemplatedParent/Setup/TestDP.cs new file mode 100644 index 000000000000..a8066f3c04a6 --- /dev/null +++ b/src/Uno.UI.RuntimeTests/Tests/TemplatedParent/Setup/TestDP.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Microsoft.UI.Xaml; + +namespace Uno.UI.RuntimeTests.Tests.TemplatedParent.Setup; + +public static class TestDP +{ + #region DependencyProperty: TestValue + + public static DependencyProperty TestValueProperty { get; } = DependencyProperty.RegisterAttached( + "TestValue", + typeof(object), + typeof(TestDP), + new PropertyMetadata(default(object))); + + public static object GetTestValue(DependencyObject obj) => (object)obj.GetValue(TestValueProperty); + public static void SetTestValue(DependencyObject obj, object value) => obj.SetValue(TestValueProperty, value); + + #endregion +} diff --git a/src/Uno.UI.RuntimeTests/Tests/TemplatedParent/Setup/Uno12624.xaml b/src/Uno.UI.RuntimeTests/Tests/TemplatedParent/Setup/Uno12624.xaml new file mode 100644 index 000000000000..bb702c71b020 --- /dev/null +++ b/src/Uno.UI.RuntimeTests/Tests/TemplatedParent/Setup/Uno12624.xaml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Uno.UI.RuntimeTests/Tests/TemplatedParent/Setup/Uno12624.xaml.cs b/src/Uno.UI.RuntimeTests/Tests/TemplatedParent/Setup/Uno12624.xaml.cs new file mode 100644 index 000000000000..bc0d3e6dac5f --- /dev/null +++ b/src/Uno.UI.RuntimeTests/Tests/TemplatedParent/Setup/Uno12624.xaml.cs @@ -0,0 +1,88 @@ +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Controls; + +namespace Uno.UI.RuntimeTests.Tests.TemplatedParent.Setup; + +public sealed partial class Uno12624 : Page +{ + public Uno12624() + { + this.InitializeComponent(); + } +} + +public partial class Uno12624_LeftRightControl : Control +{ + #region DependencyProperty: Left + + public static DependencyProperty LeftProperty { get; } = DependencyProperty.Register( + nameof(Left), + typeof(object), + typeof(Uno12624_LeftRightControl), + new PropertyMetadata(default(object))); + +#if !__ANDROID__ + public object Left +#else + public new object Left +#endif + { + get => (object)GetValue(LeftProperty); + set => SetValue(LeftProperty, value); + } + + #endregion + #region DependencyProperty: Right + + public static DependencyProperty RightProperty { get; } = DependencyProperty.Register( + nameof(Right), + typeof(object), + typeof(Uno12624_LeftRightControl), + new PropertyMetadata(default(object))); + +#if !__ANDROID__ + public object Right +#else + public new object Right +#endif + { + get => (object)GetValue(RightProperty); + set => SetValue(RightProperty, value); + } + + #endregion +} + +public partial class Uno12624_WestEastControl : Control +{ + #region DependencyProperty: West + + public static DependencyProperty WestProperty { get; } = DependencyProperty.Register( + nameof(West), + typeof(object), + typeof(Uno12624_WestEastControl), + new PropertyMetadata(default(object))); + + public object West + { + get => (object)GetValue(WestProperty); + set => SetValue(WestProperty, value); + } + + #endregion + #region DependencyProperty: East + + public static DependencyProperty EastProperty { get; } = DependencyProperty.Register( + nameof(East), + typeof(object), + typeof(Uno12624_WestEastControl), + new PropertyMetadata(default(object))); + + public object East + { + get => (object)GetValue(EastProperty); + set => SetValue(EastProperty, value); + } + + #endregion +} diff --git a/src/Uno.UI.RuntimeTests/Tests/TemplatedParent/Setup/Uno17313.xaml b/src/Uno.UI.RuntimeTests/Tests/TemplatedParent/Setup/Uno17313.xaml new file mode 100644 index 000000000000..ecbf865a03f2 --- /dev/null +++ b/src/Uno.UI.RuntimeTests/Tests/TemplatedParent/Setup/Uno17313.xaml @@ -0,0 +1,64 @@ + + + + + + + + + + + + + + + + + + diff --git a/src/Uno.UI.RuntimeTests/Tests/TemplatedParent/Setup/Uno17313.xaml.cs b/src/Uno.UI.RuntimeTests/Tests/TemplatedParent/Setup/Uno17313.xaml.cs new file mode 100644 index 000000000000..b048df3e5020 --- /dev/null +++ b/src/Uno.UI.RuntimeTests/Tests/TemplatedParent/Setup/Uno17313.xaml.cs @@ -0,0 +1,50 @@ +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Controls; + +namespace Uno.UI.RuntimeTests.Tests.TemplatedParent.Setup; + +public sealed partial class Uno17313 : Page +{ + public Uno17313() + { + this.InitializeComponent(); + } +} + +public partial class Uno17313_HeaderedCC : ContentControl +{ + #region DependencyProperty: Header + + public static DependencyProperty HeaderProperty { get; } = DependencyProperty.Register( + nameof(Header), + typeof(object), + typeof(Uno17313_HeaderedCC), + new PropertyMetadata(default(object))); + + public object Header + { + get => (object)GetValue(HeaderProperty); + set => SetValue(HeaderProperty, value); + } + + #endregion + #region DependencyProperty: HeaderTemplate + + public static DependencyProperty HeaderTemplateProperty { get; } = DependencyProperty.Register( + nameof(HeaderTemplate), + typeof(DataTemplate), + typeof(Uno17313_HeaderedCC), + new PropertyMetadata(default(DataTemplate))); + + public DataTemplate HeaderTemplate + { + get => (DataTemplate)GetValue(HeaderTemplateProperty); + set => SetValue(HeaderTemplateProperty, value); + } + + #endregion +} + +public partial class Uno17313_Expander : Uno17313_HeaderedCC { } +public partial class Uno17313_SettingsExpander : Uno17313_HeaderedCC { } +public partial class Uno17313_SettingsCard : Uno17313_HeaderedCC { } diff --git a/src/Uno.UI.RuntimeTests/Tests/TemplatedParent/Setup/Uno7497.xaml b/src/Uno.UI.RuntimeTests/Tests/TemplatedParent/Setup/Uno7497.xaml new file mode 100644 index 000000000000..2e5105d2922e --- /dev/null +++ b/src/Uno.UI.RuntimeTests/Tests/TemplatedParent/Setup/Uno7497.xaml @@ -0,0 +1,14 @@ + + + + + + diff --git a/src/Uno.UI.RuntimeTests/Tests/TemplatedParent/Setup/Uno7497.xaml.cs b/src/Uno.UI.RuntimeTests/Tests/TemplatedParent/Setup/Uno7497.xaml.cs new file mode 100644 index 000000000000..caed4890e773 --- /dev/null +++ b/src/Uno.UI.RuntimeTests/Tests/TemplatedParent/Setup/Uno7497.xaml.cs @@ -0,0 +1,55 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices.WindowsRuntime; +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Controls; +using Microsoft.UI.Xaml.Controls.Primitives; +using Microsoft.UI.Xaml.Data; +using Microsoft.UI.Xaml.Input; +using Microsoft.UI.Xaml.Media; +using Microsoft.UI.Xaml.Navigation; +using Windows.Foundation; +using Windows.Foundation.Collections; + +namespace Uno.UI.RuntimeTests.Tests.TemplatedParent.Setup; + +public sealed partial class Uno7497 : Page +{ + public Uno7497() + { + this.InitializeComponent(); + } +} + + +public partial class Control7497 : Control +{ + #region DependencyProperty: TestList + + public static DependencyProperty TestListProperty { get; } = DependencyProperty.Register( + nameof(TestList), + typeof(ObservableCollection), + typeof(Control7497), + new PropertyMetadata(default(ObservableCollection))); + + public ObservableCollection TestList + { + get => (ObservableCollection)GetValue(TestListProperty); + set => SetValue(TestListProperty, value); + } + + #endregion + + public Control7497() + { + DefaultStyleKey = typeof(Control7497); + TestList = new ObservableCollection() + { + "Test 1", + "Test 2" + }; + } +} diff --git a/src/Uno.UI.RuntimeTests/Tests/TemplatedParent/Setup/Uno8049.xaml b/src/Uno.UI.RuntimeTests/Tests/TemplatedParent/Setup/Uno8049.xaml new file mode 100644 index 000000000000..acdf043d76af --- /dev/null +++ b/src/Uno.UI.RuntimeTests/Tests/TemplatedParent/Setup/Uno8049.xaml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + diff --git a/src/Uno.UI.RuntimeTests/Tests/TemplatedParent/Setup/Uno8049.xaml.cs b/src/Uno.UI.RuntimeTests/Tests/TemplatedParent/Setup/Uno8049.xaml.cs new file mode 100644 index 000000000000..5382456aa02a --- /dev/null +++ b/src/Uno.UI.RuntimeTests/Tests/TemplatedParent/Setup/Uno8049.xaml.cs @@ -0,0 +1,11 @@ +using Microsoft.UI.Xaml.Controls; + +namespace Uno.UI.RuntimeTests.Tests.TemplatedParent.Setup; + +public sealed partial class Uno8049 : Page +{ + public Uno8049() + { + this.InitializeComponent(); + } +} diff --git a/src/Uno.UI.RuntimeTests/Tests/TemplatedParent/Setup/Uno9059.xaml b/src/Uno.UI.RuntimeTests/Tests/TemplatedParent/Setup/Uno9059.xaml new file mode 100644 index 000000000000..6b29e5e43129 --- /dev/null +++ b/src/Uno.UI.RuntimeTests/Tests/TemplatedParent/Setup/Uno9059.xaml @@ -0,0 +1,30 @@ + + + + + + + diff --git a/src/Uno.UI.RuntimeTests/Tests/TemplatedParent/Setup/Uno9059.xaml.cs b/src/Uno.UI.RuntimeTests/Tests/TemplatedParent/Setup/Uno9059.xaml.cs new file mode 100644 index 000000000000..d763c215c31c --- /dev/null +++ b/src/Uno.UI.RuntimeTests/Tests/TemplatedParent/Setup/Uno9059.xaml.cs @@ -0,0 +1,61 @@ +using System; +using System.Windows.Input; +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Controls; + +namespace Uno.UI.RuntimeTests.Tests.TemplatedParent.Setup; + +public sealed partial class Uno9059 : Page +{ + public Uno9059() + { + this.InitializeComponent(); + this.DataContext = new Uno9059_VM(() => + { + }); + } +} + +public class Uno9059_VM +{ + public Uno9059_VM(Action action) + { + CustomCommand = new Uno9059_RelayCommand(action); + } + + public Uno9059_RelayCommand CustomCommand { get; set; } +} +public partial class Uno9059_CustomControl : Control +{ + #region DependencyProperty: Action1 + + public static DependencyProperty Action1Property { get; } = DependencyProperty.Register( + nameof(Action1), + typeof(ICommand), + typeof(Uno9059_CustomControl), + new PropertyMetadata(default(ICommand))); + + public ICommand Action1 + { + get => (ICommand)GetValue(Action1Property); + set => SetValue(Action1Property, value); + } + + #endregion +} +public class Uno9059_RelayCommand : ICommand +{ + public event EventHandler CanExecuteChanged; + private Action _execute; + + public Uno9059_RelayCommand(Action execute) + { + _execute = execute; + + CanExecuteChanged?.ToString(); // error CS0067: The event 'Uno9059_RelayCommand.CanExecuteChanged' is never used + } + + public bool CanExecute(object parameter) => true; + + public void Execute(object parameter) => _execute?.Invoke(); +} diff --git a/src/Uno.UI.RuntimeTests/Tests/TemplatedParent/Setup/VisualStateGroup_Full.xaml b/src/Uno.UI.RuntimeTests/Tests/TemplatedParent/Setup/VisualStateGroup_Full.xaml new file mode 100644 index 000000000000..f8a710454264 --- /dev/null +++ b/src/Uno.UI.RuntimeTests/Tests/TemplatedParent/Setup/VisualStateGroup_Full.xaml @@ -0,0 +1,65 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Uno.UI.RuntimeTests/Tests/TemplatedParent/Setup/VisualStateGroup_Full.xaml.cs b/src/Uno.UI.RuntimeTests/Tests/TemplatedParent/Setup/VisualStateGroup_Full.xaml.cs new file mode 100644 index 000000000000..1572bad9a5e2 --- /dev/null +++ b/src/Uno.UI.RuntimeTests/Tests/TemplatedParent/Setup/VisualStateGroup_Full.xaml.cs @@ -0,0 +1,11 @@ +using Microsoft.UI.Xaml.Controls; + +namespace Uno.UI.RuntimeTests.Tests.TemplatedParent.Setup; + +public sealed partial class VisualStateGroup_Full : Page +{ + public VisualStateGroup_Full() + { + this.InitializeComponent(); + } +} diff --git a/src/Uno.UI.RuntimeTests/Tests/TemplatedParent/TemplatedParentTests.cs b/src/Uno.UI.RuntimeTests/Tests/TemplatedParent/TemplatedParentTests.cs new file mode 100644 index 000000000000..2d2b4b02aeaa --- /dev/null +++ b/src/Uno.UI.RuntimeTests/Tests/TemplatedParent/TemplatedParentTests.cs @@ -0,0 +1,464 @@ +#if __ANDROID__ || __IOS__ +// On droid and ios, ContentPresenter bypass can be potentially enabled (based on if a base control template is present, or not). +// As such, ContentPresenter may be omitted, and altering its descendants templated-parent too. +#define NEED_CUSTOM_ADJUSTMENTS_FOR_CP_BYPASS +#endif + +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading.Tasks; +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Controls; +using Microsoft.UI.Xaml.Data; +using Microsoft.UI.Xaml.Media; +using Microsoft.UI.Xaml.Media.Animation; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Uno.Extensions; +using Uno.UI.Extensions; +using Uno.UI.Helpers; +using Uno.UI.RuntimeTests.Helpers; +using Uno.UI.RuntimeTests.Tests.TemplatedParent.Setup; +#if HAS_UNO +using Uno.UI.Xaml; +using Uno.UI.DataBinding; +#endif +using WindowHelper = Private.Infrastructure.TestServices.WindowHelper; + +namespace Uno.UI.RuntimeTests.Tests.TemplatedParent; + +[TestClass] +[RunsOnUIThread] +public partial class TemplatedParentTests // tests +{ + [TestMethod] + public async Task Uno8049_Test() + { + var setup = new Uno8049(); + await UITestHelper.Load(setup); + + var tree = setup.TreeGraph(DebugVT_TP); + var sut = setup.Content as TextBox ?? throw new Exception("Invalid content root"); + var cc = sut.FindFirstDescendantOrThrow("HeaderTemplate_ContentControl"); + var tb = cc.FindFirstDescendantOrThrow("Header_TextBlock"); + + Assert.IsNotNull(sut.Header); + Assert.AreEqual(sut.Header, tb); + } + + [TestMethod] + public async Task Uno9059_Test() + { + try + { + var setup = new Uno9059(); + await UITestHelper.Load(setup); + + var sut = setup.Content as Uno9059_CustomControl ?? throw new Exception("Invalid content root"); + var button = sut.FindFirstDescendantOrThrow