committed by
GitHub
21 changed files with 266 additions and 141 deletions
@ -1,51 +0,0 @@ |
|||
// Copyright (c) The Avalonia Project. All rights reserved.
|
|||
// Licensed under the MIT license. See licence.md file in the project root for full license information.
|
|||
|
|||
using Avalonia.Data; |
|||
|
|||
namespace Avalonia.Markup.Xaml.MarkupExtensions |
|||
{ |
|||
using System; |
|||
using Avalonia.Data.Converters; |
|||
using Avalonia.Markup.Data; |
|||
using Portable.Xaml.Markup; |
|||
|
|||
[MarkupExtensionReturnType(typeof(IBinding))] |
|||
public class TemplateBindingExtension : MarkupExtension |
|||
{ |
|||
public TemplateBindingExtension() |
|||
{ |
|||
} |
|||
|
|||
public TemplateBindingExtension(string path) |
|||
{ |
|||
Path = path; |
|||
} |
|||
|
|||
public override object ProvideValue(IServiceProvider serviceProvider) |
|||
{ |
|||
return new Binding |
|||
{ |
|||
Converter = Converter, |
|||
ElementName = ElementName, |
|||
Mode = Mode, |
|||
RelativeSource = new RelativeSource(RelativeSourceMode.TemplatedParent), |
|||
Path = Path ?? string.Empty, |
|||
Priority = Priority, |
|||
}; |
|||
} |
|||
|
|||
public IValueConverter Converter { get; set; } |
|||
|
|||
public string ElementName { get; set; } |
|||
|
|||
public object FallbackValue { get; set; } |
|||
|
|||
public BindingMode Mode { get; set; } |
|||
|
|||
[ConstructorArgument("path")] |
|||
public string Path { get; set; } |
|||
|
|||
public BindingPriority Priority { get; set; } = BindingPriority.TemplatedParent; |
|||
} |
|||
} |
|||
@ -0,0 +1,178 @@ |
|||
using System; |
|||
using System.Globalization; |
|||
using System.Reactive.Subjects; |
|||
using Avalonia.Data.Converters; |
|||
using Avalonia.Reactive; |
|||
|
|||
namespace Avalonia.Data |
|||
{ |
|||
/// <summary>
|
|||
/// A XAML binding to a property on a control's templated parent.
|
|||
/// </summary>
|
|||
public class TemplateBinding : SingleSubscriberObservableBase<object>, |
|||
IBinding, |
|||
IDescription, |
|||
ISubject<object> |
|||
{ |
|||
private IStyledElement _target; |
|||
private Type _targetType; |
|||
|
|||
public TemplateBinding() |
|||
{ |
|||
} |
|||
|
|||
public TemplateBinding(AvaloniaProperty property) |
|||
{ |
|||
Property = property; |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public InstancedBinding Initiate( |
|||
IAvaloniaObject target, |
|||
AvaloniaProperty targetProperty, |
|||
object anchor = null, |
|||
bool enableDataValidation = false) |
|||
{ |
|||
// Usually each `TemplateBinding` will only be instantiated once; in this case we can
|
|||
// use the `TemplateBinding` object itself as the instanced binding in order to save
|
|||
// allocating a new object. If the binding *is* instantiated more than once (which can
|
|||
// happen if it appears in a `Setter` for example, then just make a clone and instantiate
|
|||
// that.
|
|||
if (_target == null) |
|||
{ |
|||
_target = (IStyledElement)target; |
|||
_targetType = targetProperty?.PropertyType; |
|||
|
|||
return new InstancedBinding( |
|||
this, |
|||
Mode == BindingMode.Default ? BindingMode.OneWay : Mode, |
|||
BindingPriority.TemplatedParent); |
|||
} |
|||
else |
|||
{ |
|||
var clone = new TemplateBinding |
|||
{ |
|||
Converter = Converter, |
|||
ConverterParameter = ConverterParameter, |
|||
Property = Property, |
|||
}; |
|||
|
|||
return clone.Initiate(target, targetProperty, anchor, enableDataValidation); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the <see cref="IValueConverter"/> to use.
|
|||
/// </summary>
|
|||
public IValueConverter Converter { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets a parameter to pass to <see cref="Converter"/>.
|
|||
/// </summary>
|
|||
public object ConverterParameter { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the binding mode.
|
|||
/// </summary>
|
|||
public BindingMode Mode { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the name of the source property on the templated parent.
|
|||
/// </summary>
|
|||
public AvaloniaProperty Property { get; set; } |
|||
|
|||
/// <inheritdoc/>
|
|||
public string Description => "TemplateBinding: " + Property; |
|||
|
|||
void IObserver<object>.OnCompleted() => throw new NotImplementedException(); |
|||
void IObserver<object>.OnError(Exception error) => throw new NotImplementedException(); |
|||
|
|||
void IObserver<object>.OnNext(object value) |
|||
{ |
|||
if (_target.TemplatedParent != null && Property != null) |
|||
{ |
|||
if (Converter != null) |
|||
{ |
|||
value = Converter.ConvertBack( |
|||
value, |
|||
Property.PropertyType, |
|||
ConverterParameter, |
|||
CultureInfo.CurrentCulture); |
|||
} |
|||
|
|||
_target.TemplatedParent.SetValue(Property, value, BindingPriority.TemplatedParent); |
|||
} |
|||
} |
|||
|
|||
protected override void Subscribed() |
|||
{ |
|||
TemplatedParentChanged(); |
|||
_target.PropertyChanged += TargetPropertyChanged; |
|||
} |
|||
|
|||
protected override void Unsubscribed() |
|||
{ |
|||
if (_target.TemplatedParent != null) |
|||
{ |
|||
_target.TemplatedParent.PropertyChanged -= TemplatedParentPropertyChanged; |
|||
} |
|||
|
|||
_target.PropertyChanged -= TargetPropertyChanged; |
|||
} |
|||
|
|||
private void PublishValue() |
|||
{ |
|||
if (_target.TemplatedParent != null) |
|||
{ |
|||
var value = Property != null ? |
|||
_target.TemplatedParent.GetValue(Property) : |
|||
_target.TemplatedParent; |
|||
|
|||
if (Converter != null) |
|||
{ |
|||
value = Converter.Convert(value, _targetType, ConverterParameter, CultureInfo.CurrentCulture); |
|||
} |
|||
|
|||
PublishNext(value); |
|||
} |
|||
else |
|||
{ |
|||
PublishNext(AvaloniaProperty.UnsetValue); |
|||
} |
|||
} |
|||
|
|||
private void TemplatedParentChanged() |
|||
{ |
|||
if (_target.TemplatedParent != null) |
|||
{ |
|||
_target.TemplatedParent.PropertyChanged += TemplatedParentPropertyChanged; |
|||
} |
|||
|
|||
PublishValue(); |
|||
} |
|||
|
|||
private void TargetPropertyChanged(object sender, AvaloniaPropertyChangedEventArgs e) |
|||
{ |
|||
if (e.Property == StyledElement.TemplatedParentProperty) |
|||
{ |
|||
var oldValue = (IAvaloniaObject)e.OldValue; |
|||
var newValue = (IAvaloniaObject)e.OldValue; |
|||
|
|||
if (oldValue != null) |
|||
{ |
|||
oldValue.PropertyChanged -= TemplatedParentPropertyChanged; |
|||
} |
|||
|
|||
TemplatedParentChanged(); |
|||
} |
|||
} |
|||
|
|||
private void TemplatedParentPropertyChanged(object sender, AvaloniaPropertyChangedEventArgs e) |
|||
{ |
|||
if (e.Property == Property) |
|||
{ |
|||
PublishNext(_target.TemplatedParent.GetValue(Property)); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue