Browse Source

Added ResourceInclude.

pull/1136/head
Steven Kirk 9 years ago
parent
commit
a72ce3671d
  1. 4
      src/Avalonia.Controls/Application.cs
  2. 4
      src/Avalonia.Controls/Control.cs
  3. 21
      src/Avalonia.Styling/Controls/IResourceDictionary.cs
  4. 26
      src/Avalonia.Styling/Controls/IResourceNode.cs
  5. 32
      src/Avalonia.Styling/Controls/IResourceProvider.cs
  6. 17
      src/Avalonia.Styling/Controls/ResourceDictionary.cs
  7. 4
      src/Avalonia.Styling/Styling/Style.cs
  8. 1
      src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj
  9. 63
      src/Markup/Avalonia.Markup.Xaml/Data/ResourceInclude.cs
  10. 2
      src/Markup/Avalonia.Markup.Xaml/Styling/StyleInclude.cs
  11. 55
      tests/Avalonia.Markup.Xaml.UnitTests/Data/ResourceIncludeTests.cs
  12. 2
      tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/DynamicResourceExtensionTests.cs
  13. 2
      tests/Avalonia.Styling.UnitTests/ResourceDictionaryTests.cs

4
src/Avalonia.Controls/Application.cs

@ -149,7 +149,7 @@ namespace Avalonia
IStyleHost IStyleHost.StylingParent => null;
/// <inheritdoc/>
bool IResourceNode.HasResources => _resources?.Count > 0;
bool IResourceProvider.HasResources => _resources?.Count > 0;
/// <inheritdoc/>
IResourceNode IResourceNode.ResourceParent => null;
@ -182,7 +182,7 @@ namespace Avalonia
}
/// <inheritdoc/>
bool IResourceNode.TryGetResource(string key, out object value)
bool IResourceProvider.TryGetResource(string key, out object value)
{
value = null;
return (_resources?.TryGetResource(key, out value) ?? false) ||

4
src/Avalonia.Controls/Control.cs

@ -401,7 +401,7 @@ namespace Avalonia.Controls
IAvaloniaReadOnlyList<ILogical> ILogical.LogicalChildren => LogicalChildren;
/// <inheritdoc/>
bool IResourceNode.HasResources => _resources?.Count > 0 || Styles.HasResources;
bool IResourceProvider.HasResources => _resources?.Count > 0 || Styles.HasResources;
/// <inheritdoc/>
IResourceNode IResourceNode.ResourceParent => ((IStyleHost)this).StylingParent as IResourceNode;
@ -491,7 +491,7 @@ namespace Avalonia.Controls
}
/// <inheritdoc/>
bool IResourceNode.TryGetResource(string key, out object value)
bool IResourceProvider.TryGetResource(string key, out object value)
{
value = null;
return (_resources?.TryGetResource(key, out value) ?? false) ||

21
src/Avalonia.Styling/Controls/IResourceDictionary.cs

@ -9,28 +9,11 @@ namespace Avalonia.Controls
/// <summary>
/// An indexed dictionary of resources.
/// </summary>
public interface IResourceDictionary : IDictionary<string, object>
public interface IResourceDictionary : IResourceProvider, IDictionary<string, object>
{
/// <summary>
/// Raised when resources in the dictionary are changed.
/// </summary>
event EventHandler<ResourcesChangedEventArgs> ResourcesChanged;
/// <summary>
/// Gets a collection of child resource dictionaries.
/// </summary>
IList<IResourceDictionary> MergedDictionaries { get; }
/// <summary>
/// Tries to find a resource within the dictionary.
/// </summary>
/// <param name="key">The resource key.</param>
/// <param name="value">
/// When this method returns, contains the value associated with the specified key,
/// if the key is found; otherwise, null
/// <returns>
/// True if the resource if found, otherwise false.
/// </returns>
bool TryGetResource(string key, out object value);
IList<IResourceProvider> MergedDictionaries { get; }
}
}

26
src/Avalonia.Styling/Controls/IResourceNode.cs

