AvaloniaObjectTests_BatchUpdate.cs 23 KB

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