Browse Source
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.cspull/237/head
25 changed files with 110 additions and 672 deletions
@ -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; } |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -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; |
|
||||
} |
|
||||
} |
|
||||
@ -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); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -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; |
|
||||
} |
|
||||
} |
|
||||
@ -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; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -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,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); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -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); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -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)); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
Loading…
Reference in new issue