Browse Source

Merge remote-tracking branch 'origin' into RelativeSourceSyntaxSugar

pull/1209/head
Jeremy Koritzinsky 9 years ago
parent
commit
fb5abf3a93
  1. 4
      appveyor.yml
  2. 2
      src/Avalonia.Base/AvaloniaObjectExtensions.cs
  3. 17
      src/Avalonia.Base/Data/IndexerBinding.cs
  4. 155
      src/Avalonia.Base/Data/InstancedBinding.cs
  5. 4
      src/Avalonia.Controls/Control.cs
  6. 2
      src/Avalonia.Controls/Templates/FuncTreeDataTemplate.cs
  7. 3
      src/Avalonia.Remote.Protocol/BsonStreamTransport.cs
  8. 60
      src/Avalonia.Styling/Styling/Setter.cs
  9. 3
      src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj
  10. 3
      src/Markup/Avalonia.Markup.Xaml/Data/Binding.cs
  11. 35
      src/Markup/Avalonia.Markup.Xaml/Data/MultiBinding.cs
  12. 69
      src/Markup/Avalonia.Markup.Xaml/Data/StyleResourceBinding.cs
  13. 25
      src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/BindingExtension.cs
  14. 2
      src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/DynamicResourceExtension.cs
  15. 31
      src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/StyleResourceExtension.cs
  16. 12
      src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaXamlType.cs
  17. 63
      src/Markup/Avalonia.Markup.Xaml/PortableXaml/XamlBinding.cs
  18. 2
      src/Markup/Avalonia.Markup.Xaml/Templates/TreeDataTemplate.cs
  19. 2
      tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Binding.cs
  20. 44
      tests/Avalonia.Controls.UnitTests/Primitives/PopupTests.cs
  21. 2
      tests/Avalonia.Controls.UnitTests/TreeViewTests.cs
  22. 4
      tests/Avalonia.Markup.Xaml.UnitTests/Data/MultiBindingTests.cs
  23. 19
      tests/Avalonia.Markup.Xaml.UnitTests/Xaml/BasicTests.cs
  24. 165
      tests/Avalonia.Markup.Xaml.UnitTests/Xaml/StyleTests.cs
  25. 2
      tests/Avalonia.Styling.UnitTests/SetterTests.cs

4
appveyor.yml

@ -23,10 +23,6 @@ before_build:
- git submodule update --init
build_script:
- ps: .\build.ps1 -Target "AppVeyor" -Platform "$env:platform" -Configuration "$env:configuration"
after_build:
- "SET PATH=C:\\Python34;C:\\Python34\\Scripts;%PATH%"
- pip install codecov
- codecov -f "./artifacts/coverage.xml"
test: off
artifacts:

2
src/Avalonia.Base/AvaloniaObjectExtensions.cs

@ -326,7 +326,7 @@ namespace Avalonia
object anchor = null,
bool enableDataValidation = false)
{
return new InstancedBinding(_source);
return InstancedBinding.OneWay(_source);
}
}
}

17
src/Avalonia.Base/Data/IndexerBinding.cs

@ -31,13 +31,18 @@ namespace Avalonia.Data
targetProperty.GetMetadata(target.GetType()).DefaultBindingMode :
Mode;
if (mode == BindingMode.TwoWay)
switch (mode)
{
return new InstancedBinding(Source.GetSubject(Property), mode);
}
else
{
return new InstancedBinding(Source.GetObservable(Property), mode);
case BindingMode.OneTime:
return InstancedBinding.OneTime(Source.GetObservable(Property));
case BindingMode.OneWay:
return InstancedBinding.OneWay(Source.GetObservable(Property));
case BindingMode.OneWayToSource:
return InstancedBinding.OneWayToSource(Source.GetSubject(Property));
case BindingMode.TwoWay:
return InstancedBinding.TwoWay(Source.GetSubject(Property));
default:
throw new NotSupportedException("Unsupported BindingMode.");
}
}
}

155
src/Avalonia.Base/Data/InstancedBinding.cs

