using System; using System.Globalization; using NUnit.Framework; namespace Xamarin.Forms.Core.UnitTests { [TypeConverter (typeof(ToBarConverter))] internal class Bar { } internal class Baz { } internal class MockBindable : VisualElement { public static readonly BindableProperty TextProperty = BindableProperty.Create ( b => b.Text, "default", BindingMode.TwoWay); public string Text { get { return (string)GetValue (TextProperty); } set { SetValue (TextProperty, value); } } public string Foo { get; set; } public int TargetInt { get; set; } public static readonly BindableProperty BarProperty = BindableProperty.Create (w => w.Bar, default(Bar)); public Bar Bar { get { return (Bar)GetValue (BarProperty); } set { SetValue (BarProperty, value); } } public static readonly BindableProperty BazProperty = BindableProperty.Create (w => w.Baz, default(Baz)); [TypeConverter (typeof (ToBazConverter))] public Baz Baz { get { return (Baz)GetValue (BazProperty); } set { SetValue (BazProperty, value); } } public static readonly BindableProperty QuxProperty = BindableProperty.Create (w => w.Qux, default(Baz)); public Baz Qux { get { return (Baz)GetValue (QuxProperty); } set { SetValue (QuxProperty, value); } } } internal class ToBarConverter : TypeConverter { public override object ConvertFrom (System.Globalization.CultureInfo culture, object value) { return new Bar (); } } internal class ToBazConverter : TypeConverter { public override object ConvertFrom (System.Globalization.CultureInfo culture, object value) { return new Baz (); } } [TestFixture] public class BindableObjectUnitTests : BaseTestFixture { [SetUp] public void Setup() { Device.PlatformServices = new MockPlatformServices (); } [TearDown] public void TearDown() { Device.PlatformServices = null; } [Test] public void BindingContext() { var mock = new MockBindable(); Assert.IsNull (mock.BindingContext); object obj = new object(); mock.BindingContext = obj; Assert.AreSame (obj, mock.BindingContext); } [Test] public void BindingContextChangedEvent() { var mock = new MockBindable(); mock.BindingContextChanged += (sender, args) => Assert.Pass(); mock.BindingContext = new object(); Assert.Fail ("The BindingContextChanged event was not fired."); } [Test] [Description ("When the BindingContext changes, any bindings should be immediately applied.")] public void BindingContextChangedBindingsApplied() { var mock = new MockBindable(); mock.SetBinding (MockBindable.TextProperty, new Binding (".")); mock.BindingContext = "Test"; Assert.AreEqual ("Test", mock.GetValue (MockBindable.TextProperty)); mock.BindingContext = "Testing"; Assert.AreEqual ("Testing", mock.GetValue (MockBindable.TextProperty), "Bindings were not reapplied to the new binding context"); } [Test] [Description ("When the BindingContext changes, the new context needs to listen for updates.")] public void BindingContextChangedBindingsListening() { var mock = new MockBindable(); mock.SetBinding (MockBindable.TextProperty, new Binding ("Text")); var vm = new MockViewModel(); mock.BindingContext = vm; mock.BindingContext = (vm = new MockViewModel()); vm.Text = "test"; Assert.AreEqual ("test", mock.GetValue (MockBindable.TextProperty), "The new ViewModel was not being listened to after being set"); } [Test] [Description ("When an INPC implementer is unset as the BindingContext, its changes shouldn't be listened to any further.")] public void BindingContextUnsetStopsListening() { var mock = new MockBindable(); mock.SetBinding (MockBindable.TextProperty, new Binding ("Text")); var vm = new MockViewModel(); mock.BindingContext = vm; mock.BindingContext = null; vm.Text = "test"; Assert.IsNull (mock.GetValue (Entry.TextProperty), "ViewModel was still being listened to after set to null"); } [Test] public void PropertyChanging() { var mock = new MockBindable(); mock.PropertyChanging += (sender, args) => { Assert.AreEqual (MockBindable.TextProperty.PropertyName, args.PropertyName); Assert.AreEqual (MockBindable.TextProperty.DefaultValue, mock.GetValue (MockBindable.TextProperty)); Assert.Pass(); }; mock.SetValue (MockBindable.TextProperty, "foo"); Assert.Fail ("The PropertyChanging event was not fired."); } [Test] public void PropertyChangingSameValue() { const string value = "foo"; var mock = new MockBindable(); mock.SetValue (MockBindable.TextProperty, value); mock.PropertyChanging += (s,e) => Assert.Fail(); mock.SetValue (MockBindable.TextProperty, value); Assert.Pass(); } [Test] public void PropertyChangingDefaultValue() { var prop = BindableProperty.Create (w => w.Foo, "DefaultValue"); var mock = new MockBindable(); mock.PropertyChanging += (s,e) => Assert.Fail(); mock.SetValue (prop, prop.DefaultValue); Assert.Pass(); } [Test] public void PropertyChanged() { const string value = "foo"; var mock = new MockBindable(); mock.PropertyChanged += (sender, args) => { Assert.AreEqual (MockBindable.TextProperty.PropertyName, args.PropertyName); Assert.AreEqual (value, mock.GetValue (MockBindable.TextProperty)); Assert.Pass(); }; mock.SetValue (MockBindable.TextProperty, value); Assert.Fail ("The PropertyChanged event was not fired."); } [Test] public void PropertyChangedSameValue() { const string value = "foo"; var mock = new MockBindable(); mock.SetValue (MockBindable.TextProperty, value); mock.PropertyChanged += (s,e) => Assert.Fail(); mock.SetValue (MockBindable.TextProperty, value); Assert.Pass(); } [Test] public void PropertyChangedDefaultValue() { var prop = BindableProperty.Create (w => w.Foo, "DefaultValue"); var mock = new MockBindable(); mock.PropertyChanged += (s,e) => Assert.Fail(); mock.SetValue (prop, prop.DefaultValue); Assert.Pass(); } [Test] public void GetSetValue() { const string value = "foo"; var mock = new MockBindable(); mock.SetValue (MockBindable.TextProperty, value); Assert.AreEqual (value, mock.GetValue (MockBindable.TextProperty)); } [Test] public void GetValueDefault() { var nulldefault = BindableProperty.Create (w => w.Foo, null); TestGetValueDefault (nulldefault); var foodefault = BindableProperty.Create (w => w.Foo, "Foo"); TestGetValueDefault (foodefault); } void TestGetValueDefault (BindableProperty property) { var mock = new MockBindable(); object value = mock.GetValue (property); Assert.AreEqual (property.DefaultValue, value); } [Test] public void SetValueInvalid() { var mock = new MockBindable(); Assert.Throws (() => mock.SetValue ((BindableProperty)null, "null")); } [Test] public void GetValueInvalid() { var mock = new MockBindable(); Assert.Throws (() => mock.GetValue (null)); } [Test] public void ClearValueInvalid() { var mock = new MockBindable(); Assert.Throws (() => mock.ClearValue ((BindableProperty)null)); } [Test] public void ClearValue() { const string value = "foo"; var mock = new MockBindable(); mock.SetValue (MockBindable.TextProperty, value); Assert.AreEqual (value, mock.GetValue (MockBindable.TextProperty)); mock.ClearValue (MockBindable.TextProperty); TestGetValueDefault (MockBindable.TextProperty); } [Test] public void ClearValueTriggersINPC () { var bindable = new MockBindable (); bool changingfired = false; bool changedfired = false; bool changingdelegatefired = false; bool changeddelegatefired = false; var property = BindableProperty.Create ("Foo", typeof(string), typeof(MockBindable), "foo", propertyChanged: (b, o, n) => changeddelegatefired = true, propertyChanging: (b, o, n) => changingdelegatefired = true ); bindable.PropertyChanged += (sender, e) => { changedfired |= e.PropertyName == "Foo"; }; bindable.PropertyChanging += (sender, e) => { changingfired |= e.PropertyName == "Foo"; }; bindable.SetValue (property, "foobar"); changingfired = changedfired = changeddelegatefired = changingdelegatefired = false; bindable.ClearValue (property); Assert.True (changingfired); Assert.True (changedfired); Assert.True (changingdelegatefired); Assert.True (changeddelegatefired); } [Test] public void ClearValueDoesNotTriggersINPCOnSameValues () { var bindable = new MockBindable (); bool changingfired = false; bool changedfired = false; bool changingdelegatefired = false; bool changeddelegatefired = false; var property = BindableProperty.Create ("Foo", typeof(string), typeof(MockBindable), "foo", propertyChanged: (b, o, n) => changeddelegatefired = true, propertyChanging: (b, o, n) => changingdelegatefired = true ); bindable.PropertyChanged += (sender, e) => { changedfired |= e.PropertyName == "Foo"; }; bindable.PropertyChanging += (sender, e) => { changingfired |= e.PropertyName == "Foo"; }; bindable.SetValue (property, "foobar"); bindable.SetValue (property, "foo"); changingfired = changedfired = changeddelegatefired = changingdelegatefired = false; bindable.ClearValue (property); Assert.False (changingfired); Assert.False (changedfired); Assert.False (changingdelegatefired); Assert.False (changeddelegatefired); } [Test] public void SetBindingInvalid() { var mock = new MockBindable(); Assert.Throws (() => mock.SetBinding (null, new Binding ("."))); Assert.Throws (() => mock.SetBinding (MockBindable.TextProperty, null)); } [Test] public void RemoveUnaddedBinding() { var mock = new MockBindable(); Assert.That (() => mock.RemoveBinding (MockBindable.TextProperty), Throws.Nothing); } [Test] public void RemoveBindingInvalid() { var mock = new MockBindable(); Assert.Throws (() => mock.RemoveBinding (null)); } [Test] public void RemovedBindingDoesNotUpdate() { const string newvalue = "New Value"; var viewmodel = new MockViewModel { Text = "Foo" }; var binding = new Binding ("Text"); var bindable = new MockBindable(); bindable.BindingContext = viewmodel; bindable.SetBinding (MockBindable.TextProperty, binding); string original = (string)bindable.GetValue (MockBindable.TextProperty); bindable.RemoveBinding (MockBindable.TextProperty); viewmodel.Text = newvalue; Assert.AreEqual (original, bindable.GetValue (MockBindable.TextProperty), "Property updated from a removed binding"); } [Test] public void CoerceValue() { var property = BindableProperty.Create (w => w.Foo, null, coerceValue: (bo, o) => o.ToUpper()); const string value = "value"; var mock = new MockBindable(); mock.SetValue (property, value); Assert.AreEqual (value.ToUpper(), mock.GetValue (property)); } [Test] public void ValidateValue() { var property = BindableProperty.Create (w => w.Foo, null, validateValue: (b, v) => false); var mock = new MockBindable(); Assert.Throws (() => mock.SetValue (property, null)); } [Test] public void BindablePropertyChanged() { bool changed = false; string oldv = "bar"; string newv = "foo"; var property = BindableProperty.Create (w => w.Foo, oldv, propertyChanged: (b, ov, nv) => { Assert.AreSame (oldv, ov); Assert.AreSame (newv, nv); changed = true; }); var mock = new MockBindable(); mock.SetValue (property, newv); Assert.IsTrue (changed, "PropertyChanged was not called"); } [Test] public void RecursiveChange () { bool changedA1 = false, changedA2 = false, changedB1 = false, changedB2 = false; var mock = new MockBindable (); mock.PropertyChanged += (sender, args) => { if (!changedA1) { Assert.AreEqual ("1", mock.GetValue (MockBindable.TextProperty)); Assert.IsFalse (changedA2); Assert.IsFalse (changedB1); Assert.IsFalse (changedB2); mock.SetValue (MockBindable.TextProperty, "2"); changedA1 = true; } else { Assert.AreEqual ("2", mock.GetValue (MockBindable.TextProperty)); Assert.IsFalse (changedA2); Assert.IsTrue (changedB1); Assert.IsFalse (changedB2); changedA2 = true; } }; mock.PropertyChanged += (sender, args) => { if (!changedB1) { Assert.AreEqual ("1", mock.GetValue (MockBindable.TextProperty)); Assert.IsTrue (changedA1); Assert.IsFalse (changedA2); Assert.IsFalse (changedB2); changedB1 = true; } else { Assert.AreEqual ("2", mock.GetValue (MockBindable.TextProperty)); Assert.IsTrue (changedA1); Assert.IsTrue (changedA2); Assert.IsFalse (changedB2); changedB2 = true; } }; mock.SetValue (MockBindable.TextProperty, "1"); Assert.AreEqual ("2", mock.GetValue (MockBindable.TextProperty)); Assert.IsTrue (changedA1); Assert.IsTrue (changedA2); Assert.IsTrue (changedB1); Assert.IsTrue (changedB2); } [Test] public void RaiseOnEqual() { string foo = "foo"; var mock = new MockBindable(); mock.SetValue (MockBindable.TextProperty, foo); bool changing = false; mock.PropertyChanging += (o, e) => { Assert.That (e.PropertyName, Is.EqualTo (MockBindable.TextProperty.PropertyName)); changing = true; }; bool changed = true; mock.PropertyChanged += (o, e) => { Assert.That (e.PropertyName, Is.EqualTo (MockBindable.TextProperty.PropertyName)); changed = true; }; mock.SetValueCore (MockBindable.TextProperty, foo, BindableObject.SetValueFlags.ClearOneWayBindings | BindableObject.SetValueFlags.ClearDynamicResource | BindableObject.SetValueFlags.RaiseOnEqual); Assert.That (changing, Is.True, "PropertyChanging event did not fire"); Assert.That (changed, Is.True, "PropertyChanged event did not fire"); } [Test] public void BindingContextGetter () { var label = new Label (); var view = new StackLayout { Children = {label} }; view.BindingContext = new {item0 = "Foo", item1 = "Bar"}; label.SetBinding (BindableObject.BindingContextProperty, "item0"); label.SetBinding (Label.TextProperty, Binding.SelfPath); Assert.AreSame (label.BindingContext, label.GetValue (BindableObject.BindingContextProperty)); } [Test] public void BoundBindingContextUpdate () { var label = new Label (); var view = new StackLayout { Children = {label} }; var vm = new MockViewModel { Text = "FooBar" }; view.BindingContext = vm; label.SetBinding (BindableObject.BindingContextProperty, "Text"); label.SetBinding (Label.TextProperty, Binding.SelfPath); Assert.AreEqual ("FooBar", label.BindingContext); vm.Text = "Baz"; Assert.AreEqual ("Baz", label.BindingContext); } [Test] public void BoundBindingContextChange () { var label = new Label (); var view = new StackLayout { Children = {label} }; view.BindingContext = new MockViewModel { Text = "FooBar" };; label.SetBinding (BindableObject.BindingContextProperty, "Text"); label.SetBinding (Label.TextProperty, Binding.SelfPath); Assert.AreEqual ("FooBar", label.BindingContext); view.BindingContext = new MockViewModel { Text = "Baz" };; Assert.AreEqual ("Baz", label.BindingContext); } [Test] public void TestReadOnly () { var bindablePropertyKey = BindableProperty.CreateReadOnly (w => w.Foo, "DefaultValue"); var bindableProperty = bindablePropertyKey.BindableProperty; var bindable = new MockBindable (); Assert.AreEqual ("DefaultValue", bindable.GetValue (bindableProperty)); bindable.SetValue (bindablePropertyKey, "Bar"); Assert.AreEqual ("Bar", bindable.GetValue (bindableProperty)); Assert.Throws (() => bindable.SetValue (bindableProperty, "Baz")); Assert.AreEqual ("Bar", bindable.GetValue (bindableProperty)); Assert.Throws (() => bindable.ClearValue (bindableProperty)); bindable.ClearValue (bindablePropertyKey); Assert.AreEqual ("DefaultValue", bindable.GetValue (bindableProperty)); } [Test] public void TestBindingTwoWayOnReadOnly () { var bindablePropertyKey = BindableProperty.CreateReadOnly (w => w.Foo, "DefaultValue", BindingMode.OneWayToSource); var bindableProperty = bindablePropertyKey.BindableProperty; var bindable = new MockBindable (); var vm = new MockViewModel (); bindable.SetBinding (bindableProperty, new Binding ("Text", BindingMode.TwoWay)); Assert.DoesNotThrow (() => bindable.BindingContext = vm); Assert.AreEqual ("DefaultValue", bindable.GetValue (bindableProperty)); } [Test] public void DefaultValueCreator () { int invoked = 0; object defaultValue = new object (); var bindableProperty = BindableProperty.Create ("Foo", typeof(object), typeof(MockBindable), defaultValue, defaultValueCreator: o => { invoked++; return new object (); }); var bindable = new MockBindable (); Assert.AreSame (defaultValue, bindableProperty.DefaultValue); var newvalue = bindable.GetValue (bindableProperty); Assert.AreNotSame (defaultValue, newvalue); Assert.NotNull (newvalue); Assert.AreEqual (1, invoked); } [Test] public void DefaultValueCreatorIsInvokedOnlyAtFirstTime () { int invoked = 0; var bindableProperty = BindableProperty.Create ("Foo", typeof(object), typeof(MockBindable), null, defaultValueCreator: o => { invoked++; return new object (); }); var bindable = new MockBindable (); var value0 = bindable.GetValue (bindableProperty); var value1 = bindable.GetValue (bindableProperty); Assert.NotNull (value0); Assert.NotNull (value1); Assert.AreSame (value0, value1); Assert.AreEqual (1, invoked); } [Test] public void DefaultValueCreatorNotSharedAccrossInstances () { int invoked = 0; var bindableProperty = BindableProperty.Create ("Foo", typeof(object), typeof(MockBindable), null, defaultValueCreator: o=> { invoked++; return new object (); }); var bindable0 = new MockBindable (); var bindable1 = new MockBindable (); var value0 = bindable0.GetValue (bindableProperty); var value1 = bindable1.GetValue (bindableProperty); Assert.AreNotSame (value0, value1); Assert.AreEqual (2, invoked); } [Test] public void DefaultValueCreatorInvokedAfterClearValue () { int invoked = 0; var bindableProperty = BindableProperty.Create ("Foo", typeof(object), typeof(MockBindable), null, defaultValueCreator: o => { invoked++; return new object (); }); var bindable = new MockBindable (); Assert.AreEqual (0, invoked); var value0 = bindable.GetValue (bindableProperty); Assert.NotNull (value0); Assert.AreEqual (1, invoked); bindable.ClearValue (bindableProperty); var value1 = bindable.GetValue (bindableProperty); Assert.NotNull (value1); Assert.AreEqual (2, invoked); Assert.AreNotSame (value0, value1); } [Test] public void DefaultValueCreatorOnlyInvokedOnGetValue () { int invoked = 0; var bindableProperty = BindableProperty.Create ("Foo", typeof(object), typeof(MockBindable), null, defaultValueCreator: o => { invoked++; return new object (); }); var bindable = new MockBindable (); Assert.AreEqual (0, invoked); var newvalue = bindable.GetValue (bindableProperty); Assert.NotNull (newvalue); Assert.AreEqual (1, invoked); } [Test] public void DefaultValueCreatorDoesNotTriggerINPC () { int invoked = 0; int propertychanged = 0; int changedfired = 0; var bindableProperty = BindableProperty.Create ("Foo", typeof(object), typeof(MockBindable), null, propertyChanged: (bindable,oldvalue,newvalue) =>{ propertychanged ++; }, defaultValueCreator: o => { invoked++; return new object (); }); var bp = new MockBindable (); bp.PropertyChanged += (sender, e) => { if (e.PropertyName == "Foo") changedfired++; }; var value0 = bp.GetValue (bindableProperty); Assert.NotNull (value0); Assert.AreEqual (1, invoked); Assert.AreEqual (0, propertychanged); Assert.AreEqual (0, changedfired); } [Test] public void StyleValueIsOverridenByValue () { var label = new Label (); label.SetValue (Label.TextProperty, "Foo", true); Assert.AreEqual ("Foo", label.Text); label.SetValue (Label.TextProperty, "Bar"); Assert.AreEqual ("Bar", label.Text); } [Test] public void StyleBindingIsOverridenByValue () { var label = new Label (); label.SetBinding (Label.TextProperty, new Binding ("foo"), true); label.BindingContext = new {foo = "Foo"}; Assert.AreEqual ("Foo", label.Text); label.SetValue (Label.TextProperty, "Bar"); Assert.AreEqual ("Bar", label.Text); } [Test] public void StyleDynResourceIsOverridenByValue () { var label = new Label (); label.SetDynamicResource (Label.TextProperty, "foo", true); label.Resources = new ResourceDictionary { {"foo", "Foo"} }; Assert.AreEqual ("Foo", label.Text); label.SetValue (Label.TextProperty, "Bar"); Assert.AreEqual ("Bar", label.Text); } [Test] public void StyleValueIsOverridenByBinding () { var label = new Label (); label.BindingContext = new {foo = "Foo", bar = "Bar"}; label.SetValue (Label.TextProperty, "Foo", true); Assert.AreEqual ("Foo", label.Text); label.SetBinding (Label.TextProperty, "bar"); Assert.AreEqual ("Bar", label.Text); } [Test] public void StyleBindingIsOverridenByBinding () { var label = new Label (); label.BindingContext = new {foo = "Foo", bar = "Bar"}; label.SetBinding (Label.TextProperty, new Binding ("foo"), true); Assert.AreEqual ("Foo", label.Text); label.SetBinding (Label.TextProperty, "bar"); Assert.AreEqual ("Bar", label.Text); } [Test] public void StyleDynResourceIsOverridenByBinding () { var label = new Label (); label.BindingContext = new {foo = "Foo", bar = "Bar"}; label.SetDynamicResource (Label.TextProperty, "foo", true); label.Resources = new ResourceDictionary { {"foo", "Foo"}, }; Assert.AreEqual ("Foo", label.Text); label.SetBinding (Label.TextProperty, "bar"); Assert.AreEqual ("Bar", label.Text); } [Test] public void StyleValueIsOverridenByDynResource () { var label = new Label (); label.Resources = new ResourceDictionary { {"foo", "Foo"}, {"bar", "Bar"} }; label.BindingContext = new {foo = "Foo", bar = "Bar"}; label.SetValue (Label.TextProperty, "Foo", true); Assert.AreEqual ("Foo", label.Text); label.SetDynamicResource (Label.TextProperty, "bar"); Assert.AreEqual ("Bar", label.Text); } [Test] public void StyleBindingIsOverridenByDynResource () { var label = new Label (); label.Resources = new ResourceDictionary { {"foo", "Foo"}, {"bar", "Bar"} }; label.BindingContext = new {foo = "Foo", bar = "Bar"}; label.SetBinding (Label.TextProperty, new Binding ("foo"), true); Assert.AreEqual ("Foo", label.Text); label.SetDynamicResource (Label.TextProperty, "bar"); Assert.AreEqual ("Bar", label.Text); } [Test] public void StyleDynResourceIsOverridenByDynResource () { var label = new Label (); label.Resources = new ResourceDictionary { {"foo", "Foo"}, {"bar", "Bar"} }; label.BindingContext = new {foo = "Foo", bar = "Bar"}; label.SetDynamicResource (Label.TextProperty, "foo", true); Assert.AreEqual ("Foo", label.Text); label.SetDynamicResource (Label.TextProperty, "bar"); Assert.AreEqual ("Bar", label.Text); } [Test] public void ValueIsPreservedOnStyleValue () { var label = new Label (); label.SetValue (Label.TextProperty, "Foo"); Assert.AreEqual ("Foo", label.Text); label.SetValue (Label.TextProperty, "Bar", true); Assert.AreEqual ("Foo", label.Text); } [Test] public void BindingIsPreservedOnStyleValue () { var label = new Label (); label.SetBinding (Label.TextProperty, new Binding ("foo")); label.BindingContext = new {foo = "Foo"}; Assert.AreEqual ("Foo", label.Text); label.SetValue (Label.TextProperty, "Bar", true); Assert.AreEqual ("Foo", label.Text); } [Test] public void DynResourceIsPreservedOnStyleValue () { var label = new Label (); label.SetDynamicResource (Label.TextProperty, "foo"); label.Resources = new ResourceDictionary { {"foo", "Foo"} }; Assert.AreEqual ("Foo", label.Text); label.SetValue (Label.TextProperty, "Bar", true); Assert.AreEqual ("Foo", label.Text); } [Test] public void ValueIsPreservedOnStyleBinding () { var label = new Label (); label.BindingContext = new {foo = "Foo", bar = "Bar"}; label.SetValue (Label.TextProperty, "Foo"); Assert.AreEqual ("Foo", label.Text); label.SetBinding (Label.TextProperty, new Binding ("bar"), true); Assert.AreEqual ("Foo", label.Text); } [Test] public void BindingIsPreservedOnStyleBinding () { var label = new Label (); label.BindingContext = new {foo = "Foo", bar = "Bar"}; label.SetBinding (Label.TextProperty, new Binding ("foo")); Assert.AreEqual ("Foo", label.Text); label.SetBinding (Label.TextProperty, new Binding ("bar"), true); Assert.AreEqual ("Foo", label.Text); } [Test] public void DynResourceIsPreservedOnStyleBinding () { var label = new Label (); label.BindingContext = new {foo = "Foo", bar = "Bar"}; label.SetDynamicResource (Label.TextProperty, "foo"); label.Resources = new ResourceDictionary { {"foo", "Foo"}, }; Assert.AreEqual ("Foo", label.Text); label.SetBinding (Label.TextProperty, new Binding ("bar"), true); Assert.AreEqual ("Foo", label.Text); } [Test] public void ValueIsPreservedOnStyleDynResource () { var label = new Label (); label.Resources = new ResourceDictionary { {"foo", "Foo"}, {"bar", "Bar"} }; label.BindingContext = new {foo = "Foo", bar = "Bar"}; label.SetValue (Label.TextProperty, "Foo"); Assert.AreEqual ("Foo", label.Text); label.SetDynamicResource (Label.TextProperty, "bar", true); Assert.AreEqual ("Foo", label.Text); } [Test] public void BindingIsPreservedOnStyleDynResource () { var label = new Label (); label.Resources = new ResourceDictionary { {"foo", "Foo"}, {"bar", "Bar"} }; label.BindingContext = new {foo = "Foo", bar = "Bar"}; label.SetBinding (Label.TextProperty, new Binding ("foo")); Assert.AreEqual ("Foo", label.Text); label.SetDynamicResource (Label.TextProperty, "bar", true); Assert.AreEqual ("Foo", label.Text); } [Test] public void DynResourceIsPreservedOnStyleDynResource () { var label = new Label (); label.Resources = new ResourceDictionary { {"foo", "Foo"}, {"bar", "Bar"} }; label.BindingContext = new {foo = "Foo", bar = "Bar"}; label.SetDynamicResource (Label.TextProperty, "foo"); Assert.AreEqual ("Foo", label.Text); label.SetDynamicResource (Label.TextProperty, "bar", true); Assert.AreEqual ("Foo", label.Text); } [Test] public void StyleValueIsOverridenByStyleValue () { var label = new Label (); label.SetValue (Label.TextProperty, "Foo", true); Assert.AreEqual ("Foo", label.Text); label.SetValue (Label.TextProperty, "Bar", true); Assert.AreEqual ("Bar", label.Text); } [Test] public void StyleBindingIsOverridenByStyleValue () { var label = new Label (); label.SetBinding (Label.TextProperty, new Binding ("foo"), true); label.BindingContext = new {foo = "Foo"}; Assert.AreEqual ("Foo", label.Text); label.SetValue (Label.TextProperty, "Bar", true); Assert.AreEqual ("Bar", label.Text); } [Test] public void StyleDynResourceIsOverridenByStyleValue () { var label = new Label (); label.SetDynamicResource (Label.TextProperty, "foo", true); label.Resources = new ResourceDictionary { {"foo", "Foo"} }; Assert.AreEqual ("Foo", label.Text); label.SetValue (Label.TextProperty, "Bar", true); Assert.AreEqual ("Bar", label.Text); } [Test] public void StyleValueIsOverridenByStyleBinding () { var label = new Label (); label.BindingContext = new {foo = "Foo", bar = "Bar"}; label.SetValue (Label.TextProperty, "Foo", true); Assert.AreEqual ("Foo", label.Text); label.SetBinding (Label.TextProperty, new Binding("bar"), true); Assert.AreEqual ("Bar", label.Text); } [Test] public void StyleBindingIsOverridenByStyleBinding () { var label = new Label (); label.BindingContext = new {foo = "Foo", bar = "Bar"}; label.SetBinding (Label.TextProperty, new Binding ("foo"), true); Assert.AreEqual ("Foo", label.Text); label.SetBinding (Label.TextProperty, new Binding("bar"), true); Assert.AreEqual ("Bar", label.Text); } [Test] public void StyleDynResourceIsOverridenByStyleBinding () { var label = new Label (); label.BindingContext = new {foo = "Foo", bar = "Bar"}; label.SetDynamicResource (Label.TextProperty, "foo", true); label.Resources = new ResourceDictionary { {"foo", "Foo"}, }; Assert.AreEqual ("Foo", label.Text); label.SetBinding (Label.TextProperty, new Binding("bar"), true); Assert.AreEqual ("Bar", label.Text); } [Test] public void StyleValueIsOverridenByStyleDynResource () { var label = new Label (); label.Resources = new ResourceDictionary { {"foo", "Foo"}, {"bar", "Bar"} }; label.BindingContext = new {foo = "Foo", bar = "Bar"}; label.SetValue (Label.TextProperty, "Foo", true); Assert.AreEqual ("Foo", label.Text); label.SetDynamicResource (Label.TextProperty, "bar", true); Assert.AreEqual ("Bar", label.Text); } [Test] public void StyleBindingIsOverridenByStyleDynResource () { var label = new Label (); label.Resources = new ResourceDictionary { {"foo", "Foo"}, {"bar", "Bar"} }; label.BindingContext = new {foo = "Foo", bar = "Bar"}; label.SetBinding (Label.TextProperty, new Binding ("foo"), true); Assert.AreEqual ("Foo", label.Text); label.SetDynamicResource (Label.TextProperty, "bar", true); Assert.AreEqual ("Bar", label.Text); } [Test] public void StyleDynResourceIsOverridenByStyleDynResource () { var label = new Label (); label.Resources = new ResourceDictionary { {"foo", "Foo"}, {"bar", "Bar"} }; label.BindingContext = new {foo = "Foo", bar = "Bar"}; label.SetDynamicResource (Label.TextProperty, "foo", true); Assert.AreEqual ("Foo", label.Text); label.SetDynamicResource (Label.TextProperty, "bar", true); Assert.AreEqual ("Bar", label.Text); } [Test] public void SetValueCoreImplicitelyCastBasicType () { var prop = BindableProperty.Create ("Foo", typeof(int), typeof(MockBindable), 0); var bindable = new MockBindable (); Assert.DoesNotThrow (() => bindable.SetValue (prop, (object)(short)42)); Assert.AreEqual (42, bindable.GetValue (prop)); bindable.SetValue (prop, (object)(long)-42); Assert.AreNotEqual (-42, bindable.GetValue (prop)); } class CastFromString { public string Result { get; private set; } public static implicit operator CastFromString (string source) { var o = new CastFromString (); o.Result = source; return o; } } [Test] public void SetValueCoreInvokesOpImplicitOnPropertyType () { var prop = BindableProperty.Create ("Foo", typeof(CastFromString), typeof(MockBindable), null); var bindable = new MockBindable (); Assert.Null (bindable.GetValue (prop)); bindable.SetValue (prop, "foo"); Assert.AreEqual ("foo", ((CastFromString)bindable.GetValue (prop)).Result); } class CastToString { string Result { get; set; } public CastToString (string result) { Result = result; } public static implicit operator string (CastToString source) { return source.Result; } public override string ToString () { throw new InvalidOperationException (); } } [Test] public void SetValueCoreInvokesOpImplicitOnValue () { var prop = BindableProperty.Create ("Foo", typeof(string), typeof(MockBindable), null); var bindable = new MockBindable (); Assert.Null (bindable.GetValue (prop)); bindable.SetValue (prop, new CastToString ("foo")); Assert.AreEqual ("foo", bindable.GetValue (prop)); } [Test] public void DefaultValueCreatorCalledForChangeDelegates () { int changedOld = -1; int changedNew = -1; int changingOld = -1; int changingNew = -1; var prop = BindableProperty.Create ("Foo", typeof (int), typeof (MockBindable), 0, defaultValueCreator: b => 10, propertyChanged: (b, value, newValue) => { changedOld = (int) value; changedNew = (int) newValue; }, propertyChanging: (b, value, newValue) => { changingOld = (int) value; changingNew = (int) newValue; }); var bindable = new MockBindable (); var defaultValue = (int) bindable.GetValue (prop); Assert.AreEqual (10, defaultValue); bindable.SetValue (prop, 5); bindable.ClearValue (prop); Assert.AreEqual (5, changedOld); Assert.AreEqual (5, changingOld); Assert.AreEqual (10, changedNew); Assert.AreEqual (10, changingNew); } [Test] public void GetValuesDefaults() { var prop = BindableProperty.Create ("Foo", typeof(int), typeof(MockBindable), 0); var prop1 = BindableProperty.Create ("Foo1", typeof(int), typeof(MockBindable), 1); var prop2 = BindableProperty.Create ("Foo2", typeof(int), typeof(MockBindable), 2); var bindable = new MockBindable (); object[] values = bindable.GetValues (prop, prop1, prop2); Assert.That (values.Length == 3); Assert.That (values[0], Is.EqualTo (0)); Assert.That (values[1], Is.EqualTo (1)); Assert.That (values[2], Is.EqualTo (2)); } [Test] public void GetValues() { var prop = BindableProperty.Create ("Foo", typeof(int), typeof(MockBindable), 0); var prop1 = BindableProperty.Create ("Foo1", typeof(int), typeof(MockBindable), 1); var prop2 = BindableProperty.Create ("Foo2", typeof(int), typeof(MockBindable), 2); var bindable = new MockBindable (); bindable.SetValue (prop, 3); bindable.SetValue (prop2, 5); object[] values = bindable.GetValues (prop, prop1, prop2); Assert.That (values.Length == 3); Assert.That (values[0], Is.EqualTo (3)); Assert.That (values[1], Is.EqualTo (1)); Assert.That (values[2], Is.EqualTo (5)); } class BindingContextConverter : IValueConverter { public object Convert (object value, Type targetType, object parameter, CultureInfo culture) { return new MockViewModel { Text = value + "Converted" }; } public object ConvertBack (object value, Type targetType, object parameter, CultureInfo culture) { throw new NotImplementedException(); } } [Test] //https://bugzilla.xamarin.com/show_bug.cgi?id=24485 public void BindingContextBoundThroughConverter() { var bindable = new MockBindable(); bindable.BindingContext = "test"; bindable.SetBinding (BindableObject.BindingContextProperty, new Binding (".", converter: new BindingContextConverter())); bindable.SetBinding (MockBindable.TextProperty, "Text"); Assert.That (() => bindable.Text, Is.EqualTo ("testConverted")); } public class VMLocator { public event EventHandler Invoked; public int Count; public object VM { get { Count++; var eh = Invoked; if (eh != null) eh (this, EventArgs.Empty); return new object (); } } } [Test] //https://bugzilla.xamarin.com/show_bug.cgi?id=27299 public void BindingOnBindingContextDoesntReapplyBindingContextBinding () { var bindable = new MockBindable (); var locator = new VMLocator (); Assert.AreEqual (0, locator.Count); locator.Invoked += (sender, e) => Assert.IsTrue (locator.Count <= 1); bindable.SetBinding (BindableObject.BindingContextProperty, new Binding ("VM", source: locator)); Assert.IsTrue (locator.Count == 1); } [Test] public void BindingsEditableAfterUnapplied() { var bindable = new MockBindable(); var binding = new Binding ("."); bindable.SetBinding (MockBindable.TextProperty, binding); bindable.BindingContext = "foo"; Assume.That (bindable.Text, Is.EqualTo (bindable.BindingContext)); bindable.RemoveBinding (MockBindable.TextProperty); Assert.That (() => binding.Path = "foo", Throws.Nothing); } [Test] // https://bugzilla.xamarin.com/show_bug.cgi?id=24054 public void BindingsAppliedUnappliedWithNullContext() { var bindable = new MockBindable(); var binding = new Binding ("."); bindable.SetBinding (MockBindable.TextProperty, binding); bindable.BindingContext = "foo"; Assume.That (bindable.Text, Is.EqualTo (bindable.BindingContext)); bindable.BindingContext = null; Assume.That (bindable.Text, Is.EqualTo (bindable.BindingContext)); bindable.BindingContext = "bar"; Assume.That (bindable.Text, Is.EqualTo (bindable.BindingContext)); bindable.RemoveBinding (MockBindable.TextProperty); Assert.That (() => binding.Path = "Foo", Throws.Nothing); } } }