AvaloniaObjectTests_BatchUpdate.cs 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Reactive;
  4. using System.Reactive.Disposables;
  5. using System.Reactive.Linq;
  6. using System.Text;
  7. using Avalonia.Data;
  8. using Avalonia.Layout;
  9. using Xunit;
  10. namespace Avalonia.Base.UnitTests
  11. {
  12. public class AvaloniaObjectTests_BatchUpdate
  13. {
  14. [Fact]
  15. public void SetValue_Should_Not_Raise_Property_Changes_During_Batch_Update()
  16. {
  17. var target = new TestClass();
  18. var raised = new List<string>();
  19. target.GetObservable(TestClass.FooProperty).Skip(1).Subscribe(x => raised.Add(x));
  20. target.BeginBatchUpdate();
  21. target.SetValue(TestClass.FooProperty, "foo", BindingPriority.LocalValue);
  22. Assert.Empty(raised);
  23. }
  24. [Fact]
  25. public void Binding_Should_Not_Raise_Property_Changes_During_Batch_Update()
  26. {
  27. var target = new TestClass();
  28. var observable = new TestObservable<string>("foo");
  29. var raised = new List<string>();
  30. target.GetObservable(TestClass.FooProperty).Skip(1).Subscribe(x => raised.Add(x));
  31. target.BeginBatchUpdate();
  32. target.Bind(TestClass.FooProperty, observable, BindingPriority.LocalValue);
  33. Assert.Empty(raised);
  34. }
  35. [Fact]
  36. public void Binding_Completion_Should_Not_Raise_Property_Changes_During_Batch_Update()
  37. {
  38. var target = new TestClass();
  39. var observable = new TestObservable<string>("foo");
  40. var raised = new List<string>();
  41. target.Bind(TestClass.FooProperty, observable, BindingPriority.LocalValue);
  42. target.GetObservable(TestClass.FooProperty).Skip(1).Subscribe(x => raised.Add(x));
  43. target.BeginBatchUpdate();
  44. observable.OnCompleted();
  45. Assert.Empty(raised);
  46. }
  47. [Fact]
  48. public void Binding_Disposal_Should_Not_Raise_Property_Changes_During_Batch_Update()
  49. {
  50. var target = new TestClass();
  51. var observable = new TestObservable<string>("foo");
  52. var raised = new List<string>();
  53. var sub = target.Bind(TestClass.FooProperty, observable, BindingPriority.LocalValue);
  54. target.GetObservable(TestClass.FooProperty).Skip(1).Subscribe(x => raised.Add(x));
  55. target.BeginBatchUpdate();
  56. sub.Dispose();
  57. Assert.Empty(raised);
  58. }
  59. [Fact]
  60. public void SetValue_Change_Should_Be_Raised_After_Batch_Update_1()
  61. {
  62. var target = new TestClass();
  63. var raised = new List<AvaloniaPropertyChangedEventArgs>();
  64. target.PropertyChanged += (s, e) => raised.Add(e);
  65. target.BeginBatchUpdate();
  66. target.SetValue(TestClass.FooProperty, "foo", BindingPriority.LocalValue);
  67. target.EndBatchUpdate();
  68. Assert.Equal(1, raised.Count);
  69. Assert.Equal("foo", target.Foo);
  70. Assert.Null(raised[0].OldValue);
  71. Assert.Equal("foo", raised[0].NewValue);
  72. }
  73. [Fact]
  74. public void SetValue_Change_Should_Be_Raised_After_Batch_Update_2()
  75. {
  76. var target = new TestClass();
  77. var raised = new List<AvaloniaPropertyChangedEventArgs>();
  78. target.SetValue(TestClass.FooProperty, "foo", BindingPriority.LocalValue);
  79. target.PropertyChanged += (s, e) => raised.Add(e);
  80. target.BeginBatchUpdate();
  81. target.SetValue(TestClass.FooProperty, "bar", BindingPriority.LocalValue);
  82. target.SetValue(TestClass.FooProperty, "baz", BindingPriority.LocalValue);
  83. target.EndBatchUpdate();
  84. Assert.Equal(1, raised.Count);
  85. Assert.Equal("baz", target.Foo);
  86. }
  87. [Fact]
  88. public void SetValue_Change_Should_Be_Raised_After_Batch_Update_3()
  89. {
  90. var target = new TestClass();
  91. var raised = new List<AvaloniaPropertyChangedEventArgs>();
  92. target.PropertyChanged += (s, e) => raised.Add(e);
  93. target.BeginBatchUpdate();
  94. target.SetValue(TestClass.BazProperty, Orientation.Horizontal, BindingPriority.LocalValue);
  95. target.EndBatchUpdate();
  96. Assert.Equal(1, raised.Count);
  97. Assert.Equal(TestClass.BazProperty, raised[0].Property);
  98. Assert.Equal(Orientation.Vertical, raised[0].OldValue);
  99. Assert.Equal(Orientation.Horizontal, raised[0].NewValue);
  100. Assert.Equal(Orientation.Horizontal, target.Baz);
  101. }
  102. [Fact]
  103. public void SetValue_Changes_Should_Be_Raised_In_Correct_Order_After_Batch_Update()
  104. {
  105. var target = new TestClass();
  106. var raised = new List<AvaloniaPropertyChangedEventArgs>();
  107. target.PropertyChanged += (s, e) => raised.Add(e);
  108. target.BeginBatchUpdate();
  109. target.SetValue(TestClass.FooProperty, "foo", BindingPriority.LocalValue);
  110. target.SetValue(TestClass.BarProperty, "bar", BindingPriority.LocalValue);
  111. target.SetValue(TestClass.FooProperty, "baz", BindingPriority.LocalValue);
  112. target.EndBatchUpdate();
  113. Assert.Equal(2, raised.Count);
  114. Assert.Equal(TestClass.BarProperty, raised[0].Property);
  115. Assert.Equal(TestClass.FooProperty, raised[1].Property);
  116. Assert.Equal("baz", target.Foo);
  117. Assert.Equal("bar", target.Bar);
  118. }
  119. [Fact]
  120. public void SetValue_And_Binding_Changes_Should_Be_Raised_In_Correct_Order_After_Batch_Update_1()
  121. {
  122. var target = new TestClass();
  123. var observable = new TestObservable<string>("baz");
  124. var raised = new List<AvaloniaPropertyChangedEventArgs>();
  125. target.PropertyChanged += (s, e) => raised.Add(e);
  126. target.BeginBatchUpdate();
  127. target.SetValue(TestClass.FooProperty, "foo", BindingPriority.LocalValue);
  128. target.SetValue(TestClass.BarProperty, "bar", BindingPriority.LocalValue);
  129. target.Bind(TestClass.FooProperty, observable, BindingPriority.LocalValue);
  130. target.EndBatchUpdate();
  131. Assert.Equal(2, raised.Count);
  132. Assert.Equal(TestClass.BarProperty, raised[0].Property);
  133. Assert.Equal(TestClass.FooProperty, raised[1].Property);
  134. Assert.Equal("baz", target.Foo);
  135. Assert.Equal("bar", target.Bar);
  136. }
  137. [Fact]
  138. public void SetValue_And_Binding_Changes_Should_Be_Raised_In_Correct_Order_After_Batch_Update_2()
  139. {
  140. var target = new TestClass();
  141. var observable = new TestObservable<string>("foo");
  142. var raised = new List<AvaloniaPropertyChangedEventArgs>();
  143. target.PropertyChanged += (s, e) => raised.Add(e);
  144. target.BeginBatchUpdate();
  145. target.Bind(TestClass.FooProperty, observable, BindingPriority.LocalValue);
  146. target.SetValue(TestClass.BarProperty, "bar", BindingPriority.LocalValue);
  147. target.SetValue(TestClass.FooProperty, "baz", BindingPriority.LocalValue);
  148. target.EndBatchUpdate();
  149. Assert.Equal(2, raised.Count);
  150. Assert.Equal(TestClass.BarProperty, raised[0].Property);
  151. Assert.Equal(TestClass.FooProperty, raised[1].Property);
  152. Assert.Equal("baz", target.Foo);
  153. Assert.Equal("bar", target.Bar);
  154. }
  155. [Fact]
  156. public void SetValue_And_Binding_Changes_Should_Be_Raised_In_Correct_Order_After_Batch_Update_3()
  157. {
  158. var target = new TestClass();
  159. var observable1 = new TestObservable<string>("foo");
  160. var observable2 = new TestObservable<string>("qux");
  161. var raised = new List<AvaloniaPropertyChangedEventArgs>();
  162. target.PropertyChanged += (s, e) => raised.Add(e);
  163. target.BeginBatchUpdate();
  164. target.Bind(TestClass.FooProperty, observable2, BindingPriority.LocalValue);
  165. target.Bind(TestClass.FooProperty, observable1, BindingPriority.LocalValue);
  166. target.SetValue(TestClass.BarProperty, "bar", BindingPriority.LocalValue);
  167. target.SetValue(TestClass.FooProperty, "baz", BindingPriority.LocalValue);
  168. target.EndBatchUpdate();
  169. Assert.Equal(2, raised.Count);
  170. Assert.Equal(TestClass.BarProperty, raised[0].Property);
  171. Assert.Equal(TestClass.FooProperty, raised[1].Property);
  172. Assert.Equal("baz", target.Foo);
  173. Assert.Equal("bar", target.Bar);
  174. }
  175. [Fact]
  176. public void Binding_Change_Should_Be_Raised_After_Batch_Update_1()
  177. {
  178. var target = new TestClass();
  179. var observable = new TestObservable<string>("foo");
  180. var raised = new List<AvaloniaPropertyChangedEventArgs>();
  181. target.PropertyChanged += (s, e) => raised.Add(e);
  182. target.BeginBatchUpdate();
  183. target.Bind(TestClass.FooProperty, observable, BindingPriority.LocalValue);
  184. target.EndBatchUpdate();
  185. Assert.Equal(1, raised.Count);
  186. Assert.Equal("foo", target.Foo);
  187. Assert.Null(raised[0].OldValue);
  188. Assert.Equal("foo", raised[0].NewValue);
  189. }
  190. [Fact]
  191. public void Binding_Change_Should_Be_Raised_After_Batch_Update_2()
  192. {
  193. var target = new TestClass();
  194. var observable1 = new TestObservable<string>("bar");
  195. var observable2 = new TestObservable<string>("baz");
  196. var raised = new List<AvaloniaPropertyChangedEventArgs>();
  197. target.SetValue(TestClass.FooProperty, "foo", BindingPriority.LocalValue);
  198. target.PropertyChanged += (s, e) => raised.Add(e);
  199. target.BeginBatchUpdate();
  200. target.Bind(TestClass.FooProperty, observable1, BindingPriority.LocalValue);
  201. target.Bind(TestClass.FooProperty, observable2, BindingPriority.LocalValue);
  202. target.EndBatchUpdate();
  203. Assert.Equal(1, raised.Count);
  204. Assert.Equal("baz", target.Foo);
  205. Assert.Equal("foo", raised[0].OldValue);
  206. Assert.Equal("baz", raised[0].NewValue);
  207. }
  208. [Fact]
  209. public void Binding_Change_Should_Be_Raised_After_Batch_Update_3()
  210. {
  211. var target = new TestClass();
  212. var observable = new TestObservable<Orientation>(Orientation.Horizontal);
  213. var raised = new List<AvaloniaPropertyChangedEventArgs>();
  214. target.PropertyChanged += (s, e) => raised.Add(e);
  215. target.BeginBatchUpdate();
  216. target.Bind(TestClass.BazProperty, observable, BindingPriority.LocalValue);
  217. target.EndBatchUpdate();
  218. Assert.Equal(1, raised.Count);
  219. Assert.Equal(TestClass.BazProperty, raised[0].Property);
  220. Assert.Equal(Orientation.Vertical, raised[0].OldValue);
  221. Assert.Equal(Orientation.Horizontal, raised[0].NewValue);
  222. Assert.Equal(Orientation.Horizontal, target.Baz);
  223. }
  224. [Fact]
  225. public void Binding_Completion_Should_Be_Raised_After_Batch_Update()
  226. {
  227. var target = new TestClass();
  228. var observable = new TestObservable<string>("foo");
  229. var raised = new List<AvaloniaPropertyChangedEventArgs>();
  230. target.Bind(TestClass.FooProperty, observable, BindingPriority.LocalValue);
  231. target.PropertyChanged += (s, e) => raised.Add(e);
  232. target.BeginBatchUpdate();
  233. observable.OnCompleted();
  234. target.EndBatchUpdate();
  235. Assert.Equal(1, raised.Count);
  236. Assert.Null(target.Foo);
  237. Assert.Equal("foo", raised[0].OldValue);
  238. Assert.Null(raised[0].NewValue);
  239. Assert.Equal(BindingPriority.Unset, raised[0].Priority);
  240. }
  241. [Fact]
  242. public void Binding_Disposal_Should_Be_Raised_After_Batch_Update()
  243. {
  244. var target = new TestClass();
  245. var observable = new TestObservable<string>("foo");
  246. var raised = new List<AvaloniaPropertyChangedEventArgs>();
  247. var sub = target.Bind(TestClass.FooProperty, observable, BindingPriority.LocalValue);
  248. target.PropertyChanged += (s, e) => raised.Add(e);
  249. target.BeginBatchUpdate();
  250. sub.Dispose();
  251. target.EndBatchUpdate();
  252. Assert.Equal(1, raised.Count);
  253. Assert.Null(target.Foo);
  254. Assert.Equal("foo", raised[0].OldValue);
  255. Assert.Null(raised[0].NewValue);
  256. Assert.Equal(BindingPriority.Unset, raised[0].Priority);
  257. }
  258. [Fact]
  259. public void ClearValue_Change_Should_Be_Raised_After_Batch_Update_1()
  260. {
  261. var target = new TestClass();
  262. var raised = new List<AvaloniaPropertyChangedEventArgs>();
  263. target.Foo = "foo";
  264. target.PropertyChanged += (s, e) => raised.Add(e);
  265. target.BeginBatchUpdate();
  266. target.ClearValue(TestClass.FooProperty);
  267. target.EndBatchUpdate();
  268. Assert.Equal(1, raised.Count);
  269. Assert.Null(target.Foo);
  270. Assert.Equal("foo", raised[0].OldValue);
  271. Assert.Null(raised[0].NewValue);
  272. Assert.Equal(BindingPriority.Unset, raised[0].Priority);
  273. }
  274. [Fact]
  275. public void Bindings_Should_Be_Subscribed_Before_Batch_Update()
  276. {
  277. var target = new TestClass();
  278. var observable1 = new TestObservable<string>("foo");
  279. var observable2 = new TestObservable<string>("bar");
  280. target.Bind(TestClass.FooProperty, observable1, BindingPriority.LocalValue);
  281. target.Bind(TestClass.FooProperty, observable2, BindingPriority.LocalValue);
  282. Assert.Equal(1, observable1.SubscribeCount);
  283. Assert.Equal(1, observable2.SubscribeCount);
  284. }
  285. [Fact]
  286. public void Non_Active_Binding_Should_Not_Be_Subscribed_Before_Batch_Update()
  287. {
  288. var target = new TestClass();
  289. var observable1 = new TestObservable<string>("foo");
  290. var observable2 = new TestObservable<string>("bar");
  291. target.Bind(TestClass.FooProperty, observable1, BindingPriority.LocalValue);
  292. target.Bind(TestClass.FooProperty, observable2, BindingPriority.Style);
  293. Assert.Equal(1, observable1.SubscribeCount);
  294. Assert.Equal(0, observable2.SubscribeCount);
  295. }
  296. [Fact]
  297. public void LocalValue_Bindings_Should_Be_Subscribed_During_Batch_Update()
  298. {
  299. var target = new TestClass();
  300. var observable1 = new TestObservable<string>("foo");
  301. var observable2 = new TestObservable<string>("bar");
  302. var raised = new List<AvaloniaPropertyChangedEventArgs>();
  303. target.PropertyChanged += (s, e) => raised.Add(e);
  304. // We need to subscribe to LocalValue bindings even if we've got a batch operation
  305. // in progress because otherwise we don't know whether the binding or a subsequent
  306. // SetValue with local priority will win. Notifications however shouldn't be sent.
  307. target.BeginBatchUpdate();
  308. target.Bind(TestClass.FooProperty, observable1, BindingPriority.LocalValue);
  309. target.Bind(TestClass.FooProperty, observable2, BindingPriority.LocalValue);
  310. Assert.Equal(1, observable1.SubscribeCount);
  311. Assert.Equal(1, observable2.SubscribeCount);
  312. Assert.Empty(raised);
  313. }
  314. [Fact]
  315. public void Style_Bindings_Should_Not_Be_Subscribed_During_Batch_Update()
  316. {
  317. var target = new TestClass();
  318. var observable1 = new TestObservable<string>("foo");
  319. var observable2 = new TestObservable<string>("bar");
  320. target.BeginBatchUpdate();
  321. target.Bind(TestClass.FooProperty, observable1, BindingPriority.Style);
  322. target.Bind(TestClass.FooProperty, observable2, BindingPriority.StyleTrigger);
  323. Assert.Equal(0, observable1.SubscribeCount);
  324. Assert.Equal(0, observable2.SubscribeCount);
  325. }
  326. [Fact]
  327. public void Active_Style_Binding_Should_Be_Subscribed_After_Batch_Uppdate_1()
  328. {
  329. var target = new TestClass();
  330. var observable1 = new TestObservable<string>("foo");
  331. var observable2 = new TestObservable<string>("bar");
  332. target.BeginBatchUpdate();
  333. target.Bind(TestClass.FooProperty, observable1, BindingPriority.Style);
  334. target.Bind(TestClass.FooProperty, observable2, BindingPriority.Style);
  335. target.EndBatchUpdate();
  336. Assert.Equal(0, observable1.SubscribeCount);
  337. Assert.Equal(1, observable2.SubscribeCount);
  338. }
  339. [Fact]
  340. public void Active_Style_Binding_Should_Be_Subscribed_After_Batch_Uppdate_2()
  341. {
  342. var target = new TestClass();
  343. var observable1 = new TestObservable<string>("foo");
  344. var observable2 = new TestObservable<string>("bar");
  345. target.BeginBatchUpdate();
  346. target.Bind(TestClass.FooProperty, observable1, BindingPriority.StyleTrigger);
  347. target.Bind(TestClass.FooProperty, observable2, BindingPriority.Style);
  348. target.EndBatchUpdate();
  349. Assert.Equal(1, observable1.SubscribeCount);
  350. Assert.Equal(0, observable2.SubscribeCount);
  351. }
  352. [Fact]
  353. public void Change_Can_Be_Triggered_By_Ending_Batch_Update_1()
  354. {
  355. var target = new TestClass();
  356. var raised = new List<AvaloniaPropertyChangedEventArgs>();
  357. target.PropertyChanged += (s, e) => raised.Add(e);
  358. target.BeginBatchUpdate();
  359. target.Foo = "foo";
  360. target.PropertyChanged += (s, e) =>
  361. {
  362. if (e.Property == TestClass.FooProperty && (string)e.NewValue == "foo")
  363. target.Bar = "bar";
  364. };
  365. target.EndBatchUpdate();
  366. Assert.Equal("foo", target.Foo);
  367. Assert.Equal("bar", target.Bar);
  368. Assert.Equal(2, raised.Count);
  369. Assert.Equal(TestClass.FooProperty, raised[0].Property);
  370. Assert.Equal(TestClass.BarProperty, raised[1].Property);
  371. }
  372. [Fact]
  373. public void Change_Can_Be_Triggered_By_Ending_Batch_Update_2()
  374. {
  375. var target = new TestClass();
  376. var raised = new List<AvaloniaPropertyChangedEventArgs>();
  377. target.PropertyChanged += (s, e) => raised.Add(e);
  378. target.BeginBatchUpdate();
  379. target.Foo = "foo";
  380. target.Bar = "baz";
  381. target.PropertyChanged += (s, e) =>
  382. {
  383. if (e.Property == TestClass.FooProperty && (string)e.NewValue == "foo")
  384. target.Bar = "bar";
  385. };
  386. target.EndBatchUpdate();
  387. Assert.Equal("foo", target.Foo);
  388. Assert.Equal("bar", target.Bar);
  389. Assert.Equal(2, raised.Count);
  390. }
  391. [Fact]
  392. public void Batch_Update_Can_Be_Triggered_By_Ending_Batch_Update()
  393. {
  394. var target = new TestClass();
  395. var raised = new List<AvaloniaPropertyChangedEventArgs>();
  396. target.PropertyChanged += (s, e) => raised.Add(e);
  397. target.BeginBatchUpdate();
  398. target.Foo = "foo";
  399. target.Bar = "baz";
  400. // Simulates the following scenario:
  401. // - A control is added to the logical tree
  402. // - A batch update is started to apply styles
  403. // - Ending the batch update triggers something which removes the control from the logical tree
  404. // - A new batch update is started to detach styles
  405. target.PropertyChanged += (s, e) =>
  406. {
  407. if (e.Property == TestClass.FooProperty && (string)e.NewValue == "foo")
  408. {
  409. target.BeginBatchUpdate();
  410. target.ClearValue(TestClass.FooProperty);
  411. target.ClearValue(TestClass.BarProperty);
  412. target.EndBatchUpdate();
  413. }
  414. };
  415. target.EndBatchUpdate();
  416. Assert.Null(target.Foo);
  417. Assert.Null(target.Bar);
  418. Assert.Equal(2, raised.Count);
  419. Assert.Equal(TestClass.FooProperty, raised[0].Property);
  420. Assert.Null(raised[0].OldValue);
  421. Assert.Equal("foo", raised[0].NewValue);
  422. Assert.Equal(TestClass.FooProperty, raised[1].Property);
  423. Assert.Equal("foo", raised[1].OldValue);
  424. Assert.Null(raised[1].NewValue);
  425. }
  426. [Fact]
  427. public void Can_Set_Cleared_Value_When_Ending_Batch_Update()
  428. {
  429. var target = new TestClass();
  430. var raised = 0;
  431. target.Foo = "foo";
  432. target.BeginBatchUpdate();
  433. target.ClearValue(TestClass.FooProperty);
  434. target.PropertyChanged += (sender, e) =>
  435. {
  436. if (e.Property == TestClass.FooProperty && e.NewValue is null)
  437. {
  438. target.Foo = "bar";
  439. ++raised;
  440. }
  441. };
  442. target.EndBatchUpdate();
  443. Assert.Equal("bar", target.Foo);
  444. Assert.Equal(1, raised);
  445. }
  446. [Fact]
  447. public void Can_Bind_Cleared_Value_When_Ending_Batch_Update()
  448. {
  449. var target = new TestClass();
  450. var raised = 0;
  451. var notifications = new List<AvaloniaPropertyChangedEventArgs>();
  452. target.Foo = "foo";
  453. target.BeginBatchUpdate();
  454. target.ClearValue(TestClass.FooProperty);
  455. target.PropertyChanged += (sender, e) =>
  456. {
  457. if (e.Property == TestClass.FooProperty && e.NewValue is null)
  458. {
  459. target.Bind(TestClass.FooProperty, new TestObservable<string>("bar"));
  460. ++raised;
  461. }
  462. notifications.Add(e);
  463. };
  464. target.EndBatchUpdate();
  465. Assert.Equal("bar", target.Foo);
  466. Assert.Equal(1, raised);
  467. Assert.Equal(2, notifications.Count);
  468. Assert.Equal(null, notifications[0].NewValue);
  469. Assert.Equal("bar", notifications[1].NewValue);
  470. }
  471. [Fact]
  472. public void Can_Bind_Completed_Binding_Back_To_Original_Value_When_Ending_Batch_Update()
  473. {
  474. var target = new TestClass();
  475. var raised = 0;
  476. var notifications = new List<AvaloniaPropertyChangedEventArgs>();
  477. var observable1 = new TestObservable<string>("foo");
  478. var observable2 = new TestObservable<string>("foo");
  479. target.Bind(TestClass.FooProperty, observable1);
  480. target.BeginBatchUpdate();
  481. observable1.OnCompleted();
  482. target.PropertyChanged += (sender, e) =>
  483. {
  484. if (e.Property == TestClass.FooProperty && e.NewValue is null)
  485. {
  486. target.Bind(TestClass.FooProperty, observable2);
  487. ++raised;
  488. }
  489. notifications.Add(e);
  490. };
  491. target.EndBatchUpdate();
  492. Assert.Equal("foo", target.Foo);
  493. Assert.Equal(1, raised);
  494. Assert.Equal(2, notifications.Count);
  495. Assert.Equal(null, notifications[0].NewValue);
  496. Assert.Equal("foo", notifications[1].NewValue);
  497. }
  498. public class TestClass : AvaloniaObject
  499. {
  500. public static readonly StyledProperty<string> FooProperty =
  501. AvaloniaProperty.Register<TestClass, string>(nameof(Foo));
  502. public static readonly StyledProperty<string> BarProperty =
  503. AvaloniaProperty.Register<TestClass, string>(nameof(Bar));
  504. public static readonly StyledProperty<Orientation> BazProperty =
  505. AvaloniaProperty.Register<TestClass, Orientation>(nameof(Bar), Orientation.Vertical);
  506. public string Foo
  507. {
  508. get => GetValue(FooProperty);
  509. set => SetValue(FooProperty, value);
  510. }
  511. public string Bar
  512. {
  513. get => GetValue(BarProperty);
  514. set => SetValue(BarProperty, value);
  515. }
  516. public Orientation Baz
  517. {
  518. get => GetValue(BazProperty);
  519. set => SetValue(BazProperty, value);
  520. }
  521. }
  522. public class TestObservable<T> : ObservableBase<BindingValue<T>>
  523. {
  524. private readonly T _value;
  525. private IObserver<BindingValue<T>> _observer;
  526. public TestObservable(T value) => _value = value;
  527. public int SubscribeCount { get; private set; }
  528. public void OnCompleted() => _observer.OnCompleted();
  529. public void OnError(Exception e) => _observer.OnError(e);
  530. protected override IDisposable SubscribeCore(IObserver<BindingValue<T>> observer)
  531. {
  532. ++SubscribeCount;
  533. _observer = observer;
  534. observer.OnNext(_value);
  535. return Disposable.Empty;
  536. }
  537. }
  538. }
  539. }