@ -3,35 +3,13 @@
namespace Avalonia.Controls
{
/// <summary>
/// Defines an element that can be queried for resources.
/// Represents resource provider in a tree.
/// </summary>
public interface IResourceNode
public interface IResourceNode : IResourceProvider
{
/// <summary>
/// Raised when resources in the element are changed.
/// </summary>
event EventHandler<ResourcesChangedEventArgs> ResourcesChanged;
/// <summary>
/// Gets a value indicating whether the node has resources.
/// </summary>
bool HasResources { get; }
/// <summary>
/// Gets the parent resource node, if any.
/// </summary>
IResourceNode ResourceParent { get; }
/// <summary>
/// Tries to find a resource within the element.
/// </summary>
/// <param name="key">The resource key.</param>
/// <param name="value">
/// When this method returns, contains the value associated with the specified key,
/// if the key is found; otherwise, null
/// <returns>
/// True if the resource if found, otherwise false.
/// </returns>
bool TryGetResource(string key, out object value);
}
}

32
src/Avalonia.Styling/Controls/IResourceProvider.cs

@ -0,0 +1,32 @@
using System;
namespace Avalonia.Controls
{
/// <summary>
/// Represents an object that can be queried for resources.
/// </summary>
public interface IResourceProvider
{
/// <summary>
/// Raised when resources in the provider are changed.
/// </summary>
event EventHandler<ResourcesChangedEventArgs> ResourcesChanged;
/// <summary>
/// Gets a value indicating whether the element has resources.
/// </summary>
bool HasResources { get; }
/// <summary>
/// Tries to find a resource within the provider.
/// </summary>
/// <param name="key">The resource key.</param>
/// <param name="value">
/// When this method returns, contains the value associated with the specified key,
/// if the key is found; otherwise, null
/// <returns>
/// True if the resource if found, otherwise false.
/// </returns>
bool TryGetResource(string key, out object value);
}
}

17
src/Avalonia.Styling/Controls/ResourceDictionary.cs

