Browse Source

Start allowing Setters to contain bindings.

This is needed so that things like the following can be done:

<TreeView>
<TreeView.Styles>
<Style Selector="TreeViewItem">
<Setter Property="IsExpanded" Value="{Binding IsExpanded}"/>
</Style>
</TreeView.Styles>
<TreeView>

(At the moment we have TreeDataTemplate.IsExpanded but this isn't good
enough: it only supports setting IsExpanded on creation of the item with
no binding.)

With this commit, setters can now contain bindings, but they aren't yet
applied correctly. As part of this commit, classes related to binding
have been moved to the Perspex.Data namespace.
pull/396/head
Steven Kirk 11 years ago
parent
commit
1841e7ad7a
  1. 1
      samples/TestApplicationShared/MainWindow.cs
  2. 4
      src/Markup/Perspex.Markup.Xaml/Context/PerspexWiringContext.cs
  3. 53
      src/Markup/Perspex.Markup.Xaml/Context/PerspexXamlMemberValuePlugin.cs
  4. 41
      src/Markup/Perspex.Markup.Xaml/Data/Binding.cs
  5. 35
      src/Markup/Perspex.Markup.Xaml/Data/IXamlBinding.cs
  6. 22
      src/Markup/Perspex.Markup.Xaml/Data/MultiBinding.cs
  7. 1
      src/Markup/Perspex.Markup.Xaml/MarkupExtensions/BindingExtension.cs
  8. 1
      src/Markup/Perspex.Markup.Xaml/MarkupExtensions/TemplateBindingExtension.cs
  9. 1
      src/Markup/Perspex.Markup.Xaml/Perspex.Markup.Xaml.csproj
  10. 25
      src/Markup/Perspex.Markup.Xaml/PerspexXamlLoader.cs
  11. 2
      src/Markup/Perspex.Markup.Xaml/Templates/TreeDataTemplate.cs
  12. 1
      src/Perspex.Animation/Animatable.cs
  13. 1
      src/Perspex.Animation/Animate.cs
  14. 20
      src/Perspex.Base/Data/AssignBindingAttribute.cs
  15. 36
      src/Perspex.Base/Data/BindingMode.cs
  16. 2
      src/Perspex.Base/Data/BindingPriority.cs
  17. 33
      src/Perspex.Base/Data/IBinding.cs
  18. 45
      src/Perspex.Base/Data/IndexerDescriptor.cs
  19. 2
      src/Perspex.Base/Diagnostics/PerspexObjectExtensions.cs
  20. 2
      src/Perspex.Base/Diagnostics/PerspexPropertyValue.cs
  21. 1
      src/Perspex.Base/IPerspexObject.cs
  22. 7
      src/Perspex.Base/Perspex.Base.csproj
  23. 11
      src/Perspex.Base/PerspexObject.cs
  24. 3
      src/Perspex.Base/PerspexObjectExtensions.cs
  25. 19
      src/Perspex.Base/PerspexProperty.cs
  26. 2
      src/Perspex.Base/PerspexPropertyChangedEventArgs.cs
  27. 1
      src/Perspex.Base/PerspexProperty`1.cs
  28. 1
      src/Perspex.Controls/Control.cs
  29. 31
      src/Perspex.Controls/ControlExtensions.cs
  30. 1
      src/Perspex.Controls/Primitives/ScrollBar.cs
  31. 1
      src/Perspex.Controls/Primitives/SelectingItemsControl.cs
  32. 3
      src/Perspex.Controls/Primitives/TemplatedControl.cs
  33. 1
      src/Perspex.Controls/TextBlock.cs
  34. 1
      src/Perspex.Controls/TextBox.cs
  35. 1
      src/Perspex.Diagnostics/Debug.cs
  36. 1
      src/Perspex.Diagnostics/ViewModels/PropertyDetails.cs
  37. 1
      src/Perspex.SceneGraph/Visual.cs
  38. 1
      src/Perspex.Styling/Perspex.Styling.csproj
  39. 65
      src/Perspex.Styling/Styling/ObservableSetter.cs
  40. 2
      src/Perspex.Styling/Styling/Setter.cs
  41. 1
      tests/Perspex.Base.UnitTests/Perspex.Base.UnitTests.csproj
  42. 7
      tests/Perspex.Base.UnitTests/PerspexObjectTests_Binding.cs
  43. 1
      tests/Perspex.Base.UnitTests/PerspexObjectTests_Direct.cs
  44. 1
      tests/Perspex.Base.UnitTests/PerspexObjectTests_SetValue.cs
  45. 1
      tests/Perspex.Base.UnitTests/PerspexPropertyTests.cs
  46. 7
      tests/Perspex.Controls.UnitTests/Primitives/SelectingItemsControlTests_Multiple.cs
  47. 3
      tests/Perspex.LeakTests/ControlTests.cs
  48. 26
      tests/Perspex.Markup.Xaml.UnitTests/Data/BindingTests.cs
  49. 8
      tests/Perspex.Markup.Xaml.UnitTests/Data/BindingTests_ElementName.cs
  50. 9
      tests/Perspex.Markup.Xaml.UnitTests/Data/BindingTests_TemplatedParent.cs
  51. 2
      tests/Perspex.Markup.Xaml.UnitTests/Data/MultiBindingTests.cs
  52. 5
      tests/Perspex.Markup.Xaml.UnitTests/Perspex.Markup.Xaml.UnitTests.csproj
  53. 33
      tests/Perspex.Markup.Xaml.UnitTests/StyleTests.cs
  54. 34
      tests/Perspex.Markup.Xaml.UnitTests/Templates/TreeDataTemplateTests.cs
  55. 1
      tests/Perspex.Styling.UnitTests/SelectorTests_Child.cs
  56. 1
      tests/Perspex.Styling.UnitTests/SelectorTests_Descendent.cs
  57. 46
      tests/Perspex.Styling.UnitTests/StyleTests.cs
  58. 1
      tests/Perspex.Styling.UnitTests/TestControlBase.cs
  59. 1
      tests/Perspex.Styling.UnitTests/TestTemplatedControl.cs

1
samples/TestApplicationShared/MainWindow.cs

@ -11,6 +11,7 @@ using Perspex.Controls.Html;
using Perspex.Controls.Primitives;
using Perspex.Controls.Shapes;
using Perspex.Controls.Templates;
using Perspex.Data;
using Perspex.Diagnostics;
using Perspex.Layout;
using Perspex.Media;

4
src/Markup/Perspex.Markup.Xaml/Context/PerspexWiringContext.cs

@ -15,6 +15,7 @@ using Perspex.Controls.Primitives;
using Perspex.Controls.Templates;
using Perspex.Input;
using Perspex.Markup.Xaml.Converters;
using Perspex.Markup.Xaml.Data;
using Perspex.Media;
using Perspex.Media.Imaging;
using Perspex.Metadata;
@ -58,9 +59,10 @@ namespace Perspex.Markup.Xaml.Context
var forcedAssemblies = new[]
{
typeof(Binding),
typeof(Control),
typeof(Style),
typeof(IValueConverter),
typeof(Style),
}.Select(t => t.GetTypeInfo().Assembly);
foreach (var nsa in

53
src/Markup/Perspex.Markup.Xaml/Context/PerspexXamlMemberValuePlugin.cs

@ -9,6 +9,8 @@ using System.Runtime.CompilerServices;
using Glass;
using OmniXaml.ObjectAssembler;
using OmniXaml.Typing;
using Perspex.Controls;
using Perspex.Data;
using Perspex.Markup.Xaml.Data;
using Perspex.Styling;
@ -39,9 +41,9 @@ namespace Perspex.Markup.Xaml.Context
public override void SetValue(object instance, object value)
{
if (value is IXamlBinding)
if (value is IBinding)
{
HandleBinding(instance, (IXamlBinding)value);
HandleBinding(instance, (IBinding)value);
}
else if (IsPerspexProperty)
{
@ -68,35 +70,38 @@ namespace Perspex.Markup.Xaml.Context
po.SetValue(pp, value);
}
private void HandleBinding(object instance, IXamlBinding binding)
private void HandleBinding(object instance, IBinding binding)
{
if (typeof(IXamlBinding).GetTypeInfo().IsAssignableFrom(_xamlMember.XamlType.UnderlyingType.GetTypeInfo()))
if (!(AssignBinding(instance, binding) || ApplyBinding(instance, binding)))
{
var property = instance.GetType().GetRuntimeProperty(_xamlMember.Name);
throw new InvalidOperationException(
$"Cannot assign to '{_xamlMember.Name}' on '{instance.GetType()}");
}
}
if (property == null || !property.CanWrite)
{
throw new InvalidOperationException(
$"Cannot assign to '{_xamlMember.Name}' on '{instance.GetType()}");
}
private bool AssignBinding(object instance, IBinding binding)
{
var property = instance.GetType()
.GetRuntimeProperties()
.FirstOrDefault(x => x.Name == _xamlMember.Name);
if (property?.GetCustomAttribute<AssignBindingAttribute>() != null)
{
property.SetValue(instance, binding);
return true;
}
else
{
ApplyBinding(instance, binding);
}
return false;
}
private void ApplyBinding(object instance, IXamlBinding binding)
private bool ApplyBinding(object instance, IBinding binding)
{
var perspexObject = instance as PerspexObject;
var targetControl = instance as IControl;
var attached = _xamlMember as PerspexAttachableXamlMember;
if (perspexObject == null)
if (targetControl == null)
{
throw new InvalidOperationException(
$"Cannot bind to an object of type '{instance.GetType()}");
return false;
}
PerspexProperty property;
@ -105,7 +110,7 @@ namespace Perspex.Markup.Xaml.Context
if (attached == null)
{
propertyName = _xamlMember.Name;
property = PerspexPropertyRegistry.Instance.GetRegistered(perspexObject)
property = PerspexPropertyRegistry.Instance.GetRegistered((PerspexObject)targetControl)
.FirstOrDefault(x => x.Name == propertyName);
}
else
@ -115,18 +120,18 @@ namespace Perspex.Markup.Xaml.Context
propertyName = attached.DeclaringType.UnderlyingType.Name + '.' + _xamlMember.Name;
property = PerspexPropertyRegistry.Instance.GetRegistered(perspexObject)
property = PerspexPropertyRegistry.Instance.GetRegistered((PerspexObject)targetControl)
.Where(x => x.IsAttached && x.OwnerType == attached.DeclaringType.UnderlyingType)
.FirstOrDefault(x => x.Name == _xamlMember.Name);
}
if (property == null)
{
throw new InvalidOperationException(
$"Cannot find '{propertyName}' on '{instance.GetType()}");
return false;
}
binding.Bind(perspexObject, property);
targetControl.Bind(property, binding);
return true;
}
private bool ValueRequiresSpecialHandling(object value)

41
src/Markup/Perspex.Markup.Xaml/Data/Binding.cs

@ -6,6 +6,7 @@ using System.Reactive;
using System.Reactive.Linq;
using System.Reactive.Subjects;
using Perspex.Controls;
using Perspex.Data;
using Perspex.Markup.Data;
namespace Perspex.Markup.Xaml.Data
@ -13,7 +14,7 @@ namespace Perspex.Markup.Xaml.Data
/// <summary>
/// A XAML binding.
/// </summary>
public class Binding : IXamlBinding
public class Binding : IBinding
{
/// <summary>
/// Gets or sets the <see cref="IValueConverter"/> to use.
@ -50,49 +51,23 @@ namespace Perspex.Markup.Xaml.Data
/// </summary>
public string Path { get; set; }
/// <summary>
/// Applies the binding to a property on an instance.
/// </summary>
/// <param name="instance">The target instance.</param>
/// <param name="property">The target property.</param>
public void Bind(IPerspexObject instance, PerspexProperty property)
{
Contract.Requires<ArgumentNullException>(instance != null);
Contract.Requires<ArgumentNullException>(property != null);
var subject = CreateSubject(
instance,
property.PropertyType,
property == Control.DataContextProperty);
if (subject != null)
{
var mode = Mode == BindingMode.Default ? property.DefaultBindingMode : Mode;
instance.Bind(property, subject, mode, Priority);
}
}
/// <summary>
/// Creates a subject that can be used to get and set the value of the binding.
/// </summary>
/// <param name="target">The target instance.</param>
/// <param name="targetType">The type of the target property.</param>
/// <param name="targetIsDataContext">
/// Whether the target property is the DataContext property.
/// </param>
/// <returns>An <see cref="ISubject{object}"/>.</returns>
/// <param name="targetProperty">The target property. May be null.</param>
/// <returns>An <see cref="ISubject{Object}"/>.</returns>
public ISubject<object> CreateSubject(
IPerspexObject target,
Type targetType,
bool targetIsDataContext = false)
IPerspexObject target,
PerspexProperty targetProperty)
{
Contract.Requires<ArgumentNullException>(target != null);
Contract.Requires<ArgumentNullException>(targetType != null);
var pathInfo = ParsePath(Path);
ValidateState(pathInfo);
ExpressionObserver observer;
var targetIsDataContext = targetProperty == Control.DataContextProperty;
if (pathInfo.ElementName != null || ElementName != null)
{
@ -121,7 +96,7 @@ namespace Perspex.Markup.Xaml.Data
return new ExpressionSubject(
observer,
targetType,
targetProperty?.PropertyType ?? typeof(object),
Converter ?? DefaultValueConverter.Instance,
ConverterParameter);
}

35
src/Markup/Perspex.Markup.Xaml/Data/IXamlBinding.cs

@ -1,35 +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.Reactive.Subjects;
namespace Perspex.Markup.Xaml.Data
{
/// <summary>
/// Defines a binding that can be created in XAML markup.
/// </summary>
public interface IXamlBinding
{
/// <summary>
/// Applies the binding to a property on an instance.
/// </summary>
/// <param name="instance">The target instance.</param>
/// <param name="property">The target property.</param>
void Bind(IPerspexObject instance, PerspexProperty property);
/// <summary>
/// Creates a subject that can be used to get and set the value of the binding.
/// </summary>
/// <param name="target">The target instance.</param>
/// <param name="targetType">The type of the target property.</param>
/// <param name="targetIsDataContext">
/// Whether the target property is the DataContext property.
/// </param>
/// <returns>An <see cref="ISubject{object}"/>.</returns>
ISubject<object> CreateSubject(
IPerspexObject target,
Type targetType,
bool targetIsDataContext = false);
}
}

22
src/Markup/Perspex.Markup.Xaml/Data/MultiBinding.cs

@ -8,6 +8,7 @@ using System.Linq;
using System.Reactive.Linq;
using System.Reactive.Subjects;
using Perspex.Controls;
using Perspex.Data;
using Perspex.Metadata;
namespace Perspex.Markup.Xaml.Data
@ -15,13 +16,13 @@ namespace Perspex.Markup.Xaml.Data
/// <summary>
/// A XAML binding that calculates an aggregate value from multiple child <see cref="Bindings"/>.
/// </summary>
public class MultiBinding : IXamlBinding
public class MultiBinding : IBinding
{
/// <summary>
/// Gets the collection of child bindings.
/// </summary>
[Content]
public IList<IXamlBinding> Bindings { get; set; } = new List<IXamlBinding>();
public IList<IBinding> Bindings { get; set; } = new List<IBinding>();
/// <summary>
/// Gets or sets the <see cref="IValueConverter"/> to use.
@ -50,7 +51,7 @@ namespace Perspex.Markup.Xaml.Data
/// <param name="property">The target property.</param>
public void Bind(IPerspexObject instance, PerspexProperty property)
{
var subject = CreateSubject(instance, property.PropertyType);
var subject = CreateSubject(instance, property);
if (subject != null)
{
@ -62,23 +63,18 @@ namespace Perspex.Markup.Xaml.Data
/// Creates a subject that can be used to get and set the value of the binding.
/// </summary>
/// <param name="target">The target instance.</param>
/// <param name="targetType">The type of the target property.</param>
/// <param name="targetIsDataContext">
/// Whether the target property is the DataContext property.
/// </param>
/// <returns>An <see cref="ISubject{object}"/>.</returns>
public ISubject<object> CreateSubject(
IPerspexObject target,
Type targetType,
bool targetIsDataContext = false)
/// <param name="targetProperty">The target property.</param>
/// <returns>An <see cref="ISubject{Object}"/>.</returns>
public ISubject<object> CreateSubject(IPerspexObject target, PerspexProperty targetProperty)
{
if (Converter == null)
{
throw new NotSupportedException("MultiBinding without Converter not currently supported.");
}
var targetType = targetProperty?.PropertyType ?? typeof(object);
var result = new BehaviorSubject<object>(PerspexProperty.UnsetValue);
var children = Bindings.Select(x => x.CreateSubject(target, typeof(object)));
var children = Bindings.Select(x => x.CreateSubject(target, null));
var input = children.CombineLatest().Select(x =>
Converter.Convert(x, targetType, null, CultureInfo.CurrentUICulture));
input.Subscribe(result);

1
src/Markup/Perspex.Markup.Xaml/MarkupExtensions/BindingExtension.cs

@ -2,6 +2,7 @@
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using OmniXaml;
using Perspex.Data;
using Perspex.Markup.Xaml.Data;
namespace Perspex.Markup.Xaml.MarkupExtensions

1
src/Markup/Perspex.Markup.Xaml/MarkupExtensions/TemplateBindingExtension.cs

@ -2,6 +2,7 @@
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using OmniXaml;
using Perspex.Data;
using Perspex.Markup.Xaml.Data;
namespace Perspex.Markup.Xaml.MarkupExtensions

1
src/Markup/Perspex.Markup.Xaml/Perspex.Markup.Xaml.csproj

@ -42,7 +42,6 @@
<Compile Include="Converters\CursorTypeConverter.cs" />
<Compile Include="Converters\PerspexListTypeConverter.cs" />
<Compile Include="Converters\RelativeRectTypeConverter.cs" />
<Compile Include="Data\IXamlBinding.cs" />
<Compile Include="Data\MultiBinding.cs" />
<Compile Include="Data\RelativeSource.cs" />
<Compile Include="Data\SourceBindingEndpoint.cs" />

25
src/Markup/Perspex.Markup.Xaml/PerspexXamlLoader.cs

@ -5,6 +5,7 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Text;
using OmniXaml;
using Perspex.Markup.Xaml.Context;
using Perspex.Platform;
@ -39,6 +40,8 @@ namespace Perspex.Markup.Xaml
/// <param name="obj">The object to load the XAML into.</param>
public static void Load(object obj)
{
Contract.Requires<ArgumentNullException>(obj != null);
var loader = new PerspexXamlLoader();
loader.Load(obj.GetType(), obj);
}
@ -53,6 +56,8 @@ namespace Perspex.Markup.Xaml
/// <returns>The loaded object.</returns>
public object Load(Type type, object rootInstance = null)
{
Contract.Requires<ArgumentNullException>(type != null);
// HACK: Currently Visual Studio is forcing us to change the extension of xaml files
// in certain situations, so we try to load .xaml and if that's not found we try .paml.
// Ideally we'd be able to use .xaml everywhere
@ -85,6 +90,8 @@ namespace Perspex.Markup.Xaml
/// <returns>The loaded object.</returns>
public object Load(Uri uri, object rootInstance = null)
{
Contract.Requires<ArgumentNullException>(uri != null);
var assetLocator = PerspexLocator.Current.GetService<IAssetLoader>();
if (assetLocator == null)
@ -99,6 +106,24 @@ namespace Perspex.Markup.Xaml
}
}
/// <summary>
/// Loads XAML from a string.
/// </summary>
/// <param name="xaml">The string containing the XAML.</param>
/// <param name="rootInstance">
/// The optional instance into which the XAML should be loaded.
/// </param>
/// <returns>The loaded object.</returns>
public object Load(string xaml, object rootInstance = null)
{
Contract.Requires<ArgumentNullException>(xaml != null);
using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(xaml)))
{
return Load(stream, rootInstance);
}
}
/// <summary>
/// Gets the URI for a type.
/// </summary>

2
src/Markup/Perspex.Markup.Xaml/Templates/TreeDataTemplate.cs

@ -6,6 +6,7 @@ using System.Collections;
using System.Reactive.Linq;
using Perspex.Controls;
using Perspex.Controls.Templates;
using Perspex.Data;
using Perspex.Markup.Data;
using Perspex.Markup.Xaml.Data;
using Perspex.Metadata;
@ -19,6 +20,7 @@ namespace Perspex.Markup.Xaml.Templates
[Content]
public TemplateContent Content { get; set; }
[AssignBinding]
public Binding ItemsSource { get; set; }
public bool Match(object data)

1
src/Perspex.Animation/Animatable.cs

@ -2,6 +2,7 @@
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System.Linq;
using Perspex.Data;
namespace Perspex.Animation
{

1
src/Perspex.Animation/Animate.cs

@ -5,6 +5,7 @@ using System;
using System.Diagnostics;
using System.Linq;
using System.Reactive.Linq;
using Perspex.Data;
using Perspex.Threading;
namespace Perspex.Animation

20
src/Perspex.Base/Data/AssignBindingAttribute.cs

@ -0,0 +1,20 @@
// 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.Data
{
/// <summary>
/// Signifies that a binding can be assigned to a property.
/// </summary>
/// <remarks>
/// Usually in markup, when a binding is set for a property that property will be bound.
/// Applying this attribute to a property indicates that the binding should be assigned to
/// the property rather than bound.
/// </remarks>
[AttributeUsage(AttributeTargets.Property)]
public sealed class AssignBindingAttribute : Attribute
{
}
}

36
src/Perspex.Base/Data/BindingMode.cs

@ -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.
namespace Perspex.Data
{
/// <summary>
/// Defines possible binding modes.
/// </summary>
public enum BindingMode
{
/// <summary>
/// Uses the default binding mode specified for the property.
/// </summary>
Default,
/// <summary>
/// Binds one way from source to target.
/// </summary>
OneWay,
/// <summary>
/// Binds two-way with the initial value coming from the target.
/// </summary>
TwoWay,
/// <summary>
/// Updates the target when the application starts or when the data context changes.
/// </summary>
OneTime,
/// <summary>
/// Binds one way from target to source.
/// </summary>
OneWayToSource,
}
}

2
src/Perspex.Base/BindingPriority.cs → src/Perspex.Base/Data/BindingPriority.cs

@ -1,7 +1,7 @@
// 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
namespace Perspex.Data
{
/// <summary>
/// The priority of a binding.

33
src/Perspex.Base/Data/IBinding.cs

@ -0,0 +1,33 @@
// 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.Reactive.Subjects;
namespace Perspex.Data
{
/// <summary>
/// Holds a binding that can be applied to a property on an object.
/// </summary>
public interface IBinding
{
/// <summary>
/// Gets the binding mode.
/// </summary>
BindingMode Mode { get; }
/// <summary>
/// Gets the binding priority.
/// </summary>
BindingPriority Priority { get; }
/// <summary>
/// Creates a subject that can be used to get and set the value of the binding.
/// </summary>
/// <param name="target">The target instance.</param>
/// <param name="targetProperty">The target property. May be null.</param>
/// <returns>An <see cref="ISubject{Object}"/>.</returns>
ISubject<object> CreateSubject(
IPerspexObject target,
PerspexProperty targetProperty);
}
}

45
src/Perspex.Base/BindingDescriptor.cs → src/Perspex.Base/Data/IndexerDescriptor.cs

@ -4,43 +4,12 @@
using System;
using System.Reactive;
namespace Perspex
namespace Perspex.Data
{
/// <summary>
/// Defines possible binding modes.
/// Holds a description of a binding for <see cref="PerspexObject"/>'s [] operator.
/// </summary>
public enum BindingMode
{
/// <summary>
/// Uses the default binding mode specified for the property.
/// </summary>
Default,
/// <summary>
/// Binds one way from source to target.
/// </summary>
OneWay,
/// <summary>
/// Binds two-way with the initial value coming from the target.
/// </summary>
TwoWay,
/// <summary>
/// Updates the target when the application starts or when the data context changes.
/// </summary>
OneTime,
/// <summary>
/// Binds one way from target to source.
/// </summary>
OneWayToSource,
}
/// <summary>
/// Holds a description of a binding, usually for <see cref="PerspexObject"/>'s [] operator.
/// </summary>
public class BindingDescriptor : ObservableBase<object>, IDescription
public class IndexerDescriptor : ObservableBase<object>, IDescription
{
/// <summary>
/// Gets or sets the binding mode.
@ -100,7 +69,7 @@ namespace Perspex
/// </summary>
/// <param name="binding">The current binding.</param>
/// <returns>A two-way binding.</returns>
public static BindingDescriptor operator !(BindingDescriptor binding)
public static IndexerDescriptor operator !(IndexerDescriptor binding)
{
return binding.WithMode(BindingMode.TwoWay);
}
@ -110,7 +79,7 @@ namespace Perspex
/// </summary>
/// <param name="binding">The current binding.</param>
/// <returns>A two-way binding.</returns>
public static BindingDescriptor operator ~(BindingDescriptor binding)
public static IndexerDescriptor operator ~(IndexerDescriptor binding)
{
return binding.WithMode(BindingMode.TwoWay);
}
@ -120,7 +89,7 @@ namespace Perspex
/// </summary>
/// <param name="mode">The binding mode.</param>
/// <returns>The object that the method was called on.</returns>
public BindingDescriptor WithMode(BindingMode mode)
public IndexerDescriptor WithMode(BindingMode mode)
{
Mode = mode;
return this;
@ -131,7 +100,7 @@ namespace Perspex
/// </summary>
/// <param name="priority">The binding priority.</param>
/// <returns>The object that the method was called on.</returns>
public BindingDescriptor WithPriority(BindingPriority priority)
public IndexerDescriptor WithPriority(BindingPriority priority)
{
Priority = priority;
return this;

2
src/Perspex.Base/Diagnostics/PerspexObjectExtensions.cs

@ -1,6 +1,8 @@
// 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.Data;
namespace Perspex.Diagnostics
{
/// <summary>

2
src/Perspex.Base/Diagnostics/PerspexPropertyValue.cs

@ -1,6 +1,8 @@
// 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.Data;
namespace Perspex.Diagnostics
{
/// <summary>

1
src/Perspex.Base/IPerspexObject.cs

@ -2,6 +2,7 @@
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
using Perspex.Data;
namespace Perspex
{

7
src/Perspex.Base/Perspex.Base.csproj

@ -42,8 +42,11 @@
<Compile Include="..\Shared\SharedAssemblyInfo.cs">
<Link>Properties\SharedAssemblyInfo.cs</Link>
</Compile>
<Compile Include="BindingDescriptor.cs" />
<Compile Include="Data\AssignBindingAttribute.cs" />
<Compile Include="Data\IBinding.cs" />
<Compile Include="Data\IndexerDescriptor.cs" />
<Compile Include="Collections\PerspexDictionary.cs" />
<Compile Include="Data\BindingMode.cs" />
<Compile Include="Diagnostics\PerspexObjectExtensions.cs" />
<Compile Include="IPerspexObject.cs" />
<Compile Include="Metadata\ContentAttribute.cs" />
@ -55,7 +58,7 @@
<Compile Include="PerspexProperty`1.cs" />
<Compile Include="Platform\IAssetLoader.cs" />
<Compile Include="Platform\IPclPlatformWrapper.cs" />
<Compile Include="BindingPriority.cs" />
<Compile Include="Data\BindingPriority.cs" />
<Compile Include="PriorityBindingEntry.cs" />
<Compile Include="Collections\IPerspexList.cs" />
<Compile Include="Collections\PerspexList.cs" />

