committed by
GitHub
5 changed files with 258 additions and 2 deletions
@ -0,0 +1,90 @@ |
|||||
|
// 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 System.Collections.Generic; |
||||
|
|
||||
|
namespace Avalonia.Interactivity |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Tracks registered <see cref="RoutedEvent"/>s.
|
||||
|
/// </summary>
|
||||
|
public class RoutedEventRegistry |
||||
|
{ |
||||
|
private readonly Dictionary<Type, List<RoutedEvent>> _registeredRoutedEvents = |
||||
|
new Dictionary<Type, List<RoutedEvent>>(); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the <see cref="RoutedEventRegistry"/> instance.
|
||||
|
/// </summary>
|
||||
|
public static RoutedEventRegistry Instance { get; } |
||||
|
= new RoutedEventRegistry(); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Registers a <see cref="RoutedEvent"/> on a type.
|
||||
|
/// </summary>
|
||||
|
/// <param name="type">The type.</param>
|
||||
|
/// <param name="event">The event.</param>
|
||||
|
/// <remarks>
|
||||
|
/// You won't usually want to call this method directly, instead use the
|
||||
|
/// <see cref="RoutedEvent.Register{TOwner, TEventArgs}(string, RoutingStrategies)"/>
|
||||
|
/// method.
|
||||
|
/// </remarks>
|
||||
|
public void Register(Type type, RoutedEvent @event) |
||||
|
{ |
||||
|
Contract.Requires<ArgumentNullException>(type != null); |
||||
|
Contract.Requires<ArgumentNullException>(@event != null); |
||||
|
|
||||
|
if (!_registeredRoutedEvents.TryGetValue(type, out var list)) |
||||
|
{ |
||||
|
list = new List<RoutedEvent>(); |
||||
|
_registeredRoutedEvents.Add(type, list); |
||||
|
} |
||||
|
list.Add(@event); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Returns all routed events, that are currently registered in the event registry.
|
||||
|
/// </summary>
|
||||
|
/// <returns>All routed events, that are currently registered in the event registry.</returns>
|
||||
|
public IEnumerable<RoutedEvent> GetAllRegistered() |
||||
|
{ |
||||
|
foreach (var events in _registeredRoutedEvents.Values) |
||||
|
{ |
||||
|
foreach (var e in events) |
||||
|
{ |
||||
|
yield return e; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Returns all routed events registered with the provided type.
|
||||
|
/// If the type is not found or does not provide any routed events, an empty list is returned.
|
||||
|
/// </summary>
|
||||
|
/// <param name="type">The type.</param>
|
||||
|
/// <returns>All routed events registered with the provided type.</returns>
|
||||
|
public IReadOnlyList<RoutedEvent> GetRegistered(Type type) |
||||
|
{ |
||||
|
Contract.Requires<ArgumentNullException>(type != null); |
||||
|
|
||||
|
if (_registeredRoutedEvents.TryGetValue(type, out var events)) |
||||
|
{ |
||||
|
return events; |
||||
|
} |
||||
|
|
||||
|
return Array.Empty<RoutedEvent>(); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Returns all routed events registered with the provided type.
|
||||
|
/// If the type is not found or does not provide any routed events, an empty list is returned.
|
||||
|
/// </summary>
|
||||
|
/// <typeparam name="TOwner">The type.</typeparam>
|
||||
|
/// <returns>All routed events registered with the provided type.</returns>
|
||||
|
public IReadOnlyList<RoutedEvent> GetRegistered<TOwner>() |
||||
|
{ |
||||
|
return GetRegistered(typeof(TOwner)); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,106 @@ |
|||||
|
// 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.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
using Avalonia.Collections; |
||||
|
using Avalonia.Controls.Mixins; |
||||
|
using Avalonia.Controls.Presenters; |
||||
|
using Avalonia.Controls.Primitives; |
||||
|
using Avalonia.Controls.Templates; |
||||
|
using Avalonia.LogicalTree; |
||||
|
using Moq; |
||||
|
using Xunit; |
||||
|
|
||||
|
namespace Avalonia.Controls.UnitTests.Mixins |
||||
|
{ |
||||
|
public class ContentControlMixinTests |
||||
|
{ |
||||
|
[Fact] |
||||
|
public void Multiple_Mixin_Usages_Should_Not_Throw() |
||||
|
{ |
||||
|
var target = new TestControl() |
||||
|
{ |
||||
|
Template = new FuncControlTemplate(_ => new Panel |
||||
|
{ |
||||
|
Children = |
||||
|
{ |
||||
|
new ContentPresenter { Name = "Content_1_Presenter" }, |
||||
|
new ContentPresenter { Name = "Content_2_Presenter" } |
||||
|
} |
||||
|
}) |
||||
|
}; |
||||
|
|
||||
|
var ex = Record.Exception(() => target.ApplyTemplate()); |
||||
|
|
||||
|
Assert.Null(ex); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Replacing_Template_Releases_Events() |
||||
|
{ |
||||
|
var p1 = new ContentPresenter { Name = "Content_1_Presenter" }; |
||||
|
var p2 = new ContentPresenter { Name = "Content_2_Presenter" }; |
||||
|
var target = new TestControl |
||||
|
{ |
||||
|
Template = new FuncControlTemplate(_ => new Panel |
||||
|
{ |
||||
|
Children = |
||||
|
{ |
||||
|
p1, |
||||
|
p2 |
||||
|
} |
||||
|
}) |
||||
|
}; |
||||
|
target.ApplyTemplate(); |
||||
|
|
||||
|
Control tc; |
||||
|
|
||||
|
p1.Content = tc = new Control(); |
||||
|
p1.UpdateChild(); |
||||
|
Assert.Contains(tc, target.GetLogicalChildren()); |
||||
|
|
||||
|
p2.Content = tc = new Control(); |
||||
|
p2.UpdateChild(); |
||||
|
Assert.Contains(tc, target.GetLogicalChildren()); |
||||
|
|
||||
|
target.Template = null; |
||||
|
|
||||
|
p1.Content = tc = new Control(); |
||||
|
p1.UpdateChild(); |
||||
|
Assert.DoesNotContain(tc, target.GetLogicalChildren()); |
||||
|
|
||||
|
p2.Content = tc = new Control(); |
||||
|
p2.UpdateChild(); |
||||
|
Assert.DoesNotContain(tc, target.GetLogicalChildren()); |
||||
|
|
||||
|
} |
||||
|
|
||||
|
private class TestControl : TemplatedControl |
||||
|
{ |
||||
|
public static readonly StyledProperty<object> Content1Property = |
||||
|
AvaloniaProperty.Register<TestControl, object>(nameof(Content1)); |
||||
|
|
||||
|
public static readonly StyledProperty<object> Content2Property = |
||||
|
AvaloniaProperty.Register<TestControl, object>(nameof(Content2)); |
||||
|
|
||||
|
static TestControl() |
||||
|
{ |
||||
|
ContentControlMixin.Attach<TestControl>(Content1Property, x => x.LogicalChildren, "Content_1_Presenter"); |
||||
|
ContentControlMixin.Attach<TestControl>(Content2Property, x => x.LogicalChildren, "Content_2_Presenter"); |
||||
|
} |
||||
|
|
||||
|
public object Content1 |
||||
|
{ |
||||
|
get { return GetValue(Content1Property); } |
||||
|
set { SetValue(Content1Property, value); } |
||||
|
} |
||||
|
|
||||
|
public object Content2 |
||||
|
{ |
||||
|
get { return GetValue(Content2Property); } |
||||
|
set { SetValue(Content2Property, value); } |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,49 @@ |
|||||
|
// 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.Collections.Generic; |
||||
|
using Avalonia.Controls; |
||||
|
using Avalonia.Input; |
||||
|
using Xunit; |
||||
|
|
||||
|
namespace Avalonia.Interactivity.UnitTests |
||||
|
{ |
||||
|
public class RoutedEventRegistryTests |
||||
|
{ |
||||
|
[Fact] |
||||
|
public void Pointer_Events_Should_Be_Registered() |
||||
|
{ |
||||
|
var expectedEvents = new List<RoutedEvent> { InputElement.PointerPressedEvent, InputElement.PointerReleasedEvent }; |
||||
|
var registeredEvents = RoutedEventRegistry.Instance.GetRegistered<InputElement>(); |
||||
|
Assert.Contains(registeredEvents, expectedEvents.Contains); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void ClickEvent_Should_Be_Registered_On_Button() |
||||
|
{ |
||||
|
var expectedEvents = new List<RoutedEvent> { Button.ClickEvent }; |
||||
|
var registeredEvents = RoutedEventRegistry.Instance.GetRegistered<Button>(); |
||||
|
Assert.Contains(registeredEvents, expectedEvents.Contains); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void ClickEvent_Should_Not_Be_Registered_On_ContentControl() |
||||
|
{ |
||||
|
// force ContentControl type to be loaded
|
||||
|
new ContentControl(); |
||||
|
var expectedEvents = new List<RoutedEvent> { Button.ClickEvent }; |
||||
|
var registeredEvents = RoutedEventRegistry.Instance.GetRegistered<ContentControl>(); |
||||
|
Assert.DoesNotContain(registeredEvents, expectedEvents.Contains); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void InputElement_Events_Should_Not_Be_Registered_On_Button() |
||||
|
{ |
||||
|
// force Button type to be loaded
|
||||
|
new Button(); |
||||
|
var expectedEvents = new List<RoutedEvent> { InputElement.PointerPressedEvent, InputElement.PointerReleasedEvent }; |
||||
|
var registeredEvents = RoutedEventRegistry.Instance.GetRegistered<Button>(); |
||||
|
Assert.DoesNotContain(registeredEvents, expectedEvents.Contains); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue