AvaloniaObjectTests_Binding.cs 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977
  1. using System;
  2. using System.ComponentModel;
  3. using System.Reactive.Linq;
  4. using System.Reactive.Subjects;
  5. using System.Threading;
  6. using System.Threading.Tasks;
  7. using Avalonia.Controls;
  8. using Avalonia.Data;
  9. using Avalonia.Logging;
  10. using Avalonia.Platform;
  11. using Avalonia.Threading;
  12. using Avalonia.UnitTests;
  13. using Microsoft.Reactive.Testing;
  14. using Moq;
  15. using Xunit;
  16. namespace Avalonia.Base.UnitTests
  17. {
  18. public class AvaloniaObjectTests_Binding
  19. {
  20. [Fact]
  21. public void Bind_Sets_Current_Value()
  22. {
  23. var target = new Class1();
  24. var source = new Class1();
  25. var property = Class1.FooProperty;
  26. source.SetValue(property, "initial");
  27. target.Bind(property, source.GetObservable(property));
  28. Assert.Equal("initial", target.GetValue(property));
  29. }
  30. [Fact]
  31. public void Bind_Raises_PropertyChanged()
  32. {
  33. var target = new Class1();
  34. var source = new Subject<BindingValue<string>>();
  35. bool raised = false;
  36. target.PropertyChanged += (s, e) =>
  37. raised = e.Property == Class1.FooProperty &&
  38. (string)e.OldValue == "foodefault" &&
  39. (string)e.NewValue == "newvalue" &&
  40. e.Priority == BindingPriority.LocalValue;
  41. target.Bind(Class1.FooProperty, source);
  42. source.OnNext("newvalue");
  43. Assert.True(raised);
  44. }
  45. [Fact]
  46. public void PropertyChanged_Not_Raised_When_Value_Unchanged()
  47. {
  48. var target = new Class1();
  49. var source = new Subject<BindingValue<string>>();
  50. var raised = 0;
  51. target.PropertyChanged += (s, e) => ++raised;
  52. target.Bind(Class1.FooProperty, source);
  53. source.OnNext("newvalue");
  54. source.OnNext("newvalue");
  55. Assert.Equal(1, raised);
  56. }
  57. [Fact]
  58. public void Setting_LocalValue_Overrides_Binding_Until_Binding_Produces_Next_Value()
  59. {
  60. var target = new Class1();
  61. var source = new Subject<string>();
  62. var property = Class1.FooProperty;
  63. target.Bind(property, source);
  64. source.OnNext("foo");
  65. Assert.Equal("foo", target.GetValue(property));
  66. target.SetValue(property, "bar");
  67. Assert.Equal("bar", target.GetValue(property));
  68. source.OnNext("baz");
  69. Assert.Equal("baz", target.GetValue(property));
  70. }
  71. [Fact]
  72. public void Completing_LocalValue_Binding_Reverts_To_Default_Value_Even_When_Local_Value_Set_Earlier()
  73. {
  74. var target = new Class1();
  75. var source = new Subject<string>();
  76. var property = Class1.FooProperty;
  77. target.Bind(property, source);
  78. source.OnNext("foo");
  79. target.SetValue(property, "bar");
  80. source.OnNext("baz");
  81. source.OnCompleted();
  82. Assert.Equal("foodefault", target.GetValue(property));
  83. }
  84. [Fact]
  85. public void Completing_LocalValue_Binding_Should_Not_Revert_To_Set_LocalValue()
  86. {
  87. var target = new Class1();
  88. var source = new BehaviorSubject<string>("bar");
  89. target.SetValue(Class1.FooProperty, "foo");
  90. var sub = target.Bind(Class1.FooProperty, source);
  91. Assert.Equal("bar", target.GetValue(Class1.FooProperty));
  92. sub.Dispose();
  93. Assert.Equal("foodefault", target.GetValue(Class1.FooProperty));
  94. }
  95. [Fact]
  96. public void Completing_Animation_Binding_Reverts_To_Set_LocalValue()
  97. {
  98. var target = new Class1();
  99. var source = new Subject<string>();
  100. var property = Class1.FooProperty;
  101. target.SetValue(property, "foo");
  102. target.Bind(property, source, BindingPriority.Animation);
  103. source.OnNext("bar");
  104. source.OnCompleted();
  105. Assert.Equal("foo", target.GetValue(property));
  106. }
  107. [Fact]
  108. public void Completing_LocalValue_Binding_Raises_PropertyChanged()
  109. {
  110. var target = new Class1();
  111. var source = new BehaviorSubject<BindingValue<string>>("foo");
  112. var property = Class1.FooProperty;
  113. var raised = 0;
  114. target.Bind(property, source);
  115. Assert.Equal("foo", target.GetValue(property));
  116. target.PropertyChanged += (s, e) =>
  117. {
  118. Assert.Equal(BindingPriority.Unset, e.Priority);
  119. Assert.Equal(property, e.Property);
  120. Assert.Equal("foo", e.OldValue as string);
  121. Assert.Equal("foodefault", e.NewValue as string);
  122. ++raised;
  123. };
  124. source.OnCompleted();
  125. Assert.Equal("foodefault", target.GetValue(property));
  126. Assert.Equal(1, raised);
  127. }
  128. [Fact]
  129. public void Completing_Style_Binding_Raises_PropertyChanged()
  130. {
  131. var target = new Class1();
  132. var source = new BehaviorSubject<BindingValue<string>>("foo");
  133. var property = Class1.FooProperty;
  134. var raised = 0;
  135. target.Bind(property, source, BindingPriority.Style);
  136. Assert.Equal("foo", target.GetValue(property));
  137. target.PropertyChanged += (s, e) =>
  138. {
  139. Assert.Equal(BindingPriority.Unset, e.Priority);
  140. Assert.Equal(property, e.Property);
  141. Assert.Equal("foo", e.OldValue as string);
  142. Assert.Equal("foodefault", e.NewValue as string);
  143. ++raised;
  144. };
  145. source.OnCompleted();
  146. Assert.Equal("foodefault", target.GetValue(property));
  147. Assert.Equal(1, raised);
  148. }
  149. [Fact]
  150. public void Completing_LocalValue_Binding_With_Style_Binding_Raises_PropertyChanged()
  151. {
  152. var target = new Class1();
  153. var source = new BehaviorSubject<BindingValue<string>>("foo");
  154. var property = Class1.FooProperty;
  155. var raised = 0;
  156. target.Bind(property, new BehaviorSubject<string>("bar"), BindingPriority.Style);
  157. target.Bind(property, source);
  158. Assert.Equal("foo", target.GetValue(property));
  159. target.PropertyChanged += (s, e) =>
  160. {
  161. Assert.Equal(BindingPriority.Style, e.Priority);
  162. Assert.Equal(property, e.Property);
  163. Assert.Equal("foo", e.OldValue as string);
  164. Assert.Equal("bar", e.NewValue as string);
  165. ++raised;
  166. };
  167. source.OnCompleted();
  168. Assert.Equal("bar", target.GetValue(property));
  169. Assert.Equal(1, raised);
  170. }
  171. [Fact]
  172. public void Disposing_LocalValue_Binding_Raises_PropertyChanged()
  173. {
  174. var target = new Class1();
  175. var source = new BehaviorSubject<BindingValue<string>>("foo");
  176. var property = Class1.FooProperty;
  177. var raised = 0;
  178. var sub = target.Bind(property, source);
  179. Assert.Equal("foo", target.GetValue(property));
  180. target.PropertyChanged += (s, e) =>
  181. {
  182. Assert.Equal(BindingPriority.Unset, e.Priority);
  183. Assert.Equal(property, e.Property);
  184. Assert.Equal("foo", e.OldValue as string);
  185. Assert.Equal("foodefault", e.NewValue as string);
  186. ++raised;
  187. };
  188. sub.Dispose();
  189. Assert.Equal("foodefault", target.GetValue(property));
  190. Assert.Equal(1, raised);
  191. }
  192. [Fact]
  193. public void Setting_Style_Value_Overrides_Binding_Permanently()
  194. {
  195. var target = new Class1();
  196. var source = new Subject<string>();
  197. target.Bind(Class1.FooProperty, source, BindingPriority.Style);
  198. source.OnNext("foo");
  199. Assert.Equal("foo", target.GetValue(Class1.FooProperty));
  200. target.SetValue(Class1.FooProperty, "bar", BindingPriority.Style);
  201. Assert.Equal("bar", target.GetValue(Class1.FooProperty));
  202. source.OnNext("baz");
  203. Assert.Equal("bar", target.GetValue(Class1.FooProperty));
  204. }
  205. [Fact]
  206. public void Second_LocalValue_Binding_Overrides_First()
  207. {
  208. var property = Class1.FooProperty;
  209. var target = new Class1();
  210. var source1 = new Subject<string>();
  211. var source2 = new Subject<string>();
  212. target.Bind(property, source1, BindingPriority.LocalValue);
  213. target.Bind(property, source2, BindingPriority.LocalValue);
  214. source1.OnNext("foo");
  215. Assert.Equal("foo", target.GetValue(property));
  216. source2.OnNext("bar");
  217. Assert.Equal("bar", target.GetValue(property));
  218. source1.OnNext("baz");
  219. Assert.Equal("bar", target.GetValue(property));
  220. }
  221. [Fact]
  222. public void Completing_Second_LocalValue_Binding_Reverts_To_First()
  223. {
  224. var property = Class1.FooProperty;
  225. var target = new Class1();
  226. var source1 = new Subject<string>();
  227. var source2 = new Subject<string>();
  228. target.Bind(property, source1, BindingPriority.LocalValue);
  229. target.Bind(property, source2, BindingPriority.LocalValue);
  230. source1.OnNext("foo");
  231. source2.OnNext("bar");
  232. source1.OnNext("baz");
  233. source2.OnCompleted();
  234. Assert.Equal("baz", target.GetValue(property));
  235. }
  236. [Fact]
  237. public void Completing_StyleTrigger_Binding_Reverts_To_StyleBinding()
  238. {
  239. var property = Class1.FooProperty;
  240. var target = new Class1();
  241. var source1 = new Subject<string>();
  242. var source2 = new Subject<string>();
  243. target.Bind(property, source1, BindingPriority.Style);
  244. target.Bind(property, source2, BindingPriority.StyleTrigger);
  245. source1.OnNext("foo");
  246. source2.OnNext("bar");
  247. source2.OnCompleted();
  248. source1.OnNext("baz");
  249. Assert.Equal("baz", target.GetValue(property));
  250. }
  251. [Fact]
  252. public void Bind_NonGeneric_Sets_Current_Value()
  253. {
  254. Class1 target = new Class1();
  255. Class1 source = new Class1();
  256. source.SetValue(Class1.FooProperty, "initial");
  257. target.Bind((AvaloniaProperty)Class1.FooProperty, source.GetObservable(Class1.FooProperty));
  258. Assert.Equal("initial", target.GetValue(Class1.FooProperty));
  259. }
  260. [Fact]
  261. public void Bind_To_ValueType_Accepts_UnsetValue()
  262. {
  263. var target = new Class1();
  264. var source = new Subject<object>();
  265. target.Bind(Class1.QuxProperty, source);
  266. source.OnNext(6.7);
  267. source.OnNext(AvaloniaProperty.UnsetValue);
  268. Assert.Equal(5.6, target.GetValue(Class1.QuxProperty));
  269. Assert.False(target.IsSet(Class1.QuxProperty));
  270. }
  271. [Fact]
  272. public void OneTime_Binding_Ignores_UnsetValue()
  273. {
  274. var target = new Class1();
  275. var source = new Subject<object>();
  276. target.Bind(Class1.QuxProperty, new TestOneTimeBinding(source));
  277. source.OnNext(AvaloniaProperty.UnsetValue);
  278. Assert.Equal(5.6, target.GetValue(Class1.QuxProperty));
  279. source.OnNext(6.7);
  280. Assert.Equal(6.7, target.GetValue(Class1.QuxProperty));
  281. }
  282. [Fact]
  283. public void OneTime_Binding_Ignores_Binding_Errors()
  284. {
  285. var target = new Class1();
  286. var source = new Subject<object>();
  287. target.Bind(Class1.QuxProperty, new TestOneTimeBinding(source));
  288. source.OnNext(new BindingNotification(new Exception(), BindingErrorType.Error));
  289. Assert.Equal(5.6, target.GetValue(Class1.QuxProperty));
  290. source.OnNext(6.7);
  291. Assert.Equal(6.7, target.GetValue(Class1.QuxProperty));
  292. }
  293. [Fact]
  294. public void Bind_Does_Not_Throw_Exception_For_Unregistered_Property()
  295. {
  296. Class1 target = new Class1();
  297. target.Bind(Class2.BarProperty, Observable.Never<string>().StartWith("foo"));
  298. Assert.Equal("foo", target.GetValue(Class2.BarProperty));
  299. }
  300. [Fact]
  301. public void Bind_Sets_Subsequent_Value()
  302. {
  303. Class1 target = new Class1();
  304. Class1 source = new Class1();
  305. source.SetValue(Class1.FooProperty, "initial");
  306. target.Bind(Class1.FooProperty, source.GetObservable(Class1.FooProperty));
  307. source.SetValue(Class1.FooProperty, "subsequent");
  308. Assert.Equal("subsequent", target.GetValue(Class1.FooProperty));
  309. }
  310. [Fact]
  311. public void Bind_Ignores_Invalid_Value_Type()
  312. {
  313. Class1 target = new Class1();
  314. target.Bind((AvaloniaProperty)Class1.FooProperty, Observable.Return((object)123));
  315. Assert.Equal("foodefault", target.GetValue(Class1.FooProperty));
  316. }
  317. [Fact]
  318. public void Observable_Is_Unsubscribed_When_Subscription_Disposed()
  319. {
  320. var scheduler = new TestScheduler();
  321. var source = scheduler.CreateColdObservable<string>();
  322. var target = new Class1();
  323. var subscription = target.Bind(Class1.FooProperty, source);
  324. Assert.Equal(1, source.Subscriptions.Count);
  325. Assert.Equal(Subscription.Infinite, source.Subscriptions[0].Unsubscribe);
  326. subscription.Dispose();
  327. Assert.Equal(1, source.Subscriptions.Count);
  328. Assert.Equal(0, source.Subscriptions[0].Unsubscribe);
  329. }
  330. [Fact]
  331. public void Two_Way_Separate_Binding_Works()
  332. {
  333. Class1 obj1 = new Class1();
  334. Class1 obj2 = new Class1();
  335. obj1.SetValue(Class1.FooProperty, "initial1");
  336. obj2.SetValue(Class1.FooProperty, "initial2");
  337. obj1.Bind(Class1.FooProperty, obj2.GetObservable(Class1.FooProperty));
  338. obj2.Bind(Class1.FooProperty, obj1.GetObservable(Class1.FooProperty));
  339. Assert.Equal("initial2", obj1.GetValue(Class1.FooProperty));
  340. Assert.Equal("initial2", obj2.GetValue(Class1.FooProperty));
  341. obj1.SetValue(Class1.FooProperty, "first");
  342. Assert.Equal("first", obj1.GetValue(Class1.FooProperty));
  343. Assert.Equal("first", obj2.GetValue(Class1.FooProperty));
  344. obj2.SetValue(Class1.FooProperty, "second");
  345. Assert.Equal("second", obj1.GetValue(Class1.FooProperty));
  346. Assert.Equal("second", obj2.GetValue(Class1.FooProperty));
  347. obj1.SetValue(Class1.FooProperty, "third");
  348. Assert.Equal("third", obj1.GetValue(Class1.FooProperty));
  349. Assert.Equal("third", obj2.GetValue(Class1.FooProperty));
  350. }
  351. [Fact]
  352. public void Two_Way_Binding_With_Priority_Works()
  353. {
  354. Class1 obj1 = new Class1();
  355. Class1 obj2 = new Class1();
  356. obj1.SetValue(Class1.FooProperty, "initial1", BindingPriority.Style);
  357. obj2.SetValue(Class1.FooProperty, "initial2", BindingPriority.Style);
  358. obj1.Bind(Class1.FooProperty, obj2.GetObservable(Class1.FooProperty), BindingPriority.Style);
  359. obj2.Bind(Class1.FooProperty, obj1.GetObservable(Class1.FooProperty), BindingPriority.Style);
  360. Assert.Equal("initial2", obj1.GetValue(Class1.FooProperty));
  361. Assert.Equal("initial2", obj2.GetValue(Class1.FooProperty));
  362. obj1.SetValue(Class1.FooProperty, "first", BindingPriority.Style);
  363. Assert.Equal("first", obj1.GetValue(Class1.FooProperty));
  364. Assert.Equal("first", obj2.GetValue(Class1.FooProperty));
  365. obj2.SetValue(Class1.FooProperty, "second", BindingPriority.Style);
  366. Assert.Equal("first", obj1.GetValue(Class1.FooProperty));
  367. Assert.Equal("second", obj2.GetValue(Class1.FooProperty));
  368. obj1.SetValue(Class1.FooProperty, "third", BindingPriority.Style);
  369. Assert.Equal("third", obj1.GetValue(Class1.FooProperty));
  370. Assert.Equal("second", obj2.GetValue(Class1.FooProperty));
  371. }
  372. [Fact]
  373. public void Local_Binding_Overwrites_Local_Value()
  374. {
  375. var target = new Class1();
  376. var binding = new Subject<string>();
  377. target.Bind(Class1.FooProperty, binding);
  378. binding.OnNext("first");
  379. Assert.Equal("first", target.GetValue(Class1.FooProperty));
  380. target.SetValue(Class1.FooProperty, "second");
  381. Assert.Equal("second", target.GetValue(Class1.FooProperty));
  382. binding.OnNext("third");
  383. Assert.Equal("third", target.GetValue(Class1.FooProperty));
  384. }
  385. [Fact]
  386. public void StyleBinding_Overrides_Default_Value()
  387. {
  388. Class1 target = new Class1();
  389. target.Bind(Class1.FooProperty, Single("stylevalue"), BindingPriority.Style);
  390. Assert.Equal("stylevalue", target.GetValue(Class1.FooProperty));
  391. }
  392. [Fact]
  393. public void this_Operator_Returns_Value_Property()
  394. {
  395. Class1 target = new Class1();
  396. target.SetValue(Class1.FooProperty, "newvalue");
  397. Assert.Equal("newvalue", target[Class1.FooProperty]);
  398. }
  399. [Fact]
  400. public void this_Operator_Sets_Value_Property()
  401. {
  402. Class1 target = new Class1();
  403. target[Class1.FooProperty] = "newvalue";
  404. Assert.Equal("newvalue", target.GetValue(Class1.FooProperty));
  405. }
  406. [Fact]
  407. public void this_Operator_Doesnt_Accept_Observable()
  408. {
  409. Class1 target = new Class1();
  410. Assert.Throws<ArgumentException>(() =>
  411. {
  412. target[Class1.FooProperty] = Observable.Return("newvalue");
  413. });
  414. }
  415. [Fact]
  416. public void this_Operator_Binds_One_Way()
  417. {
  418. Class1 target1 = new Class1();
  419. Class2 target2 = new Class2();
  420. IndexerDescriptor binding = Class2.BarProperty.Bind().WithMode(BindingMode.OneWay);
  421. target1.SetValue(Class1.FooProperty, "first");
  422. target2[binding] = target1[!Class1.FooProperty];
  423. target1.SetValue(Class1.FooProperty, "second");
  424. Assert.Equal("second", target2.GetValue(Class2.BarProperty));
  425. }
  426. [Fact]
  427. public void this_Operator_Binds_Two_Way()
  428. {
  429. Class1 target1 = new Class1();
  430. Class1 target2 = new Class1();
  431. target1.SetValue(Class1.FooProperty, "first");
  432. target2[!Class1.FooProperty] = target1[!!Class1.FooProperty];
  433. Assert.Equal("first", target2.GetValue(Class1.FooProperty));
  434. target1.SetValue(Class1.FooProperty, "second");
  435. Assert.Equal("second", target2.GetValue(Class1.FooProperty));
  436. target2.SetValue(Class1.FooProperty, "third");
  437. Assert.Equal("third", target1.GetValue(Class1.FooProperty));
  438. }
  439. [Fact]
  440. public void this_Operator_Binds_One_Time()
  441. {
  442. Class1 target1 = new Class1();
  443. Class1 target2 = new Class1();
  444. target1.SetValue(Class1.FooProperty, "first");
  445. target2[!Class1.FooProperty] = target1[Class1.FooProperty.Bind().WithMode(BindingMode.OneTime)];
  446. target1.SetValue(Class1.FooProperty, "second");
  447. Assert.Equal("first", target2.GetValue(Class1.FooProperty));
  448. }
  449. [Fact]
  450. public void Binding_Error_Reverts_To_Default_Value()
  451. {
  452. var target = new Class1();
  453. var source = new Subject<BindingValue<string>>();
  454. target.Bind(Class1.FooProperty, source);
  455. source.OnNext("initial");
  456. source.OnNext(BindingValue<string>.BindingError(new InvalidOperationException("Foo")));
  457. Assert.Equal("foodefault", target.GetValue(Class1.FooProperty));
  458. }
  459. [Fact]
  460. public void Binding_Error_With_FallbackValue_Causes_Target_Update()
  461. {
  462. var target = new Class1();
  463. var source = new Subject<BindingValue<string>>();
  464. target.Bind(Class1.FooProperty, source);
  465. source.OnNext("initial");
  466. source.OnNext(BindingValue<string>.BindingError(new InvalidOperationException("Foo"), "bar"));
  467. Assert.Equal("bar", target.GetValue(Class1.FooProperty));
  468. }
  469. [Fact]
  470. public void DataValidationError_Does_Not_Cause_Target_Update()
  471. {
  472. var target = new Class1();
  473. var source = new Subject<BindingValue<string>>();
  474. target.Bind(Class1.FooProperty, source);
  475. source.OnNext("initial");
  476. source.OnNext(BindingValue<string>.DataValidationError(new InvalidOperationException("Foo")));
  477. Assert.Equal("initial", target.GetValue(Class1.FooProperty));
  478. }
  479. [Fact]
  480. public void DataValidationError_With_FallbackValue_Causes_Target_Update()
  481. {
  482. var target = new Class1();
  483. var source = new Subject<BindingValue<string>>();
  484. target.Bind(Class1.FooProperty, source);
  485. source.OnNext("initial");
  486. source.OnNext(BindingValue<string>.DataValidationError(new InvalidOperationException("Foo"), "bar"));
  487. Assert.Equal("bar", target.GetValue(Class1.FooProperty));
  488. }
  489. [Fact]
  490. public void Bind_Logs_Binding_Error()
  491. {
  492. var target = new Class1();
  493. var source = new Subject<BindingValue<double>>();
  494. var called = false;
  495. var expectedMessageTemplate = "Error in binding to {Target}.{Property}: {Message}";
  496. LogCallback checkLogMessage = (level, area, src, mt, pv) =>
  497. {
  498. if (level == LogEventLevel.Warning &&
  499. area == LogArea.Binding &&
  500. mt == expectedMessageTemplate)
  501. {
  502. called = true;
  503. }
  504. };
  505. using (TestLogSink.Start(checkLogMessage))
  506. {
  507. target.Bind(Class1.QuxProperty, source);
  508. source.OnNext(6.7);
  509. source.OnNext(BindingValue<double>.BindingError(new InvalidOperationException("Foo")));
  510. Assert.Equal(5.6, target.GetValue(Class1.QuxProperty));
  511. Assert.True(called);
  512. }
  513. }
  514. [Fact]
  515. public async Task Bind_With_Scheduler_Executes_On_Scheduler()
  516. {
  517. var target = new Class1();
  518. var source = new Subject<double>();
  519. var currentThreadId = Thread.CurrentThread.ManagedThreadId;
  520. var threadingInterfaceMock = new Mock<IPlatformThreadingInterface>();
  521. threadingInterfaceMock.SetupGet(mock => mock.CurrentThreadIsLoopThread)
  522. .Returns(() => Thread.CurrentThread.ManagedThreadId == currentThreadId);
  523. var services = new TestServices(
  524. scheduler: AvaloniaScheduler.Instance,
  525. threadingInterface: threadingInterfaceMock.Object);
  526. using (UnitTestApplication.Start(services))
  527. {
  528. target.Bind(Class1.QuxProperty, source);
  529. await Task.Run(() => source.OnNext(6.7));
  530. }
  531. }
  532. [Fact]
  533. public void SetValue_Should_Not_Cause_StackOverflow_And_Have_Correct_Values()
  534. {
  535. var viewModel = new TestStackOverflowViewModel()
  536. {
  537. Value = 50
  538. };
  539. var target = new Class1();
  540. target.Bind(Class1.DoubleValueProperty,
  541. new Binding("Value") { Mode = BindingMode.TwoWay, Source = viewModel });
  542. var child = new Class1();
  543. child[!!Class1.DoubleValueProperty] = target[!!Class1.DoubleValueProperty];
  544. Assert.Equal(1, viewModel.SetterInvokedCount);
  545. // Issues #855 and #824 were causing a StackOverflowException at this point.
  546. target.DoubleValue = 51.001;
  547. Assert.Equal(2, viewModel.SetterInvokedCount);
  548. double expected = 51;
  549. Assert.Equal(expected, viewModel.Value);
  550. Assert.Equal(expected, target.DoubleValue);
  551. Assert.Equal(expected, child.DoubleValue);
  552. }
  553. [Fact]
  554. public void IsAnimating_On_Property_With_No_Value_Returns_False()
  555. {
  556. var target = new Class1();
  557. Assert.False(target.IsAnimating(Class1.FooProperty));
  558. }
  559. [Fact]
  560. public void IsAnimating_On_Property_With_Animation_Value_Returns_True()
  561. {
  562. var target = new Class1();
  563. target.SetValue(Class1.FooProperty, "foo", BindingPriority.Animation);
  564. Assert.True(target.IsAnimating(Class1.FooProperty));
  565. }
  566. [Fact]
  567. public void IsAnimating_On_Property_With_Non_Animation_Binding_Returns_False()
  568. {
  569. var target = new Class1();
  570. var source = new Subject<string>();
  571. target.Bind(Class1.FooProperty, source, BindingPriority.LocalValue);
  572. Assert.False(target.IsAnimating(Class1.FooProperty));
  573. }
  574. [Fact]
  575. public void IsAnimating_On_Property_With_Animation_Binding_Returns_True()
  576. {
  577. var target = new Class1();
  578. var source = new BehaviorSubject<string>("foo");
  579. target.Bind(Class1.FooProperty, source, BindingPriority.Animation);
  580. Assert.True(target.IsAnimating(Class1.FooProperty));
  581. }
  582. [Fact]
  583. public void IsAnimating_On_Property_With_Local_Value_And_Animation_Binding_Returns_True()
  584. {
  585. var target = new Class1();
  586. var source = new BehaviorSubject<string>("foo");
  587. target.SetValue(Class1.FooProperty, "bar");
  588. target.Bind(Class1.FooProperty, source, BindingPriority.Animation);
  589. Assert.True(target.IsAnimating(Class1.FooProperty));
  590. }
  591. [Fact]
  592. public void IsAnimating_Returns_True_When_Animated_Value_Is_Same_As_Local_Value()
  593. {
  594. var target = new Class1();
  595. var source = new BehaviorSubject<string>("foo");
  596. target.SetValue(Class1.FooProperty, "foo");
  597. target.Bind(Class1.FooProperty, source, BindingPriority.Animation);
  598. Assert.True(target.IsAnimating(Class1.FooProperty));
  599. }
  600. [Fact]
  601. public void TwoWay_Binding_Should_Not_Call_Setter_On_Creation()
  602. {
  603. var target = new Class1();
  604. var source = new TestTwoWayBindingViewModel();
  605. target.Bind(Class1.DoubleValueProperty, new Binding(nameof(source.Value), BindingMode.TwoWay) { Source = source });
  606. Assert.False(source.SetterCalled);
  607. }
  608. [Fact]
  609. public void TwoWay_Binding_Should_Not_Call_Setter_On_Creation_Indexer()
  610. {
  611. var target = new Class1();
  612. var source = new TestTwoWayBindingViewModel();
  613. target.Bind(Class1.DoubleValueProperty, new Binding("[0]", BindingMode.TwoWay) { Source = source });
  614. Assert.False(source.SetterCalled);
  615. }
  616. [Fact]
  617. public void TwoWay_Binding_Should_Not_Fail_With_Null_DataContext()
  618. {
  619. var target = new TextBlock();
  620. target.DataContext = null;
  621. target.Bind(TextBlock.TextProperty, new Binding("Missing", BindingMode.TwoWay));
  622. }
  623. [Fact]
  624. public void TwoWay_Binding_Should_Not_Fail_With_Null_DataContext_Indexer()
  625. {
  626. var target = new TextBlock();
  627. target.DataContext = null;
  628. target.Bind(TextBlock.TextProperty, new Binding("[0]", BindingMode.TwoWay));
  629. }
  630. [Fact]
  631. public void Disposing_Completed_Binding_Does_Not_Throw()
  632. {
  633. var target = new Class1();
  634. var source = new Subject<string>();
  635. var subscription = target.Bind(Class1.FooProperty, source);
  636. source.OnCompleted();
  637. subscription.Dispose();
  638. }
  639. [Fact]
  640. public void TwoWay_Binding_Should_Not_Call_Setter_On_Creation_With_Value()
  641. {
  642. var target = new Class1();
  643. var source = new TestTwoWayBindingViewModel() { Value = 1 };
  644. source.ResetSetterCalled();
  645. target.Bind(Class1.DoubleValueProperty, new Binding(nameof(source.Value), BindingMode.TwoWay) { Source = source });
  646. Assert.False(source.SetterCalled);
  647. }
  648. [Fact]
  649. public void TwoWay_Binding_Should_Not_Call_Setter_On_Creation_Indexer_With_Value()
  650. {
  651. var target = new Class1();
  652. var source = new TestTwoWayBindingViewModel() { [0] = 1 };
  653. source.ResetSetterCalled();
  654. target.Bind(Class1.DoubleValueProperty, new Binding("[0]", BindingMode.TwoWay) { Source = source });
  655. Assert.False(source.SetterCalled);
  656. }
  657. /// <summary>
  658. /// Returns an observable that returns a single value but does not complete.
  659. /// </summary>
  660. /// <typeparam name="T">The type of the observable.</typeparam>
  661. /// <param name="value">The value.</param>
  662. /// <returns>The observable.</returns>
  663. private IObservable<T> Single<T>(T value)
  664. {
  665. return Observable.Never<T>().StartWith(value);
  666. }
  667. private class Class1 : AvaloniaObject
  668. {
  669. public static readonly StyledProperty<string> FooProperty =
  670. AvaloniaProperty.Register<Class1, string>("Foo", "foodefault");
  671. public static readonly StyledProperty<double> QuxProperty =
  672. AvaloniaProperty.Register<Class1, double>("Qux", 5.6);
  673. public static readonly StyledProperty<double> DoubleValueProperty =
  674. AvaloniaProperty.Register<Class1, double>(nameof(DoubleValue));
  675. public double DoubleValue
  676. {
  677. get { return GetValue(DoubleValueProperty); }
  678. set { SetValue(DoubleValueProperty, value); }
  679. }
  680. }
  681. private class Class2 : Class1
  682. {
  683. public static readonly StyledProperty<string> BarProperty =
  684. AvaloniaProperty.Register<Class2, string>("Bar", "bardefault");
  685. }
  686. private class TestOneTimeBinding : IBinding
  687. {
  688. private IObservable<object> _source;
  689. public TestOneTimeBinding(IObservable<object> source)
  690. {
  691. _source = source;
  692. }
  693. public InstancedBinding Initiate(
  694. IAvaloniaObject target,
  695. AvaloniaProperty targetProperty,
  696. object anchor = null,
  697. bool enableDataValidation = false)
  698. {
  699. return InstancedBinding.OneTime(_source);
  700. }
  701. }
  702. private class TestStackOverflowViewModel : INotifyPropertyChanged
  703. {
  704. public int SetterInvokedCount { get; private set; }
  705. public const int MaxInvokedCount = 1000;
  706. private double _value;
  707. public event PropertyChangedEventHandler PropertyChanged;
  708. public double Value
  709. {
  710. get { return _value; }
  711. set
  712. {
  713. if (_value != value)
  714. {
  715. SetterInvokedCount++;
  716. if (SetterInvokedCount < MaxInvokedCount)
  717. {
  718. _value = (int)value;
  719. if (_value > 75) _value = 75;
  720. if (_value < 25) _value = 25;
  721. }
  722. else
  723. {
  724. _value = value;
  725. }
  726. PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Value)));
  727. }
  728. }
  729. }
  730. }
  731. private class TestTwoWayBindingViewModel
  732. {
  733. private double _value;
  734. public double Value
  735. {
  736. get => _value;
  737. set
  738. {
  739. _value = value;
  740. SetterCalled = true;
  741. }
  742. }
  743. public double this[int index]
  744. {
  745. get => _value;
  746. set
  747. {
  748. _value = value;
  749. SetterCalled = true;
  750. }
  751. }
  752. public bool SetterCalled { get; private set; }
  753. public void ResetSetterCalled()
  754. {
  755. SetterCalled = false;
  756. }
  757. }
  758. }
  759. }