Browse Source

Delay load static resources if necessary.

pull/1136/head
Steven Kirk 9 years ago
parent
commit
0b0667f5ee
  1. 85
      src/Markup/Avalonia.Markup.Xaml/Data/DelayedBinding.cs
  2. 18
      src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/StaticResourceExtension.cs
  3. 27
      tests/Avalonia.Markup.Xaml.UnitTests/Xaml/StaticResourceTests.cs

85
src/Markup/Avalonia.Markup.Xaml/Data/DelayedBinding.cs

@ -3,9 +3,11 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Reflection;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Data; using Avalonia.Data;
using Avalonia.Logging;
namespace Avalonia.Markup.Xaml.Data namespace Avalonia.Markup.Xaml.Data
{ {
@ -50,7 +52,36 @@ namespace Avalonia.Markup.Xaml.Data
target.Initialized += ApplyBindings; target.Initialized += ApplyBindings;
} }
bindings.Add(new Entry(binding, property)); bindings.Add(new BindingEntry(property, binding));
}
}
/// <summary>
/// Adds a delayed binding to a control.
/// </summary>
/// <param name="target">The control.</param>
/// <param name="property">The property on the control to bind to.</param>
/// <param name="binding">The binding.</param>
public static void Add(IControl target, PropertyInfo property, Func<IControl, object> value)
{
if (target.IsInitialized)
{
property.SetValue(target, value(target));
}
else
{
List<Entry> bindings;
if (!_entries.TryGetValue(target, out bindings))
{
bindings = new List<Entry>();
_entries.Add(target, bindings);
// TODO: Make this a weak event listener.
target.Initialized += ApplyBindings;
}
bindings.Add(new ClrPropertyValueEntry(property, value));
} }
} }
@ -60,13 +91,13 @@ namespace Avalonia.Markup.Xaml.Data
/// <param name="control">The control.</param> /// <param name="control">The control.</param>
public static void ApplyBindings(IControl control) public static void ApplyBindings(IControl control)
{ {
List<Entry> bindings; List<Entry> entries;
if (_entries.TryGetValue(control, out bindings)) if (_entries.TryGetValue(control, out entries))
{ {
foreach (var binding in bindings) foreach (var entry in entries)
{ {
control.Bind(binding.Property, binding.Binding); entry.Apply(control);
} }
_entries.Remove(control); _entries.Remove(control);
@ -80,9 +111,14 @@ namespace Avalonia.Markup.Xaml.Data
target.Initialized -= ApplyBindings; target.Initialized -= ApplyBindings;
} }
private class Entry private abstract class Entry
{
public abstract void Apply(IControl control);
}
private class BindingEntry : Entry
{ {
public Entry(IBinding binding, AvaloniaProperty property) public BindingEntry(AvaloniaProperty property, IBinding binding)
{ {
Binding = binding; Binding = binding;
Property = property; Property = property;
@ -90,6 +126,41 @@ namespace Avalonia.Markup.Xaml.Data
public IBinding Binding { get; } public IBinding Binding { get; }
public AvaloniaProperty Property { get; } public AvaloniaProperty Property { get; }
public override void Apply(IControl control)
{
control.Bind(Property, Binding);
}
}
private class ClrPropertyValueEntry : Entry
{
public ClrPropertyValueEntry(PropertyInfo property, Func<IControl, object> value)
{
Property = property;
Value = value;
}
public PropertyInfo Property { get; }
public Func<IControl, object> Value { get; }
public override void Apply(IControl control)
{
try
{
Property.SetValue(control, Value(control));
}
catch (Exception e)
{
Logger.Error(
LogArea.Property,
control,
"Error setting {Property} on {Target}: {Exception}",
Property.Name,
control,
e);
}
}
} }
} }
} }

18
src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/StaticResourceExtension.cs

@ -3,7 +3,9 @@
using System; using System;
using System.ComponentModel; using System.ComponentModel;
using System.Reflection;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Markup.Xaml.Data;
using Avalonia.Styling; using Avalonia.Styling;
using Portable.Xaml; using Portable.Xaml;
using Portable.Xaml.ComponentModel; using Portable.Xaml.ComponentModel;
@ -32,6 +34,8 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions
var resourceProviderType = schemaContext.GetXamlType(typeof(IResourceProvider)); var resourceProviderType = schemaContext.GetXamlType(typeof(IResourceProvider));
var resourceProviders = ambientProvider.GetAllAmbientValues(resourceProviderType); var resourceProviders = ambientProvider.GetAllAmbientValues(resourceProviderType);
// Look up the ambient context for IResourceProviders which might be able to give us
// the resource.
foreach (IResourceProvider resourceProvider in resourceProviders) foreach (IResourceProvider resourceProvider in resourceProviders)
{ {
if (resourceProvider is IControl control && control.StylingParent != null) if (resourceProvider is IControl control && control.StylingParent != null)
@ -47,7 +51,21 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions
} }
} }
// The resource still hasn't been found, so add a delayed one-time binding.
var provideTarget = context.GetService<IProvideValueTarget>();
if (provideTarget.TargetObject is IControl target &&
provideTarget.TargetProperty is PropertyInfo property)
{
DelayedBinding.Add(target, property, GetValue);
}
return AvaloniaProperty.UnsetValue; return AvaloniaProperty.UnsetValue;
} }
private object GetValue(IControl control)
{
return control.FindResource(ResourceKey);
}
} }
} }

27
tests/Avalonia.Markup.Xaml.UnitTests/Xaml/StaticResourceTests.cs

@ -81,6 +81,33 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml
} }
} }
[Fact]
public void StaticResource_From_Application_Can_Be_Assigned_To_Property_In_UserControl()
{
using (UnitTestApplication.Start(TestServices.StyledWindow))
{
Application.Current.Resources.Add("brush", new SolidColorBrush(0xff506070));
var xaml = @"
<UserControl xmlns='https://github.com/avaloniaui'
xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
<Border Name='border' Background='{StaticResource brush}'/>
</UserControl>";
var loader = new AvaloniaXamlLoader();
var userControl = (UserControl)loader.Load(xaml);
var border = userControl.FindControl<Border>("border");
// We don't actually know where the global styles are until we attach the control
// to a window, as Window has StylingParent set to Application.
var window = new Window { Content = userControl };
window.Show();
var brush = (SolidColorBrush)border.Background;
Assert.Equal(0xff506070, brush.Color.ToUint32());
}
}
[Fact] [Fact]
public void StaticResource_Can_Be_Assigned_To_Setter() public void StaticResource_Can_Be_Assigned_To_Setter()
{ {

Loading…
Cancel
Save