AvaloniaObjectTests_Binding.cs 53 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482
  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, new TestOneTimeBinding(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, new TestOneTimeBinding(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. [Fact]
  664. public void Bind_Logs_Binding_Error()
  665. {
  666. var target = new Class1();
  667. var source = new Subject<BindingValue<double>>();
  668. var called = false;
  669. var expectedMessageTemplate = "Error in binding to {Target}.{Property}: {Message}";
  670. LogCallback checkLogMessage = (level, area, src, mt, pv) =>
  671. {
  672. if (level == LogEventLevel.Warning &&
  673. area == LogArea.Binding &&
  674. mt == expectedMessageTemplate)
  675. {
  676. called = true;
  677. }
  678. };
  679. using (TestLogSink.Start(checkLogMessage))
  680. {
  681. target.Bind(Class1.QuxProperty, source);
  682. source.OnNext(6.7);
  683. source.OnNext(BindingValue<double>.BindingError(new InvalidOperationException("Foo")));
  684. Assert.Equal(5.6, target.GetValue(Class1.QuxProperty));
  685. Assert.True(called);
  686. }
  687. }
  688. [Fact]
  689. public void Untyped_LocalValue_Binding_Logs_Invalid_Value_Type()
  690. {
  691. var target = new Class1();
  692. var source = new Subject<object?>();
  693. var called = false;
  694. var expectedMessageTemplate = "Error in binding to {Target}.{Property}: {Message}";
  695. var message = "Unable to convert object 'foo' of type 'System.String' to type 'System.Double'.";
  696. LogCallback checkLogMessage = (level, area, src, mt, pv) =>
  697. {
  698. if (level == LogEventLevel.Warning &&
  699. area == LogArea.Binding &&
  700. mt == expectedMessageTemplate &&
  701. src == target &&
  702. pv[0].GetType() == typeof(Class1) &&
  703. (AvaloniaProperty)pv[1] == Class1.QuxProperty &&
  704. (string)pv[2] == message)
  705. {
  706. called = true;
  707. }
  708. };
  709. using (TestLogSink.Start(checkLogMessage))
  710. {
  711. target.Bind(Class1.QuxProperty, source);
  712. source.OnNext(1.2);
  713. source.OnNext("foo");
  714. Assert.Equal(5.6, target.GetValue(Class1.QuxProperty));
  715. Assert.True(called);
  716. }
  717. }
  718. [Fact]
  719. public void Untyped_Style_Binding_Logs_Invalid_Value_Type()
  720. {
  721. var target = new Class1();
  722. var source = new Subject<object?>();
  723. var called = false;
  724. var expectedMessageTemplate = "Error in binding to {Target}.{Property}: {Message}";
  725. var expectedMessage = "Unable to convert object 'foo' of type 'System.String' to type 'System.Double'.";
  726. LogCallback checkLogMessage = (level, area, src, mt, pv) =>
  727. {
  728. if (level == LogEventLevel.Warning &&
  729. area == LogArea.Binding &&
  730. mt == expectedMessageTemplate &&
  731. src == target &&
  732. pv[0].GetType() == typeof(Class1) &&
  733. (AvaloniaProperty)pv[1] == Class1.QuxProperty &&
  734. (string)pv[2] == expectedMessage)
  735. {
  736. called = true;
  737. }
  738. };
  739. using (TestLogSink.Start(checkLogMessage))
  740. {
  741. target.Bind(Class1.QuxProperty, source, BindingPriority.Style);
  742. source.OnNext(1.2);
  743. source.OnNext("foo");
  744. Assert.Equal(5.6, target.GetValue(Class1.QuxProperty));
  745. Assert.True(called);
  746. }
  747. }
  748. [Theory]
  749. [InlineData(BindingPriority.LocalValue)]
  750. [InlineData(BindingPriority.Style)]
  751. public void Typed_Bind_Executes_On_UIThread(BindingPriority priority)
  752. {
  753. AsyncContext.Run(async () =>
  754. {
  755. var target = new Class1();
  756. var source = new Subject<string>();
  757. var currentThreadId = Thread.CurrentThread.ManagedThreadId;
  758. var raised = 0;
  759. var threadingInterfaceMock = new Mock<IPlatformThreadingInterface>();
  760. threadingInterfaceMock.SetupGet(mock => mock.CurrentThreadIsLoopThread)
  761. .Returns(() => Thread.CurrentThread.ManagedThreadId == currentThreadId);
  762. var services = new TestServices(
  763. threadingInterface: threadingInterfaceMock.Object);
  764. target.PropertyChanged += (s, e) =>
  765. {
  766. Assert.Equal(currentThreadId, Thread.CurrentThread.ManagedThreadId);
  767. ++raised;
  768. };
  769. using (UnitTestApplication.Start(services))
  770. {
  771. target.Bind(Class1.FooProperty, source, priority);
  772. await Task.Run(() => source.OnNext("foobar"));
  773. Dispatcher.UIThread.RunJobs();
  774. Assert.Equal("foobar", target.GetValue(Class1.FooProperty));
  775. Assert.Equal(1, raised);
  776. }
  777. });
  778. }
  779. [Theory]
  780. [InlineData(BindingPriority.LocalValue)]
  781. [InlineData(BindingPriority.Style)]
  782. public void Untyped_Bind_Executes_On_UIThread(BindingPriority priority)
  783. {
  784. AsyncContext.Run(async () =>
  785. {
  786. var target = new Class1();
  787. var source = new Subject<object>();
  788. var currentThreadId = Thread.CurrentThread.ManagedThreadId;
  789. var raised = 0;
  790. var threadingInterfaceMock = new Mock<IPlatformThreadingInterface>();
  791. threadingInterfaceMock.SetupGet(mock => mock.CurrentThreadIsLoopThread)
  792. .Returns(() => Thread.CurrentThread.ManagedThreadId == currentThreadId);
  793. var services = new TestServices(
  794. threadingInterface: threadingInterfaceMock.Object);
  795. target.PropertyChanged += (s, e) =>
  796. {
  797. Assert.Equal(currentThreadId, Thread.CurrentThread.ManagedThreadId);
  798. ++raised;
  799. };
  800. using (UnitTestApplication.Start(services))
  801. {
  802. target.Bind(Class1.FooProperty, source, priority);
  803. await Task.Run(() => source.OnNext("foobar"));
  804. Dispatcher.UIThread.RunJobs();
  805. Assert.Equal("foobar", target.GetValue(Class1.FooProperty));
  806. Assert.Equal(1, raised);
  807. }
  808. });
  809. }
  810. [Theory]
  811. [InlineData(BindingPriority.LocalValue)]
  812. [InlineData(BindingPriority.Style)]
  813. public void BindingValue_Bind_Executes_On_UIThread(BindingPriority priority)
  814. {
  815. AsyncContext.Run(async () =>
  816. {
  817. var target = new Class1();
  818. var source = new Subject<BindingValue<string>>();
  819. var currentThreadId = Thread.CurrentThread.ManagedThreadId;
  820. var raised = 0;
  821. var threadingInterfaceMock = new Mock<IPlatformThreadingInterface>();
  822. threadingInterfaceMock.SetupGet(mock => mock.CurrentThreadIsLoopThread)
  823. .Returns(() => Thread.CurrentThread.ManagedThreadId == currentThreadId);
  824. var services = new TestServices(
  825. threadingInterface: threadingInterfaceMock.Object);
  826. target.PropertyChanged += (s, e) =>
  827. {
  828. Assert.Equal(currentThreadId, Thread.CurrentThread.ManagedThreadId);
  829. ++raised;
  830. };
  831. using (UnitTestApplication.Start(services))
  832. {
  833. target.Bind(Class1.FooProperty, source, priority);
  834. await Task.Run(() => source.OnNext("foobar"));
  835. Dispatcher.UIThread.RunJobs();
  836. Assert.Equal("foobar", target.GetValue(Class1.FooProperty));
  837. Assert.Equal(1, raised);
  838. }
  839. });
  840. }
  841. [Fact]
  842. public async Task Bind_With_Scheduler_Executes_On_UI_Thread()
  843. {
  844. var target = new Class1();
  845. var source = new Subject<double>();
  846. var currentThreadId = Thread.CurrentThread.ManagedThreadId;
  847. var threadingInterfaceMock = new Mock<IPlatformThreadingInterface>();
  848. threadingInterfaceMock.SetupGet(mock => mock.CurrentThreadIsLoopThread)
  849. .Returns(() => Thread.CurrentThread.ManagedThreadId == currentThreadId);
  850. var services = new TestServices(
  851. threadingInterface: threadingInterfaceMock.Object);
  852. using (UnitTestApplication.Start(services))
  853. {
  854. target.Bind(Class1.QuxProperty, source);
  855. await Task.Run(() => source.OnNext(6.7));
  856. }
  857. }
  858. [Fact]
  859. public void SetValue_Should_Not_Cause_StackOverflow_And_Have_Correct_Values()
  860. {
  861. var viewModel = new TestStackOverflowViewModel()
  862. {
  863. Value = 50
  864. };
  865. var target = new Class1();
  866. target.Bind(Class1.DoubleValueProperty,
  867. new Binding("Value") { Mode = BindingMode.TwoWay, Source = viewModel });
  868. var child = new Class1();
  869. child[!!Class1.DoubleValueProperty] = target[!!Class1.DoubleValueProperty];
  870. Assert.Equal(1, viewModel.SetterInvokedCount);
  871. // Issues #855 and #824 were causing a StackOverflowException at this point.
  872. target.DoubleValue = 51.001;
  873. Assert.Equal(2, viewModel.SetterInvokedCount);
  874. double expected = 51;
  875. Assert.Equal(expected, viewModel.Value);
  876. Assert.Equal(expected, target.DoubleValue);
  877. Assert.Equal(expected, child.DoubleValue);
  878. }
  879. [Fact]
  880. public void IsAnimating_On_Property_With_No_Value_Returns_False()
  881. {
  882. var target = new Class1();
  883. Assert.False(target.IsAnimating(Class1.FooProperty));
  884. }
  885. [Fact]
  886. public void IsAnimating_On_Property_With_Animation_Value_Returns_True()
  887. {
  888. var target = new Class1();
  889. var source = new BehaviorSubject<BindingValue<string>>("foo");
  890. target.Bind(Class1.FooProperty, source, BindingPriority.Animation);
  891. Assert.True(target.IsAnimating(Class1.FooProperty));
  892. }
  893. [Fact]
  894. public void IsAnimating_On_Property_With_Non_Animation_Binding_Returns_False()
  895. {
  896. var target = new Class1();
  897. var source = new Subject<string>();
  898. target.Bind(Class1.FooProperty, source, BindingPriority.LocalValue);
  899. Assert.False(target.IsAnimating(Class1.FooProperty));
  900. }
  901. [Fact]
  902. public void IsAnimating_On_Property_With_Animation_Binding_Returns_True()
  903. {
  904. var target = new Class1();
  905. var source = new BehaviorSubject<string>("foo");
  906. target.Bind(Class1.FooProperty, source, BindingPriority.Animation);
  907. Assert.True(target.IsAnimating(Class1.FooProperty));
  908. }
  909. [Fact]
  910. public void IsAnimating_On_Property_With_Local_Value_And_Animation_Binding_Returns_True()
  911. {
  912. var target = new Class1();
  913. var source = new BehaviorSubject<string>("foo");
  914. target.SetValue(Class1.FooProperty, "bar");
  915. target.Bind(Class1.FooProperty, source, BindingPriority.Animation);
  916. Assert.True(target.IsAnimating(Class1.FooProperty));
  917. }
  918. [Fact]
  919. public void IsAnimating_Returns_True_When_Animated_Value_Is_Same_As_Local_Value()
  920. {
  921. var target = new Class1();
  922. var source = new BehaviorSubject<string>("foo");
  923. target.SetValue(Class1.FooProperty, "foo");
  924. target.Bind(Class1.FooProperty, source, BindingPriority.Animation);
  925. Assert.True(target.IsAnimating(Class1.FooProperty));
  926. }
  927. [Fact]
  928. public void TwoWay_Binding_Should_Update_Source()
  929. {
  930. var target = new Class1();
  931. var source = new TestTwoWayBindingViewModel();
  932. target.Bind(Class1.DoubleValueProperty, new Binding(nameof(source.Value), BindingMode.TwoWay) { Source = source });
  933. target.DoubleValue = 123.4;
  934. Assert.True(source.SetterCalled);
  935. Assert.Equal(source.Value, 123.4);
  936. }
  937. [Fact]
  938. public void TwoWay_Binding_Should_Not_Call_Setter_On_Creation()
  939. {
  940. var target = new Class1();
  941. var source = new TestTwoWayBindingViewModel();
  942. target.Bind(Class1.DoubleValueProperty, new Binding(nameof(source.Value), BindingMode.TwoWay) { Source = source });
  943. Assert.False(source.SetterCalled);
  944. }
  945. [Fact]
  946. public void TwoWay_Binding_Should_Not_Call_Setter_On_Creation_Indexer()
  947. {
  948. var target = new Class1();
  949. var source = new TestTwoWayBindingViewModel();
  950. target.Bind(Class1.DoubleValueProperty, new Binding("[0]", BindingMode.TwoWay) { Source = source });
  951. Assert.False(source.SetterCalled);
  952. }
  953. [Fact]
  954. public void TwoWay_Binding_Should_Not_Fail_With_Null_DataContext()
  955. {
  956. var target = new TextBlock();
  957. target.DataContext = null;
  958. target.Bind(TextBlock.TextProperty, new Binding("Missing", BindingMode.TwoWay));
  959. }
  960. [Fact]
  961. public void TwoWay_Binding_Should_Not_Fail_With_Null_DataContext_Indexer()
  962. {
  963. var target = new TextBlock();
  964. target.DataContext = null;
  965. target.Bind(TextBlock.TextProperty, new Binding("[0]", BindingMode.TwoWay));
  966. }
  967. [Theory(Skip = "Will need changes to binding internals in order to pass")]
  968. [InlineData(BindingPriority.LocalValue)]
  969. [InlineData(BindingPriority.StyleTrigger)]
  970. [InlineData(BindingPriority.Style)]
  971. public void TwoWay_Binding_Should_Not_Update_Source_When_Higher_Priority_Value_Set(BindingPriority priority)
  972. {
  973. var target = new Class1();
  974. var source = new TestTwoWayBindingViewModel();
  975. var binding = new Binding(nameof(source.Value), BindingMode.TwoWay) { Source = source };
  976. target.Bind(Class1.DoubleValueProperty, binding, priority);
  977. target.SetValue(Class1.DoubleValueProperty, 123.4, priority - 1);
  978. // Setter should not be called because the TwoWay binding with LocalValue priority
  979. // should be overridden by the animated value and the binding made inactive.
  980. Assert.False(source.SetterCalled);
  981. }
  982. [Theory(Skip = "Will need changes to binding internals in order to pass")]
  983. [InlineData(BindingPriority.LocalValue)]
  984. [InlineData(BindingPriority.StyleTrigger)]
  985. [InlineData(BindingPriority.Style)]
  986. public void TwoWay_Binding_Should_Not_Update_Source_When_Higher_Priority_Binding_Added(BindingPriority priority)
  987. {
  988. var target = new Class1();
  989. var source = new TestTwoWayBindingViewModel();
  990. var binding1 = new Binding(nameof(source.Value), BindingMode.TwoWay) { Source = source };
  991. var binding2 = new BehaviorSubject<double>(123.4);
  992. target.Bind(Class1.DoubleValueProperty, binding1, priority);
  993. target.Bind(Class1.DoubleValueProperty, binding2, priority - 1);
  994. // Setter should not be called because the TwoWay binding with LocalValue priority
  995. // should be overridden by the animated binding and the binding made inactive.
  996. Assert.False(source.SetterCalled);
  997. }
  998. [Fact(Skip = "Will need changes to binding internals in order to pass")]
  999. public void TwoWay_Style_Binding_Should_Not_Update_Source_When_StyleTrigger_Value_Set()
  1000. {
  1001. var target = new Class1();
  1002. var source = new TestTwoWayBindingViewModel();
  1003. target.Bind(Class1.DoubleValueProperty, new Binding(nameof(source.Value), BindingMode.TwoWay) { Source = source });
  1004. target.SetValue(Class1.DoubleValueProperty, 123.4, BindingPriority.Animation);
  1005. // Setter should not be called because the TwoWay binding with Style priority
  1006. // should be overridden by the animated value and the binding made inactive.
  1007. Assert.False(source.SetterCalled);
  1008. }
  1009. [Fact(Skip = "Will need changes to binding internals in order to pass")]
  1010. public void TwoWay_Style_Binding_Should_Not_Update_Source_When_Animated_Binding_Added()
  1011. {
  1012. var target = new Class1();
  1013. var source1 = new TestTwoWayBindingViewModel();
  1014. var source2 = new BehaviorSubject<double>(123.4);
  1015. target.Bind(Class1.DoubleValueProperty, new Binding(nameof(source1.Value), BindingMode.TwoWay) { Source = source1 });
  1016. target.Bind(Class1.DoubleValueProperty, source2, BindingPriority.Animation);
  1017. // Setter should not be called because the TwoWay binding with Style priority
  1018. // should be overridden by the animated binding and the binding made inactive.
  1019. Assert.False(source1.SetterCalled);
  1020. }
  1021. [Fact]
  1022. public void Disposing_Completed_Binding_Does_Not_Throw()
  1023. {
  1024. var target = new Class1();
  1025. var source = new Subject<BindingValue<string>>();
  1026. var subscription = target.Bind(Class1.FooProperty, source);
  1027. source.OnCompleted();
  1028. subscription.Dispose();
  1029. }
  1030. [Theory]
  1031. [InlineData(BindingPriority.LocalValue)]
  1032. [InlineData(BindingPriority.Style)]
  1033. public void Binding_Producing_UnsetValue_Does_Not_Cause_Unsubscribe(BindingPriority priority)
  1034. {
  1035. var target = new Class1();
  1036. var source = new Subject<BindingValue<string>>();
  1037. target.Bind(Class1.FooProperty, source, priority);
  1038. source.OnNext("foo");
  1039. Assert.Equal("foo", target.GetValue(Class1.FooProperty));
  1040. source.OnNext(BindingValue<string>.Unset);
  1041. Assert.Equal("foodefault", target.GetValue(Class1.FooProperty));
  1042. source.OnNext("bar");
  1043. Assert.Equal("bar", target.GetValue(Class1.FooProperty));
  1044. }
  1045. [Fact]
  1046. public void Produces_Correct_Values_And_Base_Values_With_Multiple_Animation_Bindings()
  1047. {
  1048. var target = new Class1();
  1049. var source1 = new BehaviorSubject<BindingValue<double>>(12.2);
  1050. var source2 = new BehaviorSubject<BindingValue<double>>(13.3);
  1051. target.SetValue(Class1.QuxProperty, 11.1);
  1052. target.Bind(Class1.QuxProperty, source1, BindingPriority.Animation);
  1053. Assert.Equal(12.2, target.GetValue(Class1.QuxProperty));
  1054. Assert.Equal(11.1, target.GetBaseValue(Class1.QuxProperty));
  1055. target.Bind(Class1.QuxProperty, source2, BindingPriority.Animation);
  1056. Assert.Equal(13.3, target.GetValue(Class1.QuxProperty));
  1057. Assert.Equal(11.1, target.GetBaseValue(Class1.QuxProperty));
  1058. source2.OnCompleted();
  1059. Assert.Equal(12.2, target.GetValue(Class1.QuxProperty));
  1060. Assert.Equal(11.1, target.GetBaseValue(Class1.QuxProperty));
  1061. source1.OnCompleted();
  1062. Assert.Equal(11.1, target.GetValue(Class1.QuxProperty));
  1063. Assert.Equal(11.1, target.GetBaseValue(Class1.QuxProperty));
  1064. }
  1065. /// <summary>
  1066. /// Returns an observable that returns a single value but does not complete.
  1067. /// </summary>
  1068. /// <typeparam name="T">The type of the observable.</typeparam>
  1069. /// <param name="value">The value.</param>
  1070. /// <returns>The observable.</returns>
  1071. private IObservable<BindingValue<T>> Single<T>(T value)
  1072. {
  1073. return Observable.Never<BindingValue<T>>().StartWith(value);
  1074. }
  1075. private class Class1 : AvaloniaObject
  1076. {
  1077. public static readonly StyledProperty<string> FooProperty =
  1078. AvaloniaProperty.Register<Class1, string>("Foo", "foodefault");
  1079. public static readonly StyledProperty<double> QuxProperty =
  1080. AvaloniaProperty.Register<Class1, double>("Qux", 5.6);
  1081. public static readonly StyledProperty<double> DoubleValueProperty =
  1082. AvaloniaProperty.Register<Class1, double>(nameof(DoubleValue));
  1083. public double DoubleValue
  1084. {
  1085. get { return GetValue(DoubleValueProperty); }
  1086. set { SetValue(DoubleValueProperty, value); }
  1087. }
  1088. }
  1089. private class Class2 : Class1
  1090. {
  1091. public static readonly StyledProperty<string> BarProperty =
  1092. AvaloniaProperty.Register<Class2, string>("Bar", "bardefault");
  1093. }
  1094. private class TestOneTimeBinding : IBinding
  1095. {
  1096. private IObservable<object> _source;
  1097. public TestOneTimeBinding(IObservable<object> source)
  1098. {
  1099. _source = source;
  1100. }
  1101. public InstancedBinding Initiate(
  1102. AvaloniaObject target,
  1103. AvaloniaProperty? targetProperty,
  1104. object? anchor = null,
  1105. bool enableDataValidation = false)
  1106. {
  1107. return InstancedBinding.OneTime(_source);
  1108. }
  1109. }
  1110. private class TestStackOverflowViewModel : INotifyPropertyChanged
  1111. {
  1112. public int SetterInvokedCount { get; private set; }
  1113. public const int MaxInvokedCount = 1000;
  1114. private double _value;
  1115. public event PropertyChangedEventHandler? PropertyChanged;
  1116. public double Value
  1117. {
  1118. get { return _value; }
  1119. set
  1120. {
  1121. if (_value != value)
  1122. {
  1123. SetterInvokedCount++;
  1124. if (SetterInvokedCount < MaxInvokedCount)
  1125. {
  1126. _value = (int)value;
  1127. if (_value > 75)
  1128. _value = 75;
  1129. if (_value < 25)
  1130. _value = 25;
  1131. }
  1132. else
  1133. {
  1134. _value = value;
  1135. }
  1136. PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Value)));
  1137. }
  1138. }
  1139. }
  1140. }
  1141. private class TestTwoWayBindingViewModel
  1142. {
  1143. private double _value;
  1144. public double Value
  1145. {
  1146. get => _value;
  1147. set
  1148. {
  1149. _value = value;
  1150. SetterCalled = true;
  1151. }
  1152. }
  1153. public double this[int index]
  1154. {
  1155. get => _value;
  1156. set
  1157. {
  1158. _value = value;
  1159. SetterCalled = true;
  1160. }
  1161. }
  1162. public bool SetterCalled { get; private set; }
  1163. }
  1164. }
  1165. }