XamlIlTests.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394
  1. using System;
  2. using System.Collections.Generic;
  3. using System.ComponentModel;
  4. using System.Globalization;
  5. using System.Linq;
  6. using System.Runtime.CompilerServices;
  7. using Avalonia.Controls;
  8. using Avalonia.Data.Converters;
  9. using Avalonia.Data.Core;
  10. using Avalonia.Input;
  11. using Avalonia.Interactivity;
  12. using Avalonia.Media;
  13. using Avalonia.Styling;
  14. using Avalonia.Threading;
  15. using Avalonia.UnitTests;
  16. using Avalonia.VisualTree;
  17. using JetBrains.Annotations;
  18. using Xunit;
  19. namespace Avalonia.Markup.Xaml.UnitTests
  20. {
  21. public class XamlIlTests : XamlTestBase
  22. {
  23. [Fact]
  24. public void Binding_Button_IsPressed_ShouldWork()
  25. {
  26. var parsed = (Button)AvaloniaXamlLoader.Parse(@"
  27. <Button xmlns='https://github.com/avaloniaui' IsPressed='{Binding IsPressed, Mode=TwoWay}' />");
  28. var ctx = new XamlIlBugTestsDataContext();
  29. parsed.DataContext = ctx;
  30. parsed.SetValue(Button.IsPressedProperty, true);
  31. Assert.True(ctx.IsPressed);
  32. }
  33. [Fact]
  34. public void Transitions_Should_Be_Properly_Parsed()
  35. {
  36. var parsed = (Grid)AvaloniaXamlLoader.Parse(@"
  37. <Grid xmlns='https://github.com/avaloniaui' >
  38. <Grid.Transitions>
  39. <Transitions>
  40. <DoubleTransition Property='Opacity'
  41. Easing='CircularEaseIn'
  42. Duration='0:0:0.5' />
  43. </Transitions>
  44. </Grid.Transitions>
  45. </Grid>");
  46. Assert.Equal(1, parsed.Transitions.Count);
  47. Assert.Equal(Visual.OpacityProperty, parsed.Transitions[0].Property);
  48. }
  49. [Fact]
  50. public void Parser_Should_Override_Precompiled_Xaml()
  51. {
  52. var precompiled = new XamlIlClassWithPrecompiledXaml();
  53. Assert.Equal(Brushes.Red, precompiled.Background);
  54. Assert.Equal(1, precompiled.Opacity);
  55. var loaded = (XamlIlClassWithPrecompiledXaml)AvaloniaXamlLoader.Parse(@"
  56. <UserControl xmlns='https://github.com/avaloniaui'
  57. xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'
  58. x:Class='Avalonia.Markup.Xaml.UnitTests.XamlIlClassWithPrecompiledXaml'
  59. Opacity='0'>
  60. </UserControl>");
  61. Assert.Equal(loaded.Opacity, 0);
  62. Assert.Null(loaded.Background);
  63. }
  64. [Fact]
  65. public void RelativeSource_TemplatedParent_Works()
  66. {
  67. using (UnitTestApplication.Start(TestServices.StyledWindow))
  68. {
  69. new AvaloniaXamlLoader().Load(@"
  70. <Application
  71. xmlns='https://github.com/avaloniaui'
  72. xmlns:local='clr-namespace:Avalonia.Markup.Xaml.UnitTests;assembly=Avalonia.Markup.Xaml.UnitTests'
  73. >
  74. <Application.Styles>
  75. <Style Selector='Button'>
  76. <Setter Property='Template'>
  77. <ControlTemplate>
  78. <Grid><Grid><Grid>
  79. <Canvas>
  80. <Canvas.Background>
  81. <SolidColorBrush>
  82. <SolidColorBrush.Color>
  83. <MultiBinding>
  84. <MultiBinding.Converter>
  85. <local:XamlIlBugTestsBrushToColorConverter/>
  86. </MultiBinding.Converter>
  87. <Binding Path='Background' RelativeSource='{RelativeSource TemplatedParent}'/>
  88. <Binding Path='Background' RelativeSource='{RelativeSource TemplatedParent}'/>
  89. <Binding Path='Background' RelativeSource='{RelativeSource TemplatedParent}'/>
  90. </MultiBinding>
  91. </SolidColorBrush.Color>
  92. </SolidColorBrush>
  93. </Canvas.Background>
  94. </Canvas>
  95. </Grid></Grid></Grid>
  96. </ControlTemplate>
  97. </Setter>
  98. </Style>
  99. </Application.Styles>
  100. </Application>",
  101. null, Application.Current);
  102. var parsed = (Window)AvaloniaXamlLoader.Parse(@"
  103. <Window
  104. xmlns='https://github.com/avaloniaui'
  105. xmlns:local='clr-namespace:Avalonia.Markup.Xaml.UnitTests;assembly=Avalonia.Markup.Xaml.UnitTests'
  106. >
  107. <Button Background='Red' />
  108. </Window>
  109. ");
  110. var btn = ((Button)parsed.Content);
  111. btn.ApplyTemplate();
  112. var canvas = (Canvas)btn.GetVisualChildren().First()
  113. .VisualChildren.First()
  114. .VisualChildren.First()
  115. .VisualChildren.First();
  116. Assert.Equal(Brushes.Red.Color, ((ISolidColorBrush)canvas.Background).Color);
  117. }
  118. }
  119. [Fact]
  120. public void Event_Handlers_Should_Work_For_Templates()
  121. {
  122. using (UnitTestApplication.Start(TestServices.StyledWindow))
  123. {
  124. var w =new XamlIlBugTestsEventHandlerCodeBehind();
  125. w.ApplyTemplate();
  126. w.Show();
  127. Dispatcher.UIThread.RunJobs();
  128. var itemsPresenter = ((ItemsControl)w.Content).GetVisualChildren().FirstOrDefault();
  129. var item = itemsPresenter
  130. .GetVisualChildren().First()
  131. .GetVisualChildren().First()
  132. .GetVisualChildren().First();
  133. ((Control)item).DataContext = "test";
  134. Assert.Equal("test", w.SavedContext);
  135. }
  136. }
  137. [Fact]
  138. public void Custom_Properties_Should_Work_With_XClass()
  139. {
  140. var precompiled = new XamlIlClassWithCustomProperty();
  141. Assert.Equal("123", precompiled.Test);
  142. var loaded = (XamlIlClassWithCustomProperty)AvaloniaXamlLoader.Parse(@"
  143. <UserControl xmlns='https://github.com/avaloniaui'
  144. xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'
  145. x:Class='Avalonia.Markup.Xaml.UnitTests.XamlIlClassWithCustomProperty'
  146. Test='321'>
  147. </UserControl>");
  148. Assert.Equal("321", loaded.Test);
  149. }
  150. void AssertThrows(Action callback, Func<Exception, bool> check)
  151. {
  152. try
  153. {
  154. callback();
  155. }
  156. catch (Exception e) when (check(e))
  157. {
  158. return;
  159. }
  160. throw new Exception("Expected exception was not thrown");
  161. }
  162. public static object SomeStaticProperty { get; set; }
  163. [Fact]
  164. public void Bug2570()
  165. {
  166. SomeStaticProperty = "123";
  167. AssertThrows(() => new AvaloniaXamlLoader() {IsDesignMode = true}
  168. .Load(@"
  169. <UserControl
  170. xmlns='https://github.com/avaloniaui'
  171. xmlns:d='http://schemas.microsoft.com/expression/blend/2008'
  172. xmlns:tests='clr-namespace:Avalonia.Markup.Xaml.UnitTests'
  173. d:DataContext='{x:Static tests:XamlIlTests.SomeStaticPropery}'
  174. xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'/>", typeof(XamlIlTests).Assembly),
  175. e => e.Message.Contains("Unable to resolve ")
  176. && e.Message.Contains(" as static field, property, constant or enum value"));
  177. }
  178. [Fact]
  179. public void Design_Mode_DataContext_Should_Be_Set()
  180. {
  181. SomeStaticProperty = "123";
  182. var loaded = (UserControl)new AvaloniaXamlLoader() {IsDesignMode = true}
  183. .Load(@"
  184. <UserControl
  185. xmlns='https://github.com/avaloniaui'
  186. xmlns:d='http://schemas.microsoft.com/expression/blend/2008'
  187. xmlns:tests='clr-namespace:Avalonia.Markup.Xaml.UnitTests'
  188. d:DataContext='{x:Static tests:XamlIlTests.SomeStaticProperty}'
  189. xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'/>", typeof(XamlIlTests).Assembly);
  190. Assert.Equal(Design.GetDataContext(loaded), SomeStaticProperty);
  191. }
  192. [Fact]
  193. public void Attached_Properties_From_Static_Types_Should_Work_In_Style_Setters_Bug_2561()
  194. {
  195. using (UnitTestApplication.Start(TestServices.StyledWindow))
  196. {
  197. var parsed = (Window)AvaloniaXamlLoader.Parse(@"
  198. <Window
  199. xmlns='https://github.com/avaloniaui'
  200. xmlns:local='clr-namespace:Avalonia.Markup.Xaml.UnitTests;assembly=Avalonia.Markup.Xaml.UnitTests'
  201. >
  202. <Window.Styles>
  203. <Style Selector='TextBox'>
  204. <Setter Property='local:XamlIlBugTestsStaticClassWithAttachedProperty.TestInt' Value='100'/>
  205. </Style>
  206. </Window.Styles>
  207. <TextBox/>
  208. </Window>
  209. ");
  210. var tb = ((TextBox)parsed.Content);
  211. parsed.Show();
  212. tb.ApplyTemplate();
  213. Assert.Equal(100, XamlIlBugTestsStaticClassWithAttachedProperty.GetTestInt(tb));
  214. }
  215. }
  216. [Fact]
  217. public void Provide_Value_Target_Should_Provide_Clr_Property_Info()
  218. {
  219. var parsed = AvaloniaXamlLoader.Parse<XamlIlClassWithClrPropertyWithValue>(@"
  220. <XamlIlClassWithClrPropertyWithValue
  221. xmlns='clr-namespace:Avalonia.Markup.Xaml.UnitTests'
  222. Count='{XamlIlCheckClrPropertyInfo ExpectedPropertyName=Count}'
  223. />", typeof(XamlIlClassWithClrPropertyWithValue).Assembly);
  224. Assert.Equal(6, parsed.Count);
  225. }
  226. [Fact]
  227. public void DataContextType_Resolution()
  228. {
  229. using (UnitTestApplication.Start(TestServices.StyledWindow))
  230. {
  231. var parsed = AvaloniaXamlLoader.Parse<UserControl>(@"
  232. <UserControl
  233. xmlns='https://github.com/avaloniaui'
  234. xmlns:local='clr-namespace:Avalonia.Markup.Xaml.UnitTests;assembly=Avalonia.Markup.Xaml.UnitTests'
  235. xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml' x:DataType='local:XamlIlBugTestsDataContext' />");
  236. }
  237. }
  238. [Fact]
  239. public void DataTemplates_Should_Resolve_Named_Controls_From_Parent_Scope()
  240. {
  241. using (UnitTestApplication.Start(TestServices.StyledWindow))
  242. {
  243. var parsed = (Window)AvaloniaXamlLoader.Parse(@"
  244. <Window
  245. xmlns='https://github.com/avaloniaui'
  246. xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'
  247. >
  248. <StackPanel>
  249. <StackPanel.DataTemplates>
  250. <DataTemplate DataType='{x:Type x:String}'>
  251. <TextBlock Classes='target' Text='{Binding #txt.Text}'/>
  252. </DataTemplate>
  253. </StackPanel.DataTemplates>
  254. <TextBlock Text='Test' Name='txt'/>
  255. <ContentControl Content='tst'/>
  256. </StackPanel>
  257. </Window>
  258. ");
  259. parsed.DataContext = new List<string>() {"Test"};
  260. parsed.Show();
  261. parsed.ApplyTemplate();
  262. var cc = ((ContentControl)((StackPanel)parsed.Content).Children.Last());
  263. cc.ApplyTemplate();
  264. var templated = cc.GetVisualDescendants().OfType<TextBlock>()
  265. .First(x => x.Classes.Contains("target"));
  266. Assert.Equal("Test", templated.Text);
  267. }
  268. }
  269. }
  270. public class XamlIlBugTestsEventHandlerCodeBehind : Window
  271. {
  272. public object SavedContext;
  273. public void HandleDataContextChanged(object sender, EventArgs args)
  274. {
  275. SavedContext = ((Control)sender).DataContext;
  276. }
  277. public XamlIlBugTestsEventHandlerCodeBehind()
  278. {
  279. new AvaloniaXamlLoader().Load(@"
  280. <Window x:Class='Avalonia.Markup.Xaml.UnitTests.XamlIlBugTestsEventHandlerCodeBehind'
  281. xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'
  282. xmlns='https://github.com/avaloniaui'
  283. xmlns:local='clr-namespace:Avalonia.Markup.Xaml.UnitTests;assembly=Avalonia.Markup.Xaml.UnitTests'
  284. >
  285. <ItemsControl>
  286. <ItemsControl.ItemTemplate>
  287. <DataTemplate>
  288. <Button DataContextChanged='HandleDataContextChanged' Content='{Binding .}' />
  289. </DataTemplate>
  290. </ItemsControl.ItemTemplate>
  291. </ItemsControl>
  292. </Window>
  293. ", typeof(XamlIlBugTestsEventHandlerCodeBehind).Assembly, this);
  294. ((ItemsControl)Content).Items = new[] {"123"};
  295. }
  296. }
  297. public class XamlIlClassWithCustomProperty : UserControl
  298. {
  299. public string Test { get; set; }
  300. public XamlIlClassWithCustomProperty()
  301. {
  302. AvaloniaXamlLoader.Load(this);
  303. }
  304. }
  305. public class XamlIlBugTestsBrushToColorConverter : IMultiValueConverter
  306. {
  307. public object Convert(IList<object> values, Type targetType, object parameter, CultureInfo culture)
  308. {
  309. return (values[0] as ISolidColorBrush)?.Color;
  310. }
  311. }
  312. public class XamlIlBugTestsDataContext : INotifyPropertyChanged
  313. {
  314. public bool IsPressed { get; set; }
  315. public event PropertyChangedEventHandler PropertyChanged;
  316. [NotifyPropertyChangedInvocator]
  317. protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
  318. {
  319. PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
  320. }
  321. }
  322. public class XamlIlClassWithPrecompiledXaml : UserControl
  323. {
  324. }
  325. public static class XamlIlBugTestsStaticClassWithAttachedProperty
  326. {
  327. public static readonly AvaloniaProperty<int> TestIntProperty = AvaloniaProperty
  328. .RegisterAttached<Control, int>("TestInt", typeof(XamlIlBugTestsStaticClassWithAttachedProperty));
  329. public static void SetTestInt(Control control, int value)
  330. {
  331. control.SetValue(TestIntProperty, value);
  332. }
  333. public static int GetTestInt(Control control)
  334. {
  335. return (int)control.GetValue(TestIntProperty);
  336. }
  337. }
  338. public class XamlIlCheckClrPropertyInfoExtension
  339. {
  340. public string ExpectedPropertyName { get; set; }
  341. public object ProvideValue(IServiceProvider prov)
  342. {
  343. var pvt = prov.GetService<IProvideValueTarget>();
  344. var info = (ClrPropertyInfo)pvt.TargetProperty;
  345. var v = (int)info.Get(pvt.TargetObject);
  346. return v + 1;
  347. }
  348. }
  349. public class XamlIlClassWithClrPropertyWithValue
  350. {
  351. public int Count { get; set; }= 5;
  352. }
  353. }