Skip to content

Commit

Permalink
fix: TemplateBinding inheriting wrong TemplatedParent
Browse files Browse the repository at this point in the history
  • Loading branch information
Xiaoy312 committed Jun 27, 2023
1 parent 2eb2b28 commit 4f37e52
Show file tree
Hide file tree
Showing 9 changed files with 161 additions and 5 deletions.
56 changes: 53 additions & 3 deletions src/Uno.UI/DataBinding/BindingExpression.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
using Windows.UI.Xaml;
using Windows.UI.Xaml.Data;
using System.Runtime.CompilerServices;
using TemplatedParentScope = Uno.UI.TemplateParentResolver.TemplatedParentScope;

namespace Windows.UI.Xaml.Data
{
Expand All @@ -31,6 +32,7 @@ public partial class BindingExpression : IDisposable, IValueChangedListener

private BindingPath _bindingPath;
private bool _disposed;
private TemplatedParentScope _templatedParentScope;

Check warning on line 35 in src/Uno.UI/DataBinding/BindingExpression.cs

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

src/Uno.UI/DataBinding/BindingExpression.cs#L35

Make '_templatedParentScope' 'readonly'.
private ManagedWeakReference _explicitSourceStore;
private readonly bool _isCompiledSource;
private readonly bool _isElementNameSource;
Expand All @@ -45,6 +47,7 @@ public partial class BindingExpression : IDisposable, IValueChangedListener
public Binding ParentBinding { get; }

internal DependencyPropertyDetails TargetPropertyDetails { get; }
internal TemplatedParentScope TemplatedParentScopeDebug => _templatedParentScope;

private object ExplicitSource
{
Expand All @@ -58,10 +61,25 @@ private object ExplicitSource

public object DataContext
{
get => _isElementNameSource || ExplicitSource != null ? ExplicitSource : _dataContext?.Target;
get // => _isElementNameSource || ExplicitSource != null ? ExplicitSource : _dataContext?.Target;

Check warning on line 64 in src/Uno.UI/DataBinding/BindingExpression.cs

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

src/Uno.UI/DataBinding/BindingExpression.cs#L64

Remove this commented out code.
{
if (_templatedParentScope != null)
{
return _templatedParentScope.TemplatedParent;
}
if (_isElementNameSource || ExplicitSource != null)
{
return ExplicitSource;
}

return _dataContext?.Target;
}
set
{
if (ExplicitSource == null && !_disposed && DependencyObjectStore.AreDifferent(_dataContext?.Target, value))
if (!_disposed &&
_templatedParentScope == null &&
ExplicitSource == null &&
DependencyObjectStore.AreDifferent(_dataContext?.Target, value))
{
var previousContext = _dataContext;

Expand Down Expand Up @@ -111,6 +129,13 @@ Binding binding

TryGetSource(binding);

if (ParentBinding.IsTemplateBinding
//|| _templatedParentScope.TemplatedParent != null // fixme@xy: needed?
)
{
_templatedParentScope = TemplateParentResolver.CurrentScope;
}

if (ParentBinding.CompiledSource != null)
{
_isCompiledSource = true;
Expand All @@ -137,12 +162,24 @@ Binding binding
ApplyFallbackValue();
}

ApplyTemplateBindingParent();
ApplyExplicitSource();
ApplyElementName();
}

private ManagedWeakReference GetWeakDataContext()
=> _isElementNameSource || (_explicitSourceStore?.IsAlive ?? false) ? _explicitSourceStore : _dataContext;
{
if (_templatedParentScope?.TemplatedParentRef.IsAlive ?? false)
{
return _templatedParentScope.TemplatedParentRef;
}
if (_isElementNameSource || (_explicitSourceStore?.IsAlive ?? false))
{
return _explicitSourceStore;
}

return _dataContext;
}

/// <summary>
/// Sends the current binding target value to the binding source property in TwoWay bindings.
Expand Down Expand Up @@ -372,6 +409,19 @@ internal void ApplyCompiledSource()
}
}

internal void ApplyTemplateBindingParent()
{
if (_templatedParentScope != null)
{
if (this.Log().IsEnabled(Uno.Foundation.Logging.LogLevel.Debug))
{
this.Log().DebugFormat("Applying template binding parent {0} on {1}", _templatedParentScope?.TemplatedParent?.GetType(), _view.Target?.GetType());
}

ApplyBinding();
}
}

private void TryGetSource(Binding binding)
{
if (binding.Source is ElementNameSubject sourceSubject)
Expand Down
17 changes: 17 additions & 0 deletions src/Uno.UI/UI/Xaml/ControlTemplate.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#nullable enable

using System;
using Uno.UI;

#if XAMARIN_ANDROID
using View = Android.Views.View;
Expand Down Expand Up @@ -47,6 +48,22 @@ public ControlTemplate(object? owner, FrameworkTemplateBuilder? factory)
get;
set;
}

internal View? LoadContentCached(Control templatedParent)
{
using (TemplateParentResolver.RentScope(this, templatedParent, out var scope))
{
//scope.SetTemplatedParent(templatedParent);

Check warning on line 56 in src/Uno.UI/UI/Xaml/ControlTemplate.cs

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

src/Uno.UI/UI/Xaml/ControlTemplate.cs#L56

Remove this commented out code.
var root = base.LoadContentCachedCore();
//if (root is { })
//{

Check warning on line 59 in src/Uno.UI/UI/Xaml/ControlTemplate.cs

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

src/Uno.UI/UI/Xaml/ControlTemplate.cs#L59

Remove this commented out code.
// #error @xy template root is NOT the templated parent!!
// scope.SetTemplatedParent(templatedParent);
//}

return root;
}
}
}
}