@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using Avalonia.Collections;
namespace Avalonia.Controls
@ -13,7 +14,7 @@ namespace Avalonia.Controls
/// </summary>
public class ResourceDictionary : AvaloniaDictionary<string, object>, IResourceDictionary
{
private AvaloniaList<IResourceDictionary> _mergedDictionaries;
private AvaloniaList<IResourceProvider> _mergedDictionaries;
/// <summary>
/// Initializes a new instance of the <see cref="ResourceDictionary"/> class.
@ -27,18 +28,18 @@ namespace Avalonia.Controls
public event EventHandler<ResourcesChangedEventArgs> ResourcesChanged;
/// <inheritdoc/>
public IList<IResourceDictionary> MergedDictionaries
public IList<IResourceProvider> MergedDictionaries
{
get
{
if (_mergedDictionaries == null)
{
_mergedDictionaries = new AvaloniaList<IResourceDictionary>();
_mergedDictionaries = new AvaloniaList<IResourceProvider>();
_mergedDictionaries.ResetBehavior = ResetBehavior.Remove;
_mergedDictionaries.ForEachItem(
x =>
{
if (x.Count > 0)
if (x.HasResources)
{
OnResourcesChanged();
}
@ -47,7 +48,7 @@ namespace Avalonia.Controls
},
x =>
{
if (x.Count > 0)
if (x.HasResources)
{
OnResourcesChanged();
}
@ -61,6 +62,12 @@ namespace Avalonia.Controls
}
}
/// <inheritdoc/>
bool IResourceProvider.HasResources
{
get => Count > 0 || (_mergedDictionaries?.Any(x => x.HasResources) ?? false);
}
/// <inheritdoc/>
public bool TryGetResource(string key, out object value)
{

4
src/Avalonia.Styling/Styling/Style.cs

@ -79,10 +79,10 @@ namespace Avalonia.Styling
public IList<ISetter> Setters { get; set; } = new List<ISetter>();
/// <inheritdoc/>
bool IResourceNode.HasResources => _resources?.Count > 0;
IResourceNode IResourceNode.ResourceParent => _parent;
/// <inheritdoc/>
IResourceNode IResourceNode.ResourceParent => _parent;
bool IResourceProvider.HasResources => _resources?.Count > 0;
/// <summary>
/// Attaches the style to a control if the style's selector matches.

1
src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj

@ -34,6 +34,7 @@
<Compile Include="Converters\MatrixTypeConverter.cs" />
<Compile Include="Converters\RectTypeConverter.cs" />
<Compile Include="Converters\SetterValueTypeConverter.cs" />
<Compile Include="Data\ResourceInclude.cs" />
<Compile Include="MarkupExtensions\DynamicResourceExtension.cs" />
<Compile Include="MarkupExtensions\StaticResourceExtension.cs" />
<Compile Include="MarkupExtensions\StyleIncludeExtension.cs" />

63
src/Markup/Avalonia.Markup.Xaml/Data/ResourceInclude.cs

@ -0,0 +1,63 @@
using System;
using System.ComponentModel;
using Avalonia.Controls;
using Portable.Xaml.ComponentModel;
using Portable.Xaml.Markup;
namespace Avalonia.Markup.Xaml.Data
{
/// <summary>
/// Loads a resource dictionary from a specified URL.
/// </summary>
public class ResourceInclude : MarkupExtension, IResourceProvider
{
private Uri _baseUri;
private IResourceDictionary _loaded;
public event EventHandler<ResourcesChangedEventArgs> ResourcesChanged;
/// <summary>
/// Gets the loaded resource dictionary.
/// </summary>
public IResourceDictionary Loaded
{
get
{
if (_loaded == null)
{
var loader = new AvaloniaXamlLoader();
_loaded = (IResourceDictionary)loader.Load(Source, _baseUri);
if (_loaded.HasResources)
{
ResourcesChanged?.Invoke(this, new ResourcesChangedEventArgs());
}
}
return _loaded;
}
}
/// <summary>
/// Gets or sets the source URL.
/// </summary>
public Uri Source { get; set; }
/// <inhertidoc/>
bool IResourceProvider.HasResources => Loaded.HasResources;
/// <inhertidoc/>
bool IResourceProvider.TryGetResource(string key, out object value)
{
return Loaded.TryGetResource(key, out value);
}
/// <inhertidoc/>
public override object ProvideValue(IServiceProvider serviceProvider)
{
var tdc = (ITypeDescriptorContext)serviceProvider;
_baseUri = tdc?.GetBaseUri();
return this;
}
}
}

2
src/Markup/Avalonia.Markup.Xaml/Styling/StyleInclude.cs

@ -52,7 +52,7 @@ namespace Avalonia.Markup.Xaml.Styling
}
/// <inheritdoc/>
bool IResourceNode.HasResources => Loaded.HasResources;
bool IResourceProvider.HasResources => Loaded.HasResources;
/// <inheritdoc/>
IResourceNode IResourceNode.ResourceParent => _parent;

55
tests/Avalonia.Markup.Xaml.UnitTests/Data/ResourceIncludeTests.cs

@ -0,0 +1,55 @@
using System;
using Avalonia.Controls;
using Avalonia.Media;
using Avalonia.UnitTests;
using Xunit;
namespace Avalonia.Markup.Xaml.UnitTests.Data
{
public class ResourceIncludeTests
{
public class StaticResourceExtensionTests
{
[Fact]
public void ResourceInclude_Loads_ResourceDictionary()
{
var includeXaml = @"
<ResourceDictionary xmlns='https://github.com/avaloniaui'
xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
<SolidColorBrush x:Key='brush'>#ff506070</SolidColorBrush>
</ResourceDictionary>
";
using (StartWithResources(("test:include.xaml", includeXaml)))
{
var xaml = @"
<UserControl xmlns='https://github.com/avaloniaui'
xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceInclude Source='test:include.xaml'/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
<Border Name='border' Background='{StaticResource brush}'/>
</UserControl>";
var loader = new AvaloniaXamlLoader();
var userControl = (UserControl)loader.Load(xaml);
var border = userControl.FindControl<Border>("border");
var brush = (SolidColorBrush)border.Background;
Assert.Equal(0xff506070, brush.Color.ToUint32());
}
}
private IDisposable StartWithResources(params (string, string)[] assets)
{
var assetLoader = new MockAssetLoader(assets);
var services = new TestServices(assetLoader: assetLoader);
return UnitTestApplication.Start(services);
}
}
}
}

2
tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/DynamicResourceExtensionTests.cs

@ -485,7 +485,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
Assert.Null(border.Background);
userControl.Resources.MergedDictionaries[0].Add("brush", new SolidColorBrush(0xff506070));
((IResourceDictionary)userControl.Resources.MergedDictionaries[0]).Add("brush", new SolidColorBrush(0xff506070));
var brush = (SolidColorBrush)border.Background;
Assert.NotNull(brush);

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

@ -167,7 +167,7 @@ namespace Avalonia.Styling.UnitTests
var raised = false;
target.ResourcesChanged += (_, __) => raised = true;
target.MergedDictionaries[0].Add("foo", "bar");
((IResourceDictionary)target.MergedDictionaries[0]).Add("foo", "bar");
Assert.True(raised);
}

Loading…
Cancel
Save