Ver Fonte

Merge branch 'bindings' into xaml-datatemplates

Conflicts:
	samples/XamlTestApplication/ViewModels/MainWindowViewModel.cs
	samples/XamlTestApplication/ViewModels/TestItem.cs
	samples/XamlTestApplication/Views/MainWindow.paml
	src/Markup/Perspex.Markup.Xaml/DataBinding/XamlBinding.cs
	src/Markup/Perspex.Markup.Xaml/DataBinding/XamlBindingDefinition.cs
	src/Markup/Perspex.Markup.Xaml/MarkupExtensions/BindingExtension.cs
Steven Kirk há 10 anos atrás
pai
commit
dbc32b17d1
25 ficheiros alterados com 110 adições e 672 exclusões
  1. 1 1
      samples/XamlTestApplication/ViewModels/TestNode.cs
  2. 0 6
      samples/XamlTestApplication/Views/MainWindow.cs
  3. 1 1
      samples/XamlTestApplication/XamlTestApplication.csproj
  4. 13 9
      src/Markup/Perspex.Markup.Xaml/Context/PerspexXamlMemberValuePlugin.cs
  5. 0 109
      src/Markup/Perspex.Markup.Xaml/DataBinding/ChangeTracking/ObservablePropertyBranch.cs
  6. 0 52
      src/Markup/Perspex.Markup.Xaml/DataBinding/ChangeTracking/PropertyMountPoint.cs
  7. 0 36
      src/Markup/Perspex.Markup.Xaml/DataBinding/ChangeTracking/PropertyPath.cs
  8. 0 41
      src/Markup/Perspex.Markup.Xaml/DataBinding/ChangeTracking/TargettedProperty.cs
  9. 0 165
      src/Markup/Perspex.Markup.Xaml/DataBinding/DataContextChangeSynchronizer.cs
  10. 3 0
      src/Markup/Perspex.Markup.Xaml/DataBinding/IPerspexPropertyBinder.cs
  11. 5 6
      src/Markup/Perspex.Markup.Xaml/DataBinding/PerspexPropertyBinder.cs
  12. 28 30
      src/Markup/Perspex.Markup.Xaml/DataBinding/XamlBinding.cs
  13. 0 3
      src/Markup/Perspex.Markup.Xaml/DataBinding/XamlBindingDefinition.cs
  14. 0 4
      src/Markup/Perspex.Markup.Xaml/MarkupExtensions/BindingExtension.cs
  15. 4 5
      src/Markup/Perspex.Markup.Xaml/Perspex.Markup.Xaml.csproj
  16. 1 3
      src/Markup/Perspex.Markup/Binding/ExpressionObserver.cs
  17. 48 0
      src/Markup/Perspex.Markup/Binding/ExpressionSubject.cs
  18. 1 0
      src/Markup/Perspex.Markup/Perspex.Markup.csproj
  19. 1 0
      src/Perspex.Controls/Properties/AssemblyInfo.cs
  20. 1 2
      tests/Perspex.Markup.Xaml.UnitTests/BindingDefinitionBuilder.cs
  21. 0 72
      tests/Perspex.Markup.Xaml.UnitTests/ChangeBranchTest.cs
  22. 0 103
      tests/Perspex.Markup.Xaml.UnitTests/DataContextChangeSynchronizerTest.cs
  23. 0 3
      tests/Perspex.Markup.Xaml.UnitTests/Perspex.Markup.Xaml.UnitTests.csproj
  24. 0 18
      tests/Perspex.Markup.Xaml.UnitTests/PropertyMountPointTest.cs
  25. 3 3
      tests/Perspex.Markup.Xaml.UnitTests/XamlBindingTest.cs

+ 1 - 1
samples/XamlTestApplication/Views/TestNode.cs → samples/XamlTestApplication/ViewModels/TestNode.cs

@@ -11,4 +11,4 @@ namespace XamlTestApplication.ViewModels
         public string SubHeader { get; set; }
         public IEnumerable<TestNode> Children { get; set; }
     }
-}
+}

+ 0 - 6
samples/XamlTestApplication/Views/MainWindow.cs

@@ -1,12 +1,6 @@
 // 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.Globalization;
-using System.IO;
-using System.Reflection;
-using System.Resources;
-using OmniXaml;
 using Perspex.Controls;
 using Perspex.Diagnostics;
 using Perspex.Markup.Xaml;

+ 1 - 1
samples/XamlTestApplication/XamlTestApplication.csproj

@@ -80,8 +80,8 @@
     <Compile Include="Program.cs" />
     <Compile Include="Properties\AssemblyInfo.cs" />
     <Compile Include="ViewModels\MainWindowViewModel.cs" />
-    <Compile Include="Views\TestNode.cs" />
     <Compile Include="ViewModels\TestItem.cs" />
+    <Compile Include="ViewModels\TestNode.cs" />
     <Compile Include="Views\MainWindow.cs" />
   </ItemGroup>
   <ItemGroup>

+ 13 - 9
src/Markup/Perspex.Markup.Xaml/Context/PerspexXamlMemberValuePlugin.cs

@@ -54,22 +54,26 @@ namespace Perspex.Markup.Xaml.Context
             po.SetValue(pp, value);
         }
 