2 changes: 1 addition & 1 deletion src/Uno.UI/UI/Xaml/Controls/Control/Control.cs
Original file line number Diff line number Diff line change
Expand Up @@ -471,7 +471,7 @@ private void UpdateTemplate()

if (Template != null)
{
TemplatedRoot = Template.LoadContentCached();
TemplatedRoot = Template.LoadContentCached(this);
}
else
{
Expand Down
2 changes: 2 additions & 0 deletions src/Uno.UI/UI/Xaml/Controls/ItemsPanelTemplate.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ public ItemsPanelTemplate(object? owner, FrameworkTemplateBuilder? factory)

public new View? LoadContent()
=> (View?)base.LoadContent();

internal View? LoadContentCached() => LoadContentCachedCore();
}
}

2 changes: 2 additions & 0 deletions src/Uno.UI/UI/Xaml/Data/Binding.cs
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,8 @@ internal void SetBindingXBindProvider(object compiledSource, Func<object, (bool,
internal bool IsFallbackValueSet
=> _flags.HasFlag(BindingFlags.FallbackValueSet);

internal bool IsTemplateBinding => RelativeSource?.Mode is RelativeSourceMode.TemplatedParent;

[Flags]
private enum BindingFlags
{
Expand Down
2 changes: 2 additions & 0 deletions src/Uno.UI/UI/Xaml/DataTemplate.cs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ public DataTemplate(object? owner, FrameworkTemplateBuilder? factory)

return new DataTemplate(obj);
}

internal View? LoadContentCached() => LoadContentCachedCore();
}
}

2 changes: 1 addition & 1 deletion src/Uno.UI/UI/Xaml/FrameworkTemplate.cs
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ public FrameworkTemplate(object? owner, FrameworkTemplateBuilder? factory)
/// instance that has been detached from its parent may be reused at any time.
/// If a control needs to be the owner of a created instance, it needs to use <see cref="LoadContent"/>.
/// </remarks>
internal View? LoadContentCached() => FrameworkTemplatePool.Instance.DequeueTemplate(this);
protected View? LoadContentCachedCore() => FrameworkTemplatePool.Instance.DequeueTemplate(this);