@ -14,71 +14,35 @@ namespace Avalonia.Data
/// property on a control's DataContext"; this class represents a binding that has been
/// *instanced* by calling <see cref="IBinding.Initiate(IAvaloniaObject, AvaloniaProperty, object, bool)"/>
/// on a target object.
///
/// When a binding is initiated, it can return one of 3 possible sources for the binding:
/// - An <see cref="ISubject{Object}"/> which can be used for any type of binding.
/// - An <see cref="IObservable{Object}"/> which can be used for all types of bindings except
/// <see cref="BindingMode.OneWayToSource"/> and <see cref="BindingMode.TwoWay"/>.
/// - A plain object, which can only represent a <see cref="BindingMode.OneTime"/> binding.
/// </remarks>
public class InstancedBinding
{
/// <summary>
/// Initializes a new instance of the <see cref="InstancedBinding"/> class.
/// </summary>
/// <param name="value">
/// The value used for the <see cref="BindingMode.OneTime"/> binding.
/// </param>
/// <param name="priority">The binding priority.</param>
public InstancedBinding(object value,
BindingPriority priority = BindingPriority.LocalValue)
{
Mode = BindingMode.OneTime;
Priority = priority;
Value = value;
}
/// <summary>
/// Initializes a new instance of the <see cref="InstancedBinding"/> class.
/// </summary>
/// <param name="observable">The observable for a one-way binding.</param>
/// <param name="subject">The binding source.</param>
/// <param name="mode">The binding mode.</param>
/// <param name="priority">The binding priority.</param>
public InstancedBinding(
IObservable<object> observable,
BindingMode mode = BindingMode.OneWay,
BindingPriority priority = BindingPriority.LocalValue)
/// <param name="priority">The priority of the binding.</param>
/// <remarks>
/// This constructor can be used to create any type of binding and as such requires an
/// <see cref="ISubject{Object}"/> as the binding source because this is the only binding
/// source which can be used for all binding modes. If you wish to create an instance with
/// something other than a subject, use one of the static creation methods on this class.
/// </remarks>
public InstancedBinding(ISubject<object> subject, BindingMode mode, BindingPriority priority)
{
Contract.Requires<ArgumentNullException>(observable != null);
if (mode == BindingMode.OneWayToSource || mode == BindingMode.TwoWay)
{
throw new ArgumentException(
"Invalid BindingResult mode: OneWayToSource and TwoWay bindings" +
"require a Subject.");
}
Contract.Requires<ArgumentNullException>(subject != null);
Mode = mode;
Priority = priority;
Observable = observable;
Value = subject;
}
/// <summary>
/// Initializes a new instance of the <see cref="InstancedBinding"/> class.
/// </summary>
/// <param name="subject">The subject for a two-way binding.</param>
/// <param name="mode">The binding mode.</param>
/// <param name="priority">The binding priority.</param>
public InstancedBinding(
ISubject<object> subject,
BindingMode mode = BindingMode.OneWay,
BindingPriority priority = BindingPriority.LocalValue)
private InstancedBinding(object value, BindingMode mode, BindingPriority priority)
{
Contract.Requires<ArgumentNullException>(subject != null);
Mode = mode;
Priority = priority;
Subject = subject;
Value = value;
}
/// <summary>
@ -92,18 +56,101 @@ namespace Avalonia.Data
public BindingPriority Priority { get; }
/// <summary>
/// Gets the value used for a <see cref="BindingMode.OneTime"/> binding.
/// Gets the value or source of the binding.
/// </summary>
public object Value { get; }
/// <summary>
/// Gets the observable for a one-way binding.
/// Gets the <see cref="Value"/> as an observable.
/// </summary>
public IObservable<object> Observable { get; }
public IObservable<object> Observable => Value as IObservable<object>;
/// <summary>
/// Gets the subject for a two-way binding.
/// Gets the <see cref="Value"/> as a subject.
/// </summary>
public ISubject<object> Subject { get; }
public ISubject<object> Subject => Value as ISubject<object>;
/// <summary>
/// Creates a new one-time binding with a fixed value.
/// </summary>
/// <param name="value">The value.</param>
/// <param name="priority">The priority of the binding.</param>
/// <returns>An <see cref="InstancedBinding"/> instance.</returns>
public static InstancedBinding OneTime(
object value,
BindingPriority priority = BindingPriority.LocalValue)
{
return new InstancedBinding(value, BindingMode.OneTime, priority);
}
/// <summary>
/// Creates a new one-time binding.
/// </summary>
/// <param name="observable">The source observable.</param>
/// <param name="priority">The priority of the binding.</param>
/// <returns>An <see cref="InstancedBinding"/> instance.</returns>
public static InstancedBinding OneTime(
IObservable<object> observable,
BindingPriority priority = BindingPriority.LocalValue)
{
Contract.Requires<ArgumentNullException>(observable != null);
return new InstancedBinding(observable, BindingMode.OneTime, priority);
}
/// <summary>
/// Creates a new one-way binding.
/// </summary>
/// <param name="observable">The source observable.</param>
/// <param name="priority">The priority of the binding.</param>
/// <returns>An <see cref="InstancedBinding"/> instance.</returns>
public static InstancedBinding OneWay(
IObservable<object> observable,
BindingPriority priority = BindingPriority.LocalValue)
{
Contract.Requires<ArgumentNullException>(observable != null);
return new InstancedBinding(observable, BindingMode.OneWay, priority);
}
/// <summary>
/// Creates a new one-way to source binding.
/// </summary>
/// <param name="subject">The binding source.</param>
/// <param name="priority">The priority of the binding.</param>
/// <returns>An <see cref="InstancedBinding"/> instance.</returns>
public static InstancedBinding OneWayToSource(
ISubject<object> subject,
BindingPriority priority = BindingPriority.LocalValue)
{
Contract.Requires<ArgumentNullException>(subject != null);
return new InstancedBinding(subject, BindingMode.OneWayToSource, priority);
}
/// <summary>
/// Creates a new two-way binding.
/// </summary>
/// <param name="subject">The binding source.</param>
/// <param name="priority">The priority of the binding.</param>
/// <returns>An <see cref="InstancedBinding"/> instance.</returns>
public static InstancedBinding TwoWay(
ISubject<object> subject,
BindingPriority priority = BindingPriority.LocalValue)
{
Contract.Requires<ArgumentNullException>(subject != null);
return new InstancedBinding(subject, BindingMode.TwoWay, priority);
}
/// <summary>
/// Creates a copy of the <see cref="InstancedBinding"/> with a different priority.
/// </summary>
/// <param name="priority">The priority of the binding.</param>
/// <returns>An <see cref="InstancedBinding"/> instance.</returns>
public InstancedBinding WithPriority(BindingPriority priority)
{
return new InstancedBinding(Value, Mode, priority);
}
}
}

