AvaloniaObjectTests_Binding.cs 49 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385
  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.Base.UnitTests.Styling;
  8. using Avalonia.Controls;
  9. using Avalonia.Data;
  10. using Avalonia.Logging;
  11. using Avalonia.Platform;
  12. using Avalonia.Threading;
  13. using Avalonia.UnitTests;
  14. using Moq;
  15. using Nito.AsyncEx;
  16. using Xunit;
  17. #nullable enable
  18. namespace Avalonia.Base.UnitTests
  19. {
  20. public class AvaloniaObjectTests_Binding
  21. {
  22. [Fact]
  23. public void Bind_Sets_Current_Value()
  24. {
  25. var target = new Class1();
  26. var source = new BehaviorSubject<BindingValue<string>>("initial");
  27. var property = Class1.FooProperty;
  28. target.Bind(property, source);
  29. Assert.Equal("initial", target.GetValue(property));
  30. }
  31. [Fact]
  32. public void Bind_Raises_PropertyChanged()
  33. {
  34. var target = new Class1();
  35. var source = new Subject<BindingValue<string>>();
  36. var raised = 0;
  37. target.PropertyChanged += (s, e) =>
  38. {
  39. Assert.Equal(Class1.FooProperty, e.Property);
  40. Assert.Equal("foodefault", (string?)e.OldValue);
  41. Assert.Equal("newvalue", (string?)e.NewValue);
  42. Assert.Equal(BindingPriority.LocalValue, e.Priority);
  43. ++raised;
  44. };
  45. target.Bind(Class1.FooProperty, source);
  46. source.OnNext("newvalue");
  47. Assert.Equal(1, raised);
  48. }
  49. [Fact]
  50. public void PropertyChanged_Not_Raised_When_Value_Unchanged()
  51. {
  52. var target = new Class1();
  53. var source = new Subject<BindingValue<string>>();
  54. var raised = 0;
  55. target.PropertyChanged += (s, e) => ++raised;
  56. target.Bind(Class1.FooProperty, source);
  57. source.OnNext("newvalue");
  58. source.OnNext("newvalue");
  59. Assert.Equal(1, raised);
  60. }
  61. [Fact]
  62. public void Setting_LocalValue_Overrides_Binding_Until_Binding_Produces_Next_Value()
  63. {
  64. var target = new Class1();
  65. var source = new Subject<BindingValue<string>>();
  66. var property = Class1.FooProperty;
  67. target.Bind(property, source);
  68. source.OnNext("foo");
  69. Assert.Equal("foo", target.GetValue(property));
  70. target.SetValue(property, "bar");
  71. Assert.Equal("bar", target.GetValue(property));
  72. source.OnNext("baz");
  73. Assert.Equal("baz", target.GetValue(property));
  74. }
  75. [Fact]
  76. public void Completing_LocalValue_Binding_Reverts_To_Default_Value_Even_When_Local_Value_Set_Earlier()
  77. {
  78. var target = new Class1();
  79. var source = new Subject<BindingValue<string>>();
  80. var property = Class1.FooProperty;
  81. target.Bind(property, source);
  82. source.OnNext("foo");
  83. target.SetValue(property, "bar");
  84. source.OnNext("baz");
  85. source.OnCompleted();
  86. Assert.Equal("foodefault", target.GetValue(property));
  87. }
  88. [Fact]
  89. public void Disposing_LocalValue_Binding_Should_Not_Revert_To_Set_LocalValue()
  90. {
  91. var target = new Class1();
  92. var source = new BehaviorSubject<BindingValue<string>>("bar");
  93. target.SetValue(Class1.FooProperty, "foo");
  94. var sub = target.Bind(Class1.FooProperty, source);
  95. Assert.Equal("bar", target.GetValue(Class1.FooProperty));
  96. sub.Dispose();
  97. Assert.Equal("foodefault", target.GetValue(Class1.FooProperty));
  98. }
  99. [Fact]
  100. public void LocalValue_Binding_Should_Override_Style_Binding()
  101. {
  102. var target = new Class1();
  103. var source1 = new BehaviorSubject<BindingValue<string>>("foo");
  104. var source2 = new BehaviorSubject<BindingValue<string>>("bar");
  105. target.Bind(Class1.FooProperty, source1, BindingPriority.Style);
  106. Assert.Equal("foo", target.GetValue(Class1.FooProperty));
  107. target.Bind(Class1.FooProperty, source2, BindingPriority.LocalValue);
  108. Assert.Equal("bar", target.GetValue(Class1.FooProperty));
  109. }
  110. [Fact]
  111. public void Style_Binding_Should_NotOverride_LocalValue_Binding()
  112. {
  113. var target = new Class1();
  114. var source1 = new BehaviorSubject<BindingValue<string>>("foo");
  115. var source2 = new BehaviorSubject<BindingValue<string>>("bar");
  116. target.Bind(Class1.FooProperty, source1, BindingPriority.LocalValue);
  117. Assert.Equal("foo", target.GetValue(Class1.FooProperty));
  118. target.Bind(Class1.FooProperty, source2, BindingPriority.Style);
  119. Assert.Equal("foo", target.GetValue(Class1.FooProperty));
  120. }
  121. [Fact]
  122. public void Completing_Animation_Binding_Reverts_To_Set_LocalValue()
  123. {
  124. var target = new Class1();
  125. var source = new Subject<BindingValue<string>>();
  126. var property = Class1.FooProperty;
  127. target.SetValue(property, "foo");
  128. target.Bind(property, source, BindingPriority.Animation);
  129. source.OnNext("bar");
  130. source.OnCompleted();
  131. Assert.Equal("foo", target.GetValue(property));
  132. }
  133. [Fact]
  134. public void Completing_Animation_Binding_Reverts_To_Set_LocalValue_With_Style_Value()
  135. {
  136. var target = new Class1();
  137. var source = new Subject<BindingValue<string>>();
  138. var property = Class1.FooProperty;
  139. target.SetValue(property, "style", BindingPriority.Style);
  140. target.SetValue(property, "foo");
  141. target.Bind(property, source, BindingPriority.Animation);
  142. source.OnNext("bar");
  143. source.OnCompleted();
  144. Assert.Equal("foo", target.GetValue(property));
  145. }
  146. [Fact]
  147. public void Completing_LocalValue_Binding_Raises_PropertyChanged()
  148. {
  149. var target = new Class1();
  150. var source = new BehaviorSubject<BindingValue<string>>("foo");
  151. var property = Class1.FooProperty;
  152. var raised = 0;
  153. target.Bind(property, source);
  154. Assert.Equal("foo", target.GetValue(property));
  155. target.PropertyChanged += (s, e) =>
  156. {
  157. Assert.Equal(BindingPriority.Unset, e.Priority);
  158. Assert.Equal(property, e.Property);
  159. Assert.Equal("foo", e.OldValue as string);
  160. Assert.Equal("foodefault", e.NewValue as string);
  161. ++raised;
  162. };
  163. source.OnCompleted();
  164. Assert.Equal("foodefault", target.GetValue(property));
  165. Assert.Equal(1, raised);
  166. }
  167. [Fact]
  168. public void Completing_Style_Binding_Raises_PropertyChanged()
  169. {
  170. var target = new Class1();
  171. var source = new BehaviorSubject<BindingValue<string>>("foo");
  172. var property = Class1.FooProperty;
  173. var raised = 0;
  174. target.Bind(property, source, BindingPriority.Style);
  175. Assert.Equal("foo", target.GetValue(property));
  176. target.PropertyChanged += (s, e) =>
  177. {
  178. Assert.Equal(BindingPriority.Unset, e.Priority);
  179. Assert.Equal(property, e.Property);
  180. Assert.Equal("foo", e.OldValue as string);
  181. Assert.Equal("foodefault", e.NewValue as string);
  182. ++raised;
  183. };
  184. source.OnCompleted();
  185. Assert.Equal("foodefault", target.GetValue(property));
  186. Assert.Equal(1, raised);
  187. }
  188. [Fact]
  189. public void Completing_LocalValue_Binding_With_Style_Binding_Raises_PropertyChanged()
  190. {
  191. var target = new Class1();
  192. var source = new BehaviorSubject<BindingValue<string>>("foo");
  193. var property = Class1.FooProperty;
  194. var raised = 0;
  195. target.Bind(property, new BehaviorSubject<BindingValue<string>>("bar"), BindingPriority.Style);
  196. target.Bind(property, source);
  197. Assert.Equal("foo", target.GetValue(property));
  198. target.PropertyChanged += (s, e) =>
  199. {
  200. Assert.Equal(BindingPriority.Style, e.Priority);
  201. Assert.Equal(property, e.Property);
  202. Assert.Equal("foo", e.OldValue as string);
  203. Assert.Equal("bar", e.NewValue as string);
  204. ++raised;
  205. };
  206. source.OnCompleted();
  207. Assert.Equal("bar", target.GetValue(property));
  208. Assert.Equal(1, raised);
  209. }
  210. [Fact]
  211. public void Disposing_LocalValue_Binding_Raises_PropertyChanged()
  212. {
  213. var target = new Class1();
  214. var source = new BehaviorSubject<BindingValue<string>>("foo");
  215. var property = Class1.FooProperty;
  216. var raised = 0;
  217. var sub = target.Bind(property, source);
  218. Assert.Equal("foo", target.GetValue(property));
  219. target.PropertyChanged += (s, e) =>
  220. {
  221. Assert.Equal(BindingPriority.Unset, e.Priority);
  222. Assert.Equal(property, e.Property);
  223. Assert.Equal("foo", e.OldValue as string);
  224. Assert.Equal("foodefault", e.NewValue as string);
  225. ++raised;
  226. };
  227. sub.Dispose();
  228. Assert.Equal("foodefault", target.GetValue(property));
  229. Assert.Equal(1, raised);
  230. }
  231. [Fact]
  232. public void Setting_Style_Value_Overrides_Binding_Permanently()
  233. {
  234. var target = new Class1();
  235. var source = new Subject<string>();
  236. target.Bind(Class1.FooProperty, source, BindingPriority.Style);
  237. source.OnNext("foo");
  238. Assert.Equal("foo", target.GetValue(Class1.FooProperty));
  239. target.SetValue(Class1.FooProperty, "bar", BindingPriority.Style);
  240. Assert.Equal("bar", target.GetValue(Class1.FooProperty));
  241. source.OnNext("baz");
  242. Assert.Equal("bar", target.GetValue(Class1.FooProperty));
  243. }
  244. [Fact]
  245. public void Second_LocalValue_Binding_Unsubscribes_First()
  246. {
  247. var property = Class1.FooProperty;
  248. var target = new Class1();
  249. var source1 = new Subject<BindingValue<string>>();
  250. var source2 = new Subject<BindingValue<string>>();
  251. target.Bind(property, source1, BindingPriority.LocalValue);
  252. target.Bind(property, source2, BindingPriority.LocalValue);
  253. source1.OnNext("foo");
  254. Assert.Equal("foodefault", target.GetValue(property));
  255. source2.OnNext("bar");
  256. Assert.Equal("bar", target.GetValue(property));
  257. source1.OnNext("baz");
  258. Assert.Equal("bar", target.GetValue(property));
  259. }
  260. [Fact]
  261. public void Completing_Second_LocalValue_Binding_Doesnt_Revert_To_First()
  262. {
  263. var property = Class1.FooProperty;
  264. var target = new Class1();
  265. var source1 = new Subject<BindingValue<string>>();
  266. var source2 = new Subject<BindingValue<string>>();
  267. target.Bind(property, source1, BindingPriority.LocalValue);
  268. target.Bind(property, source2, BindingPriority.LocalValue);
  269. source1.OnNext("foo");
  270. source2.OnNext("bar");
  271. source1.OnNext("baz");
  272. source2.OnCompleted();
  273. Assert.Equal("foodefault", target.GetValue(property));
  274. }
  275. [Fact]
  276. public void Completing_StyleTrigger_Binding_Reverts_To_StyleBinding()
  277. {
  278. var property = Class1.FooProperty;
  279. var target = new Class1();
  280. var source1 = new Subject<BindingValue<string>>();
  281. var source2 = new Subject<BindingValue<string>>();
  282. target.Bind(property, source1, BindingPriority.Style);
  283. target.Bind(property, source2, BindingPriority.StyleTrigger);
  284. source1.OnNext("foo");
  285. source2.OnNext("bar");
  286. source2.OnCompleted();
  287. source1.OnNext("baz");
  288. Assert.Equal("baz", target.GetValue(property));
  289. }
  290. [Fact]
  291. public void Bind_NonGeneric_Sets_Current_Value()
  292. {
  293. Class1 target = new Class1();
  294. Class1 source = new Class1();
  295. source.SetValue(Class1.FooProperty, "initial");
  296. target.Bind((AvaloniaProperty)Class1.FooProperty, source.GetObservable(Class1.FooProperty));
  297. Assert.Equal("initial", target.GetValue(Class1.FooProperty));
  298. }
  299. [Fact]
  300. public void Bind_NonGeneric_Can_Set_Null_On_Reference_Type()
  301. {
  302. var target = new Class1();
  303. var source = new BehaviorSubject<object?>(null);
  304. var property = Class1.FooProperty;
  305. target.Bind(property, source);
  306. Assert.Null(target.GetValue(property));
  307. }
  308. [Fact]
  309. public void LocalValue_Bind_Generic_To_ValueType_Accepts_UnsetValue()
  310. {
  311. var target = new Class1();
  312. var source = new Subject<BindingValue<double>>();
  313. target.Bind(Class1.QuxProperty, source);
  314. source.OnNext(6.7);
  315. source.OnNext(BindingValue<double>.Unset);
  316. Assert.Equal(5.6, target.GetValue(Class1.QuxProperty));
  317. Assert.True(target.IsSet(Class1.QuxProperty));
  318. }
  319. [Fact]
  320. public void LocalValue_Bind_NonGeneric_To_ValueType_Accepts_UnsetValue()
  321. {
  322. var target = new Class1();
  323. var source = new Subject<object>();
  324. target.Bind(Class1.QuxProperty, source);
  325. source.OnNext(6.7);
  326. source.OnNext(AvaloniaProperty.UnsetValue);
  327. Assert.Equal(5.6, target.GetValue(Class1.QuxProperty));
  328. Assert.True(target.IsSet(Class1.QuxProperty));
  329. }
  330. [Fact]
  331. public void Style_Bind_NonGeneric_To_ValueType_Accepts_UnsetValue()
  332. {
  333. var target = new Class1();
  334. var source = new Subject<object>();
  335. target.Bind(Class1.QuxProperty, source, BindingPriority.Style);
  336. source.OnNext(6.7);
  337. source.OnNext(AvaloniaProperty.UnsetValue);
  338. Assert.Equal(5.6, target.GetValue(Class1.QuxProperty));
  339. Assert.True(target.IsSet(Class1.QuxProperty));
  340. }
  341. [Fact]
  342. public void LocalValue_Bind_NonGeneric_To_ValueType_Accepts_DoNothing()
  343. {
  344. var target = new Class1();
  345. var source = new Subject<object>();
  346. target.Bind(Class1.QuxProperty, source);
  347. source.OnNext(6.7);
  348. source.OnNext(BindingOperations.DoNothing);
  349. Assert.Equal(6.7, target.GetValue(Class1.QuxProperty));
  350. }
  351. [Fact]
  352. public void Style_Bind_NonGeneric_To_ValueType_Accepts_DoNothing()
  353. {
  354. var target = new Class1();
  355. var source = new Subject<object>();
  356. target.Bind(Class1.QuxProperty, source, BindingPriority.Style);
  357. source.OnNext(6.7);
  358. source.OnNext(BindingOperations.DoNothing);
  359. Assert.Equal(6.7, target.GetValue(Class1.QuxProperty));
  360. }
  361. [Fact]
  362. public void OneTime_Binding_Ignores_UnsetValue()
  363. {
  364. var target = new Class1();
  365. var source = new Subject<object>();
  366. target.Bind(Class1.QuxProperty, source);
  367. source.OnNext(AvaloniaProperty.UnsetValue);
  368. Assert.Equal(5.6, target.GetValue(Class1.QuxProperty));
  369. source.OnNext(6.7);
  370. Assert.Equal(6.7, target.GetValue(Class1.QuxProperty));
  371. }
  372. [Fact]
  373. public void OneTime_Binding_Ignores_Binding_Errors()
  374. {
  375. var target = new Class1();
  376. var source = new Subject<object>();
  377. target.Bind(Class1.QuxProperty, source);
  378. source.OnNext(new BindingNotification(new Exception(), BindingErrorType.Error));
  379. Assert.Equal(5.6, target.GetValue(Class1.QuxProperty));
  380. source.OnNext(6.7);
  381. Assert.Equal(6.7, target.GetValue(Class1.QuxProperty));
  382. }
  383. [Fact]
  384. public void Bind_Does_Not_Throw_Exception_For_Unregistered_Property()
  385. {
  386. Class1 target = new Class1();
  387. target.Bind(Class2.BarProperty, Observable.Never<BindingValue<string>>().StartWith("foo"));
  388. Assert.Equal("foo", target.GetValue(Class2.BarProperty));
  389. }
  390. [Fact]
  391. public void Bind_Sets_Subsequent_Value()
  392. {
  393. Class1 target = new Class1();
  394. Class1 source = new Class1();
  395. source.SetValue(Class1.FooProperty, "initial");
  396. target.Bind(Class1.FooProperty, source.GetObservable(Class1.FooProperty));
  397. source.SetValue(Class1.FooProperty, "subsequent");
  398. Assert.Equal("subsequent", target.GetValue(Class1.FooProperty));
  399. }
  400. [Fact]
  401. public void Bind_Ignores_Invalid_Value_Type()
  402. {
  403. Class1 target = new Class1();
  404. target.Bind((AvaloniaProperty)Class1.FooProperty, Observable.Return((object)123));
  405. Assert.Equal("foodefault", target.GetValue(Class1.FooProperty));
  406. }
  407. [Fact]
  408. public void Observable_Is_Unsubscribed_When_Subscription_Disposed()
  409. {
  410. var source = new TestSubject<BindingValue<string>>("foo");
  411. var target = new Class1();
  412. var subscription = target.Bind(Class1.FooProperty, source);
  413. Assert.Equal(1, source.SubscriberCount);
  414. subscription.Dispose();
  415. Assert.Equal(0, source.SubscriberCount);
  416. }
  417. [Theory]
  418. [InlineData(BindingPriority.LocalValue)]
  419. [InlineData(BindingPriority.Style)]
  420. [InlineData(BindingPriority.Animation)]
  421. public void Observable_Is_Unsubscribed_When_New_Binding_Of_Same_Priority_Is_Added(BindingPriority priority)
  422. {
  423. var source1 = new TestSubject<BindingValue<string>>("foo");
  424. var source2 = new TestSubject<BindingValue<string>>("bar");
  425. var target = new Class1();
  426. target.Bind(Class1.FooProperty, source1, priority);
  427. Assert.Equal(1, source1.SubscriberCount);
  428. target.Bind(Class1.FooProperty, source2, priority);
  429. Assert.Equal(1, source2.SubscriberCount);
  430. Assert.Equal(0, source1.SubscriberCount);
  431. }
  432. [Theory]
  433. [InlineData(BindingPriority.Style)]
  434. public void Observable_Is_Unsubscribed_When_New_Binding_Of_Higher_Priority_Is_Added(BindingPriority priority)
  435. {
  436. var source1 = new TestSubject<BindingValue<string>>("foo");
  437. var source2 = new TestSubject<BindingValue<string>>("bar");
  438. var target = new Class1();
  439. target.Bind(Class1.FooProperty, source1, priority);
  440. Assert.Equal(1, source1.SubscriberCount);
  441. target.Bind(Class1.FooProperty, source2, priority - 1);
  442. Assert.Equal(1, source2.SubscriberCount);
  443. Assert.Equal(0, source1.SubscriberCount);
  444. }
  445. [Theory]
  446. [InlineData(BindingPriority.Style)]
  447. [InlineData(BindingPriority.Animation)]
  448. public void Observable_Is_Unsubscribed_When_New_Value_Of_Same_Priority_Is_Added(BindingPriority priority)
  449. {
  450. var source = new TestSubject<BindingValue<string>>("foo");
  451. var target = new Class1();
  452. target.Bind(Class1.FooProperty, source, priority);
  453. Assert.Equal(1, source.SubscriberCount);
  454. target.SetValue(Class1.FooProperty, "foo", priority);
  455. Assert.Equal(0, source.SubscriberCount);
  456. }
  457. [Theory]
  458. [InlineData(BindingPriority.Style)]
  459. public void Observable_Is_Unsubscribed_When_New_Value_Of_Higher_Priority_Is_Added(BindingPriority priority)
  460. {
  461. var source = new TestSubject<BindingValue<string>>("foo");
  462. var target = new Class1();
  463. target.Bind(Class1.FooProperty, source, priority);
  464. Assert.Equal(1, source.SubscriberCount);
  465. target.SetValue(Class1.FooProperty, "foo", priority - 1);
  466. Assert.Equal(0, source.SubscriberCount);
  467. }
  468. [Theory]
  469. [InlineData(BindingPriority.LocalValue)]
  470. [InlineData(BindingPriority.Style)]
  471. public void Observable_Is_Not_Unsubscribed_When_Animation_Value_Is_Set(BindingPriority priority)
  472. {
  473. var source = new TestSubject<BindingValue<string>>("foo");
  474. var target = new Class1();
  475. target.Bind(Class1.FooProperty, source, priority);
  476. Assert.Equal(1, source.SubscriberCount);
  477. target.SetValue(Class1.FooProperty, "bar", BindingPriority.Animation);
  478. Assert.Equal(1, source.SubscriberCount);
  479. }
  480. [Theory]
  481. [InlineData(BindingPriority.LocalValue)]
  482. [InlineData(BindingPriority.Style)]
  483. public void Observable_Is_Not_Unsubscribed_When_Animation_Binding_Is_Added(BindingPriority priority)
  484. {
  485. var source1 = new TestSubject<BindingValue<string>>("foo");
  486. var source2 = new TestSubject<BindingValue<string>>("bar");
  487. var target = new Class1();
  488. target.Bind(Class1.FooProperty, source1, priority);
  489. Assert.Equal(1, source1.SubscriberCount);
  490. target.Bind(Class1.FooProperty, source2, BindingPriority.Animation);
  491. Assert.Equal(1, source1.SubscriberCount);
  492. Assert.Equal(1, source2.SubscriberCount);
  493. }
  494. [Fact]
  495. public void LocalValue_Binding_Is_Not_Unsubscribed_When_LocalValue_Is_Set()
  496. {
  497. var source = new TestSubject<BindingValue<string>>("foo");
  498. var target = new Class1();
  499. target.Bind(Class1.FooProperty, source);
  500. Assert.Equal(1, source.SubscriberCount);
  501. target.SetValue(Class1.FooProperty, "foo");
  502. Assert.Equal(1, source.SubscriberCount);
  503. }
  504. [Fact]
  505. public void Two_Way_Separate_Binding_Works()
  506. {
  507. Class1 obj1 = new Class1();
  508. Class1 obj2 = new Class1();
  509. obj1.SetValue(Class1.FooProperty, "initial1");
  510. obj2.SetValue(Class1.FooProperty, "initial2");
  511. obj1.Bind(Class1.FooProperty, obj2.GetObservable(Class1.FooProperty));
  512. obj2.Bind(Class1.FooProperty, obj1.GetObservable(Class1.FooProperty));
  513. Assert.Equal("initial2", obj1.GetValue(Class1.FooProperty));
  514. Assert.Equal("initial2", obj2.GetValue(Class1.FooProperty));
  515. obj1.SetValue(Class1.FooProperty, "first");
  516. Assert.Equal("first", obj1.GetValue(Class1.FooProperty));
  517. Assert.Equal("first", obj2.GetValue(Class1.FooProperty));
  518. obj2.SetValue(Class1.FooProperty, "second");
  519. Assert.Equal("second", obj1.GetValue(Class1.FooProperty));
  520. Assert.Equal("second", obj2.GetValue(Class1.FooProperty));
  521. obj1.SetValue(Class1.FooProperty, "third");
  522. Assert.Equal("third", obj1.GetValue(Class1.FooProperty));
  523. Assert.Equal("third", obj2.GetValue(Class1.FooProperty));
  524. }
  525. [Fact]
  526. public void Two_Way_Binding_With_Priority_Works()
  527. {
  528. Class1 obj1 = new Class1();
  529. Class1 obj2 = new Class1();
  530. obj1.SetValue(Class1.FooProperty, "initial1", BindingPriority.Style);
  531. obj2.SetValue(Class1.FooProperty, "initial2", BindingPriority.Style);
  532. obj1.Bind(Class1.FooProperty, obj2.GetObservable(Class1.FooProperty), BindingPriority.Style);
  533. obj2.Bind(Class1.FooProperty, obj1.GetObservable(Class1.FooProperty), BindingPriority.Style);
  534. Assert.Equal("initial2", obj1.GetValue(Class1.FooProperty));
  535. Assert.Equal("initial2", obj2.GetValue(Class1.FooProperty));
  536. obj1.SetValue(Class1.FooProperty, "first", BindingPriority.Style);
  537. Assert.Equal("first", obj1.GetValue(Class1.FooProperty));
  538. Assert.Equal("first", obj2.GetValue(Class1.FooProperty));
  539. obj2.SetValue(Class1.FooProperty, "second", BindingPriority.Style);
  540. Assert.Equal("first", obj1.GetValue(Class1.FooProperty));
  541. Assert.Equal("second", obj2.GetValue(Class1.FooProperty));
  542. obj1.SetValue(Class1.FooProperty, "third", BindingPriority.Style);
  543. Assert.Equal("third", obj1.GetValue(Class1.FooProperty));
  544. Assert.Equal("second", obj2.GetValue(Class1.FooProperty));
  545. }
  546. [Fact]
  547. public void Local_Binding_Overwrites_Local_Value()
  548. {
  549. var target = new Class1();
  550. var binding = new Subject<BindingValue<string>>();
  551. target.Bind(Class1.FooProperty, binding);
  552. binding.OnNext("first");
  553. Assert.Equal("first", target.GetValue(Class1.FooProperty));
  554. target.SetValue(Class1.FooProperty, "second");
  555. Assert.Equal("second", target.GetValue(Class1.FooProperty));
  556. binding.OnNext("third");
  557. Assert.Equal("third", target.GetValue(Class1.FooProperty));
  558. }
  559. [Fact]
  560. public void StyleBinding_Overrides_Default_Value()
  561. {
  562. Class1 target = new Class1();
  563. target.Bind(Class1.FooProperty, Single("stylevalue"), BindingPriority.Style);
  564. Assert.Equal("stylevalue", target.GetValue(Class1.FooProperty));
  565. }
  566. [Fact]
  567. public void this_Operator_Returns_Value_Property()
  568. {
  569. Class1 target = new Class1();
  570. target.SetValue(Class1.FooProperty, "newvalue");
  571. Assert.Equal("newvalue", target[Class1.FooProperty]);
  572. }
  573. [Fact]
  574. public void this_Operator_Sets_Value_Property()
  575. {
  576. Class1 target = new Class1();
  577. target[Class1.FooProperty] = "newvalue";
  578. Assert.Equal("newvalue", target.GetValue(Class1.FooProperty));
  579. }
  580. [Fact]
  581. public void this_Operator_Doesnt_Accept_Observable()
  582. {
  583. Class1 target = new Class1();
  584. Assert.Throws<ArgumentException>(() =>
  585. {
  586. target[Class1.FooProperty] = Observable.Return("newvalue");
  587. });
  588. }
  589. [Fact]
  590. public void this_Operator_Binds_One_Way()
  591. {
  592. Class1 target1 = new Class1();
  593. Class2 target2 = new Class2();
  594. IndexerDescriptor binding = Class2.BarProperty.Bind().WithMode(BindingMode.OneWay);
  595. target1.SetValue(Class1.FooProperty, "first");
  596. target2[binding] = target1[!Class1.FooProperty];
  597. target1.SetValue(Class1.FooProperty, "second");
  598. Assert.Equal("second", target2.GetValue(Class2.BarProperty));
  599. }
  600. [Fact]
  601. public void this_Operator_Binds_Two_Way()
  602. {
  603. Class1 target1 = new Class1();
  604. Class1 target2 = new Class1();
  605. target1.SetValue(Class1.FooProperty, "first");
  606. target2[!Class1.FooProperty] = target1[!!Class1.FooProperty];
  607. Assert.Equal("first", target2.GetValue(Class1.FooProperty));
  608. target1.SetValue(Class1.FooProperty, "second");
  609. Assert.Equal("second", target2.GetValue(Class1.FooProperty));
  610. target2.SetValue(Class1.FooProperty, "third");
  611. Assert.Equal("third", target1.GetValue(Class1.FooProperty));
  612. }
  613. [Fact]
  614. public void this_Operator_Binds_One_Time()
  615. {
  616. Class1 target1 = new Class1();
  617. Class1 target2 = new Class1();
  618. target1.SetValue(Class1.FooProperty, "first");
  619. target2[!Class1.FooProperty] = target1[Class1.FooProperty.Bind().WithMode(BindingMode.OneTime)];
  620. target1.SetValue(Class1.FooProperty, "second");
  621. Assert.Equal("first", target2.GetValue(Class1.FooProperty));
  622. }
  623. [Fact]
  624. public void Binding_Error_Reverts_To_Default_Value()
  625. {
  626. var target = new Class1();
  627. var source = new Subject<BindingValue<string>>();
  628. target.Bind(Class1.FooProperty, source);
  629. source.OnNext("initial");
  630. source.OnNext(BindingValue<string>.BindingError(new InvalidOperationException("Foo")));
  631. Assert.Equal("foodefault", target.GetValue(Class1.FooProperty));
  632. }
  633. [Fact]
  634. public void Binding_Error_With_FallbackValue_Causes_Target_Update()
  635. {
  636. var target = new Class1();
  637. var source = new Subject<BindingValue<string>>();
  638. target.Bind(Class1.FooProperty, source);
  639. source.OnNext("initial");
  640. source.OnNext(BindingValue<string>.BindingError(new InvalidOperationException("Foo"), "bar"));
  641. Assert.Equal("bar", target.GetValue(Class1.FooProperty));
  642. }
  643. [Fact]
  644. public void DataValidationError_Does_Not_Cause_Target_Update()
  645. {
  646. var target = new Class1();
  647. var source = new Subject<BindingValue<string>>();
  648. target.Bind(Class1.FooProperty, source);
  649. source.OnNext("initial");
  650. source.OnNext(BindingValue<string>.DataValidationError(new InvalidOperationException("Foo")));
  651. Assert.Equal("initial", target.GetValue(Class1.FooProperty));
  652. }
  653. [Fact]
  654. public void DataValidationError_With_FallbackValue_Causes_Target_Update()
  655. {
  656. var target = new Class1();
  657. var source = new Subject<BindingValue<string>>();
  658. target.Bind(Class1.FooProperty, source);
  659. source.OnNext("initial");
  660. source.OnNext(BindingValue<string>.DataValidationError(new InvalidOperationException("Foo"), "bar"));
  661. Assert.Equal("bar", target.GetValue(Class1.FooProperty));
  662. }
  663. [Theory]
  664. [InlineData(BindingPriority.LocalValue)]
  665. [InlineData(BindingPriority.Style)]
  666. public void Typed_Bind_Executes_On_UIThread(BindingPriority priority)
  667. {
  668. AsyncContext.Run(async () =>
  669. {
  670. var target = new Class1();
  671. var source = new Subject<string>();
  672. var currentThreadId = Thread.CurrentThread.ManagedThreadId;
  673. var raised = 0;
  674. var dispatcherMock = new Mock<IDispatcherImpl>();
  675. dispatcherMock.SetupGet(mock => mock.CurrentThreadIsLoopThread)
  676. .Returns(() => Thread.CurrentThread.ManagedThreadId == currentThreadId);
  677. var services = new TestServices(
  678. dispatcherImpl: dispatcherMock.Object);
  679. target.PropertyChanged += (s, e) =>
  680. {
  681. Assert.Equal(currentThreadId, Thread.CurrentThread.ManagedThreadId);
  682. ++raised;
  683. };
  684. using (UnitTestApplication.Start(services))
  685. {
  686. target.Bind(Class1.FooProperty, source, priority);
  687. await Task.Run(() => source.OnNext("foobar"));
  688. Dispatcher.UIThread.RunJobs();
  689. Assert.Equal("foobar", target.GetValue(Class1.FooProperty));
  690. Assert.Equal(1, raised);
  691. }
  692. });
  693. }
  694. [Theory]
  695. [InlineData(BindingPriority.LocalValue)]
  696. [InlineData(BindingPriority.Style)]
  697. public void Untyped_Bind_Executes_On_UIThread(BindingPriority priority)
  698. {
  699. AsyncContext.Run(async () =>
  700. {
  701. var target = new Class1();
  702. var source = new Subject<object>();
  703. var currentThreadId = Thread.CurrentThread.ManagedThreadId;
  704. var raised = 0;
  705. var dispatcherMock = new Mock<IDispatcherImpl>();
  706. dispatcherMock.SetupGet(mock => mock.CurrentThreadIsLoopThread)
  707. .Returns(() => Thread.CurrentThread.ManagedThreadId == currentThreadId);
  708. var services = new TestServices(
  709. dispatcherImpl: dispatcherMock.Object);
  710. target.PropertyChanged += (s, e) =>
  711. {
  712. Assert.Equal(currentThreadId, Thread.CurrentThread.ManagedThreadId);
  713. ++raised;
  714. };
  715. using (UnitTestApplication.Start(services))
  716. {
  717. target.Bind(Class1.FooProperty, source, priority);
  718. await Task.Run(() => source.OnNext("foobar"));
  719. Dispatcher.UIThread.RunJobs();
  720. Assert.Equal("foobar", target.GetValue(Class1.FooProperty));
  721. Assert.Equal(1, raised);
  722. }
  723. });
  724. }
  725. [Theory]
  726. [InlineData(BindingPriority.LocalValue)]
  727. [InlineData(BindingPriority.Style)]
  728. public void BindingValue_Bind_Executes_On_UIThread(BindingPriority priority)
  729. {
  730. AsyncContext.Run(async () =>
  731. {
  732. var target = new Class1();
  733. var source = new Subject<BindingValue<string>>();
  734. var currentThreadId = Thread.CurrentThread.ManagedThreadId;
  735. var raised = 0;
  736. var threadingInterfaceMock = new Mock<IDispatcherImpl>();
  737. threadingInterfaceMock.SetupGet(mock => mock.CurrentThreadIsLoopThread)
  738. .Returns(() => Thread.CurrentThread.ManagedThreadId == currentThreadId);
  739. var services = new TestServices(
  740. dispatcherImpl: threadingInterfaceMock.Object);
  741. target.PropertyChanged += (s, e) =>
  742. {
  743. Assert.Equal(currentThreadId, Thread.CurrentThread.ManagedThreadId);
  744. ++raised;
  745. };
  746. using (UnitTestApplication.Start(services))
  747. {
  748. target.Bind(Class1.FooProperty, source, priority);
  749. await Task.Run(() => source.OnNext("foobar"));
  750. Dispatcher.UIThread.RunJobs();
  751. Assert.Equal("foobar", target.GetValue(Class1.FooProperty));
  752. Assert.Equal(1, raised);
  753. }
  754. });
  755. }
  756. [Fact]
  757. public async Task Bind_With_Scheduler_Executes_On_UI_Thread()
  758. {
  759. var target = new Class1();
  760. var source = new Subject<double>();
  761. var currentThreadId = Thread.CurrentThread.ManagedThreadId;
  762. var threadingInterfaceMock = new Mock<IDispatcherImpl>();
  763. threadingInterfaceMock.SetupGet(mock => mock.CurrentThreadIsLoopThread)
  764. .Returns(() => Thread.CurrentThread.ManagedThreadId == currentThreadId);
  765. var services = new TestServices(
  766. dispatcherImpl: threadingInterfaceMock.Object);
  767. using (UnitTestApplication.Start(services))
  768. {
  769. target.Bind(Class1.QuxProperty, source);
  770. await Task.Run(() => source.OnNext(6.7));
  771. }
  772. }
  773. [Fact]
  774. public void SetValue_Should_Not_Cause_StackOverflow_And_Have_Correct_Values()
  775. {
  776. var viewModel = new TestStackOverflowViewModel()
  777. {
  778. Value = 50
  779. };
  780. var target = new Class1();
  781. target.Bind(Class1.DoubleValueProperty,
  782. new Binding("Value") { Mode = BindingMode.TwoWay, Source = viewModel });
  783. var child = new Class1();
  784. child[!!Class1.DoubleValueProperty] = target[!!Class1.DoubleValueProperty];
  785. Assert.Equal(1, viewModel.SetterInvokedCount);
  786. // Issues #855 and #824 were causing a StackOverflowException at this point.
  787. target.DoubleValue = 51.001;
  788. Assert.Equal(2, viewModel.SetterInvokedCount);
  789. double expected = 51;
  790. Assert.Equal(expected, viewModel.Value);
  791. Assert.Equal(expected, target.DoubleValue);
  792. Assert.Equal(expected, child.DoubleValue);
  793. }
  794. [Fact]
  795. public void IsAnimating_On_Property_With_No_Value_Returns_False()
  796. {
  797. var target = new Class1();
  798. Assert.False(target.IsAnimating(Class1.FooProperty));
  799. }
  800. [Fact]
  801. public void IsAnimating_On_Property_With_Animation_Value_Returns_True()
  802. {
  803. var target = new Class1();
  804. var source = new BehaviorSubject<BindingValue<string>>("foo");
  805. target.Bind(Class1.FooProperty, source, BindingPriority.Animation);
  806. Assert.True(target.IsAnimating(Class1.FooProperty));
  807. }
  808. [Fact]
  809. public void IsAnimating_On_Property_With_Non_Animation_Binding_Returns_False()
  810. {
  811. var target = new Class1();
  812. var source = new Subject<string>();
  813. target.Bind(Class1.FooProperty, source, BindingPriority.LocalValue);
  814. Assert.False(target.IsAnimating(Class1.FooProperty));
  815. }
  816. [Fact]
  817. public void IsAnimating_On_Property_With_Animation_Binding_Returns_True()
  818. {
  819. var target = new Class1();
  820. var source = new BehaviorSubject<string>("foo");
  821. target.Bind(Class1.FooProperty, source, BindingPriority.Animation);
  822. Assert.True(target.IsAnimating(Class1.FooProperty));
  823. }
  824. [Fact]
  825. public void IsAnimating_On_Property_With_Local_Value_And_Animation_Binding_Returns_True()
  826. {
  827. var target = new Class1();
  828. var source = new BehaviorSubject<string>("foo");
  829. target.SetValue(Class1.FooProperty, "bar");
  830. target.Bind(Class1.FooProperty, source, BindingPriority.Animation);
  831. Assert.True(target.IsAnimating(Class1.FooProperty));
  832. }
  833. [Fact]
  834. public void IsAnimating_Returns_True_When_Animated_Value_Is_Same_As_Local_Value()
  835. {
  836. var target = new Class1();
  837. var source = new BehaviorSubject<string>("foo");
  838. target.SetValue(Class1.FooProperty, "foo");
  839. target.Bind(Class1.FooProperty, source, BindingPriority.Animation);
  840. Assert.True(target.IsAnimating(Class1.FooProperty));
  841. }
  842. [Fact]
  843. public void TwoWay_Binding_Should_Update_Source()
  844. {
  845. var target = new Class1();
  846. var source = new TestTwoWayBindingViewModel();
  847. target.Bind(Class1.DoubleValueProperty, new Binding(nameof(source.Value), BindingMode.TwoWay) { Source = source });
  848. target.DoubleValue = 123.4;
  849. Assert.True(source.SetterCalled);
  850. Assert.Equal(source.Value, 123.4);
  851. }
  852. [Fact]
  853. public void TwoWay_Binding_Should_Not_Call_Setter_On_Creation()
  854. {
  855. var target = new Class1();
  856. var source = new TestTwoWayBindingViewModel();
  857. target.Bind(Class1.DoubleValueProperty, new Binding(nameof(source.Value), BindingMode.TwoWay) { Source = source });
  858. Assert.False(source.SetterCalled);
  859. }
  860. [Fact]
  861. public void TwoWay_Binding_Should_Not_Call_Setter_On_Creation_Indexer()
  862. {
  863. var target = new Class1();
  864. var source = new TestTwoWayBindingViewModel();
  865. target.Bind(Class1.DoubleValueProperty, new Binding("[0]", BindingMode.TwoWay) { Source = source });
  866. Assert.False(source.SetterCalled);
  867. }
  868. [Fact]
  869. public void TwoWay_Binding_Should_Not_Fail_With_Null_DataContext()
  870. {
  871. var target = new TextBlock();
  872. target.DataContext = null;
  873. target.Bind(TextBlock.TextProperty, new Binding("Missing", BindingMode.TwoWay));
  874. }
  875. [Fact]
  876. public void TwoWay_Binding_Should_Not_Fail_With_Null_DataContext_Indexer()
  877. {
  878. var target = new TextBlock();
  879. target.DataContext = null;
  880. target.Bind(TextBlock.TextProperty, new Binding("[0]", BindingMode.TwoWay));
  881. }
  882. [Theory(Skip = "Will need changes to binding internals in order to pass")]
  883. [InlineData(BindingPriority.LocalValue)]
  884. [InlineData(BindingPriority.StyleTrigger)]
  885. [InlineData(BindingPriority.Style)]
  886. public void TwoWay_Binding_Should_Not_Update_Source_When_Higher_Priority_Value_Set(BindingPriority priority)
  887. {
  888. var target = new Class1();
  889. var source = new TestTwoWayBindingViewModel();
  890. var binding = new Binding(nameof(source.Value), BindingMode.TwoWay) { Source = source };
  891. target.Bind(Class1.DoubleValueProperty, binding, priority);
  892. target.SetValue(Class1.DoubleValueProperty, 123.4, priority - 1);
  893. // Setter should not be called because the TwoWay binding with LocalValue priority
  894. // should be overridden by the animated value and the binding made inactive.
  895. Assert.False(source.SetterCalled);
  896. }
  897. [Theory(Skip = "Will need changes to binding internals in order to pass")]
  898. [InlineData(BindingPriority.LocalValue)]
  899. [InlineData(BindingPriority.StyleTrigger)]
  900. [InlineData(BindingPriority.Style)]
  901. public void TwoWay_Binding_Should_Not_Update_Source_When_Higher_Priority_Binding_Added(BindingPriority priority)
  902. {
  903. var target = new Class1();
  904. var source = new TestTwoWayBindingViewModel();
  905. var binding1 = new Binding(nameof(source.Value), BindingMode.TwoWay) { Source = source };
  906. var binding2 = new BehaviorSubject<double>(123.4);
  907. target.Bind(Class1.DoubleValueProperty, binding1, priority);
  908. target.Bind(Class1.DoubleValueProperty, binding2, priority - 1);
  909. // Setter should not be called because the TwoWay binding with LocalValue priority
  910. // should be overridden by the animated binding and the binding made inactive.
  911. Assert.False(source.SetterCalled);
  912. }
  913. [Fact(Skip = "Will need changes to binding internals in order to pass")]
  914. public void TwoWay_Style_Binding_Should_Not_Update_Source_When_StyleTrigger_Value_Set()
  915. {
  916. var target = new Class1();
  917. var source = new TestTwoWayBindingViewModel();
  918. target.Bind(Class1.DoubleValueProperty, new Binding(nameof(source.Value), BindingMode.TwoWay) { Source = source });
  919. target.SetValue(Class1.DoubleValueProperty, 123.4, BindingPriority.Animation);
  920. // Setter should not be called because the TwoWay binding with Style priority
  921. // should be overridden by the animated value and the binding made inactive.
  922. Assert.False(source.SetterCalled);
  923. }
  924. [Fact(Skip = "Will need changes to binding internals in order to pass")]
  925. public void TwoWay_Style_Binding_Should_Not_Update_Source_When_Animated_Binding_Added()
  926. {
  927. var target = new Class1();
  928. var source1 = new TestTwoWayBindingViewModel();
  929. var source2 = new BehaviorSubject<double>(123.4);
  930. target.Bind(Class1.DoubleValueProperty, new Binding(nameof(source1.Value), BindingMode.TwoWay) { Source = source1 });
  931. target.Bind(Class1.DoubleValueProperty, source2, BindingPriority.Animation);
  932. // Setter should not be called because the TwoWay binding with Style priority
  933. // should be overridden by the animated binding and the binding made inactive.
  934. Assert.False(source1.SetterCalled);
  935. }
  936. [Fact]
  937. public void Disposing_Completed_Binding_Does_Not_Throw()
  938. {
  939. var target = new Class1();
  940. var source = new Subject<BindingValue<string>>();
  941. var subscription = target.Bind(Class1.FooProperty, source);
  942. source.OnCompleted();
  943. subscription.Dispose();
  944. }
  945. [Theory]
  946. [InlineData(BindingPriority.LocalValue)]
  947. [InlineData(BindingPriority.Style)]
  948. public void Binding_Producing_UnsetValue_Does_Not_Cause_Unsubscribe(BindingPriority priority)
  949. {
  950. var target = new Class1();
  951. var source = new Subject<BindingValue<string>>();
  952. target.Bind(Class1.FooProperty, source, priority);
  953. source.OnNext("foo");
  954. Assert.Equal("foo", target.GetValue(Class1.FooProperty));
  955. source.OnNext(BindingValue<string>.Unset);
  956. Assert.Equal("foodefault", target.GetValue(Class1.FooProperty));
  957. source.OnNext("bar");
  958. Assert.Equal("bar", target.GetValue(Class1.FooProperty));
  959. }
  960. [Fact]
  961. public void Produces_Correct_Values_And_Base_Values_With_Multiple_Animation_Bindings()
  962. {
  963. var target = new Class1();
  964. var source1 = new BehaviorSubject<BindingValue<double>>(12.2);
  965. var source2 = new BehaviorSubject<BindingValue<double>>(13.3);
  966. target.SetValue(Class1.QuxProperty, 11.1);
  967. target.Bind(Class1.QuxProperty, source1, BindingPriority.Animation);
  968. Assert.Equal(12.2, target.GetValue(Class1.QuxProperty));
  969. Assert.Equal(11.1, target.GetBaseValue(Class1.QuxProperty));
  970. target.Bind(Class1.QuxProperty, source2, BindingPriority.Animation);
  971. Assert.Equal(13.3, target.GetValue(Class1.QuxProperty));
  972. Assert.Equal(11.1, target.GetBaseValue(Class1.QuxProperty));
  973. source2.OnCompleted();
  974. Assert.Equal(12.2, target.GetValue(Class1.QuxProperty));
  975. Assert.Equal(11.1, target.GetBaseValue(Class1.QuxProperty));
  976. source1.OnCompleted();
  977. Assert.Equal(11.1, target.GetValue(Class1.QuxProperty));
  978. Assert.Equal(11.1, target.GetBaseValue(Class1.QuxProperty));
  979. }
  980. /// <summary>
  981. /// Returns an observable that returns a single value but does not complete.
  982. /// </summary>
  983. /// <typeparam name="T">The type of the observable.</typeparam>
  984. /// <param name="value">The value.</param>
  985. /// <returns>The observable.</returns>
  986. private IObservable<BindingValue<T>> Single<T>(T value)
  987. {
  988. return Observable.Never<BindingValue<T>>().StartWith(value);
  989. }
  990. private class Class1 : AvaloniaObject
  991. {
  992. public static readonly StyledProperty<string> FooProperty =
  993. AvaloniaProperty.Register<Class1, string>("Foo", "foodefault");
  994. public static readonly StyledProperty<double> QuxProperty =
  995. AvaloniaProperty.Register<Class1, double>("Qux", 5.6);
  996. public static readonly StyledProperty<double> DoubleValueProperty =
  997. AvaloniaProperty.Register<Class1, double>(nameof(DoubleValue));
  998. public double DoubleValue
  999. {
  1000. get { return GetValue(DoubleValueProperty); }
  1001. set { SetValue(DoubleValueProperty, value); }
  1002. }
  1003. }
  1004. private class Class2 : Class1
  1005. {
  1006. public static readonly StyledProperty<string> BarProperty =
  1007. AvaloniaProperty.Register<Class2, string>("Bar", "bardefault");
  1008. }
  1009. private class TestOneTimeBinding : IBinding
  1010. {
  1011. private IObservable<object> _source;
  1012. public TestOneTimeBinding(IObservable<object> source)
  1013. {
  1014. _source = source;
  1015. }
  1016. public InstancedBinding Initiate(
  1017. AvaloniaObject target,
  1018. AvaloniaProperty? targetProperty,
  1019. object? anchor = null,
  1020. bool enableDataValidation = false)
  1021. {
  1022. return InstancedBinding.OneTime(_source);
  1023. }
  1024. }
  1025. private class TestStackOverflowViewModel : INotifyPropertyChanged
  1026. {
  1027. public int SetterInvokedCount { get; private set; }
  1028. public const int MaxInvokedCount = 1000;
  1029. private double _value;
  1030. public event PropertyChangedEventHandler? PropertyChanged;
  1031. public double Value
  1032. {
  1033. get { return _value; }
  1034. set
  1035. {
  1036. if (_value != value)
  1037. {
  1038. SetterInvokedCount++;
  1039. if (SetterInvokedCount < MaxInvokedCount)
  1040. {
  1041. _value = (int)value;
  1042. if (_value > 75)
  1043. _value = 75;
  1044. if (_value < 25)
  1045. _value = 25;
  1046. }
  1047. else
  1048. {
  1049. _value = value;
  1050. }
  1051. PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Value)));
  1052. }
  1053. }
  1054. }
  1055. }
  1056. private class TestTwoWayBindingViewModel
  1057. {
  1058. private double _value;
  1059. public double Value
  1060. {
  1061. get => _value;
  1062. set
  1063. {
  1064. _value = value;
  1065. SetterCalled = true;
  1066. }
  1067. }
  1068. public double this[int index]
  1069. {
  1070. get => _value;
  1071. set
  1072. {
  1073. _value = value;
  1074. SetterCalled = true;
  1075. }
  1076. }
  1077. public bool SetterCalled { get; private set; }
  1078. }
  1079. }
  1080. }