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.Collections.Generic;
using System.Reflection;
using System.Runtime.CompilerServices;
using Avalonia.Controls;
using Avalonia.Data;
using Avalonia.Logging;
namespace Avalonia.Markup.Xaml.Data
{
@ -50,7 +52,36 @@ namespace Avalonia.Markup.Xaml.Data
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>
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);
@ -80,9 +111,14 @@ namespace Avalonia.Markup.Xaml.Data
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;
Property = property;
@ -90,6 +126,41 @@ namespace Avalonia.Markup.Xaml.Data
public IBinding Binding { 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.ComponentModel;
using System.Reflection;
using Avalonia.Controls;
using Avalonia.Markup.Xaml.Data;
using Avalonia.Styling;
using Portable.Xaml;
using Portable.Xaml.ComponentModel;
@ -32,6 +34,8 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions
var resourceProviderType = schemaContext.GetXamlType(typeof(IResourceProvider));
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)
{
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;
}
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]
public void StaticResource_Can_Be_Assigned_To_Setter()
{

Loading…
Cancel
Save