-        private void HandleXamlBindingDefinition(XamlBindingDefinition xamlBindingDefinition)
+        private void HandleXamlBindingDefinition(XamlBindingDefinition def)
         {
-            PerspexObject subjectObject = xamlBindingDefinition.Target;
-            _propertyBinder.Create(xamlBindingDefinition);
+            var binding = new XamlBinding(_propertyBinder.TypeConverterProvider)
+            {
+                BindingMode = def.BindingMode,
+                SourcePropertyPath = def.SourcePropertyPath,
+                Target = def.Target,
+                TargetProperty = def.TargetProperty,
+            };
 
-            var observableForDataContext = subjectObject.GetObservable(Control.DataContextProperty);
-            observableForDataContext.Where(o => o != null).Subscribe(_ => BindToDataContextWhenItsSet(xamlBindingDefinition));
+            binding.Bind();
         }
 
         private void BindToDataContextWhenItsSet(XamlBindingDefinition definition)
         {
-            var target = definition.Target;
-            var dataContext = target.DataContext;
+        //    var target = definition.Target;
+        //    var dataContext = target.DataContext;
 
-            var binding = _propertyBinder.GetBinding(target, definition.TargetProperty);
-            binding.BindToDataContext(dataContext);
+        //    var binding = _propertyBinder.GetBinding(target, definition.TargetProperty);
+        //    binding.BindToDataContext(dataContext);
         }
 
         // ReSharper disable once MemberCanBePrivate.Global

+ 0 - 109
src/Markup/Perspex.Markup.Xaml/DataBinding/ChangeTracking/ObservablePropertyBranch.cs

@@ -1,109 +0,0 @@
-// Copyright (c) The Perspex Project. All rights reserved.
-// Licensed under the MIT license. See licence.md file in the project root for full license information.
-
-using System;
-using System.Collections.Generic;
-using System.ComponentModel;
-using System.Linq;
-using System.Reactive.Linq;
-using System.Reflection;
-using Glass;
-
-namespace Perspex.Markup.Xaml.DataBinding.ChangeTracking
-{
-    public class ObservablePropertyBranch
-    {
-        private readonly object _instance;
-        private readonly PropertyPath _propertyPath;
-        private readonly PropertyMountPoint _mountPoint;
-
-        public ObservablePropertyBranch(object instance, PropertyPath propertyPath)
-        {
-            Guard.ThrowIfNull(instance, nameof(instance));
-            Guard.ThrowIfNull(propertyPath, nameof(propertyPath));
-
-            _instance = instance;
-            _propertyPath = propertyPath;
-            _mountPoint = new PropertyMountPoint(instance, propertyPath);
-            var properties = GetPropertiesThatRaiseNotifications();
-            Values = CreateUnifiedObservableFromNodes(properties);
-        }
-
-        public IObservable<object> Values { get; private set; }
-
-        private IObservable<object> CreateUnifiedObservableFromNodes(IEnumerable<PropertyDefinition> subscriptions)
-        {
-            return subscriptions.Select(GetObservableFromProperty).Merge();
-        }
-
-        private IObservable<object> GetObservableFromProperty(PropertyDefinition subscription)
-        {
-            return Observable.FromEventPattern<PropertyChangedEventHandler, PropertyChangedEventArgs>(
-                parentOnPropertyChanged => subscription.Parent.PropertyChanged += parentOnPropertyChanged,
-                parentOnPropertyChanged => subscription.Parent.PropertyChanged -= parentOnPropertyChanged)
-                .Where(pattern => pattern.EventArgs.PropertyName == subscription.PropertyName)
-                .Select(pattern => _mountPoint.Value);
-        }
-
-        private IEnumerable<PropertyDefinition> GetPropertiesThatRaiseNotifications()
-        {
-            return GetSubscriptionsRecursive(_instance, _propertyPath, 0);
-        }
-
-        private IEnumerable<PropertyDefinition> GetSubscriptionsRecursive(object current, PropertyPath propertyPath, int i)
-        {
-            var subscriptions = new List<PropertyDefinition>();
-            var inpc = current as INotifyPropertyChanged;
-
-            if (inpc == null)
-            {
-                return subscriptions;
-            }
-
-            var nextPropertyName = propertyPath.Chunks[i];
-            subscriptions.Add(new PropertyDefinition(inpc, nextPropertyName));
-
-            if (i < _propertyPath.Chunks.Length)
-            {
-                var currentObjectTypeInfo = current.GetType().GetTypeInfo();
-                var nextProperty = currentObjectTypeInfo.GetDeclaredProperty(nextPropertyName);
-                var nextInstance = nextProperty.GetValue(current);
-
-                if (i < _propertyPath.Chunks.Length - 1)
-                {
-                    subscriptions.AddRange(GetSubscriptionsRecursive(nextInstance, propertyPath, i + 1));
-                }
-            }
-
-            return subscriptions;
-        }
-
-        public object Value
-        {
-            get
-            {
-                return _mountPoint.Value;
-            }
-
-            set
-            {
-                _mountPoint.Value = value;
-            }
-        }
-
-        public Type Type => _mountPoint.ProperyType;
-
-        private class PropertyDefinition
-        {
-            public PropertyDefinition(INotifyPropertyChanged parent, string propertyName)
-            {
-                Parent = parent;
-                PropertyName = propertyName;
-            }
-
-            public INotifyPropertyChanged Parent { get; }
-
-            public string PropertyName { get; }
-        }
-    }
-}

