153 changed files with 4401 additions and 2000 deletions
@ -0,0 +1,84 @@ |
|||
using System; |
|||
using Perspex; |
|||
using Perspex.Controls; |
|||
using Perspex.Controls.Primitives; |
|||
using Perspex.Media; |
|||
|
|||
namespace XamlTestApplication |
|||
{ |
|||
public class TestScrollable : Control, IScrollable |
|||
{ |
|||
private int itemCount = 100; |
|||
private Size _extent; |
|||
private Vector _offset; |
|||
private Size _viewport; |
|||
private Size _lineSize; |
|||
|
|||
public Action InvalidateScroll { get; set; } |
|||
|
|||
Size IScrollable.Extent |
|||
{ |
|||
get { return _extent; } |
|||
} |
|||
|
|||
Vector IScrollable.Offset |
|||
{ |
|||
get { return _offset; } |
|||
|
|||
set |
|||
{ |
|||
_offset = value; |
|||
InvalidateVisual(); |
|||
} |
|||
} |
|||
|
|||
Size IScrollable.Viewport |
|||
{ |
|||
get { return _viewport; } |
|||
} |
|||
|
|||
protected override Size MeasureOverride(Size availableSize) |
|||
{ |
|||
using (var line = new FormattedText( |
|||
"Item 100", |
|||
TextBlock.GetFontFamily(this), |
|||
TextBlock.GetFontSize(this), |
|||
TextBlock.GetFontStyle(this), |
|||
TextAlignment.Left, |
|||
TextBlock.GetFontWeight(this))) |
|||
{ |
|||
line.Constraint = availableSize; |
|||
_lineSize = line.Measure(); |
|||
return new Size(_lineSize.Width, _lineSize.Height * itemCount); |
|||
} |
|||
} |
|||
|
|||
protected override Size ArrangeOverride(Size finalSize) |
|||
{ |
|||
_viewport = new Size(finalSize.Width, finalSize.Height / _lineSize.Height); |
|||
_extent = new Size(_lineSize.Width, itemCount + 1); |
|||
InvalidateScroll?.Invoke(); |
|||
return finalSize; |
|||
} |
|||
|
|||
public override void Render(DrawingContext context) |
|||
{ |
|||
var y = 0.0; |
|||
|
|||
for (var i = (int)_offset.Y; i < itemCount; ++i) |
|||
{ |
|||
using (var line = new FormattedText( |
|||
"Item " + (i + 1), |
|||
TextBlock.GetFontFamily(this), |
|||
TextBlock.GetFontSize(this), |
|||
TextBlock.GetFontStyle(this), |
|||
TextAlignment.Left, |
|||
TextBlock.GetFontWeight(this))) |
|||
{ |
|||
context.DrawText(Brushes.Black, new Point(-_offset.X, y), line); |
|||
y += _lineSize.Height; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,253 @@ |
|||
// 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.Linq; |
|||
using Perspex.Collections; |
|||
|
|||
namespace Perspex.Controls |
|||
{ |
|||
/// <summary>
|
|||
/// Holds a collection of style classes for an <see cref="IControl"/>.
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// Similar to CSS, each control may have any number of styling classes applied.
|
|||
/// </remarks>
|
|||
public class Classes : PerspexList<string>, IPseudoClasses |
|||
{ |
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="Classes"/> class.
|
|||
/// </summary>
|
|||
public Classes() |
|||
{ |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="Classes"/> class.
|
|||
/// </summary>
|
|||
/// <param name="items">The initial items.</param>
|
|||
public Classes(IEnumerable<string> items) |
|||
: base(items) |
|||
{ |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="Classes"/> class.
|
|||
/// </summary>
|
|||
/// <param name="items">The initial items.</param>
|
|||
public Classes(params string[] items) |
|||
: base(items) |
|||
{ |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Adds a style class to the collection.
|
|||
/// </summary>
|
|||
/// <param name="name">The class name.</param>
|
|||
/// <remarks>
|
|||
/// Only standard classes may be added via this method. To add pseudoclasses (classes
|
|||
/// beginning with a ':' character) use the protected <see cref="Control.PseudoClasses"/>
|
|||
/// property.
|
|||
/// </remarks>
|
|||
public override void Add(string name) |
|||
{ |
|||
ThrowIfPseudoclass(name, "added"); |
|||
|
|||
if (!Contains(name)) |
|||
{ |
|||
base.Add(name); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Adds a style classes to the collection.
|
|||
/// </summary>
|
|||
/// <param name="names">The class names.</param>
|
|||
/// <remarks>
|
|||
/// Only standard classes may be added via this method. To add pseudoclasses (classes
|
|||
/// beginning with a ':' character) use the protected <see cref="Control.PseudoClasses"/>
|
|||
/// property.
|
|||
/// </remarks>
|
|||
public override void AddRange(IEnumerable<string> names) |
|||
{ |
|||
var c = new List<string>(); |
|||
|
|||
foreach (var name in names) |
|||
{ |
|||
ThrowIfPseudoclass(name, "added"); |
|||
|
|||
if (!Contains(name)) |
|||
{ |
|||
c.Add(name); |
|||
} |
|||
} |
|||
|
|||
base.AddRange(c); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Inserts a style class into the collection.
|
|||
/// </summary>
|
|||
/// <param name="index">The index to insert the class at.</param>
|
|||
/// <param name="name">The class name.</param>
|
|||
/// <remarks>
|
|||
/// Only standard classes may be added via this method. To add pseudoclasses (classes
|
|||
/// beginning with a ':' character) use the protected <see cref="Control.PseudoClasses"/>
|
|||
/// property.
|
|||
/// </remarks>
|
|||
public override void Insert(int index, string name) |
|||
{ |
|||
ThrowIfPseudoclass(name, "added"); |
|||
|
|||
if (!Contains(name)) |
|||
{ |
|||
base.Insert(index, name); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Inserts style classes into the collection.
|
|||
/// </summary>
|
|||
/// <param name="index">The index to insert the class at.</param>
|
|||
/// <param name="names">The class names.</param>
|
|||
/// <remarks>
|
|||
/// Only standard classes may be added via this method. To add pseudoclasses (classes
|
|||
/// beginning with a ':' character) use the protected <see cref="Control.PseudoClasses"/>
|
|||
/// property.
|
|||
/// </remarks>
|
|||
public override void InsertRange(int index, IEnumerable<string> names) |
|||
{ |
|||
var c = new List<string>(); |
|||
|
|||
foreach (var name in names) |
|||
{ |
|||
ThrowIfPseudoclass(name, "added"); |
|||
|
|||
if (!Contains(name)) |
|||
{ |
|||
c.Add(name); |
|||
} |
|||
} |
|||
|
|||
base.InsertRange(index, c); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Removes a style class from the collection.
|
|||
/// </summary>
|
|||
/// <param name="name">The class name.</param>
|
|||
/// <remarks>
|
|||
/// Only standard classes may be removed via this method. To remove pseudoclasses (classes
|
|||
/// beginning with a ':' character) use the protected <see cref="Control.PseudoClasses"/>
|
|||
/// property.
|
|||
/// </remarks>
|
|||
public override bool Remove(string name) |
|||
{ |
|||
ThrowIfPseudoclass(name, "removed"); |
|||
return base.Remove(name); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Removes style classes from the collection.
|
|||
/// </summary>
|
|||
/// <param name="names">The class name.</param>
|
|||
/// <remarks>
|
|||
/// Only standard classes may be removed via this method. To remove pseudoclasses (classes
|
|||
/// beginning with a ':' character) use the protected <see cref="Control.PseudoClasses"/>
|
|||
/// property.
|
|||
/// </remarks>
|
|||
public override void RemoveAll(IEnumerable<string> names) |
|||
{ |
|||
var c = new List<string>(); |
|||
|
|||
foreach (var name in names) |
|||
{ |
|||
ThrowIfPseudoclass(name, "removed"); |
|||
|
|||
if (!Contains(name)) |
|||
{ |
|||
c.Add(name); |
|||
} |
|||
} |
|||
|
|||
base.RemoveAll(c); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Removes a style class from the collection.
|
|||
/// </summary>
|
|||
/// <param name="index">The index of the class in the collection.</param>
|
|||
/// <remarks>
|
|||
/// Only standard classes may be removed via this method. To remove pseudoclasses (classes
|
|||
/// beginning with a ':' character) use the protected <see cref="Control.PseudoClasses"/>
|
|||
/// property.
|
|||
/// </remarks>
|
|||
public override void RemoveAt(int index) |
|||
{ |
|||
var name = this[index]; |
|||
ThrowIfPseudoclass(name, "removed"); |
|||
base.RemoveAt(index); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Removes style classes from the collection.
|
|||
/// </summary>
|
|||
/// <param name="index">The first index to remove.</param>
|
|||
/// <param name="count">The number of items to remove.</param>
|
|||
public override void RemoveRange(int index, int count) |
|||
{ |
|||
var names = GetRange(index, count); |
|||
base.RemoveRange(index, count); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Removes all non-pseudoclasses in the collection and adds a new set.
|
|||
/// </summary>
|
|||
/// <param name="source">The new contents of the collection.</param>
|
|||
public void Replace(IList<string> source) |
|||
{ |
|||
var toRemove = new List<string>(); |
|||
|
|||
foreach (var name in source) |
|||
{ |
|||
ThrowIfPseudoclass(name, "added"); |
|||
} |
|||
|
|||
foreach (var name in this) |
|||
{ |
|||
if (!name.StartsWith(":")) |
|||
{ |
|||
toRemove.Add(name); |
|||
} |
|||
} |
|||
|
|||
base.RemoveAll(toRemove); |
|||
base.AddRange(source); |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
void IPseudoClasses.Add(string name) |
|||
{ |
|||
if (!Contains(name)) |
|||
{ |
|||
base.Add(name); |
|||
} |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
bool IPseudoClasses.Remove(string name) |
|||
{ |
|||
return base.Remove(name); |
|||
} |
|||
|
|||
private void ThrowIfPseudoclass(string name, string operation) |
|||
{ |
|||
if (name.StartsWith(":")) |
|||
{ |
|||
throw new ArgumentException( |
|||
$"The pseudoclass '{name}' may only be {operation} by the control itself."); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,74 @@ |
|||
using Perspex.Animation; |
|||
using Perspex.Controls.Primitives; |
|||
|
|||
namespace Perspex.Controls |
|||
{ |
|||
public enum ExpandDirection |
|||
{ |
|||
Down, |
|||
Up, |
|||
Left, |
|||
Right |
|||
} |
|||
|
|||
public class Expander : HeaderedContentControl |
|||
{ |
|||
public static readonly PerspexProperty<bool> IsExpandedProperty = |
|||
PerspexProperty.Register<Expander, bool>(nameof(IsExpanded), true); |
|||
|
|||
public static readonly PerspexProperty<ExpandDirection> ExpandDirectionProperty = |
|||
PerspexProperty.Register<Expander, ExpandDirection>(nameof(ExpandDirection), ExpandDirection.Down); |
|||
|
|||
public static readonly PerspexProperty<IPageTransition> ContentTransitionProperty = |
|||
PerspexProperty.Register<Expander, IPageTransition>(nameof(ContentTransition)); |
|||
|
|||
static Expander() |
|||
{ |
|||
PseudoClass(ExpandDirectionProperty, d => d == ExpandDirection.Down, ":down"); |
|||
PseudoClass(ExpandDirectionProperty, d => d == ExpandDirection.Up, ":up"); |
|||
PseudoClass(ExpandDirectionProperty, d => d == ExpandDirection.Left, ":left"); |
|||
PseudoClass(ExpandDirectionProperty, d => d == ExpandDirection.Right, ":right"); |
|||
|
|||
PseudoClass(IsExpandedProperty, ":expanded"); |
|||
|
|||
IsExpandedProperty.Changed.AddClassHandler<Expander>(x => x.OnIsExpandedChanged); |
|||
} |
|||
|
|||
public bool IsExpanded |
|||
{ |
|||
get { return GetValue(IsExpandedProperty); } |
|||
set { SetValue(IsExpandedProperty, value); } |
|||
} |
|||
|
|||
public ExpandDirection ExpandDirection |
|||
{ |
|||
get { return GetValue(ExpandDirectionProperty); } |
|||
set { SetValue(ExpandDirectionProperty, value); } |
|||
} |
|||
|
|||
public IPageTransition ContentTransition |
|||
{ |
|||
get { return GetValue(ContentTransitionProperty); } |
|||
set { SetValue(ContentTransitionProperty, value); } |
|||
} |
|||
|
|||
protected virtual void OnIsExpandedChanged(PerspexPropertyChangedEventArgs e) |
|||
{ |
|||
IVisual visualContent = Presenter; |
|||
|
|||
if (Content != null && ContentTransition != null && visualContent != null) |
|||
{ |
|||
bool forward = ExpandDirection == ExpandDirection.Left || |
|||
ExpandDirection == ExpandDirection.Up; |
|||
if (IsExpanded) |
|||
{ |
|||
ContentTransition.Start(null, visualContent, forward); |
|||
} |
|||
else |
|||
{ |
|||
ContentTransition.Start(visualContent, null, !forward); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,45 @@ |
|||
// 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.Controls.Generators |
|||
{ |
|||
/// <summary>
|
|||
/// Holds information about an item container generated by an
|
|||
/// <see cref="IItemContainerGenerator"/>.
|
|||
/// </summary>
|
|||
public class ItemContainer |
|||
{ |
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="ItemContainer"/> class.
|
|||
/// </summary>
|
|||
/// <param name="container">The container control.</param>
|
|||
/// <param name="item">The item that the container represents.</param>
|
|||
/// <param name="index">
|
|||
/// The index of the item in the <see cref="ItemsControl.Items"/> collection.
|
|||
/// </param>
|
|||
public ItemContainer(IControl container, object item, int index) |
|||
{ |
|||
ContainerControl = container; |
|||
Item = item; |
|||
Index = index; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the container control.
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// This will be null if <see cref="Item"/> is null.
|
|||
/// </remarks>
|
|||
public IControl ContainerControl { get; } |
|||
|
|||
/// <summary>
|
|||
/// Gets the item that the container represents.
|
|||
/// </summary>
|
|||
public object Item { get; } |
|||
|
|||
/// <summary>
|
|||
/// Gets the index of the item in the <see cref="ItemsControl.Items"/> collection.
|
|||
/// </summary>
|
|||
public int Index { get; } |
|||
} |
|||
} |
|||
@ -1,34 +1,38 @@ |
|||
// 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.Controls.Generators |
|||
{ |
|||
/// <summary>
|
|||
/// Holds details about a set of item containers in an <see cref="IItemContainerGenerator"/>.
|
|||
/// Provides details for the <see cref="IItemContainerGenerator.Materialized"/>
|
|||
/// and <see cref="IItemContainerGenerator.Dematerialized"/> events.
|
|||
/// </summary>
|
|||
public class ItemContainers |
|||
public class ItemContainerEventArgs : EventArgs |
|||
{ |
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="ItemContainers"/> class.
|
|||
/// Initializes a new instance of the <see cref="ItemContainerEventArgs"/> class.
|
|||
/// </summary>
|
|||
/// <param name="startingIndex">The index of the first container in the source items.</param>
|
|||
/// <param name="containers">The containers.</param>
|
|||
public ItemContainers(int startingIndex, IList<IControl> containers) |
|||
public ItemContainerEventArgs( |
|||
int startingIndex, |
|||
IList<ItemContainer> containers) |
|||
{ |
|||
StartingIndex = startingIndex; |
|||
Items = containers; |
|||
Containers = containers; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the index of the first container in the source items.
|
|||
/// Gets the containers.
|
|||
/// </summary>
|
|||
public int StartingIndex { get; } |
|||
public IList<ItemContainer> Containers { get; } |
|||
|
|||
/// <summary>
|
|||
/// Gets the containers. May contain null entries.
|
|||
/// Gets the index of the first container in the source items.
|
|||
/// </summary>
|
|||
public IList<IControl> Items { get; } |
|||
public int StartingIndex { get; } |
|||
} |
|||
} |
|||
@ -0,0 +1,87 @@ |
|||
// 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.Collections.Generic; |
|||
|
|||
namespace Perspex.Controls.Generators |
|||
{ |
|||
/// <summary>
|
|||
/// Maintains an index of all item containers currently materialized by a <see cref="TreeView"/>.
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// Each <see cref="TreeViewItem"/> has its own <see cref="TreeItemContainerGenerator{T}"/>
|
|||
/// that maintains the list of its direct children, but they also share an instance of this
|
|||
/// class in their <see cref="TreeItemContainerGenerator{T}.Index"/> property which tracks
|
|||
/// the containers materialized for the entire tree.
|
|||
/// </remarks>
|
|||
public class TreeContainerIndex |
|||
{ |
|||
private readonly Dictionary<object, IControl> _itemToContainer = new Dictionary<object, IControl>(); |
|||
private readonly Dictionary<IControl, object> _containerToItem = new Dictionary<IControl, object>(); |
|||
|
|||
/// <summary>
|
|||
/// Gets the currently materialized containers.
|
|||
/// </summary>
|
|||
public IEnumerable<IControl> Items => _containerToItem.Keys; |
|||
|
|||
/// <summary>
|
|||
/// Adds an entry to the index.
|
|||
/// </summary>
|
|||
/// <param name="item">The item.</param>
|
|||
/// <param name="container">The item container.</param>
|
|||
public void Add(object item, IControl container) |
|||
{ |
|||
_itemToContainer.Add(item, container); |
|||
_containerToItem.Add(container, item); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Removes a container from the index.
|
|||
/// </summary>
|
|||
/// <param name="container">The item container.</param>
|
|||
public void Remove(IControl container) |
|||
{ |
|||
var item = _containerToItem[container]; |
|||
_containerToItem.Remove(container); |
|||
_itemToContainer.Remove(item); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Removes a set of containers from the index.
|
|||
/// </summary>
|
|||
/// <param name="containers">The item containers.</param>
|
|||
public void Remove(IEnumerable<ItemContainer> containers) |
|||
{ |
|||
foreach (var container in containers) |
|||
{ |
|||
var item = _containerToItem[container.ContainerControl]; |
|||
_containerToItem.Remove(container.ContainerControl); |
|||
_itemToContainer.Remove(item); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the container for an item.
|
|||
/// </summary>
|
|||
/// <param name="item">The item.</param>
|
|||
/// <returns>The container, or null of not found.</returns>
|
|||
public IControl ContainerFromItem(object item) |
|||
{ |
|||
IControl result; |
|||
_itemToContainer.TryGetValue(item, out result); |
|||
return result; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the item for a container.
|
|||
/// </summary>
|
|||
/// <param name="container">The container.</param>
|
|||
/// <returns>The item, or null of not found.</returns>
|
|||
public object ItemFromContainer(IControl container) |
|||
{ |
|||
object result; |
|||
_containerToItem.TryGetValue(container, out result); |
|||
return result; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,25 @@ |
|||
// 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.Controls |
|||
{ |
|||
/// <summary>
|
|||
/// Exposes an interface for setting pseudoclasses on a <see cref="Classes"/> collection.
|
|||
/// </summary>
|
|||
public interface IPseudoClasses |
|||
{ |
|||
/// <summary>
|
|||
/// Adds a pseudoclass to the collection.
|
|||
/// </summary>
|
|||
/// <param name="name">The pseudoclass name.</param>
|
|||
void Add(string name); |
|||
|
|||
/// <summary>
|
|||
/// Removes a pseudoclass from the collection.
|
|||
/// </summary>
|
|||
/// <param name="name">The pseudoclass name.</param>
|
|||
bool Remove(string name); |
|||
} |
|||
} |
|||
@ -1,25 +0,0 @@ |
|||
// 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.Collections; |
|||
|
|||
namespace Perspex.Controls |
|||
{ |
|||
/// <summary>
|
|||
/// A control that can make its visual children the logical children of another control.
|
|||
/// </summary>
|
|||
public interface IReparentingControl : IControl |
|||
{ |
|||
/// <summary>
|
|||
/// Requests that the visual children of the control use another control as their logical
|
|||
/// parent.
|
|||
/// </summary>
|
|||
/// <param name="logicalParent">
|
|||
/// The logical parent for the visual children of the control.
|
|||
/// </param>
|
|||
/// <param name="children">
|
|||
/// The <see cref="ILogical.LogicalChildren"/> collection to modify.
|
|||
/// </param>
|
|||
void ReparentLogicalChildren(ILogical logicalParent, IPerspexList<ILogical> children); |
|||
} |
|||
} |
|||
@ -1,28 +0,0 @@ |
|||
// 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.Collections; |
|||
|
|||
namespace Perspex.Controls |
|||
{ |
|||
/// <summary>
|
|||
/// A control that can use the visual children of another control as its logical children.
|
|||
/// </summary>
|
|||
public interface IReparentingHost : ILogical |
|||
{ |
|||
/// <summary>
|
|||
/// Gets a writeable logical children collection from the host.
|
|||
/// </summary>
|
|||
new IPerspexList<ILogical> LogicalChildren { get; } |
|||
|
|||
/// <summary>
|
|||
/// Asks the control whether it wants to reparent the logical children of the specified
|
|||
/// control.
|
|||
/// </summary>
|
|||
/// <param name="control">The control.</param>
|
|||
/// <returns>
|
|||
/// True if the control wants to reparent its logical children otherwise false.
|
|||
/// </returns>
|
|||
bool WillReparentChildrenOf(IControl control); |
|||
} |
|||
} |
|||
@ -0,0 +1,31 @@ |
|||
// 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.Styling; |
|||
|
|||
namespace Perspex.Controls |
|||
{ |
|||
/// <summary>
|
|||
/// Holds the event arguments for the <see cref="Control.AttachedToLogicalTree"/> and
|
|||
/// <see cref="Control.DetachedFromLogicalTree"/> events.
|
|||
/// </summary>
|
|||
public class LogicalTreeAttachmentEventArgs : EventArgs |
|||
{ |
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="LogicalTreeAttachmentEventArgs"/> class.
|
|||
/// </summary>
|
|||
/// <param name="root">The root of the logical tree.</param>
|
|||
public LogicalTreeAttachmentEventArgs(IStyleHost root) |
|||
{ |
|||
Contract.Requires<ArgumentNullException>(root != null); |
|||
|
|||
Root = root; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the root of the logical tree that the control is being attached to or detached from.
|
|||
/// </summary>
|
|||
public IStyleHost Root { get; } |
|||
} |
|||
} |
|||
@ -0,0 +1,136 @@ |
|||
// 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.Runtime.CompilerServices; |
|||
using Perspex.Collections; |
|||
using Perspex.Controls.Presenters; |
|||
using Perspex.Controls.Primitives; |
|||
using Perspex.Interactivity; |
|||
|
|||
namespace Perspex.Controls.Mixins |
|||
{ |
|||
/// <summary>
|
|||
/// Adds content control functionality to control classes.
|
|||
/// </summary>
|
|||
/// <para>
|
|||
/// The <see cref="ContentControlMixin"/> adds behavior to a control which acts as a content
|
|||
/// control such as <see cref="ContentControl"/> and <see cref="HeaderedItemsControl"/>. It
|
|||
/// updates keeps the control's logical children in sync with the content being displayed by
|
|||
/// the control.
|
|||
/// </para>
|
|||
public class ContentControlMixin |
|||
{ |
|||
private static Lazy<ConditionalWeakTable<TemplatedControl, IDisposable>> subscriptions = |
|||
new Lazy<ConditionalWeakTable<TemplatedControl, IDisposable>>(() => |
|||
new ConditionalWeakTable<TemplatedControl, IDisposable>()); |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="SelectableMixin"/> class.
|
|||
/// </summary>
|
|||
/// <typeparam name="TControl">The control type.</typeparam>
|
|||
/// <param name="content">The content property.</param>
|
|||
/// <param name="logicalChildrenSelector">
|
|||
/// Given an control of <typeparamref name="TControl"/> should return the control's
|
|||
/// logical children collection.
|
|||
/// </param>
|
|||
/// <param name="presenterName">
|
|||
/// The name of the content presenter in the control's template.
|
|||
/// </param>
|
|||
public static void Attach<TControl>( |
|||
PerspexProperty content, |
|||
Func<TControl, IPerspexList<ILogical>> logicalChildrenSelector, |
|||
string presenterName = "PART_ContentPresenter") |
|||
where TControl : TemplatedControl |
|||
{ |
|||
Contract.Requires<ArgumentNullException>(content != null); |
|||
Contract.Requires<ArgumentNullException>(logicalChildrenSelector != null); |
|||
|
|||
EventHandler<RoutedEventArgs> templateApplied = (s, ev) => |
|||
{ |
|||
var sender = s as TControl; |
|||
|
|||
if (sender != null) |
|||
{ |
|||
var e = (TemplateAppliedEventArgs)ev; |
|||
var presenter = (IControl)e.NameScope.Find(presenterName); |
|||
|
|||
if (presenter != null) |
|||
{ |
|||
var logicalChildren = logicalChildrenSelector(sender); |
|||
var subscription = presenter |
|||
.GetObservable(ContentPresenter.ChildProperty) |
|||
.Subscribe(child => UpdateLogicalChild( |
|||
logicalChildren, |
|||
logicalChildren.FirstOrDefault(), |
|||
child)); |
|||
subscriptions.Value.Add(sender, subscription); |
|||
} |
|||
} |
|||
}; |
|||
|
|||
TemplatedControl.TemplateAppliedEvent.AddClassHandler( |
|||
typeof(TControl), |
|||
templateApplied, |
|||
RoutingStrategies.Direct); |
|||
|
|||
content.Changed.Subscribe(e => |
|||
{ |
|||
var sender = e.Sender as TControl; |
|||
|
|||
if (sender != null) |
|||
{ |
|||
var logicalChildren = logicalChildrenSelector(sender); |
|||
UpdateLogicalChild(logicalChildren, e.OldValue, e.NewValue); |
|||
} |
|||
}); |
|||
|
|||
TemplatedControl.TemplateProperty.Changed.Subscribe(e => |
|||
{ |
|||
var sender = e.Sender as TControl; |
|||
|
|||
if (sender != null) |
|||
{ |
|||
IDisposable subscription; |
|||
|
|||
if (subscriptions.Value.TryGetValue(sender, out subscription)) |
|||
{ |
|||
subscription.Dispose(); |
|||
subscriptions.Value.Remove(sender); |
|||
} |
|||
} |
|||
}); |
|||
} |
|||
|
|||
private static event EventHandler<TemplateAppliedEventArgs> TemplateApplied; |
|||
|
|||
private static void OnTemplateApplied(object sender, RoutedEventArgs e) |
|||
{ |
|||
TemplateApplied?.Invoke(sender, (TemplateAppliedEventArgs)e); |
|||
} |
|||
|
|||
private static void UpdateLogicalChild( |
|||
IPerspexList<ILogical> logicalChildren, |
|||
object oldValue, |
|||
object newValue) |
|||
{ |
|||
if (oldValue != newValue) |
|||
{ |
|||
var logical = oldValue as ILogical; |
|||
|
|||
if (logical != null) |
|||
{ |
|||
logicalChildren.Remove(logical); |
|||
} |
|||
|
|||
logical = newValue as ILogical; |
|||
|
|||
if (logical != null) |
|||
{ |
|||
logicalChildren.Add(logical); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -1,17 +1,58 @@ |
|||
// 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 Perspex.Controls.Mixins; |
|||
using Perspex.Controls.Presenters; |
|||
|
|||
namespace Perspex.Controls.Primitives |
|||
{ |
|||
/// <summary>
|
|||
/// Represents an <see cref="ItemsControl"/> with a related header.
|
|||
/// </summary>
|
|||
public class HeaderedItemsControl : ItemsControl |
|||
{ |
|||
/// <summary>
|
|||
/// Defines the <see cref="Header"/> property.
|
|||
/// </summary>
|
|||
public static readonly PerspexProperty<object> HeaderProperty = |
|||
HeaderedContentControl.HeaderProperty.AddOwner<HeaderedItemsControl>(); |
|||
|
|||
/// <summary>
|
|||
/// Initializes static members of the <see cref="ContentControl"/> class.
|
|||
/// </summary>
|
|||
static HeaderedItemsControl() |
|||
{ |
|||
ContentControlMixin.Attach<HeaderedItemsControl>( |
|||
HeaderProperty, |
|||
x => x.LogicalChildren, |
|||
"PART_HeaderPresenter"); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the content of the control's header.
|
|||
/// </summary>
|
|||
public object Header |
|||
{ |
|||
get { return GetValue(HeaderProperty); } |
|||
set { SetValue(HeaderProperty, value); } |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the header presenter from the control's template.
|
|||
/// </summary>
|
|||
public ContentPresenter HeaderPresenter |
|||
{ |
|||
get; |
|||
private set; |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
protected override void OnTemplateApplied(TemplateAppliedEventArgs e) |
|||
{ |
|||
base.OnTemplateApplied(e); |
|||
HeaderPresenter = e.NameScope.Find<ContentPresenter>("PART_HeaderPresenter"); |
|||
} |
|||
} |
|||
} |
|||
|
|||
@ -0,0 +1,58 @@ |
|||
// 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 Perspex.Controls.Mixins; |
|||
using Perspex.Controls.Presenters; |
|||
|
|||
namespace Perspex.Controls.Primitives |
|||
{ |
|||
/// <summary>
|
|||
/// Represents a <see cref="SelectingItemsControl"/> with a related header.
|
|||
/// </summary>
|
|||
public class HeaderedSelectingItemsControl : SelectingItemsControl |
|||
{ |
|||
/// <summary>
|
|||
/// Defines the <see cref="Header"/> property.
|
|||
/// </summary>
|
|||
public static readonly PerspexProperty<object> HeaderProperty = |
|||
HeaderedContentControl.HeaderProperty.AddOwner<HeaderedSelectingItemsControl>(); |
|||
|
|||
/// <summary>
|
|||
/// Initializes static members of the <see cref="ContentControl"/> class.
|
|||
/// </summary>
|
|||
static HeaderedSelectingItemsControl() |
|||
{ |
|||
ContentControlMixin.Attach<HeaderedSelectingItemsControl>( |
|||
HeaderProperty, |
|||
x => x.LogicalChildren, |
|||
"PART_HeaderPresenter"); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the content of the control's header.
|
|||
/// </summary>
|
|||
public object Header |
|||
{ |
|||
get { return GetValue(HeaderProperty); } |
|||
set { SetValue(HeaderProperty, value); } |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the header presenter from the control's template.
|
|||
/// </summary>
|
|||
public ContentPresenter HeaderPresenter |
|||
{ |
|||
get; |
|||
private set; |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
protected override void OnTemplateApplied(TemplateAppliedEventArgs e) |
|||
{ |
|||
base.OnTemplateApplied(e); |
|||
HeaderPresenter = e.NameScope.Find<ContentPresenter>("PART_HeaderPresenter"); |
|||
} |
|||
} |
|||
} |
|||
@ -1,97 +0,0 @@ |
|||
// 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.Linq; |
|||
using System.Text; |
|||
using System.Threading.Tasks; |
|||
|
|||
namespace Perspex.Controls.Primitives |
|||
{ |
|||
public interface IScrollInfoBase |
|||
{ |
|||
/// <summary>
|
|||
/// ScrollOwner is the container that controls any scrollbars, headers, etc... that are dependant
|
|||
/// on this IScrollInfo's properties. Implementers of IScrollInfo should call InvalidateScrollInfo()
|
|||
/// on this object when properties change.
|
|||
/// </summary>
|
|||
ScrollViewer ScrollOwner { get; set; } |
|||
|
|||
Rect MakeVisible(Visual visual, Rect rectangle); |
|||
} |
|||
|
|||
public interface IVerticalScrollInfo : IScrollInfoBase |
|||
{ |
|||
/// <summary>
|
|||
/// VerticalOffset is the vertical offset into the scrolled content that represents the first unit visible.
|
|||
/// </summary>
|
|||
double VerticalOffset { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// ExtentHeight contains the full vertical range of the scrolled content.
|
|||
/// </summary>
|
|||
double ExtentHeight { get; } |
|||
|
|||
/// <summary>
|
|||
/// ViewportHeight contains the currently visible vertical range of the scrolled content.
|
|||
/// </summary>
|
|||
double ViewportHeight { get; } |
|||
|
|||
/// <summary>
|
|||
/// This property indicates to the IScrollInfo whether or not it can scroll in the vertical given dimension.
|
|||
/// </summary>
|
|||
bool CanVerticallyScroll { get; set; } |
|||
|
|||
void LineDown(); |
|||
|
|||
void LineUp(); |
|||
|
|||
void MouseWheelDown(); |
|||
|
|||
void MouseWheelUp(); |
|||
|
|||
void PageDown(); |
|||
|
|||
void PageUp(); |
|||
} |
|||
|
|||
public interface IHorizontalScrollInfo : IScrollInfoBase |
|||
{ |
|||
/// <summary>
|
|||
/// ExtentWidth contains the full horizontal range of the scrolled content.
|
|||
/// </summary>
|
|||
double ExtentWidth { get; } |
|||
|
|||
/// <summary>
|
|||
/// ViewportWidth contains the currently visible horizontal range of the scrolled content.
|
|||
/// </summary>
|
|||
double ViewportWidth { get; } |
|||
|
|||
/// <summary>
|
|||
/// HorizontalOffset is the horizontal offset into the scrolled content that represents the first unit visible.
|
|||
/// </summary>
|
|||
double HorizontalOffset { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// This property indicates to the IScrollInfo whether or not it can scroll in the horizontal given dimension.
|
|||
/// </summary>
|
|||
bool CanHorizontallyScroll { get; set; } |
|||
|
|||
void LineLeft(); |
|||
|
|||
void LineRight(); |
|||
|
|||
void MouseWheelLeft(); |
|||
|
|||
void MouseWheelRight(); |
|||
|
|||
void PageLeft(); |
|||
|
|||
void PageRight(); |
|||
} |
|||
|
|||
public interface IScrollInfo : IHorizontalScrollInfo, IVerticalScrollInfo |
|||
{ |
|||
} |
|||
} |
|||
@ -0,0 +1,44 @@ |
|||
// 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.Controls.Primitives |
|||
{ |
|||
/// <summary>
|
|||
/// Interface implemented by controls that handle their own scrolling when placed inside a
|
|||
/// <see cref="ScrollViewer"/>.
|
|||
/// </summary>
|
|||
public interface IScrollable |
|||
{ |
|||
/// <summary>
|
|||
/// Gets or sets the scroll invalidation method.
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// <para>
|
|||
/// This method notifies the attached <see cref="ScrollViewer"/> of a change in
|
|||
/// the <see cref="Extent"/>, <see cref="Offset"/> or <see cref="Viewport"/> properties.
|
|||
/// </para>
|
|||
/// <para>
|
|||
/// This property is set by the parent <see cref="ScrollViewer"/> when the
|
|||
/// <see cref="IScrollable"/> is placed inside it.
|
|||
/// </para>
|
|||
/// </remarks>
|
|||
Action InvalidateScroll { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Gets the extent of the scrollable content, in logical units
|
|||
/// </summary>
|
|||
Size Extent { get; } |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the current scroll offset, in logical units.
|
|||
/// </summary>
|
|||
Vector Offset { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Gets the size of the viewport, in logical units.
|
|||
/// </summary>
|
|||
Size Viewport { get; } |
|||
} |
|||
} |
|||
@ -1,126 +0,0 @@ |
|||
// 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.Controls.Primitives |
|||
{ |
|||
public class ScrollInfoAdapter : IScrollInfo |
|||
{ |
|||
private readonly IScrollInfoBase _nfo; |
|||
public ScrollInfoAdapter(IScrollInfoBase nfo) |
|||
{ |
|||
_nfo = nfo; |
|||
} |
|||
|
|||
public ScrollViewer ScrollOwner |
|||
{ |
|||
get { return _nfo.ScrollOwner; } |
|||
set { _nfo.ScrollOwner = value; } |
|||
} |
|||
|
|||
public double ExtentWidth => (_nfo as IHorizontalScrollInfo)?.ExtentWidth ?? 0; |
|||
|
|||
public double ViewportWidth => (_nfo as IHorizontalScrollInfo)?.ViewportWidth ?? 0; |
|||
|
|||
public double ExtentHeight => (_nfo as IVerticalScrollInfo)?.ExtentHeight ?? 0; |
|||
|
|||
public double ViewportHeight => (_nfo as IVerticalScrollInfo)?.ViewportHeight ?? 0; |
|||
|
|||
private double _horizontalOffset; |
|||
public double HorizontalOffset |
|||
{ |
|||
get |
|||
{ |
|||
return (_nfo as IHorizontalScrollInfo)?.HorizontalOffset ?? _horizontalOffset; |
|||
} |
|||
|
|||
set |
|||
{ |
|||
var info = (_nfo as IHorizontalScrollInfo); |
|||
if (info == null) |
|||
_horizontalOffset = value; |
|||
else |
|||
info.HorizontalOffset = value; |
|||
} |
|||
} |
|||
|
|||
private double _verticalOffset; |
|||
public double VerticalOffset |
|||
{ |
|||
get |
|||
{ |
|||
return (_nfo as IVerticalScrollInfo)?.VerticalOffset ?? _verticalOffset; |
|||
} |
|||
|
|||
set |
|||
{ |
|||
var info = (_nfo as IVerticalScrollInfo); |
|||
if (info == null) |
|||
_verticalOffset = value; |
|||
else |
|||
info.VerticalOffset = value; |
|||
} |
|||
} |
|||
|
|||
public void LineLeft() => (_nfo as IHorizontalScrollInfo)?.LineLeft(); |
|||
|
|||
public void LineRight() => (_nfo as IHorizontalScrollInfo)?.LineRight(); |
|||
|
|||
public void MouseWheelLeft() => (_nfo as IHorizontalScrollInfo)?.MouseWheelLeft(); |
|||
|
|||
public void MouseWheelRight() => (_nfo as IHorizontalScrollInfo)?.MouseWheelRight(); |
|||
|
|||
public void PageLeft() => (_nfo as IHorizontalScrollInfo)?.PageLeft(); |
|||
|
|||
public Rect MakeVisible(Visual visual, Rect rectangle) => _nfo.MakeVisible(visual, rectangle); |
|||
|
|||
public void PageRight() => (_nfo as IHorizontalScrollInfo)?.PageRight(); |
|||
|
|||
public void LineDown() => (_nfo as IVerticalScrollInfo)?.LineDown(); |
|||
|
|||
public void LineUp() => (_nfo as IVerticalScrollInfo)?.LineUp(); |
|||
|
|||
public void MouseWheelDown() => (_nfo as IVerticalScrollInfo)?.MouseWheelDown(); |
|||
|
|||
public void MouseWheelUp() => (_nfo as IVerticalScrollInfo)?.MouseWheelUp(); |
|||
|
|||
public void PageDown() => (_nfo as IVerticalScrollInfo)?.PageDown(); |
|||
|
|||
public void PageUp() => (_nfo as IVerticalScrollInfo)?.PageUp(); |
|||
|
|||
private bool _canVerticallyScroll; |
|||
public bool CanVerticallyScroll |
|||
{ |
|||
get |
|||
{ |
|||
return (_nfo as IVerticalScrollInfo)?.CanVerticallyScroll ?? _canVerticallyScroll; |
|||
} |
|||
|
|||
set |
|||
{ |
|||
var info = (_nfo as IVerticalScrollInfo); |
|||
if (info == null) |
|||
_canVerticallyScroll = value; |
|||
else |
|||
info.CanVerticallyScroll = value; |
|||
} |
|||
} |
|||
|
|||
private bool _canHorizontallyScroll; |
|||
public bool CanHorizontallyScroll |
|||
{ |
|||
get |
|||
{ |
|||
return (_nfo as IHorizontalScrollInfo)?.CanHorizontallyScroll ?? _canHorizontallyScroll; |
|||
} |
|||
|
|||
set |
|||
{ |
|||
var info = (_nfo as IHorizontalScrollInfo); |
|||
if (info == null) |
|||
_canHorizontallyScroll = value; |
|||
else |
|||
info.CanHorizontallyScroll = value; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,12 @@ |
|||
// 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.Controls.Primitives |
|||
{ |
|||
/// <summary>
|
|||
/// Represents a tab in a <see cref="TabStrip"/>.
|
|||
/// </summary>
|
|||
public class TabStripItem : ListBoxItem |
|||
{ |
|||
} |
|||
} |
|||
@ -0,0 +1,28 @@ |
|||
// 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.Interactivity; |
|||
|
|||
namespace Perspex.Controls.Primitives |
|||
{ |
|||
/// <summary>
|
|||
/// Holds the details of the <see cref="TemplatedControl.TemplateApplied"/> event.
|
|||
/// </summary>
|
|||
public class TemplateAppliedEventArgs : RoutedEventArgs |
|||
{ |
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="TemplateAppliedEventArgs"/> class.
|
|||
/// </summary>
|
|||
/// <param name="nameScope">The applied template's name scope.</param>
|
|||
public TemplateAppliedEventArgs(INameScope nameScope) |
|||
: base(TemplatedControl.TemplateAppliedEvent) |
|||
{ |
|||
NameScope = nameScope; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the name scope of the applied template.
|
|||
/// </summary>
|
|||
public INameScope NameScope { get; } |
|||
} |
|||
} |
|||
@ -1,155 +0,0 @@ |
|||
// 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; |
|||
using System.Collections.Generic; |
|||
using System.Collections.Specialized; |
|||
using System.Linq; |
|||
using System.Reactive; |
|||
using System.Reactive.Subjects; |
|||
|
|||
namespace Perspex.Styling |
|||
{ |
|||
public class Classes : ICollection<string>, INotifyCollectionChanged |
|||
{ |
|||
private readonly List<string> _inner; |
|||
|
|||
private readonly Subject<NotifyCollectionChangedEventArgs> _beforeChanged |
|||
= new Subject<NotifyCollectionChangedEventArgs>(); |
|||
|
|||
private readonly Subject<NotifyCollectionChangedEventArgs> _changed |
|||
= new Subject<NotifyCollectionChangedEventArgs>(); |
|||
|
|||
private readonly Subject<NotifyCollectionChangedEventArgs> _afterChanged |
|||
= new Subject<NotifyCollectionChangedEventArgs>(); |
|||
|
|||
public Classes() |
|||
{ |
|||
_inner = new List<string>(); |
|||
} |
|||
|
|||
public Classes(params string[] classes) |
|||
{ |
|||
_inner = new List<string>(classes); |
|||
} |
|||
|
|||
public Classes(IEnumerable<string> classes) |
|||
{ |
|||
_inner = new List<string>(classes); |
|||
} |
|||
|
|||
public event NotifyCollectionChangedEventHandler CollectionChanged; |
|||
|
|||
public int Count => _inner.Count; |
|||
|
|||
public bool IsReadOnly => false; |
|||
|
|||
public IObservable<NotifyCollectionChangedEventArgs> BeforeChanged => _beforeChanged; |
|||
|
|||
public IObservable<NotifyCollectionChangedEventArgs> Changed => _changed; |
|||
|
|||
public IObservable<NotifyCollectionChangedEventArgs> AfterChanged => _afterChanged; |
|||
|
|||
public void Add(string item) |
|||
{ |
|||
Add(Enumerable.Repeat(item, 1)); |
|||
} |
|||
|
|||
public void Add(params string[] items) |
|||
{ |
|||
Add((IEnumerable<string>)items); |
|||
} |
|||
|
|||
public void Add(IEnumerable<string> items) |
|||
{ |
|||
items = items.Except(_inner); |
|||
|
|||
NotifyCollectionChangedEventArgs e = new NotifyCollectionChangedEventArgs( |
|||
NotifyCollectionChangedAction.Add, |
|||
items); |
|||
|
|||
_beforeChanged.OnNext(e); |
|||
_inner.AddRange(items); |
|||
RaiseChanged(e); |
|||
} |
|||
|
|||
public void Clear() |
|||
{ |
|||
NotifyCollectionChangedEventArgs e = new NotifyCollectionChangedEventArgs( |
|||
NotifyCollectionChangedAction.Reset); |
|||
|
|||
_beforeChanged.OnNext(e); |
|||
_inner.Clear(); |
|||
RaiseChanged(e); |
|||
} |
|||
|
|||
public bool Contains(string item) |
|||
{ |
|||
return _inner.Contains(item); |
|||
} |
|||
|
|||
public void CopyTo(string[] array, int arrayIndex) |
|||
{ |
|||
_inner.CopyTo(array, arrayIndex); |
|||
} |
|||
|
|||
public IEnumerator<string> GetEnumerator() |
|||
{ |
|||
return _inner.GetEnumerator(); |
|||
} |
|||
|
|||
public override string ToString() |
|||
{ |
|||
return string.Join(" ", this); |
|||
} |
|||
|
|||
IEnumerator IEnumerable.GetEnumerator() |
|||
{ |
|||
return _inner.GetEnumerator(); |
|||
} |
|||
|
|||
public bool Remove(string item) |
|||
{ |
|||
return Remove(Enumerable.Repeat(item, 1)); |
|||
} |
|||
|
|||
public bool Remove(params string[] items) |
|||
{ |
|||
return Remove((IEnumerable<string>)items); |
|||
} |
|||
|
|||
public bool Remove(IEnumerable<string> items) |
|||
{ |
|||
items = items.Intersect(_inner); |
|||
|
|||
if (items.Any()) |
|||
{ |
|||
NotifyCollectionChangedEventArgs e = new NotifyCollectionChangedEventArgs( |
|||
NotifyCollectionChangedAction.Remove, |
|||
items); |
|||
|
|||
_beforeChanged.OnNext(e); |
|||
|
|||
foreach (string item in items) |
|||
{ |
|||
_inner.Remove(item); |
|||
} |
|||
|
|||
RaiseChanged(e); |
|||
return true; |
|||
} |
|||
else |
|||
{ |
|||
return false; |
|||
} |
|||
} |
|||
|
|||
private void RaiseChanged(NotifyCollectionChangedEventArgs e) |
|||
{ |
|||
CollectionChanged?.Invoke(this, e); |
|||
_changed.OnNext(e); |
|||
_afterChanged.OnNext(e); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,12 @@ |
|||
// 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.Styling |
|||
{ |
|||
/// <summary>
|
|||
/// Denotes the root <see cref="IStyleHost"/> in a tree.
|
|||
/// </summary>
|
|||
public interface IStyleRoot : IStyleHost |
|||
{ |
|||
} |
|||
} |
|||
@ -0,0 +1,123 @@ |
|||
<Styles xmlns="https://github.com/perspex"> |
|||
|
|||
<Style Selector="Expander"> |
|||
<Setter Property="ContentTransition"> |
|||
<Setter.Value> |
|||
<CrossFade Duration="00:00:00.25" /> |
|||
</Setter.Value> |
|||
</Setter> |
|||
</Style> |
|||
<Style Selector="Expander[ExpandDirection=Down]"> |
|||
<Setter Property="Template"> |
|||
<ControlTemplate> |
|||
<Border Background="{TemplateBinding Background}"> |
|||
<Grid RowDefinitions="Auto,*"> |
|||
<ToggleButton Name="PART_toggle" Grid.Row="0" Content="{TemplateBinding Header}" IsChecked="{TemplateBinding IsExpanded, Mode=TwoWay}" /> |
|||
<ContentPresenter Name="PART_ContentPresenter" |
|||
Grid.Row="1" |
|||
IsVisible="{TemplateBinding IsExpanded}" |
|||
Content="{TemplateBinding Content}" |
|||
HorizontalAlignment="Stretch" |
|||
VerticalAlignment="Stretch" /> |
|||
</Grid> |
|||
</Border> |
|||
</ControlTemplate> |
|||
</Setter> |
|||
</Style> |
|||
<Style Selector="Expander[ExpandDirection=Up]"> |
|||
<Setter Property="Template"> |
|||
<ControlTemplate> |
|||
<Border Background="{TemplateBinding Background}"> |
|||
<Grid RowDefinitions="*,Auto"> |
|||
<ToggleButton Name="PART_toggle" Grid.Row="1" Content="{TemplateBinding Header}" IsChecked="{TemplateBinding IsExpanded, Mode=TwoWay}" /> |
|||
<ContentPresenter Name="PART_ContentPresenter" |
|||
Grid.Row="0" |
|||
IsVisible="{TemplateBinding IsExpanded}" |
|||
Content="{TemplateBinding Content}" |
|||
HorizontalAlignment="Stretch" |
|||
VerticalAlignment="Stretch" /> |
|||
</Grid> |
|||
</Border> |
|||
</ControlTemplate> |
|||
</Setter> |
|||
</Style> |
|||
<Style Selector="Expander[ExpandDirection=Right]"> |
|||
<Setter Property="Template"> |
|||
<ControlTemplate> |
|||
<Border Background="{TemplateBinding Background}"> |
|||
<Grid ColumnDefinitions="Auto,*"> |
|||
<ToggleButton Name="PART_toggle" Grid.Column="0" Content="{TemplateBinding Header}" IsChecked="{TemplateBinding IsExpanded, Mode=TwoWay}" /> |
|||
<ContentPresenter Name="PART_ContentPresenter" |
|||
Grid.Column="1" |
|||
IsVisible="{TemplateBinding IsExpanded}" |
|||
Content="{TemplateBinding Content}" |
|||
HorizontalAlignment="Stretch" |
|||
VerticalAlignment="Stretch" /> |
|||
</Grid> |
|||
</Border> |
|||
</ControlTemplate> |
|||
</Setter> |
|||
</Style> |
|||
<Style Selector="Expander[ExpandDirection=Left]"> |
|||
<Setter Property="Template"> |
|||
<ControlTemplate> |
|||
<Border Background="{TemplateBinding Background}"> |
|||
<Grid ColumnDefinitions="*,Auto"> |
|||
<ToggleButton Name="PART_toggle" Grid.Column="1" Content="{TemplateBinding Header}" IsChecked="{TemplateBinding IsExpanded, Mode=TwoWay}" /> |
|||
<ContentPresenter Name="PART_ContentPresenter" |
|||
Grid.Column="0" |
|||
IsVisible="{TemplateBinding IsExpanded}" |
|||
Content="{TemplateBinding Content}" |
|||
HorizontalAlignment="Stretch" |
|||
VerticalAlignment="Stretch" /> |
|||
</Grid> |
|||
</Border> |
|||
</ControlTemplate> |
|||
</Setter> |
|||
</Style> |
|||
<Style Selector="Expander /template/ ToggleButton#PART_toggle"> |
|||
<Setter Property="Template"> |
|||
<ControlTemplate> |
|||
<Border BorderThickness="1"> |
|||
<Grid ColumnDefinitions="Auto,Auto"> |
|||
<Border Grid.Column="0" Width="20" Height="20" HorizontalAlignment="Center" VerticalAlignment="Center"> |
|||
<Path Fill="Black" |
|||
HorizontalAlignment="Center" |
|||
VerticalAlignment="Center" |
|||
Data="M 0 2 L 4 6 L 0 10 Z" /> |
|||
</Border> |
|||
<ContentPresenter Grid.Column="1" Content="{TemplateBinding Content}" VerticalAlignment="Center" /> |
|||
</Grid> |
|||
</Border> |
|||
</ControlTemplate> |
|||
</Setter> |
|||
</Style> |
|||
<Style Selector="Expander /template/ ToggleButton#PART_toggle:pointerover /template/ Border"> |
|||
<Setter Property="BorderBrush" Value="#ffaaaaaa" /> |
|||
</Style> |
|||
<Style Selector="Expander:down:expanded /template/ ToggleButton#PART_toggle /template/ Path"> |
|||
<Setter Property="RenderTransform"> |
|||
<RotateTransform Angle="90" /> |
|||
</Setter> |
|||
</Style> |
|||
<Style Selector="Expander:up:expanded /template/ ToggleButton#PART_toggle /template/ Path"> |
|||
<Setter Property="RenderTransform"> |
|||
<RotateTransform Angle="-90" /> |
|||
</Setter> |
|||
</Style> |
|||
<Style Selector="Expander:left:expanded /template/ ToggleButton#PART_toggle /template/ Path"> |
|||
<Setter Property="RenderTransform"> |
|||
<RotateTransform Angle="180" /> |
|||
</Setter> |
|||
</Style> |
|||
<Style Selector="Expander:right /template/ ToggleButton#PART_toggle /template/ Path"> |
|||
<Setter Property="RenderTransform"> |
|||
<RotateTransform Angle="180" /> |
|||
</Setter> |
|||
</Style> |
|||
<Style Selector="Expander:right:expanded /template/ ToggleButton#PART_toggle /template/ Path"> |
|||
<Setter Property="RenderTransform"> |
|||
<RotateTransform Angle="0" /> |
|||
</Setter> |
|||
</Style> |
|||
</Styles> |
|||
@ -1,14 +1,16 @@ |
|||
<Styles xmlns="https://github.com/perspex"> |
|||
<Style Selector="TabItem"> |
|||
<Style Selector="TabStripItem"> |
|||
<Setter Property="FontSize" Value="16"/> |
|||
<Setter Property="Foreground" Value="Gray"/> |
|||
<Setter Property="Template"> |
|||
<ControlTemplate> |
|||
<ContentPresenter Name="PART_HeaderPresenter" Content="{TemplateBinding Header}"/> |
|||
<ContentPresenter Name="PART_ContentPresenter" |
|||
Content="{TemplateBinding Content}" |
|||
Margin="{TemplateBinding Padding}"/> |
|||
</ControlTemplate> |
|||
</Setter> |
|||
</Style> |
|||
<Style Selector="TabItem:selected"> |
|||
<Style Selector="TabStripItem:selected"> |
|||
<Setter Property="Foreground" Value="Black"/> |
|||
</Style> |
|||
</Styles> |
|||
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue