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