+ 0 - 52
src/Markup/Perspex.Markup.Xaml/DataBinding/ChangeTracking/PropertyMountPoint.cs

@@ -1,52 +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.Reflection;
-using Glass;
-
-namespace Perspex.Markup.Xaml.DataBinding.ChangeTracking
-{
-    public class PropertyMountPoint
-    {
-        private readonly TargettedProperty _referencedTargettedProperty;
-
-        public PropertyMountPoint(object origin, PropertyPath propertyPath)
-        {
-            Guard.ThrowIfNull(origin, nameof(origin));
-            Guard.ThrowIfNull(propertyPath, nameof(propertyPath));
-
-            _referencedTargettedProperty = GetReferencedPropertyInfo(origin, propertyPath, 0);
-        }
-
-        private static TargettedProperty GetReferencedPropertyInfo(object current, PropertyPath propertyPath, int level)
-        {
-            var typeInfo = current.GetType().GetTypeInfo();
-            var leftPropertyInfo = typeInfo.GetDeclaredProperty(propertyPath.Chunks[level]);
-
-            if (level == propertyPath.Chunks.Length - 1)
-            {
-                return new TargettedProperty(current, leftPropertyInfo);
-            }
-
-            var nextInstance = leftPropertyInfo.GetValue(current);
-
-            return GetReferencedPropertyInfo(nextInstance, propertyPath, level + 1);
-        }
-
-        public object Value
-        {
-            get
-            {
-                return _referencedTargettedProperty.Value;
-            }
-
-            set
-            {
-                _referencedTargettedProperty.Value = value;
-            }
-        }
-
-        public Type ProperyType => _referencedTargettedProperty.PropertyType;
-    }
-}

+ 0 - 36
src/Markup/Perspex.Markup.Xaml/DataBinding/ChangeTracking/PropertyPath.cs

@@ -1,36 +0,0 @@
-// Copyright (c) The Perspex Project. All rights reserved.
-// Licensed under the MIT license. See licence.md file in the project root for full license information.
-
-namespace Perspex.Markup.Xaml.DataBinding.ChangeTracking
-{
-    public class PropertyPath
-    {
-        private string[] _chunks;
-
-        private PropertyPath(PropertyPath propertyPath)
-        {
-            _chunks = propertyPath.Chunks;
-        }
-
-        public PropertyPath(string path)
-        {
-            _chunks = path.Split('.');
-        }
-
-        public string[] Chunks
-        {
-            get { return _chunks; }
-            set { _chunks = value; }
-        }
-
-        public PropertyPath Clone()
-        {
-            return new PropertyPath(this);
-        }
-
-        public override string ToString()
-        {
-            return string.Join(".", _chunks);
-        }
-    }
-}

+ 0 - 41
src/Markup/Perspex.Markup.Xaml/DataBinding/ChangeTracking/TargettedProperty.cs

@@ -1,41 +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.Reflection;
-using Glass;
-
-namespace Perspex.Markup.Xaml.DataBinding.ChangeTracking
-{
-    internal class TargettedProperty
-    {
-        private readonly object _instance;
-        private readonly PropertyInfo _propertyInfo;
-
-        public TargettedProperty(object instance, PropertyInfo propertyInfo)
-        {
-            Guard.ThrowIfNull(instance, nameof(instance));
-            Guard.ThrowIfNull(propertyInfo, nameof(propertyInfo));
-
-            _instance = instance;
-            _propertyInfo = propertyInfo;
-        }
-
-        public object Value
-        {
-            get
-            {
-                return _propertyInfo.GetValue(_instance);
-            }
-
-            set
-            {
-                _propertyInfo.SetValue(_instance, value);
-            }
-        }
-
-        public Type PropertyType => _propertyInfo.PropertyType;
-
-        public string Name => _propertyInfo.Name;
-    }
-}

+ 0 - 165
src/Markup/Perspex.Markup.Xaml/DataBinding/DataContextChangeSynchronizer.cs