11
src/Perspex.Base/PerspexObject.cs

@ -8,8 +8,7 @@ using System.Linq;
using System.Reactive.Disposables;
using System.Reactive.Linq;
using System.Reactive.Subjects;
using System.Reflection;
using Perspex.Reactive;
using Perspex.Data;
using Perspex.Threading;
using Perspex.Utilities;
using Serilog;
@ -154,7 +153,7 @@ namespace Perspex
/// Gets or sets a binding for a <see cref="PerspexProperty"/>.
/// </summary>
/// <param name="binding">The binding information.</param>
public IObservable<object> this[BindingDescriptor binding]
public IObservable<object> this[IndexerDescriptor binding]
{
get
{
@ -166,7 +165,7 @@ namespace Perspex
var mode = (binding.Mode == BindingMode.Default) ?
binding.Property.DefaultBindingMode :
binding.Mode;
var sourceBinding = value as BindingDescriptor;
var sourceBinding = value as IndexerDescriptor;
if (sourceBinding == null && mode > BindingMode.OneWay)
{
@ -192,9 +191,9 @@ namespace Perspex
}
}
protected virtual BindingDescriptor CreateBindingDescriptor(BindingDescriptor source)
protected virtual IndexerDescriptor CreateBindingDescriptor(IndexerDescriptor source)
{
return new BindingDescriptor
return new IndexerDescriptor
{
Mode = source.Mode,
Priority = source.Priority,

3
src/Perspex.Base/PerspexObjectExtensions.cs

@ -6,6 +6,7 @@ using System.Reactive;
using System.Reactive.Disposables;
using System.Reactive.Linq;
using System.Reactive.Subjects;
using Perspex.Data;
using Perspex.Reactive;
namespace Perspex
@ -141,7 +142,7 @@ namespace Perspex
/// <param name="source">The binding source.</param>
/// <param name="mode">The binding mode.</param>
/// <param name="priority">The binding priority.</param>
/// <returns></returns>
/// <returns>An <see cref="IDisposable"/> which can be used to cancel the binding.</returns>
public static IDisposable Bind(
this IPerspexObject o,
PerspexProperty property,

19
src/Perspex.Base/PerspexProperty.cs

@ -6,6 +6,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Reactive.Subjects;
using System.Reflection;
using Perspex.Data;
using Perspex.Utilities;
namespace Perspex
@ -323,10 +324,10 @@ namespace Perspex
/// indexer.
/// </summary>
/// <param name="property">The property.</param>
/// <returns>A <see cref="BindingDescriptor"/> describing the binding.</returns>
public static BindingDescriptor operator !(PerspexProperty property)
/// <returns>A <see cref="IndexerDescriptor"/> describing the binding.</returns>
public static IndexerDescriptor operator !(PerspexProperty property)
{
return new BindingDescriptor
return new IndexerDescriptor
{
Priority = BindingPriority.LocalValue,
Property = property,
@ -338,10 +339,10 @@ namespace Perspex
/// indexer.
/// </summary>
/// <param name="property">The property.</param>
/// <returns>A <see cref="BindingDescriptor"/> describing the binding.</returns>
public static BindingDescriptor operator ~(PerspexProperty property)
/// <returns>A <see cref="IndexerDescriptor"/> describing the binding.</returns>
public static IndexerDescriptor operator ~(PerspexProperty property)
{
return new BindingDescriptor
return new IndexerDescriptor
{
Priority = BindingPriority.TemplatedParent,
Property = property,
@ -557,13 +558,13 @@ namespace Perspex
/// Returns a binding accessor that can be passed to <see cref="PerspexObject"/>'s []
/// operator to initiate a binding.
/// </summary>
/// <returns>A <see cref="BindingDescriptor"/>.</returns>
/// <returns>A <see cref="IndexerDescriptor"/>.</returns>
/// <remarks>
/// The ! and ~ operators are short forms of this.
/// </remarks>
public BindingDescriptor Bind()
public IndexerDescriptor Bind()
{
return new BindingDescriptor
return new IndexerDescriptor
{
Property = this,
};

2
src/Perspex.Base/PerspexPropertyChangedEventArgs.cs

@ -1,6 +1,8 @@
// 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.Data;
namespace Perspex
{
/// <summary>

1
src/Perspex.Base/PerspexProperty`1.cs

@ -2,6 +2,7 @@
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
using Perspex.Data;
namespace Perspex
{

1
src/Perspex.Controls/Control.cs

@ -11,6 +11,7 @@ using System.Reactive.Subjects;
using Perspex.Collections;
using Perspex.Controls.Primitives;
using Perspex.Controls.Templates;
using Perspex.Data;
using Perspex.Input;
using Perspex.Interactivity;
using Perspex.LogicalTree;

31
src/Perspex.Controls/ControlExtensions.cs

@ -3,6 +3,7 @@
using System;
using System.Linq;
using Perspex.Data;
using Perspex.LogicalTree;
using Perspex.Styling;
@ -13,6 +14,36 @@ namespace Perspex.Controls
/// </summary>
public static class ControlExtensions
{
/// <summary>
/// Binds a property on an <see cref="IControl"/> to an <see cref="IBinding"/>.
/// </summary>
/// <param name="o">The object.</param>
/// <param name="property">The property to bind.</param>
/// <param name="binding">The binding.</param>
/// <returns>An <see cref="IDisposable"/> which can be used to cancel the binding.</returns>
public static IDisposable Bind(
this IControl o,
PerspexProperty property,
IBinding binding)
{
Contract.Requires<ArgumentNullException>(o != null);
Contract.Requires<ArgumentNullException>(property != null);
Contract.Requires<ArgumentNullException>(binding != null);
var mode = binding.Mode;
if (mode == BindingMode.Default)
{
mode = property.DefaultBindingMode;
}
return o.Bind(
property,
binding.CreateSubject(o, property),
mode,
binding.Priority);
}
/// <summary>
/// Tries to being the control into view.
/// </summary>

1
src/Perspex.Controls/Primitives/ScrollBar.cs

@ -4,6 +4,7 @@
using System;
using System.Reactive;
using System.Reactive.Linq;
using Perspex.Data;
namespace Perspex.Controls.Primitives
{

1
src/Perspex.Controls/Primitives/SelectingItemsControl.cs

@ -8,6 +8,7 @@ using System.Collections.Specialized;
using System.Linq;
using Perspex.Collections;
using Perspex.Controls.Generators;
using Perspex.Data;
using Perspex.Input;
using Perspex.Interactivity;
using Perspex.Styling;

3
src/Perspex.Controls/Primitives/TemplatedControl.cs

@ -6,6 +6,7 @@ using System.Linq;
using System.Reactive.Linq;
using Perspex.Controls.Presenters;
using Perspex.Controls.Templates;
using Perspex.Data;
using Perspex.Interactivity;
using Perspex.Media;
using Perspex.Styling;
@ -224,7 +225,7 @@ namespace Perspex.Controls.Primitives
}
}
protected sealed override BindingDescriptor CreateBindingDescriptor(BindingDescriptor source)
protected sealed override IndexerDescriptor CreateBindingDescriptor(IndexerDescriptor source)
{
var result = base.CreateBindingDescriptor(source);

1
src/Perspex.Controls/TextBlock.cs

@ -4,6 +4,7 @@
using System;
using System.Reactive;
using System.Reactive.Linq;
using Perspex.Data;
using Perspex.Media;
using Perspex.Metadata;

1
src/Perspex.Controls/TextBox.cs

@ -14,6 +14,7 @@ using Perspex.Input;
using Perspex.Interactivity;
using Perspex.Media;
using Perspex.Metadata;
using Perspex.Data;
namespace Perspex.Controls
{

1
src/Perspex.Diagnostics/Debug.cs

@ -7,6 +7,7 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Perspex.Controls;
using Perspex.Data;
namespace Perspex.Diagnostics
{

1
src/Perspex.Diagnostics/ViewModels/PropertyDetails.cs

@ -2,6 +2,7 @@
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
using Perspex.Data;
using ReactiveUI;
namespace Perspex.Diagnostics.ViewModels

1
src/Perspex.SceneGraph/Visual.cs

@ -8,6 +8,7 @@ using System.Linq;
using System.Reactive.Linq;
using Perspex.Animation;
using Perspex.Collections;
using Perspex.Data;
using Perspex.Media;
using Perspex.Platform;
using Perspex.Rendering;

1
src/Perspex.Styling/Perspex.Styling.csproj

@ -56,7 +56,6 @@
<Compile Include="Styling\SelectorMatch.cs" />
<Compile Include="Styling\Selector.cs" />
<Compile Include="Styling\Selectors.cs" />
<Compile Include="Styling\ObservableSetter.cs" />
<Compile Include="Styling\Setter.cs" />
<Compile Include="Styling\Style.cs" />
<Compile Include="Styling\StyleActivator.cs" />

65
src/Perspex.Styling/Styling/ObservableSetter.cs

@ -1,65 +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;
namespace Perspex.Styling
{
/// <summary>
/// A setter for a <see cref="Style"/> whose source is an observable.
/// </summary>
/// <remarks>
/// A <see cref="Setter"/> is used to set a <see cref="PerspexProperty"/> value on a
/// <see cref="PerspexObject"/> depending on a condition.
/// </remarks>
public class ObservableSetter : ISetter
{
/// <summary>
/// Initializes a new instance of the <see cref="ObservableSetter"/> class.
/// </summary>
/// <param name="property">The property to set.</param>
/// <param name="source">An observable which produces the value for the property.</param>
public ObservableSetter(PerspexProperty property, IObservable<object> source)
{
Property = property;
Source = source;
}
/// <summary>
/// Gets or sets the property to set.
/// </summary>
public PerspexProperty Property
{
get;
set;
}
/// <summary>
/// Gets or sets an observable which produces the value for the property.
/// </summary>
public IObservable<object> Source
{
get;
set;
}
/// <summary>
/// Applies the setter to the control.
/// </summary>
/// <param name="style">The style that is being applied.</param>
/// <param name="control">The control.</param>
/// <param name="activator">An optional activator.</param>
public void Apply(IStyle style, IStyleable control, IObservable<bool> activator)
{
if (activator == null)
{
control.Bind(Property, Source, BindingPriority.Style);
}
else
{
var binding = new StyleBinding(activator, Source, style.ToString());
control.Bind(Property, binding, BindingPriority.StyleTrigger);
}
}
}
}

2
src/Perspex.Styling/Styling/Setter.cs

@ -2,6 +2,7 @@
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
using Perspex.Data;
using Perspex.Metadata;
namespace Perspex.Styling
@ -46,6 +47,7 @@ namespace Perspex.Styling
/// Gets or sets the property value.
/// </summary>
[Content]
[AssignBinding]
public object Value
{
get;

1
tests/Perspex.Base.UnitTests/Perspex.Base.UnitTests.csproj

@ -82,6 +82,7 @@
<Compile Include="Collections\PerspexListTests.cs" />
<Compile Include="Collections\PropertyChangedTracker.cs" />
<Compile Include="PerspexObjectTests_Direct.cs" />
<Compile Include="PerspexObjectTests_GetSubject.cs" />
<Compile Include="PerspexObjectTests_GetObservable.cs" />
<Compile Include="PerspexPropertyRegistryTests.cs" />
<Compile Include="PerspexObjectTests_Validation.cs" />

7
tests/Perspex.Base.UnitTests/PerspexObjectTests_Binding.cs

@ -4,6 +4,7 @@
using System;
using System.Reactive.Linq;
using System.Reactive.Subjects;
using Perspex.Data;
using Xunit;
namespace Perspex.Base.UnitTests
@ -234,7 +235,7 @@ namespace Perspex.Base.UnitTests
{
Class1 target1 = new Class1();
Class1 target2 = new Class1();
BindingDescriptor binding = Class1.FooProperty.Bind().WithMode(BindingMode.OneWay);
IndexerDescriptor binding = Class1.FooProperty.Bind().WithMode(BindingMode.OneWay);
target1.SetValue(Class1.FooProperty, "first");
target2[binding] = target1[!Class1.FooProperty];
@ -248,7 +249,7 @@ namespace Perspex.Base.UnitTests
{
Class1 target1 = new Class1();
Class1 target2 = new Class1();
BindingDescriptor binding = Class1.FooProperty.Bind().WithMode(BindingMode.TwoWay);
IndexerDescriptor binding = Class1.FooProperty.Bind().WithMode(BindingMode.TwoWay);
target1.SetValue(Class1.FooProperty, "first");
target2[binding] = target1[!Class1.FooProperty];
@ -264,7 +265,7 @@ namespace Perspex.Base.UnitTests
{
Class1 target1 = new Class1();
Class1 target2 = new Class1();
BindingDescriptor binding = Class1.FooProperty.Bind().WithMode(BindingMode.OneTime);
IndexerDescriptor binding = Class1.FooProperty.Bind().WithMode(BindingMode.OneTime);
target1.SetValue(Class1.FooProperty, "first");
target2[binding] = target1[!Class1.FooProperty];

1
tests/Perspex.Base.UnitTests/PerspexObjectTests_Direct.cs

@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
using System.Reactive.Subjects;
using Perspex.Data;
using Xunit;
namespace Perspex.Base.UnitTests

1
tests/Perspex.Base.UnitTests/PerspexObjectTests_SetValue.cs

@ -2,6 +2,7 @@
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
using Perspex.Data;
using Xunit;
namespace Perspex.Base.UnitTests

1
tests/Perspex.Base.UnitTests/PerspexPropertyTests.cs

@ -2,6 +2,7 @@
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
using Perspex.Data;
using Xunit;
namespace Perspex.Base.UnitTests

7
tests/Perspex.Controls.UnitTests/Primitives/SelectingItemsControlTests_Multiple.cs

@ -9,6 +9,7 @@ using Perspex.Collections;
using Perspex.Controls.Presenters;
using Perspex.Controls.Primitives;
using Perspex.Controls.Templates;
using Perspex.Data;
using Perspex.Markup.Xaml.Data;
using Xunit;
@ -418,8 +419,8 @@ namespace Perspex.Controls.UnitTests.Primitives
};
// Bind Items and SelectedItems to the VM.
itemsBinding.Bind(target, TestSelector.ItemsProperty);
selectedItemsBinding.Bind(target, TestSelector.SelectedItemsProperty);
target.Bind(TestSelector.ItemsProperty, itemsBinding);
target.Bind(TestSelector.SelectedItemsProperty, selectedItemsBinding);
// Set DataContext and SelectedIndex
target.DataContext = vm;
@ -451,7 +452,7 @@ namespace Perspex.Controls.UnitTests.Primitives
};
var itemsBinding = new Binding { Path = "Items" };
itemsBinding.Bind(target, TestSelector.ItemsProperty);
target.Bind(TestSelector.ItemsProperty, itemsBinding);
Assert.Same(data.Items, target.Items);

3
tests/Perspex.LeakTests/ControlTests.cs

@ -194,7 +194,8 @@ namespace Perspex.LeakTests
Path = "Name"
};
binding.Bind((TextBox)window.Content, TextBox.TextProperty);
var textBox = (TextBox)window.Content;
textBox.Bind(TextBox.TextProperty, binding);
// Do a layout and make sure that TextBox gets added to visual tree and its
// Text property set.

26
tests/Perspex.Markup.Xaml.UnitTests/Data/BindingTests.cs

@ -3,6 +3,7 @@
using Moq;
using Perspex.Controls;
using Perspex.Data;
using Perspex.Markup.Data;
using Perspex.Markup.Xaml.Data;
using ReactiveUI;
@ -23,7 +24,7 @@ namespace Perspex.Markup.Xaml.UnitTests.Data
Mode = BindingMode.OneWay,
};
binding.Bind(target, TextBox.TextProperty);
target.Bind(TextBox.TextProperty, binding);
Assert.Equal("foo", target.Text);
source.Foo = "bar";
@ -43,7 +44,7 @@ namespace Perspex.Markup.Xaml.UnitTests.Data
Mode = BindingMode.TwoWay,
};
binding.Bind(target, TextBox.TextProperty);
target.Bind(TextBox.TextProperty, binding);
Assert.Equal("foo", target.Text);
source.Foo = "bar";
@ -63,7 +64,7 @@ namespace Perspex.Markup.Xaml.UnitTests.Data
Mode = BindingMode.OneTime,
};
binding.Bind(target, TextBox.TextProperty);
target.Bind(TextBox.TextProperty, binding);
Assert.Equal("foo", target.Text);
source.Foo = "bar";
@ -83,7 +84,7 @@ namespace Perspex.Markup.Xaml.UnitTests.Data
Mode = BindingMode.OneWayToSource,
};
binding.Bind(target, TextBox.TextProperty);
target.Bind(TextBox.TextProperty, binding);
Assert.Equal("bar", source.Foo);
target.Text = "baz";
@ -103,7 +104,7 @@ namespace Perspex.Markup.Xaml.UnitTests.Data
Path = "Foo",
};
binding.Bind(target, TextBox.TextProperty);
target.Bind(TextBox.TextProperty, binding);
Assert.Equal("foo", target.Text);
source.Foo = "bar";
@ -128,7 +129,7 @@ namespace Perspex.Markup.Xaml.UnitTests.Data
Path = "Header",
};
binding.Bind(parent.Child, Control.DataContextProperty);
parent.Child.Bind(Control.DataContextProperty, binding);
Assert.Equal("Foo", parent.Child.DataContext);
@ -152,7 +153,8 @@ namespace Perspex.Markup.Xaml.UnitTests.Data
Path = "Foo",
};
binding.Bind(child, Control.DataContextProperty);
child.Bind(Control.DataContextProperty, binding);
Assert.Null(child.DataContext);
parent.Child = child;
Assert.Equal("foo", child.DataContext);
@ -167,7 +169,7 @@ namespace Perspex.Markup.Xaml.UnitTests.Data
Path = "Foo",
};
var result = binding.CreateSubject(target, TextBox.TextProperty.PropertyType);
var result = binding.CreateSubject(target, TextBox.TextProperty);
Assert.IsType<DefaultValueConverter>(((ExpressionSubject)result).Converter);
}
@ -183,7 +185,7 @@ namespace Perspex.Markup.Xaml.UnitTests.Data
Path = "Foo",
};
var result = binding.CreateSubject(target, TextBox.TextProperty.PropertyType);
var result = binding.CreateSubject(target, TextBox.TextProperty);
Assert.Same(converter.Object, ((ExpressionSubject)result).Converter);
}
@ -200,7 +202,7 @@ namespace Perspex.Markup.Xaml.UnitTests.Data
Path = "Bar",
};
var result = binding.CreateSubject(target, TextBox.TextProperty.PropertyType);
var result = binding.CreateSubject(target, TextBox.TextProperty);
Assert.Same("foo", ((ExpressionSubject)result).ConverterParameter);
}
@ -236,8 +238,8 @@ namespace Perspex.Markup.Xaml.UnitTests.Data
};
// Bind Foo and Bar to the VM.
fooBinding.Bind(target, OldDataContextTest.FooProperty);
barBinding.Bind(target, OldDataContextTest.BarProperty);
target.Bind(OldDataContextTest.FooProperty, fooBinding);
target.Bind(OldDataContextTest.BarProperty, barBinding);
target.DataContext = vm;
// Make sure the control's Foo and Bar properties are read from the VM

8
tests/Perspex.Markup.Xaml.UnitTests/Data/BindingTests_ElementName.cs

@ -38,7 +38,7 @@ namespace Perspex.Markup.Xaml.UnitTests.Data
Path = "Text",
};
binding.Bind(target, TextBlock.TextProperty);
target.Bind(TextBox.TextProperty, binding);
Assert.Equal("foo", target.Text);
}
@ -73,7 +73,7 @@ namespace Perspex.Markup.Xaml.UnitTests.Data
ElementName = "source",
};
binding.Bind(target, ContentControl.ContentProperty);
target.Bind(ContentControl.ContentProperty, binding);
Assert.Same(source, target.Content);
}
@ -104,7 +104,7 @@ namespace Perspex.Markup.Xaml.UnitTests.Data
Path = "Text",
};
binding.Bind(target, TextBlock.TextProperty);
target.Bind(TextBox.TextProperty, binding);
stackPanel.Children.Add(new TextBlock
{
@ -140,7 +140,7 @@ namespace Perspex.Markup.Xaml.UnitTests.Data
ElementName = "source",
};
binding.Bind(target, ContentControl.ContentProperty);
target.Bind(ContentControl.ContentProperty, binding);
var source = new TextBlock
{

9
tests/Perspex.Markup.Xaml.UnitTests/Data/BindingTests_TemplatedParent.cs

@ -6,6 +6,7 @@ using System.Reactive.Linq;
using System.Reactive.Subjects;
using Moq;
using Perspex.Controls;
using Perspex.Data;
using Perspex.Markup.Xaml.Data;
using Perspex.Styling;
using Xunit;
@ -26,7 +27,7 @@ namespace Perspex.Markup.Xaml.UnitTests.Data
Path = "Foo",
};
binding.Bind(target.Object, TextBox.TextProperty);
target.Object.Bind(TextBox.TextProperty, binding);
target.Verify(x => x.Bind(
TextBox.TextProperty,
@ -46,7 +47,7 @@ namespace Perspex.Markup.Xaml.UnitTests.Data
Path = "Foo",
};
binding.Bind(target.Object, TextBox.TextProperty);
target.Object.Bind(TextBox.TextProperty, binding);
target.Verify(x => x.Bind(
TextBox.TextProperty,
@ -59,11 +60,11 @@ namespace Perspex.Markup.Xaml.UnitTests.Data
return CreateTarget(templatedParent: templatedParent);
}
private Mock<IPerspexObject> CreateTarget(
private Mock<IControl> CreateTarget(
ITemplatedControl templatedParent = null,
string text = null)
{
var result = new Mock<IPerspexObject>();
var result = new Mock<IControl>();
result.Setup(x => x.GetValue(Control.TemplatedParentProperty)).Returns(templatedParent);
result.Setup(x => x.GetValue((PerspexProperty)Control.TemplatedParentProperty)).Returns(templatedParent);

2
tests/Perspex.Markup.Xaml.UnitTests/Data/MultiBindingTests.cs

@ -33,7 +33,7 @@ namespace Perspex.Markup.Xaml.UnitTests.Data
var target = new Mock<IPerspexObject>();
target.Setup(x => x.GetValue(Control.DataContextProperty)).Returns(source);
var subject = binding.CreateSubject(target.Object, typeof(string));
var subject = binding.CreateSubject(target.Object, null);
var result = await subject.Take(1);
Assert.Equal("1,2,3", result);

5
tests/Perspex.Markup.Xaml.UnitTests/Perspex.Markup.Xaml.UnitTests.csproj

@ -43,9 +43,6 @@
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="Octokit">
<HintPath>..\..\packages\Octokit.0.14.0\lib\net45\Octokit.dll</HintPath>
</Reference>
<Reference Include="Splat, Version=1.6.2.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\packages\Splat.1.6.2\lib\Net45\Splat.dll</HintPath>
<Private>True</Private>
@ -104,7 +101,9 @@
<Compile Include="SampleModel\Repository.cs" />
<Compile Include="SampleModel\UserRepositoriesViewModel.cs" />
<Compile Include="SamplePerspexObject.cs" />
<Compile Include="StyleTests.cs" />
<Compile Include="Templates\DataTemplateTests.cs" />
<Compile Include="Templates\TreeDataTemplateTests.cs" />
<Compile Include="TestRoot.cs" />
<Compile Include="TypeProviderMock.cs" />
<Compile Include="ViewModelMock.cs" />

33
tests/Perspex.Markup.Xaml.UnitTests/StyleTests.cs

@ -0,0 +1,33 @@
// 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.Linq;
using Moq;
using Perspex.Markup.Xaml.Data;
using Perspex.Platform;
using Perspex.Styling;
using Xunit;
namespace Perspex.Markup.Xaml.UnitTests
{
public class StyleTests
{
[Fact]
public void Binding_Should_Be_Assigned_To_Setter_Value_Instead_Of_Bound()
{
using (PerspexLocator.EnterScope())
{
PerspexLocator.CurrentMutable
.Bind<IPclPlatformWrapper>()
.ToConstant(Mock.Of<IPclPlatformWrapper>());
var xaml = "<Style xmlns='https://github.com/perspex'><Setter Value='{Binding}'/></Style>";
var loader = new PerspexXamlLoader();
var style = (Style)loader.Load(xaml);
var setter = (Setter)(style.Setters.First());
Assert.IsType<Binding>(setter.Value);
}
}
}
}

34
tests/Perspex.Markup.Xaml.UnitTests/Templates/TreeDataTemplateTests.cs

@ -0,0 +1,34 @@
// 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.Linq;
using Moq;
using Perspex.Controls.Templates;
using Perspex.Markup.Xaml.Data;
using Perspex.Markup.Xaml.Templates;
using Perspex.Platform;
using Xunit;
namespace Perspex.Markup.Xaml.UnitTests
{
public class TreeDataTemplateTests
{
[Fact]
public void Binding_Should_Be_Assigned_To_ItemsSource_Instead_Of_Bound()
{
using (PerspexLocator.EnterScope())
{
PerspexLocator.CurrentMutable
.Bind<IPclPlatformWrapper>()
.ToConstant(Mock.Of<IPclPlatformWrapper>());
var xaml = "<DataTemplates xmlns='https://github.com/perspex'><TreeDataTemplate ItemsSource='{Binding}'/></DataTemplates>";
var loader = new PerspexXamlLoader();
var templates = (DataTemplates)loader.Load(xaml);
var template = (TreeDataTemplate)(templates.First());
Assert.IsType<Binding>(template.ItemsSource);
}
}
}
}

1
tests/Perspex.Styling.UnitTests/SelectorTests_Child.cs

@ -8,6 +8,7 @@ using System.Reactive;
using System.Reactive.Linq;
using Perspex.Collections;
using Perspex.Controls;
using Perspex.Data;
using Xunit;
namespace Perspex.Styling.UnitTests

1
tests/Perspex.Styling.UnitTests/SelectorTests_Descendent.cs

@ -8,6 +8,7 @@ using System.Reactive.Linq;
using System.Threading.Tasks;
using Perspex.Collections;
using Perspex.Controls;
using Perspex.Data;
using Xunit;
namespace Perspex.Styling.UnitTests

46
tests/Perspex.Styling.UnitTests/StyleTests.cs

@ -141,52 +141,6 @@ namespace Perspex.Styling.UnitTests
Assert.Equal(new[] { "foodefault", "Foo", "Bar", "foodefault" }, values);
}
[Fact]
public void Style_With_ObservableSetter_Should_Update_Value()
{
var source = new BehaviorSubject<string>("Foo");
Style style = new Style(x => x.OfType<Class1>())
{
Setters = new[]
{
new ObservableSetter(Class1.FooProperty, source),
},
};
var target = new Class1();
style.Attach(target, null);
Assert.Equal("Foo", target.Foo);
}
[Fact]
public void Style_With_ObservableSetter_Should_Update_And_Restore_Value()
{
var source = new BehaviorSubject<string>("Foo");
var style = new Style(x => x.OfType<Class1>().Class("foo"))
{
Setters = new[]
{
new ObservableSetter(Class1.FooProperty, source),
},
};
var target = new Class1();
style.Attach(target, null);
Assert.Equal("foodefault", target.Foo);
target.Classes.Add("foo");
Assert.Equal("Foo", target.Foo);
source.OnNext("Bar");
Assert.Equal("Bar", target.Foo);
target.Classes.Remove("foo");
Assert.Equal("foodefault", target.Foo);
}
[Fact]
public void Style_Should_Detach_When_Removed_From_Logical_Tree()
{

1
tests/Perspex.Styling.UnitTests/TestControlBase.cs

@ -5,6 +5,7 @@ using System;
using System.Reactive;
using Perspex.Collections;
using Perspex.Controls;
using Perspex.Data;
namespace Perspex.Styling.UnitTests
{

1
tests/Perspex.Styling.UnitTests/TestTemplatedControl.cs

@ -5,6 +5,7 @@ using System;
using System.Reactive;
using Perspex.Collections;
using Perspex.Controls;
using Perspex.Data;
namespace Perspex.Styling.UnitTests
{

Loading…
Cancel
Save