DynamicResourceExtensionTests.cs 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505
  1. // Copyright (c) The Avalonia Project. All rights reserved.
  2. // Licensed under the MIT license. See licence.md file in the project root for full license information.
  3. using System;
  4. using System.Linq;
  5. using Avalonia.Controls;
  6. using Avalonia.Controls.Presenters;
  7. using Avalonia.Controls.Templates;
  8. using Avalonia.Markup.Xaml.Data;
  9. using Avalonia.Media;
  10. using Avalonia.Styling;
  11. using Avalonia.UnitTests;
  12. using Avalonia.VisualTree;
  13. using Xunit;
  14. namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
  15. {
  16. public class DynamicResourceExtensionTests
  17. {
  18. [Fact]
  19. public void DynamicResource_Can_Be_Assigned_To_Property()
  20. {
  21. var xaml = @"
  22. <UserControl xmlns='https://github.com/avaloniaui'
  23. xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
  24. <UserControl.Resources>
  25. <SolidColorBrush x:Key='brush'>#ff506070</SolidColorBrush>
  26. </UserControl.Resources>
  27. <Border Name='border' Background='{DynamicResource brush}'/>
  28. </UserControl>";
  29. var loader = new AvaloniaXamlLoader();
  30. var userControl = (UserControl)loader.Load(xaml);
  31. var border = userControl.FindControl<Border>("border");
  32. DelayedBinding.ApplyBindings(border);
  33. var brush = (SolidColorBrush)border.Background;
  34. Assert.Equal(0xff506070, brush.Color.ToUint32());
  35. }
  36. [Fact]
  37. public void DynamicResource_Can_Be_Assigned_To_Attached_Property()
  38. {
  39. var xaml = @"
  40. <UserControl xmlns='https://github.com/avaloniaui'
  41. xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
  42. <UserControl.Resources>
  43. <x:Int32 x:Key='col'>5</x:Int32>
  44. </UserControl.Resources>
  45. <Border Name='border' Grid.Column='{DynamicResource col}'/>
  46. </UserControl>";
  47. var loader = new AvaloniaXamlLoader();
  48. var userControl = (UserControl)loader.Load(xaml);
  49. var border = userControl.FindControl<Border>("border");
  50. DelayedBinding.ApplyBindings(border);
  51. Assert.Equal(5, Grid.GetColumn(border));
  52. }
  53. [Fact]
  54. public void DynamicResource_From_Style_Can_Be_Assigned_To_Property()
  55. {
  56. var xaml = @"
  57. <UserControl xmlns='https://github.com/avaloniaui'
  58. xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
  59. <UserControl.Styles>
  60. <Style>
  61. <Style.Resources>
  62. <SolidColorBrush x:Key='brush'>#ff506070</SolidColorBrush>
  63. </Style.Resources>
  64. </Style>
  65. </UserControl.Styles>
  66. <Border Name='border' Background='{DynamicResource brush}'/>
  67. </UserControl>";
  68. var loader = new AvaloniaXamlLoader();
  69. var userControl = (UserControl)loader.Load(xaml);
  70. var border = userControl.FindControl<Border>("border");
  71. DelayedBinding.ApplyBindings(border);
  72. var brush = (SolidColorBrush)border.Background;
  73. Assert.Equal(0xff506070, brush.Color.ToUint32());
  74. }
  75. [Fact]
  76. public void DynamicResource_From_Application_Can_Be_Assigned_To_Property_In_Window()
  77. {
  78. using (StyledWindow())
  79. {
  80. Application.Current.Resources.Add("brush", new SolidColorBrush(0xff506070));
  81. var xaml = @"
  82. <Window xmlns='https://github.com/avaloniaui'
  83. xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
  84. <Border Name='border' Background='{DynamicResource brush}'/>
  85. </Window>";
  86. var loader = new AvaloniaXamlLoader();
  87. var window = (Window)loader.Load(xaml);
  88. var border = window.FindControl<Border>("border");
  89. var brush = (SolidColorBrush)border.Background;
  90. Assert.Equal(0xff506070, brush.Color.ToUint32());
  91. }
  92. }
  93. [Fact]
  94. public void DynamicResource_From_Application_Can_Be_Assigned_To_Property_In_UserControl()
  95. {
  96. using (UnitTestApplication.Start(TestServices.StyledWindow))
  97. {
  98. Application.Current.Resources.Add("brush", new SolidColorBrush(0xff506070));
  99. var xaml = @"
  100. <UserControl xmlns='https://github.com/avaloniaui'
  101. xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
  102. <Border Name='border' Background='{DynamicResource brush}'/>
  103. </UserControl>";
  104. var loader = new AvaloniaXamlLoader();
  105. var userControl = (UserControl)loader.Load(xaml);
  106. var border = userControl.FindControl<Border>("border");
  107. // We don't actually know where the global styles are until we attach the control
  108. // to a window, as Window has StylingParent set to Application.
  109. var window = new Window { Content = userControl };
  110. window.Show();
  111. var brush = (SolidColorBrush)border.Background;
  112. Assert.Equal(0xff506070, brush.Color.ToUint32());
  113. }
  114. }
  115. [Fact]
  116. public void DynamicResource_Can_Be_Assigned_To_Setter()
  117. {
  118. using (StyledWindow())
  119. {
  120. var xaml = @"
  121. <Window xmlns='https://github.com/avaloniaui'
  122. xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
  123. <Window.Resources>
  124. <SolidColorBrush x:Key='brush'>#ff506070</SolidColorBrush>
  125. </Window.Resources>
  126. <Window.Styles>
  127. <Style Selector='Button'>
  128. <Setter Property='Background' Value='{DynamicResource brush}'/>
  129. </Style>
  130. </Window.Styles>
  131. <Button Name='button'/>
  132. </Window>";
  133. var loader = new AvaloniaXamlLoader();
  134. var window = (Window)loader.Load(xaml);
  135. var button = window.FindControl<Button>("button");
  136. var brush = (SolidColorBrush)button.Background;
  137. Assert.Equal(0xff506070, brush.Color.ToUint32());
  138. }
  139. }
  140. [Fact]
  141. public void DynamicResource_From_Style_Can_Be_Assigned_To_Setter()
  142. {
  143. using (StyledWindow())
  144. {
  145. var xaml = @"
  146. <Window xmlns='https://github.com/avaloniaui'
  147. xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
  148. <Window.Styles>
  149. <Style>
  150. <Style.Resources>
  151. <SolidColorBrush x:Key='brush'>#ff506070</SolidColorBrush>
  152. </Style.Resources>
  153. </Style>
  154. <Style Selector='Button'>
  155. <Setter Property='Background' Value='{DynamicResource brush}'/>
  156. </Style>
  157. </Window.Styles>
  158. <Button Name='button'/>
  159. </Window>";
  160. var loader = new AvaloniaXamlLoader();
  161. var window = (Window)loader.Load(xaml);
  162. var button = window.FindControl<Button>("button");
  163. var brush = (SolidColorBrush)button.Background;
  164. Assert.Equal(0xff506070, brush.Color.ToUint32());
  165. }
  166. }
  167. [Fact]
  168. public void DynamicResource_Can_Be_Assigned_To_Setter_In_Styles_File()
  169. {
  170. var styleXaml = @"
  171. <Styles xmlns='https://github.com/avaloniaui'
  172. xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
  173. <Styles.Resources>
  174. <SolidColorBrush x:Key='brush'>#ff506070</SolidColorBrush>
  175. </Styles.Resources>
  176. <Style Selector='Border'>
  177. <Setter Property='Background' Value='{DynamicResource brush}'/>
  178. </Style>
  179. </Styles>";
  180. using (StyledWindow(assets: ("test:style.xaml", styleXaml)))
  181. {
  182. var xaml = @"
  183. <Window xmlns='https://github.com/avaloniaui'
  184. xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
  185. <Window.Styles>
  186. <StyleInclude Source='test:style.xaml'/>
  187. </Window.Styles>
  188. <Border Name='border'/>
  189. </Window>";
  190. var loader = new AvaloniaXamlLoader();
  191. var window = (Window)loader.Load(xaml);
  192. var border = window.FindControl<Border>("border");
  193. var brush = (SolidColorBrush)border.Background;
  194. Assert.Equal(0xff506070, brush.Color.ToUint32());
  195. }
  196. }
  197. [Fact]
  198. public void DynamicResource_Can_Be_Assigned_To_Property_In_ControlTemplate_In_Styles_File()
  199. {
  200. var styleXaml = @"
  201. <Styles xmlns='https://github.com/avaloniaui'
  202. xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
  203. <Styles.Resources>
  204. <SolidColorBrush x:Key='brush'>#ff506070</SolidColorBrush>
  205. </Styles.Resources>
  206. <Style Selector='Button'>
  207. <Setter Property='Template'>
  208. <ControlTemplate>
  209. <Border Name='border' Background='{DynamicResource brush}'/>
  210. </ControlTemplate>
  211. </Setter>
  212. </Style>
  213. </Styles>";
  214. using (StyledWindow(assets: ("test:style.xaml", styleXaml)))
  215. {
  216. var xaml = @"
  217. <Window xmlns='https://github.com/avaloniaui'
  218. xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
  219. <Window.Styles>
  220. <StyleInclude Source='test:style.xaml'/>
  221. </Window.Styles>
  222. <Button Name='button'/>
  223. </Window>";
  224. var loader = new AvaloniaXamlLoader();
  225. var window = (Window)loader.Load(xaml);
  226. var button = window.FindControl<Button>("button");
  227. window.Show();
  228. var border = (Border)button.GetVisualChildren().Single();
  229. var brush = (SolidColorBrush)border.Background;
  230. Assert.Equal(0xff506070, brush.Color.ToUint32());
  231. }
  232. }
  233. [Fact]
  234. public void DynamicResource_Can_Be_Assigned_To_Resource_Property()
  235. {
  236. var xaml = @"
  237. <UserControl xmlns='https://github.com/avaloniaui'
  238. xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
  239. <UserControl.Resources>
  240. <Color x:Key='color'>#ff506070</Color>
  241. <SolidColorBrush x:Key='brush' Color='{DynamicResource color}'/>
  242. </UserControl.Resources>
  243. <Border Name='border' Background='{DynamicResource brush}'/>
  244. </UserControl>";
  245. var loader = new AvaloniaXamlLoader();
  246. var userControl = (UserControl)loader.Load(xaml);
  247. var border = userControl.FindControl<Border>("border");
  248. DelayedBinding.ApplyBindings(border);
  249. var brush = (SolidColorBrush)border.Background;
  250. Assert.Equal(0xff506070, brush.Color.ToUint32());
  251. }
  252. [Fact]
  253. public void DynamicResource_Can_Be_Assigned_To_ItemTemplate_Property()
  254. {
  255. var xaml = @"
  256. <UserControl xmlns='https://github.com/avaloniaui'
  257. xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
  258. <UserControl.Resources>
  259. <DataTemplate x:Key='PurpleData'>
  260. <TextBlock Text='{Binding Name}' Background='Purple'/>
  261. </DataTemplate>
  262. </UserControl.Resources>
  263. <ListBox Name='listBox' ItemTemplate='{DynamicResource PurpleData}'/>
  264. </UserControl>";
  265. var loader = new AvaloniaXamlLoader();
  266. var userControl = (UserControl)loader.Load(xaml);
  267. var listBox = userControl.FindControl<ListBox>("listBox");
  268. DelayedBinding.ApplyBindings(listBox);
  269. Assert.NotNull(listBox.ItemTemplate);
  270. }
  271. [Fact]
  272. public void DynamicResource_Tracks_Added_Resource()
  273. {
  274. var xaml = @"
  275. <UserControl xmlns='https://github.com/avaloniaui'
  276. xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
  277. <Border Name='border' Background='{DynamicResource brush}'/>
  278. </UserControl>";
  279. var loader = new AvaloniaXamlLoader();
  280. var userControl = (UserControl)loader.Load(xaml);
  281. var border = userControl.FindControl<Border>("border");
  282. DelayedBinding.ApplyBindings(border);
  283. Assert.Null(border.Background);
  284. userControl.Resources.Add("brush", new SolidColorBrush(0xff506070));
  285. var brush = (SolidColorBrush)border.Background;
  286. Assert.NotNull(brush);
  287. Assert.Equal(0xff506070, brush.Color.ToUint32());
  288. }
  289. [Fact]
  290. public void DynamicResource_Tracks_Added_Style_Resource()
  291. {
  292. var xaml = @"
  293. <UserControl xmlns='https://github.com/avaloniaui'
  294. xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
  295. <Border Name='border' Background='{DynamicResource brush}'/>
  296. </UserControl>";
  297. var loader = new AvaloniaXamlLoader();
  298. var userControl = (UserControl)loader.Load(xaml);
  299. var border = userControl.FindControl<Border>("border");
  300. DelayedBinding.ApplyBindings(border);
  301. Assert.Null(border.Background);
  302. userControl.Styles.Resources.Add("brush", new SolidColorBrush(0xff506070));
  303. var brush = (SolidColorBrush)border.Background;
  304. Assert.NotNull(brush);
  305. Assert.Equal(0xff506070, brush.Color.ToUint32());
  306. }
  307. [Fact]
  308. public void DynamicResource_Tracks_Added_Nested_Style_Resource()
  309. {
  310. var xaml = @"
  311. <UserControl xmlns='https://github.com/avaloniaui'
  312. xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
  313. <UserControl.Styles>
  314. <Style>
  315. </Style>
  316. </UserControl.Styles>
  317. <Border Name='border' Background='{DynamicResource brush}'/>
  318. </UserControl>";
  319. var loader = new AvaloniaXamlLoader();
  320. var userControl = (UserControl)loader.Load(xaml);
  321. var border = userControl.FindControl<Border>("border");
  322. DelayedBinding.ApplyBindings(border);
  323. Assert.Null(border.Background);
  324. ((Style)userControl.Styles[0]).Resources.Add("brush", new SolidColorBrush(0xff506070));
  325. var brush = (SolidColorBrush)border.Background;
  326. Assert.NotNull(brush);
  327. Assert.Equal(0xff506070, brush.Color.ToUint32());
  328. }
  329. [Fact]
  330. public void DynamicResource_Can_Be_Found_Across_Xaml_Style_Files()
  331. {
  332. var style1Xaml = @"
  333. <Style xmlns='https://github.com/avaloniaui'
  334. xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
  335. <Style.Resources>
  336. <Color x:Key='Red'>Red</Color>
  337. </Style.Resources>
  338. </Style>";
  339. var style2Xaml = @"
  340. <Style xmlns='https://github.com/avaloniaui'
  341. xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
  342. <Style.Resources>
  343. <SolidColorBrush x:Key='RedBrush' Color='{DynamicResource Red}'/>
  344. </Style.Resources>
  345. </Style>";
  346. using (StyledWindow(
  347. ("test:style1.xaml", style1Xaml),
  348. ("test:style2.xaml", style2Xaml)))
  349. {
  350. var xaml = @"
  351. <Window xmlns='https://github.com/avaloniaui'
  352. xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
  353. <Window.Styles>
  354. <StyleInclude Source='test:style1.xaml'/>
  355. <StyleInclude Source='test:style2.xaml'/>
  356. </Window.Styles>
  357. <Border Name='border' Background='{DynamicResource RedBrush}'/>
  358. </Window>";
  359. var loader = new AvaloniaXamlLoader();
  360. var window = (Window)loader.Load(xaml);
  361. var border = window.FindControl<Border>("border");
  362. var borderBrush = (ISolidColorBrush)border.Background;
  363. Assert.NotNull(borderBrush);
  364. Assert.Equal(0xffff0000, borderBrush.Color.ToUint32());
  365. }
  366. }
  367. [Fact]
  368. public void Control_Property_Is_Updated_When_Parent_Is_Changed()
  369. {
  370. var xaml = @"
  371. <UserControl xmlns='https://github.com/avaloniaui'
  372. xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
  373. <UserControl.Resources>
  374. <SolidColorBrush x:Key='brush'>#ff506070</SolidColorBrush>
  375. </UserControl.Resources>
  376. <Border Name='border' Background='{DynamicResource brush}'/>
  377. </UserControl>";
  378. var loader = new AvaloniaXamlLoader();
  379. var userControl = (UserControl)loader.Load(xaml);
  380. var border = userControl.FindControl<Border>("border");
  381. DelayedBinding.ApplyBindings(border);
  382. var brush = (SolidColorBrush)border.Background;
  383. Assert.Equal(0xff506070, brush.Color.ToUint32());
  384. userControl.Content = null;
  385. Assert.Null(border.Background);
  386. userControl.Content = border;
  387. brush = (SolidColorBrush)border.Background;
  388. Assert.Equal(0xff506070, brush.Color.ToUint32());
  389. }
  390. private IDisposable StyledWindow(params (string, string)[] assets)
  391. {
  392. var services = TestServices.StyledWindow.With(
  393. assetLoader: new MockAssetLoader(assets),
  394. theme: () => new Styles
  395. {
  396. WindowStyle(),
  397. });
  398. return UnitTestApplication.Start(services);
  399. }
  400. private Style WindowStyle()
  401. {
  402. return new Style(x => x.OfType<Window>())
  403. {
  404. Setters =
  405. {
  406. new Setter(
  407. Window.TemplateProperty,
  408. new FuncControlTemplate<Window>(x =>
  409. new ContentPresenter
  410. {
  411. Name = "PART_ContentPresenter",
  412. [!ContentPresenter.ContentProperty] = x[!Window.ContentProperty],
  413. }))
  414. }
  415. };
  416. }
  417. }
  418. }