82 changed files with 1603 additions and 319 deletions
@ -0,0 +1,30 @@ |
|||||
|
// Copyright (c) The Perspex Project. All rights reserved.
|
||||
|
// Licensed under the MIT license. See licence.md file in the project root for full license information.
|
||||
|
|
||||
|
namespace Perspex.Markup.Xaml.Context |
||||
|
{ |
||||
|
internal class NameScopeWrapper : OmniXaml.INameScope |
||||
|
{ |
||||
|
private Perspex.INameScope _inner; |
||||
|
|
||||
|
public NameScopeWrapper(Perspex.INameScope inner) |
||||
|
{ |
||||
|
_inner = inner; |
||||
|
} |
||||
|
|
||||
|
public object Find(string name) |
||||
|
{ |
||||
|
return _inner.Find(name); |
||||
|
} |
||||
|
|
||||
|
public void Register(string name, object scopedElement) |
||||
|
{ |
||||
|
_inner.Register(name, scopedElement); |
||||
|
} |
||||
|
|
||||
|
public void Unregister(string name) |
||||
|
{ |
||||
|
_inner.Unregister(name); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -1 +1 @@ |
|||||
Subproject commit 55fc2abc715885d3686f8c8605f34714445cd72f |
Subproject commit 3e3b46ba66941da925092e2977003d0553cfc907 |
||||
@ -0,0 +1,60 @@ |
|||||
|
// Copyright (c) The Perspex 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.Linq; |
||||
|
using System.Reactive.Linq; |
||||
|
using Perspex.Controls; |
||||
|
|
||||
|
namespace Perspex.Markup |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Locates controls relative to other controls.
|
||||
|
/// </summary>
|
||||
|
public static class ControlLocator |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Tracks a named control relative to another control.
|
||||
|
/// </summary>
|
||||
|
/// <param name="relativeTo">
|
||||
|
/// The control relative from which the other control should be found.
|
||||
|
/// </param>
|
||||
|
/// <param name="name">The name of the control to find.</param>
|
||||
|
public static IObservable<IControl> Track(IControl relativeTo, string name) |
||||
|
{ |
||||
|
var attached = Observable.FromEventPattern<VisualTreeAttachmentEventArgs>( |
||||
|
x => relativeTo.AttachedToVisualTree += x, |
||||
|
x => relativeTo.DetachedFromVisualTree += x) |
||||
|
.Select(x => x.EventArgs.NameScope) |
||||
|
.StartWith(relativeTo.FindNameScope()); |
||||
|
|
||||
|
var detached = Observable.FromEventPattern<VisualTreeAttachmentEventArgs>( |
||||
|
x => relativeTo.DetachedFromVisualTree += x, |
||||
|
x => relativeTo.DetachedFromVisualTree += x) |
||||
|
.Select(x => (INameScope)null); |
||||
|
|
||||
|
return attached.Merge(detached).Select(nameScope => |
||||
|
{ |
||||
|
if (nameScope != null) |
||||
|
{ |
||||
|
var registered = Observable.FromEventPattern<NameScopeEventArgs>( |
||||
|
x => nameScope.Registered += x, |
||||
|
x => nameScope.Registered -= x) |
||||
|
.Where(x => x.EventArgs.Name == name) |
||||
|
.Select(x => x.EventArgs.Element) |
||||
|
.OfType<IControl>(); |
||||
|
var unregistered = Observable.FromEventPattern<NameScopeEventArgs>( |
||||
|
x => nameScope.Unregistered += x, |
||||
|
x => nameScope.Unregistered -= x); |
||||
|
return registered |
||||
|
.StartWith(nameScope.Find<IControl>(name)) |
||||
|
.TakeUntil(unregistered); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
return Observable.Return<IControl>(null); |
||||
|
} |
||||
|
}).Switch(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,43 @@ |
|||||
|
// Copyright (c) The Perspex Project. All rights reserved.
|
||||
|
// Licensed under the MIT license. See licence.md file in the project root for full license information.
|
||||
|
|
||||
|
using System; |
||||
|
|
||||
|
namespace Perspex |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Defines a name scope.
|
||||
|
/// </summary>
|
||||
|
public interface INameScope |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Raised when an element is registered with the name scope.
|
||||
|
/// </summary>
|
||||
|
event EventHandler<NameScopeEventArgs> Registered; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Raised when an element is unregistered with the name scope.
|
||||
|
/// </summary>
|
||||
|
event EventHandler<NameScopeEventArgs> Unregistered; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Registers an element eith the name scope.
|
||||
|
/// </summary>
|
||||
|
/// <param name="name">The element name.</param>
|
||||
|
/// <param name="element">The element.</param>
|
||||
|
void Register(string name, object element); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Finds a named element in the name scope.
|
||||
|
/// </summary>
|
||||
|
/// <param name="name">The name.</param>
|
||||
|
/// <returns>The element, or null if the name was not found.</returns>
|
||||
|
object Find(string name); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Unregisters an element with the name scope.
|
||||
|
/// </summary>
|
||||
|
/// <param name="name">The name.</param>
|
||||
|
void Unregister(string name); |
||||
|
} |
||||
|
} |
||||
@ -1,10 +1,16 @@ |
|||||
// Copyright (c) The Perspex Project. All rights reserved.
|
// Copyright (c) The Perspex Project. All rights reserved.
|
||||
// Licensed under the MIT license. See licence.md file in the project root for full license information.
|
// Licensed under the MIT license. See licence.md file in the project root for full license information.
|
||||
|
|
||||
namespace Perspex.Styling |
namespace Perspex |
||||
{ |
{ |
||||
|
/// <summary>
|
||||
|
/// Interface for named elements.
|
||||
|
/// </summary>
|
||||
public interface INamed |
public interface INamed |
||||
{ |
{ |
||||
|
/// <summary>
|
||||
|
/// Gets the element name.
|
||||
|
/// </summary>
|
||||
string Name { get; } |
string Name { get; } |
||||
} |
} |
||||
} |
} |
||||
@ -0,0 +1,109 @@ |
|||||
|
// Copyright (c) The Perspex 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 Perspex |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Implements a name scope.
|
||||
|
/// </summary>
|
||||
|
public class NameScope : INameScope |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Defines the NameScope attached property.
|
||||
|
/// </summary>
|
||||
|
public static readonly PerspexProperty<INameScope> NameScopeProperty = |
||||
|
PerspexProperty.RegisterAttached<NameScope, Visual, INameScope>("NameScope"); |
||||
|
|
||||
|
private readonly Dictionary<string, object> _inner = new Dictionary<string, object>(); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Raised when an element is registered with the name scope.
|
||||
|
/// </summary>
|
||||
|
public event EventHandler<NameScopeEventArgs> Registered; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Raised when an element is unregistered with the name scope.
|
||||
|
/// </summary>
|
||||
|
public event EventHandler<NameScopeEventArgs> Unregistered; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the value of the attached <see cref="NameScopeProperty"/> on a visual.
|
||||
|
/// </summary>
|
||||
|
/// <param name="visual">The visual.</param>
|
||||
|
/// <returns>The value of the NameScope attached property.</returns>
|
||||
|
public static INameScope GetNameScope(Visual visual) |
||||
|
{ |
||||
|
return visual.GetValue(NameScopeProperty); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Sets the value of the attached <see cref="NameScopeProperty"/> on a visual.
|
||||
|
/// </summary>
|
||||
|
/// <param name="visual">The visual.</param>
|
||||
|
/// <param name="value">The value to set.</param>
|
||||
|
public static void SetNameScope(Visual visual, INameScope value) |
||||
|
{ |
||||
|
visual.SetValue(NameScopeProperty, value); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Registers an element with the name scope.
|
||||
|
/// </summary>
|
||||
|
/// <param name="name">The element name.</param>
|
||||
|
/// <param name="element">The element.</param>
|
||||
|
public void Register(string name, object element) |
||||
|
{ |
||||
|
Contract.Requires<ArgumentNullException>(name != null); |
||||
|
Contract.Requires<ArgumentNullException>(element != null); |
||||
|
|
||||
|
object existing; |
||||
|
|
||||
|
if (_inner.TryGetValue(name, out existing)) |
||||
|
{ |
||||
|
if (existing != element) |
||||
|
{ |
||||
|
throw new ArgumentException($"Control with the name '{name}' already registered."); |
||||
|
} |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
_inner.Add(name, element); |
||||
|
Registered?.Invoke(this, new NameScopeEventArgs(name, element)); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Finds a named element in the name scope.
|
||||
|
/// </summary>
|
||||
|
/// <param name="name">The name.</param>
|
||||
|
/// <returns>The element, or null if the name was not found.</returns>
|
||||
|
public object Find(string name) |
||||
|
{ |
||||
|
Contract.Requires<ArgumentNullException>(name != null); |
||||
|
|
||||
|
object result; |
||||
|
_inner.TryGetValue(name, out result); |
||||
|
return result; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Unregisters an element with the name scope.
|
||||
|
/// </summary>
|
||||
|
/// <param name="name">The name.</param>
|
||||
|
public void Unregister(string name) |
||||
|
{ |
||||
|
Contract.Requires<ArgumentNullException>(name != null); |
||||
|
|
||||
|
object element; |
||||
|
|
||||
|
if (_inner.TryGetValue(name, out element)) |
||||
|
{ |
||||
|
_inner.Remove(name); |
||||
|
Registered?.Invoke(this, new NameScopeEventArgs(name, element)); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,19 @@ |
|||||
|
// Copyright (c) The Perspex Project. All rights reserved.
|
||||
|
// Licensed under the MIT license. See licence.md file in the project root for full license information.
|
||||
|
|
||||
|
using System; |
||||
|
|
||||
|
namespace Perspex |
||||
|
{ |
||||
|
public class NameScopeEventArgs : EventArgs |
||||
|
{ |
||||
|
public NameScopeEventArgs(string name, object element) |
||||
|
{ |
||||
|
Name = name; |
||||
|
Element = element; |
||||
|
} |
||||
|
|
||||
|
public string Name { get; } |
||||
|
public object Element { get; } |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,68 @@ |
|||||
|
// Copyright (c) The Perspex 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 Perspex |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Extension methods for <see cref="INameScope"/>.
|
||||
|
/// </summary>
|
||||
|
public static class NameScopeExtensions |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Finds a named element in an <see cref="INameScope"/>.
|
||||
|
/// </summary>
|
||||
|
/// <typeparam name="T">The element type.</typeparam>
|
||||
|
/// <param name="nameScope">The name scope.</param>
|
||||
|
/// <param name="name">The name.</param>
|
||||
|
/// <returns>The named element or null if not found.</returns>
|
||||
|
public static T Find<T>(this INameScope nameScope, string name) |
||||
|
where T : class |
||||
|
{ |
||||
|
Contract.Requires<ArgumentNullException>(nameScope != null); |
||||
|
Contract.Requires<ArgumentNullException>(name != null); |
||||
|
|
||||
|
var result = nameScope.Find(name); |
||||
|
|
||||
|
if (result != null && !(result is T)) |
||||
|
{ |
||||
|
throw new InvalidOperationException( |
||||
|
$"Expected control '{name}' to be '{typeof(T)} but it was '{result.GetType()}'."); |
||||
|
} |
||||
|
|
||||
|
return (T)result; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets a named element from an <see cref="INameScope"/> or throws if no element of the
|
||||
|
/// requested name was found.
|
||||
|
/// </summary>
|
||||
|
/// <typeparam name="T">The element type.</typeparam>
|
||||
|
/// <param name="nameScope">The name scope.</param>
|
||||
|
/// <param name="name">The name.</param>
|
||||
|
/// <returns>The named element.</returns>
|
||||
|
public static T Get<T>(this INameScope nameScope, string name) |
||||
|
where T : class |
||||
|
{ |
||||
|
Contract.Requires<ArgumentNullException>(nameScope != null); |
||||
|
Contract.Requires<ArgumentNullException>(name != null); |
||||
|
|
||||
|
var result = nameScope.Find(name); |
||||
|
|
||||
|
if (result == null) |
||||
|
{ |
||||
|
throw new KeyNotFoundException($"Could not find control '{name}'."); |
||||
|
} |
||||
|
|
||||
|
if (!(result is T)) |
||||
|
{ |
||||
|
throw new InvalidOperationException( |
||||
|
$"Expected control '{name}' to be '{typeof(T)} but it was '{result.GetType()}'."); |
||||
|
} |
||||
|
|
||||
|
return (T)result; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,36 @@ |
|||||
|
// Copyright (c) The Perspex Project. All rights reserved.
|
||||
|
// Licensed under the MIT license. See licence.md file in the project root for full license information.
|
||||
|
|
||||
|
using System; |
||||
|
using Perspex.Rendering; |
||||
|
|
||||
|
namespace Perspex |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Holds the event arguments for the <see cref="Visual.AttachedToVisualTree"/> and
|
||||
|
/// <see cref="Visual.DetachedFromVisualTree"/> events.
|
||||
|
/// </summary>
|
||||
|
public class VisualTreeAttachmentEventArgs : EventArgs |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Initializes a new instance of the <see cref="VisualTreeAttachmentEventArgs"/> class.
|
||||
|
/// </summary>
|
||||
|
/// <param name="root">The root visual.</param>
|
||||
|
/// <param name="nameScope">The name scope.</param>
|
||||
|
public VisualTreeAttachmentEventArgs(IRenderRoot root, INameScope nameScope) |
||||
|
{ |
||||
|
Root = root; |
||||
|
NameScope = nameScope; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the root of the visual tree that the visual is being attached to or detached from.
|
||||
|
/// </summary>
|
||||
|
public IRenderRoot Root { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the element's name scope.
|
||||
|
/// </summary>
|
||||
|
public INameScope NameScope { get; } |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,122 @@ |
|||||
|
// Copyright (c) The Perspex 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; |
||||
|
using System.Reactive.Linq; |
||||
|
using Perspex.Controls; |
||||
|
using Xunit; |
||||
|
|
||||
|
namespace Perspex.Markup.UnitTests |
||||
|
{ |
||||
|
public class ControlLocatorTests |
||||
|
{ |
||||
|
[Fact] |
||||
|
public async void Track_By_Name_Should_Find_Control_Added_Earlier() |
||||
|
{ |
||||
|
TextBlock target; |
||||
|
TextBlock relativeTo; |
||||
|
|
||||
|
var root = new TestRoot |
||||
|
{ |
||||
|
Child = new StackPanel |
||||
|
{ |
||||
|
Children = new Controls.Controls |
||||
|
{ |
||||
|
(target = new TextBlock { Name = "target" }), |
||||
|
(relativeTo = new TextBlock { Name = "start" }), |
||||
|
} |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
var locator = ControlLocator.Track(relativeTo, "target"); |
||||
|
var result = await locator.Take(1); |
||||
|
|
||||
|
Assert.Same(target, result); |
||||
|
Assert.Equal(0, root.NameScopeRegisteredSubscribers); |
||||
|
Assert.Equal(0, root.NameScopeUnregisteredSubscribers); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Track_By_Name_Should_Find_Control_Added_Later() |
||||
|
{ |
||||
|
StackPanel panel; |
||||
|
TextBlock relativeTo; |
||||
|
|
||||
|
var root = new TestRoot |
||||
|
{ |
||||
|
Child = (panel = new StackPanel |
||||
|
{ |
||||
|
Children = new Controls.Controls |
||||
|
{ |
||||
|
(relativeTo = new TextBlock |
||||
|
{ |
||||
|
Name = "start" |
||||
|
}), |
||||
|
} |
||||
|
}) |
||||
|
}; |
||||
|
|
||||
|
var locator = ControlLocator.Track(relativeTo, "target"); |
||||
|
var target = new TextBlock { Name = "target" }; |
||||
|
var result = new List<IControl>(); |
||||
|
|
||||
|
using (locator.Subscribe(x => result.Add(x))) |
||||
|
{ |
||||
|
panel.Children.Add(target); |
||||
|
} |
||||
|
|
||||
|
Assert.Equal(new[] { null, target }, result); |
||||
|
Assert.Equal(0, root.NameScopeRegisteredSubscribers); |
||||
|
Assert.Equal(0, root.NameScopeUnregisteredSubscribers); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Track_By_Name_Should_Find_Control_When_Tree_Changed() |
||||
|
{ |
||||
|
TextBlock target1; |
||||
|
TextBlock target2; |
||||
|
TextBlock relativeTo; |
||||
|
|
||||
|
var root1 = new TestRoot |
||||
|
{ |
||||
|
Child = new StackPanel |
||||
|
{ |
||||
|
Children = new Controls.Controls |
||||
|
{ |
||||
|
(relativeTo = new TextBlock |
||||
|
{ |
||||
|
Name = "start" |
||||
|
}), |
||||
|
(target1 = new TextBlock { Name = "target" }), |
||||
|
} |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
var root2 = new TestRoot |
||||
|
{ |
||||
|
Child = new StackPanel |
||||
|
{ |
||||
|
Children = new Controls.Controls |
||||
|
{ |
||||
|
(target2 = new TextBlock { Name = "target" }), |
||||
|
} |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
var locator = ControlLocator.Track(relativeTo, "target"); |
||||
|
var target = new TextBlock { Name = "target" }; |
||||
|
var result = new List<IControl>(); |
||||
|
|
||||
|
using (locator.Subscribe(x => result.Add(x))) |
||||
|
{ |
||||
|
((StackPanel)root1.Child).Children.Remove(relativeTo); |
||||
|
((StackPanel)root2.Child).Children.Add(relativeTo); |
||||
|
} |
||||
|
|
||||
|
Assert.Equal(new[] { target1, null, target2 }, result); |
||||
|
Assert.Equal(0, root1.NameScopeRegisteredSubscribers); |
||||
|
Assert.Equal(0, root1.NameScopeUnregisteredSubscribers); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,61 @@ |
|||||
|
// Copyright (c) The Perspex Project. All rights reserved.
|
||||
|
// Licensed under the MIT license. See licence.md file in the project root for full license information.
|
||||
|
|
||||
|
using System; |
||||
|
using Perspex.Controls; |
||||
|
using Perspex.Platform; |
||||
|
using Perspex.Rendering; |
||||
|
|
||||
|
namespace Perspex.Markup.UnitTests |
||||
|
{ |
||||
|
public class TestRoot : Decorator, IRenderRoot, INameScope |
||||
|
{ |
||||
|
private NameScope _nameScope = new NameScope(); |
||||
|
|
||||
|
event EventHandler<NameScopeEventArgs> INameScope.Registered |
||||
|
{ |
||||
|
add { _nameScope.Registered += value; ++NameScopeRegisteredSubscribers; } |
||||
|
remove { _nameScope.Registered -= value; --NameScopeRegisteredSubscribers; } |
||||
|
} |
||||
|
|
||||
|
public event EventHandler<NameScopeEventArgs> Unregistered |
||||
|
{ |
||||
|
add { _nameScope.Unregistered += value; ++NameScopeUnregisteredSubscribers; } |
||||
|
remove { _nameScope.Unregistered -= value; --NameScopeUnregisteredSubscribers; } |
||||
|
} |
||||
|
|
||||
|
public int NameScopeRegisteredSubscribers { get; private set; } |
||||
|
|
||||
|
public int NameScopeUnregisteredSubscribers { get; private set; } |
||||
|
|
||||
|
public IRenderTarget RenderTarget |
||||
|
{ |
||||
|
get { throw new NotImplementedException(); } |
||||
|
} |
||||
|
|
||||
|
public IRenderQueueManager RenderQueueManager |
||||
|
{ |
||||
|
get { throw new NotImplementedException(); } |
||||
|
} |
||||
|
|
||||
|
public Point TranslatePointToScreen(Point p) |
||||
|
{ |
||||
|
throw new NotImplementedException(); |
||||
|
} |
||||
|
|
||||
|
public void Register(string name, object element) |
||||
|
{ |
||||
|
_nameScope.Register(name, element); |
||||
|
} |
||||
|
|
||||
|
public object Find(string name) |
||||
|
{ |
||||
|
return _nameScope.Find(name); |
||||
|
} |
||||
|
|
||||
|
public void Unregister(string name) |
||||
|
{ |
||||
|
_nameScope.Unregister(name); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,83 @@ |
|||||
|
// Copyright (c) The Perspex Project. All rights reserved.
|
||||
|
// Licensed under the MIT license. See licence.md file in the project root for full license information.
|
||||
|
|
||||
|
using Perspex.Controls; |
||||
|
using Perspex.Markup.Xaml.Data; |
||||
|
using Xunit; |
||||
|
|
||||
|
namespace Perspex.Markup.Xaml.UnitTests.Data |
||||
|
{ |
||||
|
public class BindingTests_ElementName |
||||
|
{ |
||||
|
[Fact] |
||||
|
public void Should_Bind_To_Element() |
||||
|
{ |
||||
|
TextBlock target; |
||||
|
var root = new TestRoot |
||||
|
{ |
||||
|
Child = new StackPanel |
||||
|
{ |
||||
|
Children = new Controls.Controls |
||||
|
{ |
||||
|
new TextBlock |
||||
|
{ |
||||
|
Name = "source", |
||||
|
Text = "foo", |
||||
|
}, |
||||
|
(target = new TextBlock |
||||
|
{ |
||||
|
Name = "target", |
||||
|
}) |
||||
|
} |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
var binding = new Binding |
||||
|
{ |
||||
|
ElementName = "source", |
||||
|
Path = "Text", |
||||
|
}; |
||||
|
|
||||
|
binding.Bind(target, TextBlock.TextProperty); |
||||
|
|
||||
|
Assert.Equal("foo", target.Text); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Should_Bind_To_Later_Added_Element() |
||||
|
{ |
||||
|
TextBlock target; |
||||
|
StackPanel stackPanel; |
||||
|
|
||||
|
var root = new TestRoot |
||||
|
{ |
||||
|
Child = stackPanel = new StackPanel |
||||
|
{ |
||||
|
Children = new Controls.Controls |
||||
|
{ |
||||
|
(target = new TextBlock |
||||
|
{ |
||||
|
Name = "target", |
||||
|
}), |
||||
|
} |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
var binding = new Binding |
||||
|
{ |
||||
|
ElementName = "source", |
||||
|
Path = "Text", |
||||
|
}; |
||||
|
|
||||
|
binding.Bind(target, TextBlock.TextProperty); |
||||
|
|
||||
|
stackPanel.Children.Add(new TextBlock |
||||
|
{ |
||||
|
Name = "source", |
||||
|
Text = "foo", |
||||
|
}); |
||||
|
|
||||
|
Assert.Equal("foo", target.Text); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,61 @@ |
|||||
|
// Copyright (c) The Perspex Project. All rights reserved.
|
||||
|
// Licensed under the MIT license. See licence.md file in the project root for full license information.
|
||||
|
|
||||
|
using System; |
||||
|
using Perspex.Controls; |
||||
|
using Perspex.Platform; |
||||
|
using Perspex.Rendering; |
||||
|
|
||||
|
namespace Perspex.Markup.Xaml.UnitTests |
||||
|
{ |
||||
|
public class TestRoot : Decorator, IRenderRoot, INameScope |
||||
|
{ |
||||
|
private readonly NameScope _nameScope = new NameScope(); |
||||
|
|
||||
|
event EventHandler<NameScopeEventArgs> INameScope.Registered |
||||
|
{ |
||||
|
add { _nameScope.Registered += value; ++NameScopeRegisteredSubscribers; } |
||||
|
remove { _nameScope.Registered -= value; --NameScopeRegisteredSubscribers; } |
||||
|
} |
||||
|
|
||||
|
public event EventHandler<NameScopeEventArgs> Unregistered |
||||
|
{ |
||||
|
add { _nameScope.Unregistered += value; ++NameScopeUnregisteredSubscribers; } |
||||
|
remove { _nameScope.Unregistered -= value; --NameScopeUnregisteredSubscribers; } |
||||
|
} |
||||
|
|
||||
|
public int NameScopeRegisteredSubscribers { get; private set; } |
||||
|
|
||||
|
public int NameScopeUnregisteredSubscribers { get; private set; } |
||||
|
|
||||
|
public IRenderTarget RenderTarget |
||||
|
{ |
||||
|
get { throw new NotImplementedException(); } |
||||
|
} |
||||
|
|
||||
|
public IRenderQueueManager RenderQueueManager |
||||
|
{ |
||||
|
get { throw new NotImplementedException(); } |
||||
|
} |
||||
|
|
||||
|
public Point TranslatePointToScreen(Point p) |
||||
|
{ |
||||
|
throw new NotImplementedException(); |
||||
|
} |
||||
|
|
||||
|
public void Register(string name, object element) |
||||
|
{ |
||||
|
_nameScope.Register(name, element); |
||||
|
} |
||||
|
|
||||
|
public object Find(string name) |
||||
|
{ |
||||
|
return _nameScope.Find(name); |
||||
|
} |
||||
|
|
||||
|
public void Unregister(string name) |
||||
|
{ |
||||
|
_nameScope.Unregister(name); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,55 @@ |
|||||
|
// Copyright (c) The Perspex 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 Perspex.SceneGraph.UnitTests |
||||
|
{ |
||||
|
public class NameScopeTests |
||||
|
{ |
||||
|
[Fact] |
||||
|
public void Register_Registers_Element() |
||||
|
{ |
||||
|
var target = new NameScope(); |
||||
|
var element = new object(); |
||||
|
|
||||
|
target.Register("foo", element); |
||||
|
|
||||
|
Assert.Same(element, target.Find("foo")); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Unregister_Unregisters_Element() |
||||
|
{ |
||||
|
var target = new NameScope(); |
||||
|
var element = new object(); |
||||
|
|
||||
|
target.Register("foo", element); |
||||
|
target.Unregister("foo"); |
||||
|
|
||||
|
Assert.Null(target.Find("foo")); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Cannot_Register_New_Element_With_Existing_Name() |
||||
|
{ |
||||
|
var target = new NameScope(); |
||||
|
|
||||
|
target.Register("foo", new object()); |
||||
|
Assert.Throws<ArgumentException>(() => target.Register("foo", new object())); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Can_Register_Same_Element_More_Than_Once() |
||||
|
{ |
||||
|
var target = new NameScope(); |
||||
|
var element = new object(); |
||||
|
|
||||
|
target.Register("foo", element); |
||||
|
target.Register("foo", element); |
||||
|
|
||||
|
Assert.Same(element, target.Find("foo")); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue