PopupTests.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356
  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.Collections.Specialized;
  5. using System.Linq;
  6. using Moq;
  7. using Avalonia.Controls.Presenters;
  8. using Avalonia.Controls.Primitives;
  9. using Avalonia.Controls.Templates;
  10. using Avalonia.Layout;
  11. using Avalonia.LogicalTree;
  12. using Avalonia.Platform;
  13. using Avalonia.Styling;
  14. using Avalonia.UnitTests;
  15. using Avalonia.VisualTree;
  16. using Xunit;
  17. using Avalonia.Input;
  18. namespace Avalonia.Controls.UnitTests.Primitives
  19. {
  20. public class PopupTests
  21. {
  22. [Fact]
  23. public void Setting_Child_Should_Set_Child_Controls_LogicalParent()
  24. {
  25. var target = new Popup();
  26. var child = new Control();
  27. target.Child = child;
  28. Assert.Equal(child.Parent, target);
  29. Assert.Equal(((ILogical)child).LogicalParent, target);
  30. }
  31. [Fact]
  32. public void Clearing_Child_Should_Clear_Child_Controls_Parent()
  33. {
  34. var target = new Popup();
  35. var child = new Control();
  36. target.Child = child;
  37. target.Child = null;
  38. Assert.Null(child.Parent);
  39. Assert.Null(((ILogical)child).LogicalParent);
  40. }
  41. [Fact]
  42. public void Child_Control_Should_Appear_In_LogicalChildren()
  43. {
  44. var target = new Popup();
  45. var child = new Control();
  46. target.Child = child;
  47. Assert.Equal(new[] { child }, target.GetLogicalChildren());
  48. }
  49. [Fact]
  50. public void Clearing_Child_Should_Remove_From_LogicalChildren()
  51. {
  52. var target = new Popup();
  53. var child = new Control();
  54. target.Child = child;
  55. target.Child = null;
  56. Assert.Equal(new ILogical[0], ((ILogical)target).LogicalChildren.ToList());
  57. }
  58. [Fact]
  59. public void Setting_Child_Should_Fire_LogicalChildren_CollectionChanged()
  60. {
  61. var target = new Popup();
  62. var child = new Control();
  63. var called = false;
  64. ((ILogical)target).LogicalChildren.CollectionChanged += (s, e) =>
  65. called = e.Action == NotifyCollectionChangedAction.Add;
  66. target.Child = child;
  67. Assert.True(called);
  68. }
  69. [Fact]
  70. public void Clearing_Child_Should_Fire_LogicalChildren_CollectionChanged()
  71. {
  72. var target = new Popup();
  73. var child = new Control();
  74. var called = false;
  75. target.Child = child;
  76. ((ILogical)target).LogicalChildren.CollectionChanged += (s, e) =>
  77. called = e.Action == NotifyCollectionChangedAction.Remove;
  78. target.Child = null;
  79. Assert.True(called);
  80. }
  81. [Fact]
  82. public void Changing_Child_Should_Fire_LogicalChildren_CollectionChanged()
  83. {
  84. var target = new Popup();
  85. var child1 = new Control();
  86. var child2 = new Control();
  87. var called = false;
  88. target.Child = child1;
  89. ((ILogical)target).LogicalChildren.CollectionChanged += (s, e) => called = true;
  90. target.Child = child2;
  91. Assert.True(called);
  92. }
  93. [Fact]
  94. public void Setting_Child_Should_Not_Set_Childs_VisualParent()
  95. {
  96. var target = new Popup();
  97. var child = new Control();
  98. target.Child = child;
  99. Assert.Null(((IVisual)child).VisualParent);
  100. }
  101. [Fact]
  102. public void PopupRoot_Should_Initially_Be_Null()
  103. {
  104. using (CreateServices())
  105. {
  106. var target = new Popup();
  107. Assert.Null(target.PopupRoot);
  108. }
  109. }
  110. [Fact]
  111. public void PopupRoot_Should_Have_Null_VisualParent()
  112. {
  113. using (CreateServices())
  114. {
  115. var target = new Popup();
  116. target.Open();
  117. Assert.Null(target.PopupRoot.GetVisualParent());
  118. }
  119. }
  120. [Fact]
  121. public void PopupRoot_Should_Have_Popup_As_LogicalParent()
  122. {
  123. using (CreateServices())
  124. {
  125. var target = new Popup();
  126. target.Open();
  127. Assert.Equal(target, target.PopupRoot.Parent);
  128. Assert.Equal(target, target.PopupRoot.GetLogicalParent());
  129. }
  130. }
  131. [Fact]
  132. public void PopupRoot_Should_Be_Detached_From_Logical_Tree_When_Popup_Is_Detached()
  133. {
  134. using (CreateServices())
  135. {
  136. var target = new Popup();
  137. var root = new TestRoot { Child = target };
  138. target.Open();
  139. var popupRoot = (ILogical)target.PopupRoot;
  140. Assert.True(popupRoot.IsAttachedToLogicalTree);
  141. root.Child = null;
  142. Assert.False(((ILogical)target).IsAttachedToLogicalTree);
  143. }
  144. }
  145. [Fact]
  146. public void PopupRoot_Should_Have_Template_Applied()
  147. {
  148. using (CreateServices())
  149. {
  150. var window = new Window();
  151. var target = new Popup();
  152. var child = new Control();
  153. window.Content = target;
  154. target.Open();
  155. Assert.Single(target.PopupRoot.GetVisualChildren());
  156. var templatedChild = target.PopupRoot.GetVisualChildren().Single();
  157. Assert.IsType<ContentPresenter>(templatedChild);
  158. Assert.Equal(target.PopupRoot, ((IControl)templatedChild).TemplatedParent);
  159. }
  160. }
  161. [Fact]
  162. public void Templated_Control_With_Popup_In_Template_Should_Set_TemplatedParent()
  163. {
  164. using (CreateServices())
  165. {
  166. PopupContentControl target;
  167. var root = new TestRoot
  168. {
  169. Child = target = new PopupContentControl
  170. {
  171. Content = new Border(),
  172. Template = new FuncControlTemplate<PopupContentControl>(PopupContentControlTemplate),
  173. },
  174. StylingParent = AvaloniaLocator.Current.GetService<IGlobalStyles>()
  175. };
  176. target.ApplyTemplate();
  177. var popup = (Popup)target.GetTemplateChildren().First(x => x.Name == "popup");
  178. popup.Open();
  179. var popupRoot = popup.PopupRoot;
  180. var children = popupRoot.GetVisualDescendants().ToList();
  181. var types = children.Select(x => x.GetType().Name).ToList();
  182. Assert.Equal(
  183. new[]
  184. {
  185. "ContentPresenter",
  186. "ContentPresenter",
  187. "Border",
  188. },
  189. types);
  190. var templatedParents = children
  191. .OfType<IControl>()
  192. .Select(x => x.TemplatedParent).ToList();
  193. Assert.Equal(
  194. new object[]
  195. {
  196. popupRoot,
  197. target,
  198. null,
  199. },
  200. templatedParents);
  201. }
  202. }
  203. [Fact]
  204. public void DataContextBeginUpdate_Should_Not_Be_Called_For_Controls_That_Dont_Inherit()
  205. {
  206. using (CreateServices())
  207. {
  208. TestControl child;
  209. var popup = new Popup
  210. {
  211. Child = child = new TestControl(),
  212. DataContext = "foo",
  213. };
  214. var beginCalled = false;
  215. child.DataContextBeginUpdate += (s, e) => beginCalled = true;
  216. // Test for #1245. Here, the child's logical parent is the popup but it's not yet
  217. // attached to a visual tree because the popup hasn't been opened.
  218. Assert.Same(popup, ((ILogical)child).LogicalParent);
  219. Assert.Same(popup, child.InheritanceParent);
  220. Assert.Null(child.GetVisualRoot());
  221. popup.Open();
  222. // #1245 was caused by the fact that DataContextBeginUpdate was called on `target`
  223. // when the PopupRoot was created, even though PopupRoot isn't the
  224. // InheritanceParent of child.
  225. Assert.False(beginCalled);
  226. }
  227. }
  228. private static IDisposable CreateServices()
  229. {
  230. var result = AvaloniaLocator.EnterScope();
  231. var styles = new Styles
  232. {
  233. new Style(x => x.OfType<PopupRoot>())
  234. {
  235. Setters = new[]
  236. {
  237. new Setter(TemplatedControl.TemplateProperty, new FuncControlTemplate<PopupRoot>(PopupRootTemplate)),
  238. }
  239. },
  240. };
  241. var globalStyles = new Mock<IGlobalStyles>();
  242. globalStyles.Setup(x => x.IsStylesInitialized).Returns(true);
  243. globalStyles.Setup(x => x.Styles).Returns(styles);
  244. var renderInterface = new Mock<IPlatformRenderInterface>();
  245. AvaloniaLocator.CurrentMutable
  246. .Bind<IGlobalStyles>().ToFunc(() => globalStyles.Object)
  247. .Bind<IWindowingPlatform>().ToConstant(new WindowingPlatformMock())
  248. .Bind<IStyler>().ToTransient<Styler>()
  249. .Bind<IPlatformRenderInterface>().ToFunc(() => renderInterface.Object)
  250. .Bind<IInputManager>().ToConstant(new InputManager());
  251. return result;
  252. }
  253. private static IControl PopupRootTemplate(PopupRoot control)
  254. {
  255. return new ContentPresenter
  256. {
  257. Name = "PART_ContentPresenter",
  258. [~ContentPresenter.ContentProperty] = control[~ContentControl.ContentProperty],
  259. };
  260. }
  261. private static IControl PopupContentControlTemplate(PopupContentControl control)
  262. {
  263. return new Popup
  264. {
  265. Name = "popup",
  266. Child = new ContentPresenter
  267. {
  268. [~ContentPresenter.ContentProperty] = control[~ContentControl.ContentProperty],
  269. }
  270. };
  271. }
  272. private class PopupContentControl : ContentControl
  273. {
  274. }
  275. private class TestControl : Decorator
  276. {
  277. public event EventHandler DataContextBeginUpdate;
  278. public new IAvaloniaObject InheritanceParent => base.InheritanceParent;
  279. protected override void OnDataContextBeginUpdate()
  280. {
  281. DataContextBeginUpdate?.Invoke(this, EventArgs.Empty);
  282. base.OnDataContextBeginUpdate();
  283. }
  284. }
  285. }
  286. }