@@ -1,165 +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.Globalization;
-using System.Reactive.Linq;
-using System.Reflection;
-using Glass;
-using OmniXaml.TypeConversion;
-using Perspex.Markup.Xaml.DataBinding.ChangeTracking;
-
-namespace Perspex.Markup.Xaml.DataBinding
-{
-    public class DataContextChangeSynchronizer
-    {
-        private readonly BindingTarget _bindingTarget;
-        private readonly ITypeConverter _targetPropertyTypeConverter;
-        private readonly TargetBindingEndpoint _bindingEndpoint;
-        private readonly ObservablePropertyBranch _sourceEndpoint;
-
-        public DataContextChangeSynchronizer(BindingSource bindingSource, BindingTarget bindingTarget, ITypeConverterProvider typeConverterProvider)
-        {
-            _bindingTarget = bindingTarget;
-            Guard.ThrowIfNull(bindingTarget.Object, nameof(bindingTarget.Object));
-            Guard.ThrowIfNull(bindingTarget.Property, nameof(bindingTarget.Property));
-            Guard.ThrowIfNull(bindingSource.SourcePropertyPath, nameof(bindingSource.SourcePropertyPath));
-            Guard.ThrowIfNull(bindingSource.Source, nameof(bindingSource.Source));
-            Guard.ThrowIfNull(typeConverterProvider, nameof(typeConverterProvider));
-
-            _bindingEndpoint = new TargetBindingEndpoint(bindingTarget.Object, bindingTarget.Property);
-            _sourceEndpoint = new ObservablePropertyBranch(bindingSource.Source, bindingSource.SourcePropertyPath);
-            _targetPropertyTypeConverter = typeConverterProvider.GetTypeConverter(bindingTarget.Property.PropertyType);
-        }
-
-        public class BindingTarget
-        {
-            private readonly PerspexObject _obj;
-            private readonly PerspexProperty _property;
-
-            public BindingTarget(PerspexObject @object, PerspexProperty property)
-            {
-                _obj = @object;
-                _property = property;
-            }
-
-            public PerspexObject Object => _obj;
-
-            public PerspexProperty Property => _property;
-
-            public object Value
-            {
-                get { return _obj.GetValue(_property); }
-                set { _obj.SetValue(_property, value); }
-            }
-        }
-
-        public class BindingSource
-        {
-            private readonly PropertyPath _sourcePropertyPath;
-            private readonly object _source;
-
-            public BindingSource(PropertyPath sourcePropertyPath, object source)
-            {
-                _sourcePropertyPath = sourcePropertyPath;
-                _source = source;
-            }
-
-            public PropertyPath SourcePropertyPath => _sourcePropertyPath;
-
-            public object Source => _source;
-        }
-
-        public void StartUpdatingTargetWhenSourceChanges()
-        {
-            // TODO: commenting out this line will make the existing value to be skipped from the SourceValues. This is not supposed to happen. Is it?
-            _bindingTarget.Value = ConvertedValue(_sourceEndpoint.Value, _bindingTarget.Property.PropertyType);
-
-            // We use the native Bind method from PerspexObject to subscribe to the SourceValues observable
-            _bindingTarget.Object.Bind(_bindingTarget.Property, SourceValues);
-        }
-
-        public void StartUpdatingSourceWhenTargetChanges()
-        {
-            // We subscribe to the TargetValues and each time we have a new value, we update the source with it
-            TargetValues.Subscribe(newValue => _sourceEndpoint.Value = newValue);
-        }
-
-        private IObservable<object> SourceValues
-        {
-            get
-            {
-                return _sourceEndpoint.Values.Select(originalValue => ConvertedValue(originalValue, _bindingTarget.Property.PropertyType));
-            }
-        }
-
-        private IObservable<object> TargetValues
-        {
-            get
-            {
-                return _bindingEndpoint.Object
-                    .GetObservable(_bindingEndpoint.Property).Select(o => ConvertedValue(o, _sourceEndpoint.Type));
-            }
-        }
-
-        private bool CanAssignWithoutConversion
-        {
-            get
-            {
-                var sourceTypeInfo = _sourceEndpoint.Type.GetTypeInfo();
-                var targetTypeInfo = _bindingEndpoint.Property.PropertyType.GetTypeInfo();
-                var compatible = targetTypeInfo.IsAssignableFrom(sourceTypeInfo);
-                return compatible;
-            }
-        }
-
-        private object ConvertedValue(object originalValue, Type propertyType)
-        {
-            object converted;
-            if (TryConvert(originalValue, propertyType, out converted))
-            {
-                return converted;
-            }
-
-            return null;
-        }
-
-        private bool TryConvert(object originalValue, Type targetType, out object finalValue)
-        {
-            if (originalValue != null)
-            {
-                if (CanAssignWithoutConversion)
-                {
-                    finalValue = originalValue;
-                    return true;
-                }
-
-                if (_targetPropertyTypeConverter != null)
-                {
-                    if (_targetPropertyTypeConverter.CanConvertTo(null, targetType))
-                    {
-                        object convertedValue = _targetPropertyTypeConverter.ConvertTo(
-                            null,
-                            CultureInfo.InvariantCulture,
-                            originalValue,
-                            targetType);
-
-                        if (convertedValue != null)
-                        {
-                            finalValue = convertedValue;
-                            return true;
-                        }
-                    }
-                }
-            }
-            else
-            {
-                finalValue = null;
-                return true;
-            }
-
-            finalValue = null;
-            return false;
-        }
-    }
-}

+ 3 - 0
src/Markup/Perspex.Markup.Xaml/DataBinding/IPerspexPropertyBinder.cs

@@ -2,11 +2,14 @@
 // Licensed under the MIT license. See licence.md file in the project root for full license information.
 
 using System.Collections.Generic;