4
src/Avalonia.Controls/Control.cs

@ -774,7 +774,9 @@ namespace Avalonia.Controls
foreach (var child in control.LogicalChildren)
{
if (child is Control c && !c.IsSet(DataContextProperty))
if (child is Control c &&
c.InheritanceParent == control &&
!c.IsSet(DataContextProperty))
{
DataContextNotifying(c, notifying);
}

2
src/Avalonia.Controls/Templates/FuncTreeDataTemplate.cs

@ -62,7 +62,7 @@ namespace Avalonia.Controls.Templates
/// <returns>The child items, or null if no child items.</returns>
public InstancedBinding ItemsSelector(object item)
{
return new InstancedBinding(this?._itemsSelector(item));
return InstancedBinding.OneTime(this?._itemsSelector(item));
}
/// <summary>

3
src/Avalonia.Remote.Protocol/BsonStreamTransport.cs

@ -64,7 +64,6 @@ namespace Avalonia.Remote.Protocol
async Task Reader()
{
Task.Yield();
try
{
while (true)
@ -147,4 +146,4 @@ namespace Avalonia.Remote.Protocol
public event Action<IAvaloniaRemoteTransportConnection, object> OnMessage;
public event Action<IAvaloniaRemoteTransportConnection, Exception> OnException;
}
}
}

60
src/Avalonia.Styling/Styling/Setter.cs

@ -135,45 +135,47 @@ namespace Avalonia.Styling
private InstancedBinding Clone(InstancedBinding sourceInstance, IStyle style, IObservable<bool> activator)
{
InstancedBinding cloned;
if (activator != null)
{
var description = style?.ToString();
if (sourceInstance.Mode == BindingMode.TwoWay || sourceInstance.Mode == BindingMode.OneWayToSource)
{
var activated = new ActivatedSubject(activator, sourceInstance.Subject, description);
cloned = new InstancedBinding(activated, sourceInstance.Mode, BindingPriority.StyleTrigger);
}
else if (sourceInstance.Mode == BindingMode.OneTime)
{
var activated = new ActivatedValue(activator, sourceInstance.Value, description);
cloned = new InstancedBinding(activated, BindingMode.OneWay, BindingPriority.StyleTrigger);
}
else
switch (sourceInstance.Mode)
{
var activated = new ActivatedObservable(activator, sourceInstance.Observable ?? sourceInstance.Subject, description);
cloned = new InstancedBinding(activated, sourceInstance.Mode, BindingPriority.StyleTrigger);
case BindingMode.OneTime:
if (sourceInstance.Observable != null)
{
var activated = new ActivatedObservable(activator, sourceInstance.Observable, description);
return InstancedBinding.OneTime(activated, BindingPriority.StyleTrigger);
}
else
{
var activated = new ActivatedValue(activator, sourceInstance.Value, description);
return InstancedBinding.OneTime(activated, BindingPriority.StyleTrigger);
}
case BindingMode.OneWay:
{
var activated = new ActivatedObservable(activator, sourceInstance.Observable, description);
return InstancedBinding.OneWay(activated, BindingPriority.StyleTrigger);
}
case BindingMode.OneWayToSource:
{
var activated = new ActivatedSubject(activator, sourceInstance.Subject, description);
return InstancedBinding.OneWayToSource(activated, BindingPriority.StyleTrigger);
}
case BindingMode.TwoWay:
{
var activated = new ActivatedSubject(activator, sourceInstance.Subject, description);
return InstancedBinding.TwoWay(activated, BindingPriority.StyleTrigger);
}
default:
throw new NotSupportedException("Unsupported BindingMode.");
}
}
else
{
if (sourceInstance.Subject != null)
{
cloned = new InstancedBinding(sourceInstance.Subject, sourceInstance.Mode, BindingPriority.Style);
}
else if (sourceInstance.Observable != null)
{
cloned = new InstancedBinding(sourceInstance.Observable, sourceInstance.Mode, BindingPriority.Style);
}
else
{
cloned = new InstancedBinding(sourceInstance.Value, BindingPriority.Style);
}
return sourceInstance.WithPriority(BindingPriority.StyleTrigger);
}
return cloned;
}
}
}

