Browse Source
Fixes #492 in a fashion: `DynamicResource` now works for this scenario.pull/1136/head
16 changed files with 329 additions and 88 deletions
@ -0,0 +1,49 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Reactive; |
|||
using System.Reactive.Linq; |
|||
using System.Text; |
|||
|
|||
namespace Avalonia.Controls |
|||
{ |
|||
public static class ResourceProviderExtensions |
|||
{ |
|||
/// <summary>
|
|||
/// Finds the specified resource by searching up the logical tree and then global styles.
|
|||
/// </summary>
|
|||
/// <param name="control">The control.</param>
|
|||
/// <param name="key">The resource key.</param>
|
|||
/// <returns>The resource, or <see cref="AvaloniaProperty.UnsetValue"/> if not found.</returns>
|
|||
public static object FindResource(this IResourceProvider control, string key) |
|||
{ |
|||
Contract.Requires<ArgumentNullException>(control != null); |
|||
Contract.Requires<ArgumentNullException>(key != null); |
|||
|
|||
var current = control; |
|||
|
|||
while (current != null) |
|||
{ |
|||
if (current is IResourceProvider host) |
|||
{ |
|||
if (host.TryGetResource(key, out var value)) |
|||
{ |
|||
return value; |
|||
} |
|||
} |
|||
|
|||
current = current.ResourceParent; |
|||
} |
|||
|
|||
return AvaloniaProperty.UnsetValue; |
|||
} |
|||
|
|||
public static IObservable<object> GetResourceObservable(this IResourceProvider target, string key) |
|||
{ |
|||
return Observable.FromEventPattern<ResourcesChangedEventArgs>( |
|||
x => target.ResourcesChanged += x, |
|||
x => target.ResourcesChanged -= x) |
|||
.StartWith((EventPattern<ResourcesChangedEventArgs>)null) |
|||
.Select(x => target.FindResource(key)); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,32 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Text; |
|||
using Avalonia.Controls; |
|||
|
|||
namespace Avalonia.Styling |
|||
{ |
|||
/// <summary>
|
|||
/// Defines an interface through which a <see cref="Style"/>'s parent can be set.
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// You should not usually need to use this interface - it is for internal use only.
|
|||
/// </remarks>
|
|||
public interface ISetStyleParent : IStyle |
|||
{ |
|||
/// <summary>
|
|||
/// Sets the style parent.
|
|||
/// </summary>
|
|||
/// <param name="parent">The parent.</param>
|
|||
void SetParent(IResourceProvider parent); |
|||
|
|||
/// <summary>
|
|||
/// Notifies the style that a change has been made to resources that apply to it.
|
|||
/// </summary>
|
|||
/// <param name="e">The event args.</param>
|
|||
/// <remarks>
|
|||
/// This method will be called automatically by the framework, you should not need to call
|
|||
/// this method yourself.
|
|||
/// </remarks>
|
|||
void NotifyResourcesChanged(ResourcesChangedEventArgs e); |
|||
} |
|||
} |
|||
@ -0,0 +1,115 @@ |
|||
// 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 Xunit; |
|||
|
|||
namespace Avalonia.Styling.UnitTests |
|||
{ |
|||
public class StylesTests |
|||
{ |
|||
[Fact] |
|||
public void Adding_Style_With_Resources_Should_Raise_ResourceChanged() |
|||
{ |
|||
var style = new Style |
|||
{ |
|||
Resources = { { "foo", "bar" } }, |
|||
}; |
|||
|
|||
var target = new Styles(); |
|||
var raised = false; |
|||
|
|||
target.ResourcesChanged += (_, __) => raised = true; |
|||
target.Add(style); |
|||
|
|||
Assert.True(raised); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Removing_Style_With_Resources_Should_Raise_ResourceChanged() |
|||
{ |
|||
var target = new Styles |
|||
{ |
|||
new Style |
|||
{ |
|||
Resources = { { "foo", "bar" } }, |
|||
} |
|||
}; |
|||
|
|||
var raised = false; |
|||
|
|||
target.ResourcesChanged += (_, __) => raised = true; |
|||
target.Clear(); |
|||
|
|||
Assert.True(raised); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Adding_Style_Without_Resources_Should_Not_Raise_ResourceChanged() |
|||
{ |
|||
var style = new Style(); |
|||
var target = new Styles(); |
|||
var raised = false; |
|||
|
|||
target.ResourcesChanged += (_, __) => raised = true; |
|||
target.Add(style); |
|||
|
|||
Assert.False(raised); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Adding_Resource_Should_Raise_Child_ResourceChanged() |
|||
{ |
|||
Style child; |
|||
var target = new Styles |
|||
{ |
|||
(child = new Style()), |
|||
}; |
|||
|
|||
var raised = false; |
|||
|
|||
child.ResourcesChanged += (_, __) => raised = true; |
|||
target.Resources.Add("foo", "bar"); |
|||
|
|||
Assert.True(raised); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Adding_Resource_To_Younger_Sibling_Style_Should_Raise_ResourceChanged() |
|||
{ |
|||
Style style1; |
|||
Style style2; |
|||
var target = new Styles |
|||
{ |
|||
(style1 = new Style()), |
|||
(style2 = new Style()), |
|||
}; |
|||
|
|||
var raised = false; |
|||
|
|||
style2.ResourcesChanged += (_, __) => raised = true; |
|||
style1.Resources.Add("foo", "bar"); |
|||
|
|||
Assert.True(raised); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Adding_Resource_To_Older_Sibling_Style_Should_Raise_ResourceChanged() |
|||
{ |
|||
Style style1; |
|||
Style style2; |
|||
var target = new Styles |
|||
{ |
|||
(style1 = new Style()), |
|||
(style2 = new Style()), |
|||
}; |
|||
|
|||
var raised = false; |
|||
|
|||
style1.ResourcesChanged += (_, __) => raised = true; |
|||
style2.Resources.Add("foo", "bar"); |
|||
|
|||
Assert.False(raised); |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue