62 changed files with 1422 additions and 137 deletions
@ -0,0 +1,15 @@ |
|||
// -----------------------------------------------------------------------
|
|||
// <copyright file="IPerspexList.cs" company="Steven Kirk">
|
|||
// Copyright 2014 MIT Licence. See licence.md for more information.
|
|||
// </copyright>
|
|||
// -----------------------------------------------------------------------
|
|||
|
|||
namespace Perspex.Collections |
|||
{ |
|||
using System.Collections; |
|||
using System.Collections.Generic; |
|||
|
|||
public interface IPerspexList<T> : IList<T>, IList, IReadOnlyPerspexList<T> |
|||
{ |
|||
} |
|||
} |
|||
@ -0,0 +1,93 @@ |
|||
// -----------------------------------------------------------------------
|
|||
// <copyright file="PerspexReadOnlyListView.cs" company="Steven Kirk">
|
|||
// Copyright 2014 MIT Licence. See licence.md for more information.
|
|||
// </copyright>
|
|||
// -----------------------------------------------------------------------
|
|||
|
|||
namespace Perspex.Collections |
|||
{ |
|||
using System; |
|||
using System.Collections; |
|||
using System.Collections.Generic; |
|||
using System.Collections.Specialized; |
|||
using System.ComponentModel; |
|||
using System.Linq; |
|||
|
|||
public class PerspexReadOnlyListView<TIn, TOut> : IReadOnlyPerspexList<TOut>, IDisposable |
|||
{ |
|||
private IReadOnlyPerspexList<TIn> inner; |
|||
|
|||
private Func<TIn, TOut> convert; |
|||
|
|||
public PerspexReadOnlyListView( |
|||
IReadOnlyPerspexList<TIn> inner, |
|||
Func<TIn, TOut> convert) |
|||
{ |
|||
this.inner = inner; |
|||
this.convert = convert; |
|||
this.inner.CollectionChanged += this.InnerCollectionChanged; |
|||
} |
|||
|
|||
public TOut this[int index] |
|||
{ |
|||
get { return this.convert(this.inner[index]); } |
|||
} |
|||
|
|||
public int Count |
|||
{ |
|||
get { return this.inner.Count; } |
|||
} |
|||
|
|||
public event NotifyCollectionChangedEventHandler CollectionChanged; |
|||
|
|||
public event PropertyChangedEventHandler PropertyChanged; |
|||
|
|||
public void Dispose() |
|||
{ |
|||
this.inner.CollectionChanged -= this.InnerCollectionChanged; |
|||
} |
|||
|
|||
public IEnumerator<TOut> GetEnumerator() |
|||
{ |
|||
return this.inner.Select(convert).GetEnumerator(); |
|||
} |
|||
|
|||
IEnumerator IEnumerable.GetEnumerator() |
|||
{ |
|||
return this.GetEnumerator(); |
|||
} |
|||
|
|||
private IList<TOut> ConvertList(IList list) |
|||
{ |
|||
return list.Cast<TIn>().Select(this.convert).ToList(); |
|||
} |
|||
|
|||
private void InnerCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) |
|||
{ |
|||
if (this.CollectionChanged != null) |
|||
{ |
|||
NotifyCollectionChangedEventArgs ev; |
|||
|
|||
switch (e.Action) |
|||
{ |
|||
case NotifyCollectionChangedAction.Add: |
|||
ev = new NotifyCollectionChangedEventArgs( |
|||
NotifyCollectionChangedAction.Add, |
|||
this.ConvertList(e.NewItems), |
|||
e.NewStartingIndex); |
|||
break; |
|||
case NotifyCollectionChangedAction.Remove: |
|||
ev = new NotifyCollectionChangedEventArgs( |
|||
NotifyCollectionChangedAction.Remove, |
|||
this.ConvertList(e.OldItems), |
|||
e.NewStartingIndex); |
|||
break; |
|||
default: |
|||
throw new NotSupportedException("Action not yet implemented."); |
|||
} |
|||
|
|||
this.CollectionChanged(this, ev); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,113 @@ |
|||
// -----------------------------------------------------------------------
|
|||
// <copyright file="PerspexList.cs" company="Steven Kirk">
|
|||
// Copyright 2014 MIT Licence. See licence.md for more information.
|
|||
// </copyright>
|
|||
// -----------------------------------------------------------------------
|
|||
|
|||
namespace Perspex.Collections |
|||
{ |
|||
using System; |
|||
using System.Collections; |
|||
using System.Collections.Generic; |
|||
using System.Collections.Specialized; |
|||
using System.ComponentModel; |
|||
using System.Linq; |
|||
|
|||
/// <summary>
|
|||
/// Implements the <see cref="IReadOnlyPerspexList{T}"/> interface for single items.
|
|||
/// </summary>
|
|||
/// <typeparam name="T">The type of the single item.</typeparam>
|
|||
/// <remarks>
|
|||
/// Classes such as Border can only ever have a single logical child, but they need to
|
|||
/// implement a list of logical children in their ILogical.LogicalChildren property using the
|
|||
/// <see cref="IReadOnlyPerspexList{T}"/> interface. This class facilitates that
|
|||
/// without creating an actual <see cref="PerspexList{T}"/>.
|
|||
/// </remarks>
|
|||
public class SingleItemPerspexList<T> : IReadOnlyPerspexList<T> where T : class |
|||
{ |
|||
private T item; |
|||
|
|||
public SingleItemPerspexList() |
|||
{ |
|||
} |
|||
|
|||
public SingleItemPerspexList(T item) |
|||
{ |
|||
this.item = item; |
|||
} |
|||
|
|||
public T this[int index] |
|||
{ |
|||
get |
|||
{ |
|||
if (index < 0 || index >= this.Count) |
|||
{ |
|||
throw new ArgumentOutOfRangeException(); |
|||
} |
|||
|
|||
return item; |
|||
} |
|||
} |
|||
|
|||
public int Count |
|||
{ |
|||
get { return this.item != null ? 1 : 0; } |
|||
} |
|||
|
|||
public T SingleItem |
|||
{ |
|||
get |
|||
{ |
|||
return this.item; |
|||
} |
|||
|
|||
set |
|||
{ |
|||
NotifyCollectionChangedEventArgs e = null; |
|||
bool countChanged = false; |
|||
|
|||
if (value == null && this.item != null ) |
|||
{ |
|||
e = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, this.item, 0); |
|||
this.item = null; |
|||
countChanged = true; |
|||
} |
|||
else if (value != null && this.item == null) |
|||
{ |
|||
e = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, this.item, 0); |
|||
this.item = value; |
|||
countChanged = true; |
|||
} |
|||
else |
|||
{ |
|||
e = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, value, this.item, 0); |
|||
this.item = value; |
|||
} |
|||
|
|||
if (e != null && this.CollectionChanged != null) |
|||
{ |
|||
this.CollectionChanged(this, e); |
|||
} |
|||
|
|||
if (countChanged && this.PropertyChanged != null) |
|||
{ |
|||
this.PropertyChanged(this, new PropertyChangedEventArgs("Count")); |
|||
} |
|||
} |
|||
} |
|||
|
|||
public event NotifyCollectionChangedEventHandler CollectionChanged; |
|||
|
|||
public event PropertyChangedEventHandler PropertyChanged; |
|||
|
|||
public IEnumerator<T> GetEnumerator() |
|||
{ |
|||
return Enumerable.Repeat(this.item, this.Count).GetEnumerator(); |
|||
} |
|||
|
|||
IEnumerator IEnumerable.GetEnumerator() |
|||
{ |
|||
return this.GetEnumerator(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,114 @@ |
|||
// -----------------------------------------------------------------------
|
|||
// <copyright file="DecoratorTests.cs" company="Steven Kirk">
|
|||
// Copyright 2013 MIT Licence. See licence.md for more information.
|
|||
// </copyright>
|
|||
// -----------------------------------------------------------------------
|
|||
|
|||
namespace Perspex.Controls.UnitTests |
|||
{ |
|||
using System.Collections.Specialized; |
|||
using System.Linq; |
|||
using Microsoft.VisualStudio.TestTools.UnitTesting; |
|||
|
|||
[TestClass] |
|||
public class DecoratorTests |
|||
{ |
|||
[TestMethod] |
|||
public void Setting_Content_Should_Set_Child_Controls_Parent() |
|||
{ |
|||
var decorator = new Decorator(); |
|||
var child = new Control(); |
|||
|
|||
decorator.Content = child; |
|||
|
|||
Assert.AreEqual(child.Parent, decorator); |
|||
Assert.AreEqual(((ILogical)child).LogicalParent, decorator); |
|||
} |
|||
|
|||
[TestMethod] |
|||
public void Clearing_Content_Should_Clear_Child_Controls_Parent() |
|||
{ |
|||
var decorator = new Decorator(); |
|||
var child = new Control(); |
|||
|
|||
decorator.Content = child; |
|||
decorator.Content = null; |
|||
|
|||
Assert.IsNull(child.Parent); |
|||
Assert.IsNull(((ILogical)child).LogicalParent); |
|||
} |
|||
|
|||
[TestMethod] |
|||
public void Content_Control_Should_Appear_In_LogicalChildren() |
|||
{ |
|||
var decorator = new Decorator(); |
|||
var child = new Control(); |
|||
|
|||
decorator.Content = child; |
|||
|
|||
CollectionAssert.AreEqual(new[] { child }, ((ILogical)decorator).LogicalChildren.ToList()); |
|||
} |
|||
|
|||
[TestMethod] |
|||
public void Clearing_Content_Should_Remove_From_LogicalChildren() |
|||
{ |
|||
var decorator = new Decorator(); |
|||
var child = new Control(); |
|||
|
|||
decorator.Content = child; |
|||
decorator.Content = null; |
|||
|
|||
CollectionAssert.AreEqual(new ILogical[0], ((ILogical)decorator).LogicalChildren.ToList()); |
|||
} |
|||
|
|||
[TestMethod] |
|||
public void Setting_Content_Should_Fire_LogicalChildren_CollectionChanged() |
|||
{ |
|||
var decorator = new Decorator(); |
|||
var child = new Control(); |
|||
var called = false; |
|||
|
|||
((ILogical)decorator).LogicalChildren.CollectionChanged += (s, e) => |
|||
called = e.Action == NotifyCollectionChangedAction.Add; |
|||
|
|||
decorator.Content = child; |
|||
|
|||
Assert.IsTrue(called); |
|||
} |
|||
|
|||
[TestMethod] |
|||
public void Clearing_Content_Should_Fire_LogicalChildren_CollectionChanged() |
|||
{ |
|||
var decorator = new Decorator(); |
|||
var child = new Control(); |
|||
var called = false; |
|||
|
|||
decorator.Content = child; |
|||
|
|||
((ILogical)decorator).LogicalChildren.CollectionChanged += (s, e) => |
|||
called = e.Action == NotifyCollectionChangedAction.Remove; |
|||
|
|||
decorator.Content = null; |
|||
|
|||
Assert.IsTrue(called); |
|||
} |
|||
|
|||
[TestMethod] |
|||
public void Changing_Content_Should_Fire_LogicalChildren_CollectionChanged() |
|||
{ |
|||
var decorator = new Decorator(); |
|||
var child1 = new Control(); |
|||
var child2 = new Control(); |
|||
var called = false; |
|||
|
|||
decorator.Content = child1; |
|||
|
|||
((ILogical)decorator).LogicalChildren.CollectionChanged += (s, e) => |
|||
called = e.Action == NotifyCollectionChangedAction.Replace; |
|||
|
|||
decorator.Content = child2; |
|||
|
|||
Assert.IsTrue(called); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,294 @@ |
|||
// -----------------------------------------------------------------------
|
|||
// <copyright file="ItemsControlTests.cs" company="Steven Kirk">
|
|||
// Copyright 2014 MIT Licence. See licence.md for more information.
|
|||
// </copyright>
|
|||
// -----------------------------------------------------------------------
|
|||
|
|||
namespace Perspex.Controls.UnitTests |
|||
{ |
|||
using System; |
|||
using System.Collections.Specialized; |
|||
using System.Linq; |
|||
using Microsoft.VisualStudio.TestTools.UnitTesting; |
|||
using Moq; |
|||
using Perspex.Collections; |
|||
using Perspex.Controls; |
|||
using Perspex.Controls.Presenters; |
|||
using Perspex.Layout; |
|||
using Perspex.Platform; |
|||
using Perspex.Styling; |
|||
using Perspex.VisualTree; |
|||
using Ploeh.AutoFixture; |
|||
using Ploeh.AutoFixture.AutoMoq; |
|||
using Splat; |
|||
|
|||
[TestClass] |
|||
public class ItemsControlTests |
|||
{ |
|||
[TestMethod] |
|||
public void Template_Should_Be_Instantiated() |
|||
{ |
|||
using (var ctx = this.RegisterServices()) |
|||
{ |
|||
var target = new ItemsControl(); |
|||
target.Items = new[] { "Foo" }; |
|||
target.Template = this.GetTemplate(); |
|||
|
|||
target.Measure(new Size(100, 100)); |
|||
|
|||
var child = ((IVisual)target).VisualChildren.Single(); |
|||
Assert.IsInstanceOfType(child, typeof(Border)); |
|||
child = child.VisualChildren.Single(); |
|||
Assert.IsInstanceOfType(child, typeof(ItemsPresenter)); |
|||
child = child.VisualChildren.Single(); |
|||
Assert.IsInstanceOfType(child, typeof(StackPanel)); |
|||
child = child.VisualChildren.Single(); |
|||
Assert.IsInstanceOfType(child, typeof(TextBlock)); |
|||
} |
|||
} |
|||
|
|||
[TestMethod] |
|||
public void Templated_Children_Should_Be_Styled() |
|||
{ |
|||
using (var ctx = this.RegisterServices()) |
|||
{ |
|||
var root = new TestRoot(); |
|||
var target = new ItemsControl(); |
|||
var styler = new Mock<IStyler>(); |
|||
|
|||
Locator.CurrentMutable.Register(() => styler.Object, typeof(IStyler)); |
|||
target.Items = new[] { "Foo" }; |
|||
target.Template = this.GetTemplate(); |
|||
root.Content = target; |
|||
|
|||
target.ApplyTemplate(); |
|||
|
|||
styler.Verify(x => x.ApplyStyles(It.IsAny<ItemsControl>()), Times.Once()); |
|||
styler.Verify(x => x.ApplyStyles(It.IsAny<Border>()), Times.Once()); |
|||
styler.Verify(x => x.ApplyStyles(It.IsAny<ItemsPresenter>()), Times.Once()); |
|||
styler.Verify(x => x.ApplyStyles(It.IsAny<StackPanel>()), Times.Once()); |
|||
styler.Verify(x => x.ApplyStyles(It.IsAny<TextBlock>()), Times.Once()); |
|||
} |
|||
} |
|||
|
|||
[TestMethod] |
|||
public void ItemsPresenter_And_Panel_Should_Have_TemplatedParent_Set() |
|||
{ |
|||
var target = new ItemsControl(); |
|||
|
|||
target.Template = this.GetTemplate(); |
|||
target.Items = new[] { "Foo" }; |
|||
target.ApplyTemplate(); |
|||
|
|||
var presenter = target.GetTemplateControls().OfType<ItemsPresenter>().Single(); |
|||
var panel = target.GetTemplateControls().OfType<StackPanel>().Single(); |
|||
|
|||
Assert.AreEqual(target, presenter.TemplatedParent); |
|||
Assert.AreEqual(target, panel.TemplatedParent); |
|||
} |
|||
|
|||
[TestMethod] |
|||
public void Item_Should_Have_TemplatedParent_Set_To_Null() |
|||
{ |
|||
var target = new ItemsControl(); |
|||
|
|||
target.Template = this.GetTemplate(); |
|||
target.Items = new[] { "Foo" }; |
|||
target.ApplyTemplate(); |
|||
|
|||
var panel = target.GetTemplateControls().OfType<StackPanel>().Single(); |
|||
var item = (TextBlock)panel.GetVisualChildren().First(); |
|||
|
|||
Assert.IsNull(item.TemplatedParent); |
|||
} |
|||
|
|||
[TestMethod] |
|||
public void Control_Item_Should_Set_Control_Parent() |
|||
{ |
|||
var target = new ItemsControl(); |
|||
var child = new Control(); |
|||
|
|||
target.Template = this.GetTemplate(); |
|||
target.Items = new[] { child }; |
|||
target.ApplyTemplate(); |
|||
|
|||
Assert.AreEqual(target, child.Parent); |
|||
Assert.AreEqual(target, ((ILogical)child).LogicalParent); |
|||
} |
|||
|
|||
[TestMethod] |
|||
public void Clearing_Control_Item_Should_Clear_Child_Controls_Parent() |
|||
{ |
|||
var target = new ItemsControl(); |
|||
var child = new Control(); |
|||
|
|||
target.Template = this.GetTemplate(); |
|||
target.Items = new[] { child }; |
|||
target.ApplyTemplate(); |
|||
target.Items = null; |
|||
|
|||
Assert.IsNull(child.Parent); |
|||
Assert.IsNull(((ILogical)child).LogicalParent); |
|||
} |
|||
|
|||
[TestMethod] |
|||
public void Control_Item_Should_Make_Control_Appear_In_LogicalChildren() |
|||
{ |
|||
var target = new ItemsControl(); |
|||
var child = new Control(); |
|||
|
|||
target.Template = this.GetTemplate(); |
|||
target.Items = new[] { child }; |
|||
target.ApplyTemplate(); |
|||
|
|||
CollectionAssert.AreEqual(new[] { child }, ((ILogical)target).LogicalChildren.ToList()); |
|||
} |
|||
|
|||
[TestMethod] |
|||
public void String_Item_Should_Make_TextBlock_Appear_In_LogicalChildren() |
|||
{ |
|||
var target = new ItemsControl(); |
|||
var child = new Control(); |
|||
|
|||
target.Template = this.GetTemplate(); |
|||
target.Items = new[] { "Foo" }; |
|||
target.ApplyTemplate(); |
|||
|
|||
var logical = (ILogical)target; |
|||
Assert.AreEqual(1, logical.LogicalChildren.Count); |
|||
Assert.IsInstanceOfType(logical.LogicalChildren[0], typeof(TextBlock)); |
|||
} |
|||
|
|||
[TestMethod] |
|||
public void Setting_Items_To_Null_Should_Remove_LogicalChildren() |
|||
{ |
|||
var target = new ItemsControl(); |
|||
var child = new Control(); |
|||
|
|||
target.Template = this.GetTemplate(); |
|||
target.Items = new[] { "Foo" }; |
|||
target.ApplyTemplate(); |
|||
target.Items = null; |
|||
|
|||
CollectionAssert.AreEqual(new ILogical[0], ((ILogical)target).LogicalChildren.ToList()); |
|||
} |
|||
|
|||
[TestMethod] |
|||
public void Setting_Items_Should_Fire_LogicalChildren_CollectionChanged() |
|||
{ |
|||
var target = new ItemsControl(); |
|||
var child = new Control(); |
|||
var called = false; |
|||
|
|||
target.Template = this.GetTemplate(); |
|||
target.ApplyTemplate(); |
|||
|
|||
((ILogical)target).LogicalChildren.CollectionChanged += (s, e) => |
|||
called = e.Action == NotifyCollectionChangedAction.Add; |
|||
|
|||
target.Items = new[] { child }; |
|||
|
|||
Assert.IsTrue(called); |
|||
} |
|||
|
|||
[TestMethod] |
|||
public void Setting_Items_To_Null_Should_Fire_LogicalChildren_CollectionChanged() |
|||
{ |
|||
var target = new ItemsControl(); |
|||
var child = new Control(); |
|||
var called = false; |
|||
|
|||
target.Template = this.GetTemplate(); |
|||
target.Items = new[] { child }; |
|||
target.ApplyTemplate(); |
|||
|
|||
((ILogical)target).LogicalChildren.CollectionChanged += (s, e) => |
|||
called = e.Action == NotifyCollectionChangedAction.Remove; |
|||
|
|||
target.Items = null; |
|||
|
|||
Assert.IsTrue(called); |
|||
} |
|||
|
|||
[TestMethod] |
|||
public void Changing_Items_Should_Fire_LogicalChildren_CollectionChanged() |
|||
{ |
|||
var target = new ItemsControl(); |
|||
var child = new Control(); |
|||
var called = false; |
|||
|
|||
target.Template = this.GetTemplate(); |
|||
target.Items = new[] { child }; |
|||
target.ApplyTemplate(); |
|||
|
|||
((ILogical)target).LogicalChildren.CollectionChanged += (s, e) => called = true; |
|||
|
|||
target.Items = new[] { "Foo" }; |
|||
|
|||
Assert.IsTrue(called); |
|||
} |
|||
|
|||
[TestMethod] |
|||
public void Adding_Items_Should_Fire_LogicalChildren_CollectionChanged() |
|||
{ |
|||
var target = new ItemsControl(); |
|||
var items = new PerspexList<string> { "Foo" }; |
|||
var called = false; |
|||
|
|||
target.Template = this.GetTemplate(); |
|||
target.Items = items; |
|||
target.ApplyTemplate(); |
|||
|
|||
((ILogical)target).LogicalChildren.CollectionChanged += (s, e) => |
|||
called = e.Action == NotifyCollectionChangedAction.Add; |
|||
|
|||
items.Add("Bar"); |
|||
|
|||
Assert.IsTrue(called); |
|||
} |
|||
|
|||
[TestMethod] |
|||
public void Removing_Items_Should_Fire_LogicalChildren_CollectionChanged() |
|||
{ |
|||
var target = new ItemsControl(); |
|||
var items = new PerspexList<string> { "Foo", "Bar" }; |
|||
var called = false; |
|||
|
|||
target.Template = this.GetTemplate(); |
|||
target.Items = items; |
|||
target.ApplyTemplate(); |
|||
|
|||
((ILogical)target).LogicalChildren.CollectionChanged += (s, e) => |
|||
called = e.Action == NotifyCollectionChangedAction.Remove; |
|||
|
|||
items.Remove("Bar"); |
|||
|
|||
Assert.IsTrue(called); |
|||
} |
|||
|
|||
private ControlTemplate GetTemplate() |
|||
{ |
|||
return ControlTemplate.Create<ItemsControl>(parent => |
|||
{ |
|||
return new Border |
|||
{ |
|||
Background = new Perspex.Media.SolidColorBrush(0xffffffff), |
|||
Content = new ItemsPresenter |
|||
{ |
|||
Id = "itemsPresenter", |
|||
[~ItemsPresenter.ItemsProperty] = parent[~ItemsControl.ItemsProperty], |
|||
} |
|||
}; |
|||
}); |
|||
} |
|||
|
|||
private IDisposable RegisterServices() |
|||
{ |
|||
var result = Locator.CurrentMutable.WithResolver(); |
|||
var fixture = new Fixture().Customize(new AutoMoqCustomization()); |
|||
var renderInterface = fixture.Create<IPlatformRenderInterface>(); |
|||
Locator.CurrentMutable.RegisterConstant(renderInterface, typeof(IPlatformRenderInterface)); |
|||
return result; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,82 @@ |
|||
// -----------------------------------------------------------------------
|
|||
// <copyright file="PanelTests.cs" company="Steven Kirk">
|
|||
// Copyright 2013 MIT Licence. See licence.md for more information.
|
|||
// </copyright>
|
|||
// -----------------------------------------------------------------------
|
|||
|
|||
namespace Perspex.Controls.UnitTests |
|||
{ |
|||
using System.Linq; |
|||
using Microsoft.VisualStudio.TestTools.UnitTesting; |
|||
|
|||
[TestClass] |
|||
public class PanelTests |
|||
{ |
|||
[TestMethod] |
|||
public void Adding_Control_To_Panel_Should_Set_Child_Controls_Parent() |
|||
{ |
|||
var panel = new Panel(); |
|||
var child = new Control(); |
|||
|
|||
panel.Children.Add(child); |
|||
|
|||
Assert.AreEqual(child.Parent, panel); |
|||
Assert.AreEqual(((ILogical)child).LogicalParent, panel); |
|||
} |
|||
|
|||
[TestMethod] |
|||
public void Removing_Control_From_Panel_Should_Clear_Child_Controls_Parent() |
|||
{ |
|||
var panel = new Panel(); |
|||
var child = new Control(); |
|||
|
|||
panel.Children.Add(child); |
|||
panel.Children.Remove(child); |
|||
|
|||
Assert.IsNull(child.Parent); |
|||
Assert.IsNull(((ILogical)child).LogicalParent); |
|||
} |
|||
|
|||
[TestMethod] |
|||
public void Clearing_Panel_Children_Should_Clear_Child_Controls_Parent() |
|||
{ |
|||
var panel = new Panel(); |
|||
var child1 = new Control(); |
|||
var child2 = new Control(); |
|||
|
|||
panel.Children.Add(child1); |
|||
panel.Children.Add(child2); |
|||
panel.Children.Clear(); |
|||
|
|||
Assert.IsNull(child1.Parent); |
|||
Assert.IsNull(((ILogical)child1).LogicalParent); |
|||
Assert.IsNull(child2.Parent); |
|||
Assert.IsNull(((ILogical)child2).LogicalParent); |
|||
} |
|||
|
|||
[TestMethod] |
|||
public void Child_Control_Should_Appear_In_Panel_Children() |
|||
{ |
|||
var panel = new Panel(); |
|||
var child = new Control(); |
|||
|
|||
panel.Children.Add(child); |
|||
|
|||
CollectionAssert.AreEqual(new[] { child }, panel.Children); |
|||
CollectionAssert.AreEqual(new[] { child }, ((ILogical)panel).LogicalChildren.ToList()); |
|||
} |
|||
|
|||
[TestMethod] |
|||
public void Removing_Child_Control_Should_Remove_From_Panel_Children() |
|||
{ |
|||
var panel = new Panel(); |
|||
var child = new Control(); |
|||
|
|||
panel.Children.Add(child); |
|||
panel.Children.Remove(child); |
|||
|
|||
CollectionAssert.AreEqual(new Control[0], panel.Children); |
|||
CollectionAssert.AreEqual(new ILogical[0], ((ILogical)panel).LogicalChildren.ToList()); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,29 @@ |
|||
// -----------------------------------------------------------------------
|
|||
// <copyright file="LogicalTreeNode.cs" company="Steven Kirk">
|
|||
// Copyright 2014 MIT Licence. See licence.md for more information.
|
|||
// </copyright>
|
|||
// -----------------------------------------------------------------------
|
|||
|
|||
namespace Perspex.Diagnostics.ViewModels |
|||
{ |
|||
using System; |
|||
using Perspex.Controls; |
|||
using ReactiveUI; |
|||
|
|||
internal class LogicalTreeNode : TreeNode |
|||
{ |
|||
private string classes; |
|||
|
|||
public LogicalTreeNode(ILogical logical) |
|||
: base((Control)logical) |
|||
{ |
|||
this.Children = logical.LogicalChildren.CreateDerivedCollection(x => new LogicalTreeNode(x)); |
|||
} |
|||
|
|||
public static LogicalTreeNode[] Create(object control) |
|||
{ |
|||
var logical = control as ILogical; |
|||
return logical != null ? new[] { new LogicalTreeNode(logical) } : null; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,63 @@ |
|||
// -----------------------------------------------------------------------
|
|||
// <copyright file="LogicalTreeNode.cs" company="Steven Kirk">
|
|||
// Copyright 2014 MIT Licence. See licence.md for more information.
|
|||
// </copyright>
|
|||
// -----------------------------------------------------------------------
|
|||
|
|||
namespace Perspex.Diagnostics.ViewModels |
|||
{ |
|||
using System; |
|||
using System.Reactive; |
|||
using System.Reactive.Linq; |
|||
using Perspex.Controls; |
|||
using ReactiveUI; |
|||
|
|||
internal class TreeNode : ReactiveObject |
|||
{ |
|||
private string classes; |
|||
|
|||
public TreeNode(Control control) |
|||
{ |
|||
this.Control = control; |
|||
this.Type = control.GetType().Name; |
|||
|
|||
control.Classes.Changed.Select(_ => Unit.Default) |
|||
.StartWith(Unit.Default) |
|||
.Subscribe(_ => |
|||
{ |
|||
if (control.Classes.Count > 0) |
|||
{ |
|||
this.Classes = "(" + string.Join(" ", control.Classes) + ")"; |
|||
} |
|||
else |
|||
{ |
|||
this.Classes = string.Empty; |
|||
} |
|||
}); |
|||
} |
|||
|
|||
public IReactiveDerivedList<TreeNode> Children |
|||
{ |
|||
get; |
|||
protected set; |
|||
} |
|||
|
|||
public string Classes |
|||
{ |
|||
get { return this.classes; } |
|||
private set { this.RaiseAndSetIfChanged(ref this.classes, value); } |
|||
} |
|||
|
|||
public string Type |
|||
{ |
|||
get; |
|||
private set; |
|||
} |
|||
|
|||
public Control Control |
|||
{ |
|||
get; |
|||
private set; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,26 @@ |
|||
// -----------------------------------------------------------------------
|
|||
// <copyright file="ILogical.cs" company="Steven Kirk">
|
|||
// Copyright 2014 MIT Licence. See licence.md for more information.
|
|||
// </copyright>
|
|||
// -----------------------------------------------------------------------
|
|||
|
|||
namespace Perspex |
|||
{ |
|||
using Perspex.Collections; |
|||
|
|||
/// <summary>
|
|||
/// Represents a node in the logical tree.
|
|||
/// </summary>
|
|||
public interface ILogical |
|||
{ |
|||
/// <summary>
|
|||
/// Gets the logical parent.
|
|||
/// </summary>
|
|||
ILogical LogicalParent { get; } |
|||
|
|||
/// <summary>
|
|||
/// Gets the logical children.
|
|||
/// </summary>
|
|||
IReadOnlyPerspexList<ILogical> LogicalChildren { get; } |
|||
} |
|||
} |
|||
Loading…
Reference in new issue