3
src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj

@ -39,7 +39,6 @@
<Compile Include="MarkupExtensions\StaticResourceExtension.cs" />
<Compile Include="MarkupExtensions\StyleIncludeExtension.cs" />
<Compile Include="PortableXaml\AvaloniaXamlContext.cs" />
<Compile Include="PortableXaml\XamlBinding.cs" />
<Compile Include="PortableXaml\AttributeExtensions.cs" />
<Compile Include="PortableXaml\AvaloniaMemberAttributeProvider.cs" />
<Compile Include="PortableXaml\AvaloniaNameScope.cs" />
@ -73,8 +72,6 @@
<Compile Include="Data\DelayedBinding.cs" />
<Compile Include="Data\MultiBinding.cs" />
<Compile Include="Data\RelativeSource.cs" />
<Compile Include="Data\StyleResourceBinding.cs" />
<Compile Include="MarkupExtensions\StyleResourceExtension.cs" />
<Compile Include="MarkupExtensions\BindingExtension.cs" />
<Compile Include="MarkupExtensions\RelativeSourceExtension.cs" />
<Compile Include="MarkupExtensions\TemplateBindingExtension.cs" />

3
src/Markup/Avalonia.Markup.Xaml/Data/Binding.cs

@ -83,6 +83,8 @@ namespace Avalonia.Markup.Xaml.Data
/// </summary>
public object Source { get; set; }
internal WeakReference DefaultAnchor { get; set; }
/// <inheritdoc/>
public InstancedBinding Initiate(
IAvaloniaObject target,
@ -91,6 +93,7 @@ namespace Avalonia.Markup.Xaml.Data
bool enableDataValidation = false)
{
Contract.Requires<ArgumentNullException>(target != null);
anchor = anchor ?? DefaultAnchor?.Target;
enableDataValidation = enableDataValidation && Priority == BindingPriority.LocalValue;

35
src/Markup/Avalonia.Markup.Xaml/Data/MultiBinding.cs

@ -62,41 +62,20 @@ namespace Avalonia.Markup.Xaml.Data
}
var targetType = targetProperty?.PropertyType ?? typeof(object);
var result = new BehaviorSubject<object>(AvaloniaProperty.UnsetValue);
var children = Bindings.Select(x => x.Initiate(target, null));
var input = children.Select(x => x.Subject).CombineLatest().Select(x => ConvertValue(x, targetType));
input.Subscribe(result);
return new InstancedBinding(result, Mode, Priority);
}
/// <summary>
/// Applies a binding subject to a property on an instance.
/// </summary>
/// <param name="target">The target instance.</param>
/// <param name="property">The target property.</param>
/// <param name="subject">The binding subject.</param>
internal void Bind(IAvaloniaObject target, AvaloniaProperty property, ISubject<object> subject)
{
var mode = Mode == BindingMode.Default ?
property.GetMetadata(target.GetType()).DefaultBindingMode : Mode;
targetProperty.GetMetadata(target.GetType()).DefaultBindingMode : Mode;
switch (mode)
{
case BindingMode.Default:
case BindingMode.OneWay:
target.Bind(property, subject, Priority);
break;
case BindingMode.TwoWay:
throw new NotSupportedException("TwoWay MultiBinding not currently supported.");
case BindingMode.OneTime:
target.GetObservable(Control.DataContextProperty).Subscribe(dataContext =>
{
subject.Take(1).Subscribe(x => target.SetValue(property, x, Priority));
});
break;
case BindingMode.OneWayToSource:
target.GetObservable(property).Subscribe(subject);
break;
return InstancedBinding.OneTime(input, Priority);
case BindingMode.OneWay:
return InstancedBinding.OneWay(input, Priority);
default:
throw new NotSupportedException(
"MultiBinding currently only supports OneTime and OneWay BindingMode.");
}
}