+using OmniXaml.TypeConversion;
 
 namespace Perspex.Markup.Xaml.DataBinding
 {
     public interface IPerspexPropertyBinder
     {
+        ITypeConverterProvider TypeConverterProvider { get; }
+
         XamlBinding GetBinding(PerspexObject po, PerspexProperty pp);
 
         IEnumerable<XamlBinding> GetBindings(PerspexObject source);

+ 5 - 6
src/Markup/Perspex.Markup.Xaml/DataBinding/PerspexPropertyBinder.cs

@@ -5,22 +5,21 @@ using System;
 using System.Collections.Generic;
 using System.Linq;
 using OmniXaml.TypeConversion;
-using Perspex.Markup.Xaml.DataBinding.ChangeTracking;
 
 namespace Perspex.Markup.Xaml.DataBinding
 {
     public class PerspexPropertyBinder : IPerspexPropertyBinder
     {
-        private readonly ITypeConverterProvider _typeConverterProvider;
-
         private readonly HashSet<XamlBinding> _bindings;
 
         public PerspexPropertyBinder(ITypeConverterProvider typeConverterProvider)
         {
-            _typeConverterProvider = typeConverterProvider;
+            TypeConverterProvider = typeConverterProvider;
             _bindings = new HashSet<XamlBinding>();
         }
 
+        public ITypeConverterProvider TypeConverterProvider { get; }
+
         public XamlBinding GetBinding(PerspexObject po, PerspexProperty pp)
         {
             return _bindings.First(xamlBinding => xamlBinding.Target == po && xamlBinding.TargetProperty == pp);
@@ -45,10 +44,10 @@ namespace Perspex.Markup.Xaml.DataBinding
                 throw new InvalidOperationException();
             }
 
-            var binding = new XamlBinding(_typeConverterProvider)
+            var binding = new XamlBinding(TypeConverterProvider)
             {
                 BindingMode = xamlBinding.BindingMode,
-                SourcePropertyPath = new PropertyPath(xamlBinding.SourcePropertyPath),
+                SourcePropertyPath = xamlBinding.SourcePropertyPath,
                 Target = xamlBinding.Target,
                 TargetProperty = xamlBinding.TargetProperty
             };

+ 28 - 30
src/Markup/Perspex.Markup.Xaml/DataBinding/XamlBinding.cs

@@ -2,16 +2,15 @@
 // Licensed under the MIT license. See licence.md file in the project root for full license information.
 
 using System;
-using System.Diagnostics;
+using System.Reactive.Linq;
 using OmniXaml.TypeConversion;
-using Perspex.Markup.Xaml.DataBinding.ChangeTracking;
+using Perspex.Markup.Binding;
 
 namespace Perspex.Markup.Xaml.DataBinding
 {
     public class XamlBinding
     {
         private readonly ITypeConverterProvider _typeConverterProvider;
-        private DataContextChangeSynchronizer _changeSynchronizer;
 
         public XamlBinding(ITypeConverterProvider typeConverterProvider)
         {
@@ -22,44 +21,43 @@ namespace Perspex.Markup.Xaml.DataBinding
 
         public PerspexProperty TargetProperty { get; set; }
 
-        public PropertyPath SourcePropertyPath { get; set; }
+        public object Source { get; set; }
+
+        public string SourcePropertyPath { get; set; }
 
         public BindingMode BindingMode { get; set; }
 
-        public void BindToDataContext(object dataContext)
+        public void Bind()
         {
-            if (dataContext == null)
-            {
-                return;
-            }
+            var path = SourcePropertyPath;
+            var source = Source;
 
-            try
+            if (source == null)
             {
-                var bindingSource = new DataContextChangeSynchronizer.BindingSource(SourcePropertyPath, dataContext);
-                var bindingTarget = new DataContextChangeSynchronizer.BindingTarget(Target, TargetProperty);
-                var mode = BindingMode == BindingMode.Default ? TargetProperty.DefaultBindingMode : BindingMode;
-
-                _changeSynchronizer = new DataContextChangeSynchronizer(bindingSource, bindingTarget, _typeConverterProvider);
-
-                if (mode == BindingMode.TwoWay)
-                {
-                    _changeSynchronizer.StartUpdatingTargetWhenSourceChanges();
-                    _changeSynchronizer.StartUpdatingSourceWhenTargetChanges();
-                }
-
-                if (mode == BindingMode.OneWay || mode == BindingMode.Default)
+                if (!string.IsNullOrWhiteSpace(path))
                 {
-                    _changeSynchronizer.StartUpdatingTargetWhenSourceChanges();
+                    path = "DataContext." + path;
                 }
 
-                if (mode == BindingMode.OneWayToSource)
-                {
-                    _changeSynchronizer.StartUpdatingSourceWhenTargetChanges();
-                }
+                source = Target;
             }
-            catch (Exception e)
+
+            var observable = new ExpressionObserver(source, path);
+            var mode = BindingMode == BindingMode.Default ? 
+                TargetProperty.DefaultBindingMode : BindingMode;
+
+            switch (mode)
             {
-                Debug.WriteLine(e);
+                case BindingMode.Default:
+                case BindingMode.OneWay:
+                    Target.Bind(TargetProperty, observable.Select(x => x.Value));
+                    break;
+                case BindingMode.TwoWay:
+                    throw new NotImplementedException();
+                case BindingMode.OneTime:
+                    throw new NotImplementedException();
+                case BindingMode.OneWayToSource:
+                    throw new NotImplementedException();
             }
         }
     }

+ 0 - 3
src/Markup/Perspex.Markup.Xaml/DataBinding/XamlBindingDefinition.cs

@@ -20,11 +20,8 @@ namespace Perspex.Markup.Xaml.DataBinding
         }
 
         public Control Target { get; }
-
         public PerspexProperty TargetProperty { get; }
-
         public string SourcePropertyPath { get; }
-
         public BindingMode BindingMode { get; }
     }
 }

+ 0 - 4
src/Markup/Perspex.Markup.Xaml/MarkupExtensions/BindingExtension.cs

@@ -5,7 +5,6 @@ using System.Linq;
 using OmniXaml;
 using Perspex.Controls;
 using Perspex.Markup.Xaml.DataBinding;
-using Perspex.Markup.Xaml.DataBinding.ChangeTracking;
 
 namespace Perspex.Markup.Xaml.MarkupExtensions
 {
@@ -26,13 +25,10 @@ namespace Perspex.Markup.Xaml.MarkupExtensions
             var targetProperty = extensionContext.TargetProperty;
             var targetPropertyName = targetProperty.Name;
             var perspexProperty = target.GetRegisteredProperties().First(property => property.Name == targetPropertyName);
-
             return new XamlBindingDefinition(target, perspexProperty, Path, Mode);
         }
 
-        /// <summary> The source path (for CLR bindings).</summary>
         public string Path { get; set; }
-
         public BindingMode Mode { get; set; }
     }
 }

+ 4 - 5
src/Markup/Perspex.Markup.Xaml/Perspex.Markup.Xaml.csproj

@@ -54,10 +54,6 @@
     <Compile Include="Converters\BrushTypeConverter.cs" />
     <Compile Include="Converters\BitmapTypeConverter.cs" />
     <Compile Include="Converters\GridLengthTypeConverter.cs" />
-    <Compile Include="DataBinding\ChangeTracking\ObservablePropertyBranch.cs" />
-    <Compile Include="DataBinding\ChangeTracking\PropertyMountPoint.cs" />
-    <Compile Include="DataBinding\ChangeTracking\PropertyPath.cs" />
-    <Compile Include="DataBinding\ChangeTracking\TargettedProperty.cs" />
     <Compile Include="Context\PerspexParserFactory.cs" />
     <Compile Include="OmniXAML\Source\Glass\AutoKeyDictionary.cs" />
     <Compile Include="OmniXAML\Source\Glass\ChangeTracking\ObservableProperty.cs" />
@@ -228,7 +224,6 @@
     <Compile Include="Parsers\SelectorParser.cs" />
     <Compile Include="Parsers\SelectorGrammar.cs" />
     <Compile Include="Properties\AssemblyInfo.cs" />
-    <Compile Include="DataBinding\DataContextChangeSynchronizer.cs" />
     <Compile Include="DataBinding\IPerspexPropertyBinder.cs" />
     <Compile Include="DataBinding\PerspexPropertyBinder.cs" />
     <Compile Include="DataBinding\SourceBindingEndpoint.cs" />
@@ -290,6 +285,10 @@
       <Project>{F1BAA01A-F176-4C6A-B39D-5B40BB1B148F}</Project>
       <Name>Perspex.Styling</Name>
     </ProjectReference>
+    <ProjectReference Include="..\Perspex.Markup\Perspex.Markup.csproj">
+      <Project>{6417e941-21bc-467b-a771-0de389353ce6}</Project>
+      <Name>Perspex.Markup</Name>
+    </ProjectReference>
   </ItemGroup>
   <ItemGroup>
     <Reference Include="Serilog, Version=1.5.0.0, Culture=neutral, PublicKeyToken=24c2f752a8e58a10">

+ 1 - 3
src/Markup/Perspex.Markup/Binding/ExpressionObserver.cs

@@ -2,15 +2,13 @@
 // Licensed under the MIT license. See licence.md file in the project root for full license information.
 
 using System;
-using System.Collections.Generic;
-using System.Linq;
 using System.Reactive;
 using System.Reactive.Disposables;
 
 namespace Perspex.Markup.Binding
 {
     /// <summary>
-    /// Observes the value of an expression on a root object.
+    /// Observes and sets the value of an expression on an object.
     /// </summary>
     public class ExpressionObserver : ObservableBase<ExpressionValue>
     {

+ 48 - 0
src/Markup/Perspex.Markup/Binding/ExpressionSubject.cs

@@ -0,0 +1,48 @@
+// 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.Linq;
+using System.Reactive.Subjects;
+
+namespace Perspex.Markup.Binding
+{
+    /// <summary>
+    /// Turns an <see cref="ExpressionObserver"/> into a subject that can be bound two-ways.
+    /// </summary>
+    public class ExpressionSubject : ISubject<object>
+    {
+        private ExpressionObserver _inner;
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="ExpressionObserver"/> class.
+        /// </summary>
+        /// <param name="inner">The <see cref="ExpressionObserver"/>.</param>
+        public ExpressionSubject(ExpressionObserver inner)
+        {
+            _inner = inner;
+        }
+
+        /// <inheritdoc/>
+        public void OnCompleted()
+        {
+        }
+
+        /// <inheritdoc/>
+        public void OnError(Exception error)
+        {
+        }
+
+        /// <inheritdoc/>
+        public void OnNext(object value)
+        {
+            _inner.SetValue(value);
+        }
+
+        /// <inheritdoc/>
+        public IDisposable Subscribe(IObserver<object> observer)
+        {
+            return _inner.Select(x => x.Value).Distinct().Subscribe(observer);
+        }
+    }
+}

+ 1 - 0
src/Markup/Perspex.Markup/Perspex.Markup.csproj

@@ -36,6 +36,7 @@
   <ItemGroup>
     <Compile Include="Binding\ExpressionNodeBuilder.cs" />
     <Compile Include="Binding\ExpressionParseException.cs" />
+    <Compile Include="Binding\ExpressionSubject.cs" />
     <Compile Include="Binding\ExpressionValue.cs" />
     <Compile Include="Binding\LogicalNotNode.cs" />
     <Compile Include="Binding\IndexerNode.cs" />

+ 1 - 0
src/Perspex.Controls/Properties/AssemblyInfo.cs

@@ -9,5 +9,6 @@ using Perspex.Metadata;
 [assembly: InternalsVisibleTo("Perspex.Controls.UnitTests")]
 
 [assembly: XmlnsDefinition("https://github.com/perspex", "Perspex.Controls")]
+[assembly: XmlnsDefinition("https://github.com/perspex", "Perspex.Controls.Presenters")]
 [assembly: XmlnsDefinition("https://github.com/perspex", "Perspex.Controls.Primitives")]
 [assembly: XmlnsDefinition("https://github.com/perspex", "Perspex.Controls.Shapes")]

+ 1 - 2
tests/Perspex.Markup.Xaml.UnitTests/BindingDefinitionBuilder.cs

@@ -11,7 +11,6 @@ namespace Perspex.Xaml.Base.UnitTest
         private readonly BindingMode _bindingMode;
         private readonly string _sourcePropertyPath;
         private Control _target;
-        private PerspexProperty _targetProperty;
 
         public BindingDefinitionBuilder()
         {
@@ -31,7 +30,7 @@ namespace Perspex.Xaml.Base.UnitTest
                 bindingMode: _bindingMode,
                 sourcePropertyPath: _sourcePropertyPath,
                 target: _target,
-                targetProperty: _targetProperty);
+                targetProperty: null);
         }
     }
 }

