diff --git a/samples/TestApplicationShared/MainWindow.cs b/samples/TestApplicationShared/MainWindow.cs index 4c27018c6f..4650df1667 100644 --- a/samples/TestApplicationShared/MainWindow.cs +++ b/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; diff --git a/src/Markup/Perspex.Markup.Xaml/Context/PerspexWiringContext.cs b/src/Markup/Perspex.Markup.Xaml/Context/PerspexWiringContext.cs index 040ef313ba..e42df57d59 100644 --- a/src/Markup/Perspex.Markup.Xaml/Context/PerspexWiringContext.cs +++ b/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 diff --git a/src/Markup/Perspex.Markup.Xaml/Context/PerspexXamlMemberValuePlugin.cs b/src/Markup/Perspex.Markup.Xaml/Context/PerspexXamlMemberValuePlugin.cs index f6a33aa026..e9ce58c771 100644 --- a/src/Markup/Perspex.Markup.Xaml/Context/PerspexXamlMemberValuePlugin.cs +++ b/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() != 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) diff --git a/src/Markup/Perspex.Markup.Xaml/Data/Binding.cs b/src/Markup/Perspex.Markup.Xaml/Data/Binding.cs index f7546b3db8..55b80f5d40 100644 --- a/src/Markup/Perspex.Markup.Xaml/Data/Binding.cs +++ b/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 /// /// A XAML binding. /// - public class Binding : IXamlBinding + public class Binding : IBinding { /// /// Gets or sets the to use. @@ -50,49 +51,23 @@ namespace Perspex.Markup.Xaml.Data /// public string Path { get; set; } - /// - /// Applies the binding to a property on an instance. - /// - /// The target instance. - /// The target property. - public void Bind(IPerspexObject instance, PerspexProperty property) - { - Contract.Requires(instance != null); - Contract.Requires(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); - } - } - /// /// Creates a subject that can be used to get and set the value of the binding. /// /// The target instance. - /// The type of the target property. - /// - /// Whether the target property is the DataContext property. - /// - /// An . + /// The target property. May be null. + /// An . public ISubject CreateSubject( - IPerspexObject target, - Type targetType, - bool targetIsDataContext = false) + IPerspexObject target, + PerspexProperty targetProperty) { Contract.Requires(target != null); - Contract.Requires(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); } diff --git a/src/Markup/Perspex.Markup.Xaml/Data/IXamlBinding.cs b/src/Markup/Perspex.Markup.Xaml/Data/IXamlBinding.cs deleted file mode 100644 index 144302a9a7..0000000000 --- a/src/Markup/Perspex.Markup.Xaml/Data/IXamlBinding.cs +++ /dev/null @@ -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 -{ - /// - /// Defines a binding that can be created in XAML markup. - /// - public interface IXamlBinding - { - /// - /// Applies the binding to a property on an instance. - /// - /// The target instance. - /// The target property. - void Bind(IPerspexObject instance, PerspexProperty property); - - /// - /// Creates a subject that can be used to get and set the value of the binding. - /// - /// The target instance. - /// The type of the target property. - /// - /// Whether the target property is the DataContext property. - /// - /// An . - ISubject CreateSubject( - IPerspexObject target, - Type targetType, - bool targetIsDataContext = false); - } -} \ No newline at end of file diff --git a/src/Markup/Perspex.Markup.Xaml/Data/MultiBinding.cs b/src/Markup/Perspex.Markup.Xaml/Data/MultiBinding.cs index 41f67a0467..ea3760cf83 100644 --- a/src/Markup/Perspex.Markup.Xaml/Data/MultiBinding.cs +++ b/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 /// /// A XAML binding that calculates an aggregate value from multiple child . /// - public class MultiBinding : IXamlBinding + public class MultiBinding : IBinding { /// /// Gets the collection of child bindings. /// [Content] - public IList Bindings { get; set; } = new List(); + public IList Bindings { get; set; } = new List(); /// /// Gets or sets the to use. @@ -50,7 +51,7 @@ namespace Perspex.Markup.Xaml.Data /// The target property. 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. /// /// The target instance. - /// The type of the target property. - /// - /// Whether the target property is the DataContext property. - /// - /// An . - public ISubject CreateSubject( - IPerspexObject target, - Type targetType, - bool targetIsDataContext = false) + /// The target property. + /// An . + public ISubject 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(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); diff --git a/src/Markup/Perspex.Markup.Xaml/MarkupExtensions/BindingExtension.cs b/src/Markup/Perspex.Markup.Xaml/MarkupExtensions/BindingExtension.cs index 0bf319c913..e43ba9cb03 100644 --- a/src/Markup/Perspex.Markup.Xaml/MarkupExtensions/BindingExtension.cs +++ b/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 diff --git a/src/Markup/Perspex.Markup.Xaml/MarkupExtensions/TemplateBindingExtension.cs b/src/Markup/Perspex.Markup.Xaml/MarkupExtensions/TemplateBindingExtension.cs index b3bd1c7c75..361b04c915 100644 --- a/src/Markup/Perspex.Markup.Xaml/MarkupExtensions/TemplateBindingExtension.cs +++ b/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 diff --git a/src/Markup/Perspex.Markup.Xaml/Perspex.Markup.Xaml.csproj b/src/Markup/Perspex.Markup.Xaml/Perspex.Markup.Xaml.csproj index 69b6143bbc..d9cd4c6f63 100644 --- a/src/Markup/Perspex.Markup.Xaml/Perspex.Markup.Xaml.csproj +++ b/src/Markup/Perspex.Markup.Xaml/Perspex.Markup.Xaml.csproj @@ -42,7 +42,6 @@ - diff --git a/src/Markup/Perspex.Markup.Xaml/PerspexXamlLoader.cs b/src/Markup/Perspex.Markup.Xaml/PerspexXamlLoader.cs index c5282fc6ac..16eb793159 100644 --- a/src/Markup/Perspex.Markup.Xaml/PerspexXamlLoader.cs +++ b/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 /// The object to load the XAML into. public static void Load(object obj) { + Contract.Requires(obj != null); + var loader = new PerspexXamlLoader(); loader.Load(obj.GetType(), obj); } @@ -53,6 +56,8 @@ namespace Perspex.Markup.Xaml /// The loaded object. public object Load(Type type, object rootInstance = null) { + Contract.Requires(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 /// The loaded object. public object Load(Uri uri, object rootInstance = null) { + Contract.Requires(uri != null); + var assetLocator = PerspexLocator.Current.GetService(); if (assetLocator == null) @@ -99,6 +106,24 @@ namespace Perspex.Markup.Xaml } } + /// + /// Loads XAML from a string. + /// + /// The string containing the XAML. + /// + /// The optional instance into which the XAML should be loaded. + /// + /// The loaded object. + public object Load(string xaml, object rootInstance = null) + { + Contract.Requires(xaml != null); + + using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(xaml))) + { + return Load(stream, rootInstance); + } + } + /// /// Gets the URI for a type. /// diff --git a/src/Markup/Perspex.Markup.Xaml/Templates/TreeDataTemplate.cs b/src/Markup/Perspex.Markup.Xaml/Templates/TreeDataTemplate.cs index dc9143be1b..0ff1ec7a6d 100644 --- a/src/Markup/Perspex.Markup.Xaml/Templates/TreeDataTemplate.cs +++ b/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) diff --git a/src/Perspex.Animation/Animatable.cs b/src/Perspex.Animation/Animatable.cs index c00755f4bc..8e77b28dfe 100644 --- a/src/Perspex.Animation/Animatable.cs +++ b/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 { diff --git a/src/Perspex.Animation/Animate.cs b/src/Perspex.Animation/Animate.cs index d076c23953..1f85ffaadf 100644 --- a/src/Perspex.Animation/Animate.cs +++ b/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 diff --git a/src/Perspex.Base/Data/AssignBindingAttribute.cs b/src/Perspex.Base/Data/AssignBindingAttribute.cs new file mode 100644 index 0000000000..d44b66b58e --- /dev/null +++ b/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 +{ + /// + /// Signifies that a binding can be assigned to a property. + /// + /// + /// 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. + /// + [AttributeUsage(AttributeTargets.Property)] + public sealed class AssignBindingAttribute : Attribute + { + } +} diff --git a/src/Perspex.Base/Data/BindingMode.cs b/src/Perspex.Base/Data/BindingMode.cs new file mode 100644 index 0000000000..885f58ed1a --- /dev/null +++ b/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 +{ + /// + /// Defines possible binding modes. + /// + public enum BindingMode + { + /// + /// Uses the default binding mode specified for the property. + /// + Default, + + /// + /// Binds one way from source to target. + /// + OneWay, + + /// + /// Binds two-way with the initial value coming from the target. + /// + TwoWay, + + /// + /// Updates the target when the application starts or when the data context changes. + /// + OneTime, + + /// + /// Binds one way from target to source. + /// + OneWayToSource, + } +} diff --git a/src/Perspex.Base/BindingPriority.cs b/src/Perspex.Base/Data/BindingPriority.cs similarity index 98% rename from src/Perspex.Base/BindingPriority.cs rename to src/Perspex.Base/Data/BindingPriority.cs index e26898d202..055b252fa3 100644 --- a/src/Perspex.Base/BindingPriority.cs +++ b/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 { /// /// The priority of a binding. diff --git a/src/Perspex.Base/Data/IBinding.cs b/src/Perspex.Base/Data/IBinding.cs new file mode 100644 index 0000000000..025ef7dfb7 --- /dev/null +++ b/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 +{ + /// + /// Holds a binding that can be applied to a property on an object. + /// + public interface IBinding + { + /// + /// Gets the binding mode. + /// + BindingMode Mode { get; } + + /// + /// Gets the binding priority. + /// + BindingPriority Priority { get; } + + /// + /// Creates a subject that can be used to get and set the value of the binding. + /// + /// The target instance. + /// The target property. May be null. + /// An . + ISubject CreateSubject( + IPerspexObject target, + PerspexProperty targetProperty); + } +} diff --git a/src/Perspex.Base/BindingDescriptor.cs b/src/Perspex.Base/Data/IndexerDescriptor.cs similarity index 70% rename from src/Perspex.Base/BindingDescriptor.cs rename to src/Perspex.Base/Data/IndexerDescriptor.cs index c0c8f3406f..94563a5499 100644 --- a/src/Perspex.Base/BindingDescriptor.cs +++ b/src/Perspex.Base/Data/IndexerDescriptor.cs @@ -4,43 +4,12 @@ using System; using System.Reactive; -namespace Perspex +namespace Perspex.Data { /// - /// Defines possible binding modes. + /// Holds a description of a binding for 's [] operator. /// - public enum BindingMode - { - /// - /// Uses the default binding mode specified for the property. - /// - Default, - - /// - /// Binds one way from source to target. - /// - OneWay, - - /// - /// Binds two-way with the initial value coming from the target. - /// - TwoWay, - - /// - /// Updates the target when the application starts or when the data context changes. - /// - OneTime, - - /// - /// Binds one way from target to source. - /// - OneWayToSource, - } - - /// - /// Holds a description of a binding, usually for 's [] operator. - /// - public class BindingDescriptor : ObservableBase, IDescription + public class IndexerDescriptor : ObservableBase, IDescription { /// /// Gets or sets the binding mode. @@ -100,7 +69,7 @@ namespace Perspex /// /// The current binding. /// A two-way binding. - public static BindingDescriptor operator !(BindingDescriptor binding) + public static IndexerDescriptor operator !(IndexerDescriptor binding) { return binding.WithMode(BindingMode.TwoWay); } @@ -110,7 +79,7 @@ namespace Perspex /// /// The current binding. /// A two-way binding. - public static BindingDescriptor operator ~(BindingDescriptor binding) + public static IndexerDescriptor operator ~(IndexerDescriptor binding) { return binding.WithMode(BindingMode.TwoWay); } @@ -120,7 +89,7 @@ namespace Perspex /// /// The binding mode. /// The object that the method was called on. - public BindingDescriptor WithMode(BindingMode mode) + public IndexerDescriptor WithMode(BindingMode mode) { Mode = mode; return this; @@ -131,7 +100,7 @@ namespace Perspex /// /// The binding priority. /// The object that the method was called on. - public BindingDescriptor WithPriority(BindingPriority priority) + public IndexerDescriptor WithPriority(BindingPriority priority) { Priority = priority; return this; diff --git a/src/Perspex.Base/Diagnostics/PerspexObjectExtensions.cs b/src/Perspex.Base/Diagnostics/PerspexObjectExtensions.cs index cd7b95cf06..3efc5fbe98 100644 --- a/src/Perspex.Base/Diagnostics/PerspexObjectExtensions.cs +++ b/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 { /// diff --git a/src/Perspex.Base/Diagnostics/PerspexPropertyValue.cs b/src/Perspex.Base/Diagnostics/PerspexPropertyValue.cs index 99f5111b44..5b1d46bcf1 100644 --- a/src/Perspex.Base/Diagnostics/PerspexPropertyValue.cs +++ b/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 { /// diff --git a/src/Perspex.Base/IPerspexObject.cs b/src/Perspex.Base/IPerspexObject.cs index 47ccd7b9ff..4290bfe792 100644 --- a/src/Perspex.Base/IPerspexObject.cs +++ b/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 { diff --git a/src/Perspex.Base/Perspex.Base.csproj b/src/Perspex.Base/Perspex.Base.csproj index b08bce7e2f..1ece57e81d 100644 --- a/src/Perspex.Base/Perspex.Base.csproj +++ b/src/Perspex.Base/Perspex.Base.csproj @@ -42,8 +42,11 @@ Properties\SharedAssemblyInfo.cs - + + + + @@ -55,7 +58,7 @@ - + diff --git a/src/Perspex.Base/PerspexObject.cs b/src/Perspex.Base/PerspexObject.cs index eeb67a2482..5c4271a57d 100644 --- a/src/Perspex.Base/PerspexObject.cs +++ b/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 . /// /// The binding information. - public IObservable this[BindingDescriptor binding] + public IObservable 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, diff --git a/src/Perspex.Base/PerspexObjectExtensions.cs b/src/Perspex.Base/PerspexObjectExtensions.cs index e91ea6de0a..d7b2d5a748 100644 --- a/src/Perspex.Base/PerspexObjectExtensions.cs +++ b/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 /// The binding source. /// The binding mode. /// The binding priority. - /// + /// An which can be used to cancel the binding. public static IDisposable Bind( this IPerspexObject o, PerspexProperty property, diff --git a/src/Perspex.Base/PerspexProperty.cs b/src/Perspex.Base/PerspexProperty.cs index 0a00b21219..27c80ca323 100644 --- a/src/Perspex.Base/PerspexProperty.cs +++ b/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. /// /// The property. - /// A describing the binding. - public static BindingDescriptor operator !(PerspexProperty property) + /// A describing the binding. + public static IndexerDescriptor operator !(PerspexProperty property) { - return new BindingDescriptor + return new IndexerDescriptor { Priority = BindingPriority.LocalValue, Property = property, @@ -338,10 +339,10 @@ namespace Perspex /// indexer. /// /// The property. - /// A describing the binding. - public static BindingDescriptor operator ~(PerspexProperty property) + /// A describing the binding. + 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 's [] /// operator to initiate a binding. /// - /// A . + /// A . /// /// The ! and ~ operators are short forms of this. /// - public BindingDescriptor Bind() + public IndexerDescriptor Bind() { - return new BindingDescriptor + return new IndexerDescriptor { Property = this, }; diff --git a/src/Perspex.Base/PerspexPropertyChangedEventArgs.cs b/src/Perspex.Base/PerspexPropertyChangedEventArgs.cs index e16e2242fa..1136b5ce2a 100644 --- a/src/Perspex.Base/PerspexPropertyChangedEventArgs.cs +++ b/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 { /// diff --git a/src/Perspex.Base/PerspexProperty`1.cs b/src/Perspex.Base/PerspexProperty`1.cs index 6d97833f0a..dae476da31 100644 --- a/src/Perspex.Base/PerspexProperty`1.cs +++ b/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 { diff --git a/src/Perspex.Controls/Control.cs b/src/Perspex.Controls/Control.cs index bda72a8bfa..43eb87093c 100644 --- a/src/Perspex.Controls/Control.cs +++ b/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; diff --git a/src/Perspex.Controls/ControlExtensions.cs b/src/Perspex.Controls/ControlExtensions.cs index 5948331652..db11bff51e 100644 --- a/src/Perspex.Controls/ControlExtensions.cs +++ b/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 /// public static class ControlExtensions { + /// + /// Binds a property on an to an . + /// + /// The object. + /// The property to bind. + /// The binding. + /// An which can be used to cancel the binding. + public static IDisposable Bind( + this IControl o, + PerspexProperty property, + IBinding binding) + { + Contract.Requires(o != null); + Contract.Requires(property != null); + Contract.Requires(binding != null); + + var mode = binding.Mode; + + if (mode == BindingMode.Default) + { + mode = property.DefaultBindingMode; + } + + return o.Bind( + property, + binding.CreateSubject(o, property), + mode, + binding.Priority); + } + /// /// Tries to being the control into view. /// diff --git a/src/Perspex.Controls/Primitives/ScrollBar.cs b/src/Perspex.Controls/Primitives/ScrollBar.cs index fc40ad3d18..6cc9903531 100644 --- a/src/Perspex.Controls/Primitives/ScrollBar.cs +++ b/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 { diff --git a/src/Perspex.Controls/Primitives/SelectingItemsControl.cs b/src/Perspex.Controls/Primitives/SelectingItemsControl.cs index 53682a1ddf..56de4789e5 100644 --- a/src/Perspex.Controls/Primitives/SelectingItemsControl.cs +++ b/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; diff --git a/src/Perspex.Controls/Primitives/TemplatedControl.cs b/src/Perspex.Controls/Primitives/TemplatedControl.cs index 8ec6cece63..7e26758054 100644 --- a/src/Perspex.Controls/Primitives/TemplatedControl.cs +++ b/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); diff --git a/src/Perspex.Controls/TextBlock.cs b/src/Perspex.Controls/TextBlock.cs index 1c5bde93ea..96c47067a3 100644 --- a/src/Perspex.Controls/TextBlock.cs +++ b/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; diff --git a/src/Perspex.Controls/TextBox.cs b/src/Perspex.Controls/TextBox.cs index 3df18911fc..60d72113df 100644 --- a/src/Perspex.Controls/TextBox.cs +++ b/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 { diff --git a/src/Perspex.Diagnostics/Debug.cs b/src/Perspex.Diagnostics/Debug.cs index 1e07363a71..2977992f30 100644 --- a/src/Perspex.Diagnostics/Debug.cs +++ b/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 { diff --git a/src/Perspex.Diagnostics/ViewModels/PropertyDetails.cs b/src/Perspex.Diagnostics/ViewModels/PropertyDetails.cs index 171a8df25e..a5d1ef0fcf 100644 --- a/src/Perspex.Diagnostics/ViewModels/PropertyDetails.cs +++ b/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 diff --git a/src/Perspex.SceneGraph/Visual.cs b/src/Perspex.SceneGraph/Visual.cs index 4c1c41330f..ed9b661a7f 100644 --- a/src/Perspex.SceneGraph/Visual.cs +++ b/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; diff --git a/src/Perspex.Styling/Perspex.Styling.csproj b/src/Perspex.Styling/Perspex.Styling.csproj index 2ad2057aa5..5f79ca5820 100644 --- a/src/Perspex.Styling/Perspex.Styling.csproj +++ b/src/Perspex.Styling/Perspex.Styling.csproj @@ -56,7 +56,6 @@ - diff --git a/src/Perspex.Styling/Styling/ObservableSetter.cs b/src/Perspex.Styling/Styling/ObservableSetter.cs deleted file mode 100644 index 74e9fb07c9..0000000000 --- a/src/Perspex.Styling/Styling/ObservableSetter.cs +++ /dev/null @@ -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 -{ - /// - /// A setter for a whose source is an observable. - /// - /// - /// A is used to set a value on a - /// depending on a condition. - /// - public class ObservableSetter : ISetter - { - /// - /// Initializes a new instance of the class. - /// - /// The property to set. - /// An observable which produces the value for the property. - public ObservableSetter(PerspexProperty property, IObservable source) - { - Property = property; - Source = source; - } - - /// - /// Gets or sets the property to set. - /// - public PerspexProperty Property - { - get; - set; - } - - /// - /// Gets or sets an observable which produces the value for the property. - /// - public IObservable Source - { - get; - set; - } - - /// - /// Applies the setter to the control. - /// - /// The style that is being applied. - /// The control. - /// An optional activator. - public void Apply(IStyle style, IStyleable control, IObservable 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); - } - } - } -} diff --git a/src/Perspex.Styling/Styling/Setter.cs b/src/Perspex.Styling/Styling/Setter.cs index 88b7ac9cbc..0db359fa40 100644 --- a/src/Perspex.Styling/Styling/Setter.cs +++ b/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. /// [Content] + [AssignBinding] public object Value { get; diff --git a/tests/Perspex.Base.UnitTests/Perspex.Base.UnitTests.csproj b/tests/Perspex.Base.UnitTests/Perspex.Base.UnitTests.csproj index be37a88865..f3cfdb7f10 100644 --- a/tests/Perspex.Base.UnitTests/Perspex.Base.UnitTests.csproj +++ b/tests/Perspex.Base.UnitTests/Perspex.Base.UnitTests.csproj @@ -82,6 +82,7 @@ + diff --git a/tests/Perspex.Base.UnitTests/PerspexObjectTests_Binding.cs b/tests/Perspex.Base.UnitTests/PerspexObjectTests_Binding.cs index e54d4089a5..6788e509e2 100644 --- a/tests/Perspex.Base.UnitTests/PerspexObjectTests_Binding.cs +++ b/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]; diff --git a/tests/Perspex.Base.UnitTests/PerspexObjectTests_Direct.cs b/tests/Perspex.Base.UnitTests/PerspexObjectTests_Direct.cs index 491c31b9ad..07a4758292 100644 --- a/tests/Perspex.Base.UnitTests/PerspexObjectTests_Direct.cs +++ b/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 diff --git a/tests/Perspex.Base.UnitTests/PerspexObjectTests_SetValue.cs b/tests/Perspex.Base.UnitTests/PerspexObjectTests_SetValue.cs index 7c7dce01aa..1037c5476a 100644 --- a/tests/Perspex.Base.UnitTests/PerspexObjectTests_SetValue.cs +++ b/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 diff --git a/tests/Perspex.Base.UnitTests/PerspexPropertyTests.cs b/tests/Perspex.Base.UnitTests/PerspexPropertyTests.cs index b37cb49323..f49cf3466d 100644 --- a/tests/Perspex.Base.UnitTests/PerspexPropertyTests.cs +++ b/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 diff --git a/tests/Perspex.Controls.UnitTests/Primitives/SelectingItemsControlTests_Multiple.cs b/tests/Perspex.Controls.UnitTests/Primitives/SelectingItemsControlTests_Multiple.cs index d19d4c4999..21cce03055 100644 --- a/tests/Perspex.Controls.UnitTests/Primitives/SelectingItemsControlTests_Multiple.cs +++ b/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); diff --git a/tests/Perspex.LeakTests/ControlTests.cs b/tests/Perspex.LeakTests/ControlTests.cs index 78dc8d764e..a6275f452c 100644 --- a/tests/Perspex.LeakTests/ControlTests.cs +++ b/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. diff --git a/tests/Perspex.Markup.Xaml.UnitTests/Data/BindingTests.cs b/tests/Perspex.Markup.Xaml.UnitTests/Data/BindingTests.cs index 2cd71d4ac9..64efaa07de 100644 --- a/tests/Perspex.Markup.Xaml.UnitTests/Data/BindingTests.cs +++ b/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(((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 diff --git a/tests/Perspex.Markup.Xaml.UnitTests/Data/BindingTests_ElementName.cs b/tests/Perspex.Markup.Xaml.UnitTests/Data/BindingTests_ElementName.cs index c47d53d0a3..d1b929b4a9 100644 --- a/tests/Perspex.Markup.Xaml.UnitTests/Data/BindingTests_ElementName.cs +++ b/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 { diff --git a/tests/Perspex.Markup.Xaml.UnitTests/Data/BindingTests_TemplatedParent.cs b/tests/Perspex.Markup.Xaml.UnitTests/Data/BindingTests_TemplatedParent.cs index 75804b0748..b8ddd9a280 100644 --- a/tests/Perspex.Markup.Xaml.UnitTests/Data/BindingTests_TemplatedParent.cs +++ b/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 CreateTarget( + private Mock CreateTarget( ITemplatedControl templatedParent = null, string text = null) { - var result = new Mock(); + var result = new Mock(); result.Setup(x => x.GetValue(Control.TemplatedParentProperty)).Returns(templatedParent); result.Setup(x => x.GetValue((PerspexProperty)Control.TemplatedParentProperty)).Returns(templatedParent); diff --git a/tests/Perspex.Markup.Xaml.UnitTests/Data/MultiBindingTests.cs b/tests/Perspex.Markup.Xaml.UnitTests/Data/MultiBindingTests.cs index 9583c85525..908bb16e37 100644 --- a/tests/Perspex.Markup.Xaml.UnitTests/Data/MultiBindingTests.cs +++ b/tests/Perspex.Markup.Xaml.UnitTests/Data/MultiBindingTests.cs @@ -33,7 +33,7 @@ namespace Perspex.Markup.Xaml.UnitTests.Data var target = new Mock(); 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); diff --git a/tests/Perspex.Markup.Xaml.UnitTests/Perspex.Markup.Xaml.UnitTests.csproj b/tests/Perspex.Markup.Xaml.UnitTests/Perspex.Markup.Xaml.UnitTests.csproj index 3883b7594e..8a79e9eb28 100644 --- a/tests/Perspex.Markup.Xaml.UnitTests/Perspex.Markup.Xaml.UnitTests.csproj +++ b/tests/Perspex.Markup.Xaml.UnitTests/Perspex.Markup.Xaml.UnitTests.csproj @@ -43,9 +43,6 @@ 4 - - ..\..\packages\Octokit.0.14.0\lib\net45\Octokit.dll - ..\..\packages\Splat.1.6.2\lib\Net45\Splat.dll True @@ -104,7 +101,9 @@ + + diff --git a/tests/Perspex.Markup.Xaml.UnitTests/StyleTests.cs b/tests/Perspex.Markup.Xaml.UnitTests/StyleTests.cs new file mode 100644 index 0000000000..63c8751d82 --- /dev/null +++ b/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() + .ToConstant(Mock.Of()); + + var xaml = ""; + var loader = new PerspexXamlLoader(); + var style = (Style)loader.Load(xaml); + var setter = (Setter)(style.Setters.First()); + + Assert.IsType(setter.Value); + } + } + } +} diff --git a/tests/Perspex.Markup.Xaml.UnitTests/Templates/TreeDataTemplateTests.cs b/tests/Perspex.Markup.Xaml.UnitTests/Templates/TreeDataTemplateTests.cs new file mode 100644 index 0000000000..5bf4cea9eb --- /dev/null +++ b/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() + .ToConstant(Mock.Of()); + + var xaml = ""; + var loader = new PerspexXamlLoader(); + var templates = (DataTemplates)loader.Load(xaml); + var template = (TreeDataTemplate)(templates.First()); + + Assert.IsType(template.ItemsSource); + } + } + } +} diff --git a/tests/Perspex.Styling.UnitTests/SelectorTests_Child.cs b/tests/Perspex.Styling.UnitTests/SelectorTests_Child.cs index f9e0f1090e..427a806376 100644 --- a/tests/Perspex.Styling.UnitTests/SelectorTests_Child.cs +++ b/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 diff --git a/tests/Perspex.Styling.UnitTests/SelectorTests_Descendent.cs b/tests/Perspex.Styling.UnitTests/SelectorTests_Descendent.cs index e5477ebee3..fab2f15a97 100644 --- a/tests/Perspex.Styling.UnitTests/SelectorTests_Descendent.cs +++ b/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 diff --git a/tests/Perspex.Styling.UnitTests/StyleTests.cs b/tests/Perspex.Styling.UnitTests/StyleTests.cs index f915f4169c..7066d2fe28 100644 --- a/tests/Perspex.Styling.UnitTests/StyleTests.cs +++ b/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("Foo"); - - Style style = new Style(x => x.OfType()) - { - 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("Foo"); - - var style = new Style(x => x.OfType().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() { diff --git a/tests/Perspex.Styling.UnitTests/TestControlBase.cs b/tests/Perspex.Styling.UnitTests/TestControlBase.cs index 6743d2e468..3a0f0e5434 100644 --- a/tests/Perspex.Styling.UnitTests/TestControlBase.cs +++ b/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 { diff --git a/tests/Perspex.Styling.UnitTests/TestTemplatedControl.cs b/tests/Perspex.Styling.UnitTests/TestTemplatedControl.cs index f84a7d7ad6..847a124066 100644 --- a/tests/Perspex.Styling.UnitTests/TestTemplatedControl.cs +++ b/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 {