69
src/Markup/Avalonia.Markup.Xaml/Data/StyleResourceBinding.cs

@ -1,69 +0,0 @@
// Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
using System.Reactive.Disposables;
using System.Reactive.Linq;
using System.Reactive.Subjects;
using Avalonia.Controls;
using Avalonia.Data;
using Avalonia.Styling;
namespace Avalonia.Markup.Xaml.Data
{
public class StyleResourceBinding : IBinding
{
/// <summary>
/// Initializes a new instance of the <see cref="StyleResourceBinding"/> class.
/// </summary>
/// <param name="name">The resource name.</param>
public StyleResourceBinding(string name)
{
Name = name;
}
/// <inheritdoc/>
public BindingMode Mode => BindingMode.OneTime;
/// <summary>
/// Gets the resource name.
/// </summary>
public string Name { get; }
/// <inheritdoc/>
public BindingPriority Priority => BindingPriority.LocalValue;
/// <inheritdoc/>
public InstancedBinding Initiate(
IAvaloniaObject target,
AvaloniaProperty targetProperty,
object anchor = null,
bool enableDataValidation = false)
{
var host = (target as IControl) ?? (anchor as IControl);
var style = anchor as IStyle;
var resource = AvaloniaProperty.UnsetValue;
if (host != null)
{
resource = host.FindResource(Name);
}
else if (style != null)
{
if (!style.TryGetResource(Name, out resource))
{
resource = AvaloniaProperty.UnsetValue;
}
}
if (resource != AvaloniaProperty.UnsetValue)
{
return new InstancedBinding(resource, Priority);
}
else
{
return null;
}
}
}
}

25
src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/BindingExtension.cs

@ -34,19 +34,18 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions
var pathInfo = ParsePath(Path, descriptorContext);
ValidateState(pathInfo);
return XamlBinding.FromMarkupExtensionContext(
new Binding
{
Converter = Converter,
ConverterParameter = ConverterParameter,
ElementName = pathInfo.ElementName ?? ElementName,
FallbackValue = FallbackValue,
Mode = Mode,
Path = pathInfo.Path,
Priority = Priority,
RelativeSource = pathInfo.RelativeSource ?? RelativeSource,
},
serviceProvider);
return new Binding
{
Converter = Converter,
ConverterParameter = ConverterParameter,
ElementName = pathInfo.ElementName ?? ElementName,
FallbackValue = FallbackValue,
Mode = Mode,
Path = pathInfo.Path,
Priority = Priority,
RelativeSource = pathInfo.RelativeSource ?? RelativeSource,
DefaultAnchor = new WeakReference(GetDefaultAnchor((ITypeDescriptorContext)serviceProvider))
};
}
private class PathInfo

2
src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/DynamicResourceExtension.cs

@ -51,7 +51,7 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions
if (control != null)
{
return new InstancedBinding(control.GetResourceObservable(ResourceKey));
return InstancedBinding.OneWay(control.GetResourceObservable(ResourceKey));
}
return null;

31
src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/StyleResourceExtension.cs

@ -1,31 +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;
using Avalonia.Markup.Xaml.Data;
using System;
namespace Avalonia.Markup.Xaml.MarkupExtensions
{
using Portable.Xaml.Markup;
using PortableXaml;
[MarkupExtensionReturnType(typeof(IBinding))]
public class StyleResourceExtension : MarkupExtension
{
public StyleResourceExtension(string name)
{
Name = name;
}
public override object ProvideValue(IServiceProvider serviceProvider)
{
return XamlBinding.FromMarkupExtensionContext(
new StyleResourceBinding(Name),
serviceProvider);
}
[ConstructorArgument("name")]
public string Name { get; set; }
}
}

12
src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaXamlType.cs