+ 0 - 72
tests/Perspex.Markup.Xaml.UnitTests/ChangeBranchTest.cs

@@ -1,72 +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 Perspex.Markup.Xaml.DataBinding.ChangeTracking;
-using Perspex.Xaml.Base.UnitTest.SampleModel;
-using Xunit;
-
-namespace Perspex.Xaml.Base.UnitTest
-{
-    public class ChangeBranchTest
-    {
-        [Fact]
-        public void GetValueOfMemberOfStruct()
-        {
-            var level1 = new Level1();
-            level1.DateTime = new DateTime(1, 2, 3, 4, 5, 6);
-
-            var branch = new ObservablePropertyBranch(level1, new PropertyPath("DateTime.Minute"));
-
-            var day = branch.Value;
-            Assert.Equal(day, branch.Value);
-        }
-
-        [Fact]
-        public void OnePathOnly()
-        {
-            var level1 = new Level1();
-
-            var branch = new ObservablePropertyBranch(level1, new PropertyPath("Text"));
-            var newValue = "Hey now";
-            branch.Value = newValue;
-
-            Assert.Equal(level1.Text, newValue);
-        }
-
-        [Fact]
-        public void SettingValueToUnderlyingProperty_ChangesTheValueInBranch()
-        {
-            var level1 = new Level1();
-
-            level1.Level2.Level3.Property = 3;
-
-            var branch = new ObservablePropertyBranch(level1, new PropertyPath("Level2.Level3.Property"));
-            Assert.Equal(3, branch.Value);
-        }
-
-        [Fact]
-        public void SettingValueToBranch_ChangesTheUnderlyingProperty()
-        {
-            var level1 = new Level1();
-
-            var branch = new ObservablePropertyBranch(level1, new PropertyPath("Level2.Level3.Property"));
-            branch.Value = 3;
-            Assert.Equal(3, level1.Level2.Level3.Property);
-        }
-
-        [Fact]
-        public void SettingValueProperty_RaisesChangeInBranch()
-        {
-            var level1 = new Level1();
-
-            var branch = new ObservablePropertyBranch(level1, new PropertyPath("Level2.Level3.Property"));
-            bool received = false;
-            ObservableExtensions.Subscribe(branch.Values, v => received = ((int)v == 3));
-
-            level1.Level2.Level3.Property = 3;
-
-            Assert.True(received);
-        }
-    }
-}

