PerspexObjectTests_Binding.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305
  1. // Copyright (c) The Perspex Project. All rights reserved.
  2. // Licensed under the MIT license. See licence.md file in the project root for full license information.
  3. using System;
  4. using System.Reactive.Linq;
  5. using System.Reactive.Subjects;
  6. using Xunit;
  7. namespace Perspex.Base.UnitTests
  8. {
  9. public class PerspexObjectTests_Binding
  10. {
  11. [Fact]
  12. public void Bind_Sets_Current_Value()
  13. {
  14. Class1 target = new Class1();
  15. Class1 source = new Class1();
  16. source.SetValue(Class1.FooProperty, "initial");
  17. target.Bind(Class1.FooProperty, source.GetObservable(Class1.FooProperty));
  18. Assert.Equal("initial", target.GetValue(Class1.FooProperty));
  19. }
  20. [Fact]
  21. public void Bind_NonGeneric_Sets_Current_Value()
  22. {
  23. Class1 target = new Class1();
  24. Class1 source = new Class1();
  25. source.SetValue(Class1.FooProperty, "initial");
  26. target.Bind((PerspexProperty)Class1.FooProperty, source.GetObservable(Class1.FooProperty));
  27. Assert.Equal("initial", target.GetValue(Class1.FooProperty));
  28. }
  29. [Fact]
  30. public void Bind_To_ValueType_Accepts_UnsetValue()
  31. {
  32. var target = new Class1();
  33. var source = new Subject<object>();
  34. target.Bind(Class1.QuxProperty, source);
  35. source.OnNext(6.7);
  36. source.OnNext(PerspexProperty.UnsetValue);
  37. Assert.Equal(5.6, target.GetValue(Class1.QuxProperty));
  38. Assert.False(target.IsSet(Class1.QuxProperty));
  39. }
  40. [Fact]
  41. public void Bind_Throws_Exception_For_Unregistered_Property()
  42. {
  43. Class1 target = new Class1();
  44. Assert.Throws<ArgumentException>(() =>
  45. {
  46. target.Bind(Class2.BarProperty, Observable.Return("foo"));
  47. });
  48. }
  49. [Fact]
  50. public void Bind_Sets_Subsequent_Value()
  51. {
  52. Class1 target = new Class1();
  53. Class1 source = new Class1();
  54. source.SetValue(Class1.FooProperty, "initial");
  55. target.Bind(Class1.FooProperty, source.GetObservable(Class1.FooProperty));
  56. source.SetValue(Class1.FooProperty, "subsequent");
  57. Assert.Equal("subsequent", target.GetValue(Class1.FooProperty));
  58. }
  59. [Fact]
  60. public void Bind_Ignores_Invalid_Value_Type()
  61. {
  62. Class1 target = new Class1();
  63. target.Bind((PerspexProperty)Class1.FooProperty, Observable.Return((object)123));
  64. Assert.Equal("foodefault", target.GetValue(Class1.FooProperty));
  65. }
  66. [Fact]
  67. public void Two_Way_Binding_Works()
  68. {
  69. Class1 obj1 = new Class1();
  70. Class1 obj2 = new Class1();
  71. obj1.SetValue(Class1.FooProperty, "initial1");
  72. obj2.SetValue(Class1.FooProperty, "initial2");
  73. obj1.Bind(Class1.FooProperty, obj2.GetObservable(Class1.FooProperty));
  74. obj2.Bind(Class1.FooProperty, obj1.GetObservable(Class1.FooProperty));
  75. Assert.Equal("initial2", obj1.GetValue(Class1.FooProperty));
  76. Assert.Equal("initial2", obj2.GetValue(Class1.FooProperty));
  77. obj1.SetValue(Class1.FooProperty, "first");
  78. Assert.Equal("first", obj1.GetValue(Class1.FooProperty));
  79. Assert.Equal("first", obj2.GetValue(Class1.FooProperty));
  80. obj2.SetValue(Class1.FooProperty, "second");
  81. Assert.Equal("second", obj1.GetValue(Class1.FooProperty));
  82. Assert.Equal("second", obj2.GetValue(Class1.FooProperty));
  83. obj1.SetValue(Class1.FooProperty, "third");
  84. Assert.Equal("third", obj1.GetValue(Class1.FooProperty));
  85. Assert.Equal("third", obj2.GetValue(Class1.FooProperty));
  86. }
  87. [Fact]
  88. public void Two_Way_Binding_With_Priority_Works()
  89. {
  90. Class1 obj1 = new Class1();
  91. Class1 obj2 = new Class1();
  92. obj1.SetValue(Class1.FooProperty, "initial1", BindingPriority.Style);
  93. obj2.SetValue(Class1.FooProperty, "initial2", BindingPriority.Style);
  94. obj1.Bind(Class1.FooProperty, obj2.GetObservable(Class1.FooProperty), BindingPriority.Style);
  95. obj2.Bind(Class1.FooProperty, obj1.GetObservable(Class1.FooProperty), BindingPriority.Style);
  96. Assert.Equal("initial2", obj1.GetValue(Class1.FooProperty));
  97. Assert.Equal("initial2", obj2.GetValue(Class1.FooProperty));
  98. obj1.SetValue(Class1.FooProperty, "first", BindingPriority.Style);
  99. Assert.Equal("first", obj1.GetValue(Class1.FooProperty));
  100. Assert.Equal("first", obj2.GetValue(Class1.FooProperty));
  101. obj2.SetValue(Class1.FooProperty, "second", BindingPriority.Style);
  102. Assert.Equal("second", obj1.GetValue(Class1.FooProperty));
  103. Assert.Equal("second", obj2.GetValue(Class1.FooProperty));
  104. obj1.SetValue(Class1.FooProperty, "third", BindingPriority.Style);
  105. Assert.Equal("third", obj1.GetValue(Class1.FooProperty));
  106. Assert.Equal("third", obj2.GetValue(Class1.FooProperty));
  107. }
  108. [Fact]
  109. public void BindTwoWay_Gets_Initial_Value_From_Source()
  110. {
  111. Class1 source = new Class1();
  112. Class1 target = new Class1();
  113. source.SetValue(Class1.FooProperty, "initial");
  114. target.BindTwoWay(Class1.FooProperty, source, Class1.FooProperty);
  115. Assert.Equal("initial", target.GetValue(Class1.FooProperty));
  116. }
  117. [Fact]
  118. public void BindTwoWay_Updates_Values()
  119. {
  120. Class1 source = new Class1();
  121. Class1 target = new Class1();
  122. source.SetValue(Class1.FooProperty, "first");
  123. target.BindTwoWay(Class1.FooProperty, source, Class1.FooProperty);
  124. Assert.Equal("first", target.GetValue(Class1.FooProperty));
  125. source.SetValue(Class1.FooProperty, "second");
  126. Assert.Equal("second", target.GetValue(Class1.FooProperty));
  127. target.SetValue(Class1.FooProperty, "third");
  128. Assert.Equal("third", source.GetValue(Class1.FooProperty));
  129. }
  130. [Fact]
  131. public void Local_Binding_Overwrites_Local_Value()
  132. {
  133. var target = new Class1();
  134. var binding = new Subject<string>();
  135. target.Bind(Class1.FooProperty, binding);
  136. binding.OnNext("first");
  137. Assert.Equal("first", target.GetValue(Class1.FooProperty));
  138. target.SetValue(Class1.FooProperty, "second");
  139. Assert.Equal("second", target.GetValue(Class1.FooProperty));
  140. binding.OnNext("third");
  141. Assert.Equal("third", target.GetValue(Class1.FooProperty));
  142. }
  143. [Fact]
  144. public void StyleBinding_Overrides_Default_Value()
  145. {
  146. Class1 target = new Class1();
  147. target.Bind(Class1.FooProperty, Single("stylevalue"), BindingPriority.Style);
  148. Assert.Equal("stylevalue", target.GetValue(Class1.FooProperty));
  149. }
  150. [Fact]
  151. public void this_Operator_Returns_Value_Property()
  152. {
  153. Class1 target = new Class1();
  154. target.SetValue(Class1.FooProperty, "newvalue");
  155. Assert.Equal("newvalue", target[Class1.FooProperty]);
  156. }
  157. [Fact]
  158. public void this_Operator_Sets_Value_Property()
  159. {
  160. Class1 target = new Class1();
  161. target[Class1.FooProperty] = "newvalue";
  162. Assert.Equal("newvalue", target.GetValue(Class1.FooProperty));
  163. }
  164. [Fact]
  165. public void this_Operator_Doesnt_Accept_Observable()
  166. {
  167. Class1 target = new Class1();
  168. Assert.Throws<ArgumentException>(() =>
  169. {
  170. target[Class1.FooProperty] = Observable.Return("newvalue");
  171. });
  172. }
  173. [Fact]
  174. public void this_Operator_Binds_One_Way()
  175. {
  176. Class1 target1 = new Class1();
  177. Class1 target2 = new Class1();
  178. BindingDescriptor binding = Class1.FooProperty.Bind().WithMode(BindingMode.OneWay);
  179. target1.SetValue(Class1.FooProperty, "first");
  180. target2[binding] = target1[!Class1.FooProperty];
  181. target1.SetValue(Class1.FooProperty, "second");
  182. Assert.Equal("second", target2.GetValue(Class1.FooProperty));
  183. }
  184. [Fact]
  185. public void this_Operator_Binds_Two_Way()
  186. {
  187. Class1 target1 = new Class1();
  188. Class1 target2 = new Class1();
  189. BindingDescriptor binding = Class1.FooProperty.Bind().WithMode(BindingMode.TwoWay);
  190. target1.SetValue(Class1.FooProperty, "first");
  191. target2[binding] = target1[!Class1.FooProperty];
  192. Assert.Equal("first", target2.GetValue(Class1.FooProperty));
  193. target1.SetValue(Class1.FooProperty, "second");
  194. Assert.Equal("second", target2.GetValue(Class1.FooProperty));
  195. target2.SetValue(Class1.FooProperty, "third");
  196. Assert.Equal("third", target1.GetValue(Class1.FooProperty));
  197. }
  198. [Fact]
  199. public void this_Operator_Binds_One_Time()
  200. {
  201. Class1 target1 = new Class1();
  202. Class1 target2 = new Class1();
  203. BindingDescriptor binding = Class1.FooProperty.Bind().WithMode(BindingMode.OneTime);
  204. target1.SetValue(Class1.FooProperty, "first");
  205. target2[binding] = target1[!Class1.FooProperty];
  206. target1.SetValue(Class1.FooProperty, "second");
  207. Assert.Equal("first", target2.GetValue(Class1.FooProperty));
  208. }
  209. /// <summary>
  210. /// Returns an observable that returns a single value but does not complete.
  211. /// </summary>
  212. /// <typeparam name="T">The type of the observable.</typeparam>
  213. /// <param name="value">The value.</param>
  214. /// <returns>The observable.</returns>
  215. private IObservable<T> Single<T>(T value)
  216. {
  217. return Observable.Never<T>().StartWith(value);
  218. }
  219. private class Class1 : PerspexObject
  220. {
  221. public static readonly PerspexProperty<string> FooProperty =
  222. PerspexProperty.Register<Class1, string>("Foo", "foodefault");
  223. public static readonly PerspexProperty<string> BazProperty =
  224. PerspexProperty.Register<Class1, string>("Baz", "bazdefault", true);
  225. public static readonly PerspexProperty<double> QuxProperty =
  226. PerspexProperty.Register<Class1, double>("Qux", 5.6);
  227. }
  228. private class Class2 : Class1
  229. {
  230. public static readonly PerspexProperty<string> BarProperty =
  231. PerspexProperty.Register<Class2, string>("Bar", "bardefault");
  232. }
  233. }
  234. }