PerspexObjectTests_Binding.cs 9.7 KB

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