+ 0 - 103
tests/Perspex.Markup.Xaml.UnitTests/DataContextChangeSynchronizerTest.cs

@@ -1,103 +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 Perspex.Controls;
-using GitHubClient.ViewModels;
-using Perspex.Markup.Xaml.DataBinding;
-using Perspex.Markup.Xaml.DataBinding.ChangeTracking;
-using OmniXaml.Builder;
-using OmniXaml.TypeConversion;
-using OmniXaml.TypeConversion.BuiltInConverters;
-using Perspex.Xaml.Base.UnitTest.SampleModel;
-using Xunit;
-
-namespace Perspex.Xaml.Base.UnitTest
-{
-    public class DataContextChangeSynchronizerTest
-    {
-        private readonly TypeConverterProvider _repo;
-        private readonly SamplePerspexObject _guiObject;
-        private readonly ViewModelMock _viewModel;
-
-        public DataContextChangeSynchronizerTest()
-        {
-            _repo = new TypeConverterProvider();
-            _guiObject = new SamplePerspexObject();
-            _viewModel = new ViewModelMock();
-        }
-
-        [Fact]
-        public void SameTypesFromUIToModel()
-        {
-            var synchronizer = new DataContextChangeSynchronizer(new DataContextChangeSynchronizer.BindingSource(new PropertyPath("IntProp"), _viewModel), new DataContextChangeSynchronizer.BindingTarget(_guiObject, SamplePerspexObject.IntProperty), _repo);
-            synchronizer.StartUpdatingSourceWhenTargetChanges();
-
-            const int someValue = 4;
-            _guiObject.Int = someValue;
-
-            Assert.Equal(someValue, _viewModel.IntProp);
-        }
-
-        [Fact]
-        public void DifferentTypesFromUIToModel()
-        {
-            var synchronizer = new DataContextChangeSynchronizer(new DataContextChangeSynchronizer.BindingSource(new PropertyPath("IntProp"), _viewModel), new DataContextChangeSynchronizer.BindingTarget(_guiObject, SamplePerspexObject.StringProperty), _repo);
-            synchronizer.StartUpdatingSourceWhenTargetChanges();
-
-            _guiObject.String = "2";
-
-            Assert.Equal(2, _viewModel.IntProp);
-        }
-
-        [Fact]
-        public void DifferentTypesAndNonConvertibleValueFromUIToModel()
-        {
-            var synchronizer = new DataContextChangeSynchronizer(new DataContextChangeSynchronizer.BindingSource(new PropertyPath("IntProp"), _viewModel), new DataContextChangeSynchronizer.BindingTarget(_guiObject, SamplePerspexObject.StringProperty), _repo);
-            synchronizer.StartUpdatingSourceWhenTargetChanges();
-
-            _guiObject.String = "";
-
-            Assert.Equal(default(int), _viewModel.IntProp);
-        }
-
-
-        [Fact]
-        public void DifferentTypesFromModelToUI()
-        {
-            var synchronizer = new DataContextChangeSynchronizer(new DataContextChangeSynchronizer.BindingSource(new PropertyPath("IntProp"), _viewModel), new DataContextChangeSynchronizer.BindingTarget(_guiObject, SamplePerspexObject.StringProperty), _repo);
-            synchronizer.StartUpdatingTargetWhenSourceChanges();
-
-            _viewModel.IntProp = 2;
-
-            Assert.Equal("2", _guiObject.String);
-        }
-
-        [Fact]
-        public void SameTypesFromModelToUI()
-        {
-            var synchronizer = new DataContextChangeSynchronizer(new DataContextChangeSynchronizer.BindingSource(new PropertyPath("IntProp"), _viewModel), new DataContextChangeSynchronizer.BindingTarget(_guiObject, SamplePerspexObject.IntProperty), _repo);
-            synchronizer.StartUpdatingTargetWhenSourceChanges();
-
-            _viewModel.IntProp = 2;
-
-            Assert.Equal(2, _guiObject.Int);
-        }
-
-        [Fact]
-        public void GrokysTest()
-        {
-            var mainWindowViewModel = new MainWindowViewModel();
-            var contentControl = new ContentControl();
-
-            var synchronizer = new DataContextChangeSynchronizer(new DataContextChangeSynchronizer.BindingSource(new PropertyPath("Content"), mainWindowViewModel), new DataContextChangeSynchronizer.BindingTarget(contentControl, ContentControl.ContentProperty), _repo);
-
-            synchronizer.StartUpdatingTargetWhenSourceChanges();
-
-            var logInViewModel = new LogInViewModel();
-            mainWindowViewModel.Content = logInViewModel;
-
-            Assert.Equal(logInViewModel, contentControl.Content);
-        }
-    }
-}