/// <summary>
/// Manually return an unused template root created by <see cref="LoadContentCached"/> to the pool.
Expand Down
1 change: 1 addition & 0 deletions src/Uno.UI/UI/Xaml/FrameworkTemplatePool.cs
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,7 @@ private void OnParentChanged(object instance, object? key, DependencyObjectParen

private void TryReuseTemplateRoot(object instance, object? key, object? newParent, bool shouldCleanUpTemplateRoot)
{
// fixme@xy: add handling for TemplatedParentScope here ?if needed?
if (!IsPoolingEnabled)
{
return;
Expand Down
82 changes: 82 additions & 0 deletions src/Uno.UI/UI/Xaml/TemplateParentResolver.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
#nullable enable

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Uno.UI.DataBinding;
using Uno.Disposables;
using Windows.UI.Xaml.Data;

namespace Uno.UI;

[EditorBrowsable(EditorBrowsableState.Never)]
public static class TemplateParentResolver
{
private static readonly Stack<TemplatedParentScope> _stack = new();

internal static TemplatedParentScope? CurrentScope => _stack.TryPeek(out var scope) ? scope : default;

private static TemplatedParentScope Push(ControlTemplate template, Control templatedParent)
{
var scope = new TemplatedParentScope(template, templatedParent);
_stack.Push(scope);

return scope;
}

internal static TemplatedParentScope Pop() => _stack.Pop();

internal static IDisposable RentScope(ControlTemplate template, Control templatedParent, out TemplatedParentScope scope)
{
var capturedScope = scope = Push(template, templatedParent);
var depth = _stack.Count;

return Disposable.Create(() =>
{
if (_stack.Count == 0 ||
_stack.Count != depth ||
Pop() != capturedScope)
{
throw new InvalidOperationException("The scope has desynchronized");
}
});
}

internal class TemplatedParentScope
{
private ManagedWeakReference _controlTemplate;

Check warning on line 52 in src/Uno.UI/UI/Xaml/TemplateParentResolver.cs

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

src/Uno.UI/UI/Xaml/TemplateParentResolver.cs#L52

Make '_controlTemplate' 'readonly'.
private ManagedWeakReference _templatedParent;

Check warning on line 53 in src/Uno.UI/UI/Xaml/TemplateParentResolver.cs

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

src/Uno.UI/UI/Xaml/TemplateParentResolver.cs#L53

Make '_templatedParent' 'readonly'.
//private List<WeakReference> _nestedTemplateBindings = new();

Check warning on line 54 in src/Uno.UI/UI/Xaml/TemplateParentResolver.cs

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

src/Uno.UI/UI/Xaml/TemplateParentResolver.cs#L54

Remove this commented out code.
public ManagedWeakReference TemplatedParentRef => _templatedParent;

public ControlTemplate? ControlTemplate => _controlTemplate.Target as ControlTemplate; // fxime@xy: drop?
public Control? TemplatedParent => _templatedParent.Target as Control;
//public bool HasTemplatedParrent => _templatedParent != null;

Check warning on line 59 in src/Uno.UI/UI/Xaml/TemplateParentResolver.cs

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

src/Uno.UI/UI/Xaml/TemplateParentResolver.cs#L59

Remove this commented out code.

public TemplatedParentScope(ControlTemplate template, Control templatedParent)
{
_controlTemplate = (template as IWeakReferenceProvider).WeakReference;
_templatedParent = (templatedParent as IWeakReferenceProvider).WeakReference;
}

//public void SetTemplatedParent(Control templatedParent)
//{

Check warning on line 68 in src/Uno.UI/UI/Xaml/TemplateParentResolver.cs

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

src/Uno.UI/UI/Xaml/TemplateParentResolver.cs#L68

Remove this commented out code.
// _templatedParent = (templatedParent as IWeakReferenceProvider).WeakReference;
// //if (_templatedParent is { })
// //{
// // foreach (var wr in _nestedTemplateBindings)
// // {
// // if (wr.Target is BindingExpression binding)
// // {
// // binding.
// // }
// // }
// //}
//}
}
}

0 comments on commit 4f37e52

Please sign in to comment.