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