@ -212,11 +212,6 @@ namespace Avalonia.Markup.Xaml.PortableXaml
value);
}
if (value is XamlBinding)
{
value = (value as XamlBinding).Value;
}
if (UpdateListInsteadSet &&
value != null &&
UpdateListInsteadSetValue(instance, value))
@ -317,9 +312,7 @@ namespace Avalonia.Markup.Xaml.PortableXaml
if (!Member.AssignBinding)
ApplyBinding(obj, (IBinding)value);
else
obj.SetValue(Property, value is XamlBinding ?
(value as XamlBinding).Value :
value);
obj.SetValue(Property, value);
}
else
{
@ -348,12 +341,9 @@ namespace Avalonia.Markup.Xaml.PortableXaml
{
var control = obj as IControl;
var property = Property;
var xamlBinding = binding as XamlBinding;
if (control != null && property != Control.DataContextProperty)
DelayedBinding.Add(control, property, binding);
else if (xamlBinding != null)
obj.Bind(property, xamlBinding.Value, xamlBinding.Anchor?.Target);
else
obj.Bind(property, binding);
}

63
src/Markup/Avalonia.Markup.Xaml/PortableXaml/XamlBinding.cs

@ -1,63 +0,0 @@
using Avalonia.Controls;
using Avalonia.Data;
using Avalonia.Styling;
using Portable.Xaml;
using Portable.Xaml.ComponentModel;
using System.ComponentModel;
using Portable.Xaml.Markup;
using System;
namespace Avalonia.Markup.Xaml.PortableXaml
{
internal class XamlBinding : IBinding
{
public static IBinding FromMarkupExtensionContext(
IBinding binding,
IServiceProvider serviceProvider)
{
var context = (ITypeDescriptorContext)serviceProvider;
var pvt = context.GetService<IProvideValueTarget>();
if (pvt.TargetObject is IControl) return binding;
object anchor = GetDefaultAnchor(context);
if (anchor == null) return binding;
return new XamlBinding(binding, anchor);
}
private static object GetDefaultAnchor(ITypeDescriptorContext context)
{
object anchor = null;
// The target is not a control, so we need to find an anchor that will let us look
// up named controls and style resources. First look for the closest IControl in
// the context.
anchor = context.GetFirstAmbientValue<IControl>();
// If a control was not found, then try to find the highest-level style as the XAML
// file could be a XAML file containing only styles.
return anchor ??
context.GetService<IRootObjectProvider>()?.RootObject as IStyle ??
context.GetLastOrDefaultAmbientValue<IStyle>();
}
private XamlBinding(IBinding binding, object anchor)
{
Value = binding;
Anchor = new WeakReference(anchor);
}
public WeakReference Anchor { get; }
public IBinding Value { get; }
public InstancedBinding Initiate(IAvaloniaObject target, AvaloniaProperty targetProperty, object anchor = null, bool enableDataValidation = false)
{
return Value.Initiate(target, targetProperty,
anchor ?? Anchor.Target, enableDataValidation);
}
}
}

2
src/Markup/Avalonia.Markup.Xaml/Templates/TreeDataTemplate.cs

@ -42,7 +42,7 @@ namespace Avalonia.Markup.Xaml.Templates
if (ItemsSource != null)
{
var obs = new ExpressionObserver(item, ItemsSource.Path);
return new InstancedBinding(obs, BindingMode.OneWay, BindingPriority.Style);
return InstancedBinding.OneWay(obs, BindingPriority.Style);
}
return null;

2
tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Binding.cs

@ -428,7 +428,7 @@ namespace Avalonia.Base.UnitTests
object anchor = null,
bool enableDataValidation = false)
{
return new InstancedBinding(_source, BindingMode.OneTime);
return InstancedBinding.OneTime(_source);
}
}
}

44
tests/Avalonia.Controls.UnitTests/Primitives/PopupTests.cs

@ -249,6 +249,37 @@ namespace Avalonia.Controls.UnitTests.Primitives
}
}
[Fact]
public void DataContextBeginUpdate_Should_Not_Be_Called_For_Controls_That_Dont_Inherit()
{
using (CreateServices())
{
TestControl child;
var popup = new Popup
{
Child = child = new TestControl(),
DataContext = "foo",
};
var beginCalled = false;
child.DataContextBeginUpdate += (s, e) => beginCalled = true;
// Test for #1245. Here, the child's logical parent is the popup but it's not yet
// attached to a visual tree because the popup hasn't been opened.
Assert.Same(popup, ((ILogical)child).LogicalParent);
Assert.Same(popup, child.InheritanceParent);
Assert.Null(child.GetVisualRoot());
popup.Open();
// #1245 was caused by the fact that DataContextBeginUpdate was called on `target`
// when the PopupRoot was created, even though PopupRoot isn't the
// InheritanceParent of child.
Assert.False(beginCalled);
}
}
private static IDisposable CreateServices()
{
var result = AvaloniaLocator.EnterScope();
@ -304,5 +335,18 @@ namespace Avalonia.Controls.UnitTests.Primitives
private class PopupContentControl : ContentControl
{
}
private class TestControl : Decorator
{
public event EventHandler DataContextBeginUpdate;
public new IAvaloniaObject InheritanceParent => base.InheritanceParent;
protected override void OnDataContextBeginUpdate()
{
DataContextBeginUpdate?.Invoke(this, EventArgs.Empty);
base.OnDataContextBeginUpdate();
}
}
}
}