+ 0 - 3
tests/Perspex.Markup.Xaml.UnitTests/Perspex.Markup.Xaml.UnitTests.csproj

@@ -88,9 +88,7 @@
   </Choose>
   <ItemGroup>
     <Compile Include="BindingDefinitionBuilder.cs" />
-    <Compile Include="ChangeBranchTest.cs" />
     <Compile Include="Converters\PerspexPropertyConverterTest.cs" />
-    <Compile Include="DataContextChangeSynchronizerTest.cs" />
     <Compile Include="Parsers\SelectorGrammarTests.cs" />
     <Compile Include="Properties\AssemblyInfo.cs" />
     <Compile Include="SampleModel\Level1.cs" />
@@ -105,7 +103,6 @@
     <Compile Include="TypeProviderMock.cs" />
     <Compile Include="BinderTest.cs" />
     <Compile Include="XamlBindingTest.cs" />
-    <Compile Include="PropertyMountPointTest.cs" />
     <Compile Include="ViewModelMock.cs" />
   </ItemGroup>
   <ItemGroup>

+ 0 - 18
tests/Perspex.Markup.Xaml.UnitTests/PropertyMountPointTest.cs

@@ -1,18 +0,0 @@
-// Copyright (c) The Perspex Project. All rights reserved.
-// Licensed under the MIT license. See licence.md file in the project root for full license information.
-
-using Perspex.Markup.Xaml.DataBinding.ChangeTracking;
-using System;
-using Xunit;
-
-namespace Perspex.Xaml.Base.UnitTest
-{
-    public class PropertyMountPointTest
-    {
-        [Fact]
-        public void SourceAndPathAreNull()
-        {
-            Assert.Throws<ArgumentNullException>(() => new PropertyMountPoint(null, null));
-        }
-    }
-}

+ 3 - 3
tests/Perspex.Markup.Xaml.UnitTests/XamlBindingTest.cs

@@ -13,9 +13,9 @@ namespace Perspex.Xaml.Base.UnitTest
         [Fact]
         public void TestNullDataContext()
         {
-            var t = new Mock<ITypeConverterProvider>();
-            var sut = new XamlBinding(t.Object);
-            sut.BindToDataContext(null);
+            //var t = new Mock<ITypeConverterProvider>();
+            //var sut = new XamlBinding(t.Object);
+            //sut.BindTo(null);
         }
     }
 }