DynamicResourceExtensionTests.cs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458
  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_From_Style_Can_Be_Assigned_To_Property()
  38. {
  39. var xaml = @"
  40. <UserControl xmlns='https://github.com/avaloniaui'
  41. xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
  42. <UserControl.Styles>
  43. <Style>
  44. <Style.Resources>
  45. <SolidColorBrush x:Key='brush'>#ff506070</SolidColorBrush>
  46. </Style.Resources>
  47. </Style>
  48. </UserControl.Styles>
  49. <Border Name='border' Background='{DynamicResource brush}'/>
  50. </UserControl>";
  51. var loader = new AvaloniaXamlLoader();
  52. var userControl = (UserControl)loader.Load(xaml);
  53. var border = userControl.FindControl<Border>("border");
  54. DelayedBinding.ApplyBindings(border);
  55. var brush = (SolidColorBrush)border.Background;
  56. Assert.Equal(0xff506070, brush.Color.ToUint32());
  57. }
  58. [Fact]
  59. public void DynamicResource_From_Application_Can_Be_Assigned_To_Property_In_Window()
  60. {
  61. using (StyledWindow())
  62. {
  63. Application.Current.Resources.Add("brush", new SolidColorBrush(0xff506070));
  64. var xaml = @"
  65. <Window xmlns='https://github.com/avaloniaui'
  66. xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
  67. <Border Name='border' Background='{DynamicResource brush}'/>
  68. </Window>";
  69. var loader = new AvaloniaXamlLoader();
  70. var window = (Window)loader.Load(xaml);
  71. var border = window.FindControl<Border>("border");
  72. var brush = (SolidColorBrush)border.Background;
  73. Assert.Equal(0xff506070, brush.Color.ToUint32());
  74. }
  75. }
  76. [Fact]
  77. public void DynamicResource_From_Application_Can_Be_Assigned_To_Property_In_UserControl()
  78. {
  79. using (UnitTestApplication.Start(TestServices.StyledWindow))
  80. {
  81. Application.Current.Resources.Add("brush", new SolidColorBrush(0xff506070));
  82. var xaml = @"
  83. <UserControl xmlns='https://github.com/avaloniaui'
  84. xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
  85. <Border Name='border' Background='{DynamicResource brush}'/>
  86. </UserControl>";
  87. var loader = new AvaloniaXamlLoader();
  88. var userControl = (UserControl)loader.Load(xaml);
  89. var border = userControl.FindControl<Border>("border");
  90. // We don't actually know where the global styles are until we attach the control
  91. // to a window, as Window has StylingParent set to Application.
  92. var window = new Window { Content = userControl };
  93. window.Show();
  94. var brush = (SolidColorBrush)border.Background;
  95. Assert.Equal(0xff506070, brush.Color.ToUint32());
  96. }
  97. }
  98. [Fact]
  99. public void DynamicResource_Can_Be_Assigned_To_Setter()
  100. {
  101. using (StyledWindow())
  102. {
  103. var xaml = @"
  104. <Window xmlns='https://github.com/avaloniaui'
  105. xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
  106. <Window.Resources>
  107. <SolidColorBrush x:Key='brush'>#ff506070</SolidColorBrush>
  108. </Window.Resources>
  109. <Window.Styles>
  110. <Style Selector='Button'>
  111. <Setter Property='Background' Value='{DynamicResource brush}'/>
  112. </Style>
  113. </Window.Styles>
  114. <Button Name='button'/>
  115. </Window>";
  116. var loader = new AvaloniaXamlLoader();
  117. var window = (Window)loader.Load(xaml);
  118. var button = window.FindControl<Button>("button");
  119. var brush = (SolidColorBrush)button.Background;
  120. Assert.Equal(0xff506070, brush.Color.ToUint32());
  121. }
  122. }
  123. [Fact]
  124. public void DynamicResource_From_Style_Can_Be_Assigned_To_Setter()
  125. {
  126. using (StyledWindow())
  127. {
  128. var xaml = @"
  129. <Window xmlns='https://github.com/avaloniaui'
  130. xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
  131. <Window.Styles>
  132. <Style>
  133. <Style.Resources>
  134. <SolidColorBrush x:Key='brush'>#ff506070</SolidColorBrush>
  135. </Style.Resources>
  136. </Style>
  137. <Style Selector='Button'>
  138. <Setter Property='Background' Value='{DynamicResource brush}'/>
  139. </Style>
  140. </Window.Styles>
  141. <Button Name='button'/>
  142. </Window>";
  143. var loader = new AvaloniaXamlLoader();
  144. var window = (Window)loader.Load(xaml);
  145. var button = window.FindControl<Button>("button");
  146. var brush = (SolidColorBrush)button.Background;
  147. Assert.Equal(0xff506070, brush.Color.ToUint32());
  148. }
  149. }
  150. [Fact]
  151. public void DynamicResource_Can_Be_Assigned_To_Setter_In_Styles_File()
  152. {
  153. var styleXaml = @"
  154. <Styles xmlns='https://github.com/avaloniaui'
  155. xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
  156. <Styles.Resources>
  157. <SolidColorBrush x:Key='brush'>#ff506070</SolidColorBrush>
  158. </Styles.Resources>
  159. <Style Selector='Border'>
  160. <Setter Property='Background' Value='{DynamicResource brush}'/>
  161. </Style>
  162. </Styles>";
  163. using (StyledWindow(assets: ("test:style.xaml", styleXaml)))
  164. {
  165. var xaml = @"
  166. <Window xmlns='https://github.com/avaloniaui'
  167. xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
  168. <Window.Styles>
  169. <StyleInclude Source='test:style.xaml'/>
  170. </Window.Styles>
  171. <Border Name='border'/>
  172. </Window>";
  173. var loader = new AvaloniaXamlLoader();
  174. var window = (Window)loader.Load(xaml);
  175. var border = window.FindControl<Border>("border");
  176. var brush = (SolidColorBrush)border.Background;
  177. Assert.Equal(0xff506070, brush.Color.ToUint32());
  178. }
  179. }
  180. [Fact]
  181. public void DynamicResource_Can_Be_Assigned_To_Property_In_ControlTemplate_In_Styles_File()
  182. {
  183. var styleXaml = @"
  184. <Styles xmlns='https://github.com/avaloniaui'
  185. xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
  186. <Styles.Resources>
  187. <SolidColorBrush x:Key='brush'>#ff506070</SolidColorBrush>
  188. </Styles.Resources>
  189. <Style Selector='Button'>
  190. <Setter Property='Template'>
  191. <ControlTemplate>
  192. <Border Name='border' Background='{DynamicResource brush}'/>
  193. </ControlTemplate>
  194. </Setter>
  195. </Style>
  196. </Styles>";
  197. using (StyledWindow(assets: ("test:style.xaml", styleXaml)))
  198. {
  199. var xaml = @"
  200. <Window xmlns='https://github.com/avaloniaui'
  201. xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
  202. <Window.Styles>
  203. <StyleInclude Source='test:style.xaml'/>
  204. </Window.Styles>
  205. <Button Name='button'/>
  206. </Window>";
  207. var loader = new AvaloniaXamlLoader();
  208. var window = (Window)loader.Load(xaml);
  209. var button = window.FindControl<Button>("button");
  210. window.Show();
  211. var border = (Border)button.GetVisualChildren().Single();
  212. var brush = (SolidColorBrush)border.Background;
  213. Assert.Equal(0xff506070, brush.Color.ToUint32());
  214. }
  215. }
  216. [Fact]
  217. public void DynamicResource_Can_Be_Assigned_To_Resource_Property()
  218. {
  219. var xaml = @"
  220. <UserControl xmlns='https://github.com/avaloniaui'
  221. xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
  222. <UserControl.Resources>
  223. <Color x:Key='color'>#ff506070</Color>
  224. <SolidColorBrush x:Key='brush' Color='{DynamicResource color}'/>
  225. </UserControl.Resources>
  226. <Border Name='border' Background='{DynamicResource brush}'/>
  227. </UserControl>";
  228. var loader = new AvaloniaXamlLoader();
  229. var userControl = (UserControl)loader.Load(xaml);
  230. var border = userControl.FindControl<Border>("border");
  231. DelayedBinding.ApplyBindings(border);
  232. var brush = (SolidColorBrush)border.Background;
  233. Assert.Equal(0xff506070, brush.Color.ToUint32());
  234. }
  235. [Fact]
  236. public void DynamicResource_Tracks_Added_Resource()
  237. {
  238. var xaml = @"
  239. <UserControl xmlns='https://github.com/avaloniaui'
  240. xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
  241. <Border Name='border' Background='{DynamicResource brush}'/>
  242. </UserControl>";
  243. var loader = new AvaloniaXamlLoader();
  244. var userControl = (UserControl)loader.Load(xaml);
  245. var border = userControl.FindControl<Border>("border");
  246. DelayedBinding.ApplyBindings(border);
  247. Assert.Null(border.Background);
  248. userControl.Resources.Add("brush", new SolidColorBrush(0xff506070));
  249. var brush = (SolidColorBrush)border.Background;
  250. Assert.NotNull(brush);
  251. Assert.Equal(0xff506070, brush.Color.ToUint32());
  252. }
  253. [Fact]
  254. public void DynamicResource_Tracks_Added_Style_Resource()
  255. {
  256. var xaml = @"
  257. <UserControl xmlns='https://github.com/avaloniaui'
  258. xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
  259. <Border Name='border' Background='{DynamicResource brush}'/>
  260. </UserControl>";
  261. var loader = new AvaloniaXamlLoader();
  262. var userControl = (UserControl)loader.Load(xaml);
  263. var border = userControl.FindControl<Border>("border");
  264. DelayedBinding.ApplyBindings(border);
  265. Assert.Null(border.Background);
  266. userControl.Styles.Resources.Add("brush", new SolidColorBrush(0xff506070));
  267. var brush = (SolidColorBrush)border.Background;
  268. Assert.NotNull(brush);
  269. Assert.Equal(0xff506070, brush.Color.ToUint32());
  270. }
  271. [Fact]
  272. public void DynamicResource_Tracks_Added_Nested_Style_Resource()
  273. {
  274. var xaml = @"
  275. <UserControl xmlns='https://github.com/avaloniaui'
  276. xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
  277. <UserControl.Styles>
  278. <Style>
  279. </Style>
  280. </UserControl.Styles>
  281. <Border Name='border' Background='{DynamicResource brush}'/>
  282. </UserControl>";
  283. var loader = new AvaloniaXamlLoader();
  284. var userControl = (UserControl)loader.Load(xaml);
  285. var border = userControl.FindControl<Border>("border");
  286. DelayedBinding.ApplyBindings(border);
  287. Assert.Null(border.Background);
  288. ((Style)userControl.Styles[0]).Resources.Add("brush", new SolidColorBrush(0xff506070));
  289. var brush = (SolidColorBrush)border.Background;
  290. Assert.NotNull(brush);
  291. Assert.Equal(0xff506070, brush.Color.ToUint32());
  292. }
  293. [Fact]
  294. public void DynamicResource_Can_Be_Found_Across_Xaml_Style_Files()
  295. {
  296. var style1Xaml = @"
  297. <Style xmlns='https://github.com/avaloniaui'
  298. xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
  299. <Style.Resources>
  300. <Color x:Key='Red'>Red</Color>
  301. </Style.Resources>
  302. </Style>";
  303. var style2Xaml = @"
  304. <Style xmlns='https://github.com/avaloniaui'
  305. xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
  306. <Style.Resources>
  307. <SolidColorBrush x:Key='RedBrush' Color='{DynamicResource Red}'/>
  308. </Style.Resources>
  309. </Style>";
  310. using (StyledWindow(
  311. ("test:style1.xaml", style1Xaml),
  312. ("test:style2.xaml", style2Xaml)))
  313. {
  314. var xaml = @"
  315. <Window xmlns='https://github.com/avaloniaui'
  316. xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
  317. <Window.Styles>
  318. <StyleInclude Source='test:style1.xaml'/>
  319. <StyleInclude Source='test:style2.xaml'/>
  320. </Window.Styles>
  321. <Border Name='border' Background='{DynamicResource RedBrush}'/>
  322. </Window>";
  323. var loader = new AvaloniaXamlLoader();
  324. var window = (Window)loader.Load(xaml);
  325. var border = window.FindControl<Border>("border");
  326. var borderBrush = (ISolidColorBrush)border.Background;
  327. Assert.NotNull(borderBrush);
  328. Assert.Equal(0xffff0000, borderBrush.Color.ToUint32());
  329. }
  330. }
  331. [Fact]
  332. public void Control_Property_Is_Updated_When_Parent_Is_Changed()
  333. {
  334. var xaml = @"
  335. <UserControl xmlns='https://github.com/avaloniaui'
  336. xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
  337. <UserControl.Resources>
  338. <SolidColorBrush x:Key='brush'>#ff506070</SolidColorBrush>
  339. </UserControl.Resources>
  340. <Border Name='border' Background='{DynamicResource brush}'/>
  341. </UserControl>";
  342. var loader = new AvaloniaXamlLoader();
  343. var userControl = (UserControl)loader.Load(xaml);
  344. var border = userControl.FindControl<Border>("border");
  345. DelayedBinding.ApplyBindings(border);
  346. var brush = (SolidColorBrush)border.Background;
  347. Assert.Equal(0xff506070, brush.Color.ToUint32());
  348. userControl.Content = null;
  349. Assert.Null(border.Background);
  350. userControl.Content = border;
  351. brush = (SolidColorBrush)border.Background;
  352. Assert.Equal(0xff506070, brush.Color.ToUint32());
  353. }
  354. private IDisposable StyledWindow(params (string, string)[] assets)
  355. {
  356. var services = TestServices.StyledWindow.With(
  357. assetLoader: new MockAssetLoader(assets),
  358. theme: () => new Styles
  359. {
  360. WindowStyle(),
  361. });
  362. return UnitTestApplication.Start(services);
  363. }
  364. private Style WindowStyle()
  365. {
  366. return new Style(x => x.OfType<Window>())
  367. {
  368. Setters =
  369. {
  370. new Setter(
  371. Window.TemplateProperty,
  372. new FuncControlTemplate<Window>(x =>
  373. new ContentPresenter
  374. {
  375. Name = "PART_ContentPresenter",
  376. [!ContentPresenter.ContentProperty] = x[!Window.ContentProperty],
  377. }))
  378. }
  379. };
  380. }
  381. }
  382. }