2
tests/Avalonia.Controls.UnitTests/TreeViewTests.cs

@ -515,7 +515,7 @@ namespace Avalonia.Controls.UnitTests
public InstancedBinding ItemsSelector(object item)
{
var obs = new ExpressionObserver(item, nameof(Node.Children));
return new InstancedBinding(obs);
return InstancedBinding.OneWay(obs);
}
public bool Match(object data)

4
tests/Avalonia.Markup.Xaml.UnitTests/Data/MultiBindingTests.cs

@ -34,8 +34,8 @@ namespace Avalonia.Markup.Xaml.UnitTests.Data
var target = new Mock<IAvaloniaObject>().As<IControl>();
target.Setup(x => x.GetValue(Control.DataContextProperty)).Returns(source);
var subject = binding.Initiate(target.Object, null).Subject;
var result = await subject.Take(1);
var observable = binding.Initiate(target.Object, null).Observable;
var result = await observable.Take(1);
Assert.Equal("1,2,3", result);
}

19
tests/Avalonia.Markup.Xaml.UnitTests/Xaml/BasicTests.cs

@ -411,8 +411,8 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml
var xaml = @"
<Styles xmlns='https://github.com/avaloniaui'>
<Style Selector='CheckBox'>
<Setter Property='BorderBrush' Value='{StyleResource ThemeBorderMidBrush}'/>
<Setter Property='BorderThickness' Value='{StyleResource ThemeBorderThickness}'/>
<Setter Property='BorderBrush' Value='{DynamicResource ThemeBorderMidBrush}'/>
<Setter Property='BorderThickness' Value='{DynamicResource ThemeBorderThickness}'/>
<Setter Property='Template'>
<ControlTemplate>
<Grid ColumnDefinitions='Auto,*'>
@ -423,7 +423,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml
Height='18'
VerticalAlignment='Center'>
<Path Name='checkMark'
Fill='{StyleResource HighlightBrush}'
Fill='{StaticResource HighlightBrush}'
Width='11'
Height='10'
Stretch='Uniform'
@ -457,8 +457,6 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml
Assert.Equal(CheckBox.BorderThicknessProperty, setters[1].Property);
Assert.Equal(CheckBox.TemplateProperty, setters[2].Property);
Assert.IsType<StyleResourceBinding>(setters[0].Value);
Assert.IsType<StyleResourceBinding>(setters[1].Value);
Assert.IsType<ControlTemplate>(setters[2].Value);
}
}
@ -772,15 +770,8 @@ do we need it?")]
<Window xmlns='https://github.com/avaloniaui'
xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'
xmlns:local='clr-namespace:Avalonia.Markup.Xaml.UnitTests.Xaml;assembly=Avalonia.Markup.Xaml.UnitTests'>
<Window.Styles>
<Style>
<Style.Resources>
<x:Double x:Key='Double'>100</x:Double>
</Style.Resources>
</Style>
</Window.Styles>
<local:InitializationOrderTracker Width='100' Height='{StyleResource Double}'
Tag='{Binding Height, RelativeSource={RelativeSource Self}}' />
<local:InitializationOrderTracker Width='100' Height='100'
Tag='{Binding Height, RelativeSource={RelativeSource Self}}' />
</Window>";

165
tests/Avalonia.Markup.Xaml.UnitTests/Xaml/StyleTests.cs

@ -61,171 +61,6 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml
}
}
[Fact]
public void StyleResource_Can_Be_Assigned_To_Property()
{
var xaml = @"
<UserControl xmlns='https://github.com/avaloniaui'
xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
<UserControl.Styles>
<Style>
<Style.Resources>
<SolidColorBrush x:Key='brush'>#ff506070</SolidColorBrush>
</Style.Resources>
</Style>
</UserControl.Styles>
<Border Name='border' Background='{StyleResource brush}'/>
</UserControl>";
var loader = new AvaloniaXamlLoader();
var userControl = (UserControl)loader.Load(xaml);
var border = userControl.FindControl<Border>("border");
DelayedBinding.ApplyBindings(border);
var brush = (SolidColorBrush)border.Background;
Assert.Equal(0xff506070, brush.Color.ToUint32());
}
[Fact]
public void StyleResource_Can_Be_Assigned_To_Setter()
{
//skip default theme and styles, they are not needed
using (UnitTestApplication.Start(TestServices.StyledWindow
.With(theme: () => new Styles())))
{
var xaml = @"
<Window xmlns='https://github.com/avaloniaui'
xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
<Window.Styles>
<Style>
<Style.Resources>
<SolidColorBrush x:Key='brush'>#ff506070</SolidColorBrush>
</Style.Resources>
</Style>
<Style Selector='Button'>
<Setter Property='Background' Value='{StyleResource brush}'/>
</Style>
</Window.Styles>
<Button Name='button'/>
</Window>";
var loader = new AvaloniaXamlLoader();
var window = (Window)loader.Load(xaml);
var button = window.FindControl<Button>("button");
var brush = (SolidColorBrush)button.Background;
Assert.Equal(0xff506070, brush.Color.ToUint32());
}
}
[Fact]
public void StyleResource_Can_Be_Assigned_To_StyleResource_Property()
{
using (UnitTestApplication.Start(TestServices.StyledWindow))
{
var xaml = @"
<Window xmlns='https://github.com/avaloniaui'
xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
<Window.Styles>
<Style>
<Style.Resources>
<Color x:Key='color'>#ff506070</Color>
<SolidColorBrush x:Key='brush' Color='{StyleResource color}'/>
</Style.Resources>
</Style>
</Window.Styles>
<Button Name='button' Background='{StyleResource brush}'/>
</Window>";
var loader = new AvaloniaXamlLoader();
var window = (Window)loader.Load(xaml);
var brush = (ISolidColorBrush)window.FindResource("brush");
var button = window.FindControl<Button>("button");
DelayedBinding.ApplyBindings(button);
var buttonBrush = (ISolidColorBrush)button.Background;
Assert.Equal(0xff506070, brush.Color.ToUint32());
Assert.Equal(0xff506070, buttonBrush.Color.ToUint32());
}
}
[Fact]
public void StyleResource_Can_Be_Found_In_TopLevel_Styles()
{
var xaml = @"
<Styles xmlns='https://github.com/avaloniaui'
xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
<Style>
<Style.Resources>
<Color x:Key='color'>#ff506070</Color>
<SolidColorBrush x:Key='brush' Color='{StyleResource color}'/>
</Style.Resources>
</Style>
</Styles>";
var loader = new AvaloniaXamlLoader();
var styles = (Styles)loader.Load(xaml);
styles.TryGetResource("brush", out var brush);
Assert.Equal(0xff506070, ((SolidColorBrush)brush).Color.ToUint32());
}
[Fact]
public void StyleResource_Can_Be_Found_In_Sibling_Styles()
{
var xaml = @"
<Styles xmlns='https://github.com/avaloniaui'
xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
<Style>
<Style.Resources>
<Color x:Key='color'>#ff506070</Color>
</Style.Resources>
</Style>
<Style>
<Style.Resources>
<SolidColorBrush x:Key='brush' Color='{StyleResource color}'/>
</Style.Resources>
</Style>
</Styles>";
var loader = new AvaloniaXamlLoader();
var styles = (Styles)loader.Load(xaml);
styles.TryGetResource("brush", out var brush);
Assert.Equal(0xff506070, ((SolidColorBrush)brush).Color.ToUint32());
}
[Fact(Skip = "TODO: Issue #492")]
public void StyleResource_Can_Be_Found_Across_Xaml_Files()
{
using (UnitTestApplication.Start(TestServices.StyledWindow))
{
var xaml = @"
<Window xmlns='https://github.com/avaloniaui'
xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
<Window.Styles>
<StyleInclude Source='resm:Avalonia.Markup.Xaml.UnitTests.Xaml.Style1.xaml?assembly=Avalonia.Markup.Xaml.UnitTests'/>
<StyleInclude Source='resm:Avalonia.Markup.Xaml.UnitTests.Xaml.Style2.xaml?assembly=Avalonia.Markup.Xaml.UnitTests'/>
</Window.Styles>
<Border Name='border' Background='{StyleResource RedBrush}'/>
</Window>";
var loader = new AvaloniaXamlLoader();
var window = (Window)loader.Load(xaml);
var border = window.FindControl<Border>("border");
var borderBrush = (ISolidColorBrush)border.Background;
Assert.NotNull(borderBrush);
Assert.Equal(0xffff0000, borderBrush.Color.ToUint32());
}
}
[Fact]
public void StyleInclude_Is_Built()
{

2
tests/Avalonia.Styling.UnitTests/SetterTests.cs

@ -29,7 +29,7 @@ namespace Avalonia.Styling.UnitTests
{
var control = new TextBlock();
var subject = new BehaviorSubject<object>("foo");
var descriptor = new InstancedBinding(subject);
var descriptor = InstancedBinding.OneWay(subject);
var binding = Mock.Of<IBinding>(x => x.Initiate(control, TextBlock.TextProperty, null, false) == descriptor);
var style = Mock.Of<IStyle>();
var setter = new Setter(TextBlock.TextProperty, binding);

Loading…
Cancel
Save