MenuItemTests.cs 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using Avalonia.Controls.Presenters;
  5. using Avalonia.Controls.Templates;
  6. using Avalonia.Controls.UnitTests.Utils;
  7. using Avalonia.Data;
  8. using Avalonia.Input;
  9. using Avalonia.Platform;
  10. using Avalonia.Styling;
  11. using Avalonia.Threading;
  12. using Avalonia.UnitTests;
  13. using Moq;
  14. using Xunit;
  15. namespace Avalonia.Controls.UnitTests
  16. {
  17. public class MenuItemTests : ScopedTestBase
  18. {
  19. private Mock<IPopupImpl> popupImpl;
  20. [Fact]
  21. public void Header_Of_Minus_Should_Apply_Separator_Pseudoclass()
  22. {
  23. var target = new MenuItem { Header = "-" };
  24. Assert.True(target.Classes.Contains(":separator"));
  25. }
  26. [Fact]
  27. public void Separator_Item_Should_Set_Focusable_False()
  28. {
  29. var target = new MenuItem { Header = "-" };
  30. Assert.False(target.Focusable);
  31. }
  32. [Fact]
  33. public void MenuItem_Is_Disabled_When_Command_Is_Enabled_But_IsEnabled_Is_False()
  34. {
  35. var command = new TestCommand(true);
  36. var target = new MenuItem
  37. {
  38. IsEnabled = false,
  39. Command = command,
  40. };
  41. var root = new TestRoot { Child = target };
  42. Assert.False(((IInputElement)target).IsEffectivelyEnabled);
  43. }
  44. [Fact]
  45. public void MenuItem_Is_Disabled_When_Bound_Command_Doesnt_Exist()
  46. {
  47. var target = new MenuItem
  48. {
  49. [!MenuItem.CommandProperty] = new Binding("Command"),
  50. };
  51. Assert.True(target.IsEnabled);
  52. Assert.False(target.IsEffectivelyEnabled);
  53. }
  54. [Fact]
  55. public void MenuItem_With_Styled_Command_Binding_Should_Be_Enabled_With_Child_Missing_Command()
  56. {
  57. using var app = Application();
  58. var viewModel = new MenuViewModel("Parent")
  59. {
  60. Children = [new MenuViewModel("Child")]
  61. };
  62. var contextMenu = new ContextMenu
  63. {
  64. ItemsSource = new[] { viewModel },
  65. Styles =
  66. {
  67. new Style(x => x.OfType<MenuItem>())
  68. {
  69. Setters =
  70. {
  71. new Setter(MenuItem.HeaderProperty, new Binding("Header")),
  72. new Setter(MenuItem.ItemsSourceProperty, new Binding("Children")),
  73. new Setter(MenuItem.CommandProperty, new Binding("Command"))
  74. }
  75. }
  76. }
  77. };
  78. var window = new Window { ContextMenu = contextMenu };
  79. window.Show();
  80. contextMenu.Open();
  81. var parentMenuItem = Assert.IsType<MenuItem>(contextMenu.ContainerFromIndex(0));
  82. Assert.Same(parentMenuItem.DataContext, viewModel);
  83. Assert.Same(parentMenuItem.ItemsSource, viewModel.Children);
  84. Assert.True(parentMenuItem.IsEnabled);
  85. Assert.True(parentMenuItem.IsEffectivelyEnabled);
  86. }
  87. [Fact]
  88. public void MenuItem_Is_Disabled_When_Bound_Command_Is_Removed()
  89. {
  90. var viewModel = new
  91. {
  92. Command = new TestCommand(true),
  93. };
  94. var target = new MenuItem
  95. {
  96. DataContext = viewModel,
  97. [!MenuItem.CommandProperty] = new Binding("Command"),
  98. };
  99. Assert.True(target.IsEnabled);
  100. Assert.True(target.IsEffectivelyEnabled);
  101. target.DataContext = null;
  102. Assert.True(target.IsEnabled);
  103. Assert.False(target.IsEffectivelyEnabled);
  104. }
  105. [Fact]
  106. public void MenuItem_Is_Enabled_When_Added_To_Logical_Tree_And_Bound_Command_Is_Added()
  107. {
  108. var viewModel = new
  109. {
  110. Command = new TestCommand(true),
  111. };
  112. var target = new MenuItem
  113. {
  114. DataContext = new object(),
  115. [!MenuItem.CommandProperty] = new Binding("Command"),
  116. };
  117. var root = new TestRoot { Child = target };
  118. Dispatcher.UIThread.RunJobs(DispatcherPriority.Loaded);
  119. Assert.True(target.IsEnabled);
  120. Assert.False(target.IsEffectivelyEnabled);
  121. target.DataContext = viewModel;
  122. Assert.True(target.IsEnabled);
  123. Assert.True(target.IsEffectivelyEnabled);
  124. }
  125. [Fact]
  126. public void MenuItem_Is_Disabled_When_Disabled_Bound_Command_Is_Added()
  127. {
  128. var viewModel = new
  129. {
  130. Command = new TestCommand(false),
  131. };
  132. var target = new MenuItem
  133. {
  134. DataContext = new object(),
  135. [!MenuItem.CommandProperty] = new Binding("Command"),
  136. };
  137. Assert.True(target.IsEnabled);
  138. Assert.False(target.IsEffectivelyEnabled);
  139. target.DataContext = viewModel;
  140. Assert.True(target.IsEnabled);
  141. Assert.False(target.IsEffectivelyEnabled);
  142. }
  143. [Fact]
  144. public void MenuItem_Does_Not_Subscribe_To_Command_CanExecuteChanged_Until_Added_To_Logical_Tree()
  145. {
  146. var command = new TestCommand();
  147. var target = new MenuItem
  148. {
  149. Command = command,
  150. };
  151. Assert.Equal(0, command.SubscriptionCount);
  152. }
  153. [Fact]
  154. public void MenuItem_Subscribes_To_Command_CanExecuteChanged_When_Added_To_Logical_Tree()
  155. {
  156. var command = new TestCommand();
  157. var target = new MenuItem { Command = command };
  158. var root = new TestRoot { Child = target };
  159. Assert.Equal(1, command.SubscriptionCount);
  160. }
  161. [Fact]
  162. public void MenuItem_Unsubscribes_From_Command_CanExecuteChanged_When_Removed_From_Logical_Tree()
  163. {
  164. var command = new TestCommand();
  165. var target = new MenuItem { Command = command };
  166. var root = new TestRoot { Child = target };
  167. root.Child = null;
  168. Assert.Equal(0, command.SubscriptionCount);
  169. }
  170. [Fact]
  171. public void MenuItem_Invokes_CanExecute_When_Added_To_Logical_Tree_And_CommandParameter_Changed()
  172. {
  173. var command = new TestCommand(p => p is bool value && value);
  174. var target = new MenuItem { Command = command };
  175. var root = new TestRoot { Child = target };
  176. Dispatcher.UIThread.RunJobs(DispatcherPriority.Loaded);
  177. target.CommandParameter = true;
  178. Assert.True(target.IsEffectivelyEnabled);
  179. target.CommandParameter = false;
  180. Assert.False(target.IsEffectivelyEnabled);
  181. }
  182. [Fact]
  183. public void MenuItem_Does_Not_Invoke_CanExecute_When_ContextMenu_Closed()
  184. {
  185. using (Application())
  186. {
  187. var canExecuteCallCount = 0;
  188. var command = new TestCommand(_ =>
  189. {
  190. canExecuteCallCount++;
  191. return true;
  192. });
  193. var target = new MenuItem();
  194. var contextMenu = new ContextMenu { Items = { target } };
  195. var window = new Window { Content = new Panel { ContextMenu = contextMenu } };
  196. window.ApplyStyling();
  197. window.ApplyTemplate();
  198. window.Presenter.ApplyTemplate();
  199. Dispatcher.UIThread.RunJobs(DispatcherPriority.Loaded);
  200. Assert.True(target.IsEffectivelyEnabled);
  201. target.Command = command;
  202. Assert.Equal(0, canExecuteCallCount);
  203. target.CommandParameter = false;
  204. Assert.Equal(0, canExecuteCallCount);
  205. command.RaiseCanExecuteChanged();
  206. Assert.Equal(0, canExecuteCallCount);
  207. contextMenu.Open();
  208. Dispatcher.UIThread.RunJobs(DispatcherPriority.Loaded);
  209. Assert.Equal(3, canExecuteCallCount);// 3 because popup is changing logical child and moreover we need to invalidate again after the item is attached to the visual tree
  210. command.RaiseCanExecuteChanged();
  211. Assert.Equal(4, canExecuteCallCount);
  212. target.CommandParameter = true;
  213. Assert.Equal(5, canExecuteCallCount);
  214. }
  215. }
  216. [Fact]
  217. public void MenuItem_Does_Not_Invoke_CanExecute_When_MenuFlyout_Closed()
  218. {
  219. using (Application())
  220. {
  221. var canExecuteCallCount = 0;
  222. var command = new TestCommand(_ =>
  223. {
  224. canExecuteCallCount++;
  225. return true;
  226. });
  227. var target = new MenuItem();
  228. var flyout = new MenuFlyout { Items = { target } };
  229. var button = new Button { Flyout = flyout };
  230. var window = new Window { Content = button };
  231. window.ApplyStyling();
  232. window.ApplyTemplate();
  233. window.Presenter.ApplyTemplate();
  234. Dispatcher.UIThread.RunJobs(DispatcherPriority.Loaded);
  235. Assert.True(target.IsEffectivelyEnabled);
  236. target.Command = command;
  237. Assert.Equal(0, canExecuteCallCount);
  238. target.CommandParameter = false;
  239. Assert.Equal(0, canExecuteCallCount);
  240. command.RaiseCanExecuteChanged();
  241. Assert.Equal(0, canExecuteCallCount);
  242. flyout.ShowAt(button);
  243. Dispatcher.UIThread.RunJobs(DispatcherPriority.Loaded);
  244. Assert.Equal(2, canExecuteCallCount); // 2 because we need to invalidate after the item is attached to the visual tree
  245. command.RaiseCanExecuteChanged();
  246. Assert.Equal(3, canExecuteCallCount);
  247. target.CommandParameter = true;
  248. Assert.Equal(4, canExecuteCallCount);
  249. }
  250. }
  251. [Fact]
  252. public void MenuItem_Does_Not_Invoke_CanExecute_When_Parent_MenuItem_Closed()
  253. {
  254. using (Application())
  255. {
  256. var canExecuteCallCount = 0;
  257. var command = new TestCommand(_ =>
  258. {
  259. canExecuteCallCount++;
  260. return true;
  261. });
  262. var target = new MenuItem();
  263. var parentMenuItem = new MenuItem { Items = { target } };
  264. var contextMenu = new ContextMenu { Items = { parentMenuItem } };
  265. var window = new Window { Content = new Panel { ContextMenu = contextMenu } };
  266. window.ApplyStyling();
  267. window.ApplyTemplate();
  268. window.Presenter.ApplyTemplate();
  269. contextMenu.Open();
  270. Dispatcher.UIThread.RunJobs(DispatcherPriority.Loaded);
  271. Assert.True(target.IsEffectivelyEnabled);
  272. target.Command = command;
  273. Assert.Equal(0, canExecuteCallCount);
  274. target.CommandParameter = false;
  275. Assert.Equal(0, canExecuteCallCount);
  276. command.RaiseCanExecuteChanged();
  277. Assert.Equal(0, canExecuteCallCount);
  278. try
  279. {
  280. parentMenuItem.IsSubMenuOpen = true;
  281. }
  282. catch (InvalidOperationException)
  283. {
  284. //popup host creation failed exception
  285. }
  286. Assert.Equal(1, canExecuteCallCount);
  287. command.RaiseCanExecuteChanged();
  288. Assert.Equal(2, canExecuteCallCount);
  289. target.CommandParameter = true;
  290. Assert.Equal(3, canExecuteCallCount);
  291. }
  292. }
  293. [Fact]
  294. public void TemplatedParent_Should_Not_Be_Applied_To_Submenus()
  295. {
  296. using (Application())
  297. {
  298. MenuItem topLevelMenu;
  299. MenuItem childMenu1;
  300. MenuItem childMenu2;
  301. var menu = new Menu
  302. {
  303. Items =
  304. {
  305. (topLevelMenu = new MenuItem
  306. {
  307. Header = "Foo",
  308. Items =
  309. {
  310. (childMenu1 = new MenuItem { Header = "Bar" }),
  311. (childMenu2 = new MenuItem { Header = "Baz" }),
  312. }
  313. }),
  314. }
  315. };
  316. var window = new Window { Content = menu };
  317. window.Show();
  318. window.LayoutManager.ExecuteInitialLayoutPass();
  319. topLevelMenu.IsSubMenuOpen = true;
  320. Assert.True(childMenu1.IsAttachedToVisualTree);
  321. Assert.Null(childMenu1.TemplatedParent);
  322. Assert.Null(childMenu2.TemplatedParent);
  323. topLevelMenu.IsSubMenuOpen = false;
  324. topLevelMenu.IsSubMenuOpen = true;
  325. Assert.Null(childMenu1.TemplatedParent);
  326. Assert.Null(childMenu2.TemplatedParent);
  327. }
  328. }
  329. [Fact]
  330. public void Menu_ItemTemplate_Should_Be_Applied_To_TopLevel_MenuItem_Header()
  331. {
  332. using var app = Application();
  333. var items = new[]
  334. {
  335. new MenuViewModel("Foo"),
  336. new MenuViewModel("Bar"),
  337. };
  338. var itemTemplate = new FuncDataTemplate<MenuViewModel>((x, _) =>
  339. new TextBlock { Text = x.Header });
  340. var menu = new Menu
  341. {
  342. ItemTemplate = itemTemplate,
  343. ItemsSource = items,
  344. };
  345. var window = new Window { Content = menu };
  346. window.Show();
  347. window.LayoutManager.ExecuteInitialLayoutPass();
  348. var panel = Assert.IsType<StackPanel>(menu.Presenter.Panel);
  349. Assert.Equal(2, panel.Children.Count);
  350. for (var i = 0; i < panel.Children.Count; i++)
  351. {
  352. var menuItem = Assert.IsType<MenuItem>(panel.Children[i]);
  353. Assert.Equal(items[i], menuItem.Header);
  354. Assert.Same(itemTemplate, menuItem.HeaderTemplate);
  355. var headerPresenter = Assert.IsType<ContentPresenter>(menuItem.HeaderPresenter);
  356. Assert.Same(itemTemplate, headerPresenter.ContentTemplate);
  357. var headerControl = Assert.IsType<TextBlock>(headerPresenter.Child);
  358. Assert.Equal(items[i].Header, headerControl.Text);
  359. }
  360. }
  361. [Fact]
  362. public void Header_And_ItemsSource_Can_Be_Bound_In_Style()
  363. {
  364. using var app = Application();
  365. var items = new[]
  366. {
  367. new MenuViewModel("Foo")
  368. {
  369. Children = new[]
  370. {
  371. new MenuViewModel("FooChild"),
  372. },
  373. },
  374. new MenuViewModel("Bar"),
  375. };
  376. var target = new Menu
  377. {
  378. ItemsSource = items,
  379. Styles =
  380. {
  381. new Style(x => x.OfType<MenuItem>())
  382. {
  383. Setters =
  384. {
  385. new Setter(MenuItem.HeaderProperty, new Binding("Header")),
  386. new Setter(MenuItem.ItemsSourceProperty, new Binding("Children")),
  387. }
  388. }
  389. }
  390. };
  391. var root = new TestRoot(true, target);
  392. root.LayoutManager.ExecuteInitialLayoutPass();
  393. var children = target.GetRealizedContainers().Cast<MenuItem>().ToList();
  394. Assert.Equal(2, children.Count);
  395. Assert.Equal("Foo", children[0].Header);
  396. Assert.Equal("Bar", children[1].Header);
  397. Assert.Same(items[0].Children, children[0].ItemsSource);
  398. }
  399. [Fact]
  400. public void Header_And_ItemsSource_Can_Be_Bound_In_ItemContainerTheme()
  401. {
  402. using var app = Application();
  403. var items = new[]
  404. {
  405. new MenuViewModel("Foo")
  406. {
  407. Children = new[]
  408. {
  409. new MenuViewModel("FooChild"),
  410. },
  411. },
  412. new MenuViewModel("Bar"),
  413. };
  414. var target = new Menu
  415. {
  416. ItemsSource = items,
  417. ItemContainerTheme = new ControlTheme(typeof(MenuItem))
  418. {
  419. Setters =
  420. {
  421. new Setter(MenuItem.HeaderProperty, new Binding("Header")),
  422. new Setter(MenuItem.ItemsSourceProperty, new Binding("Children")),
  423. }
  424. }
  425. };
  426. var root = new TestRoot(true, target);
  427. root.LayoutManager.ExecuteInitialLayoutPass();
  428. var children = target.GetRealizedContainers().Cast<MenuItem>().ToList();
  429. Assert.Equal(2, children.Count);
  430. Assert.Equal("Foo", children[0].Header);
  431. Assert.Equal("Bar", children[1].Header);
  432. Assert.Same(items[0].Children, children[0].ItemsSource);
  433. }
  434. [Fact]
  435. public void Radio_MenuItem_In_Same_Group_Is_Unchecked()
  436. {
  437. using var app = Application();
  438. MenuItem menuItem1, menuItem2, menuItem3;
  439. var menu = new Menu
  440. {
  441. Items =
  442. {
  443. (menuItem1 = new MenuItem
  444. {
  445. GroupName = "A", IsChecked = false, ToggleType = MenuItemToggleType.Radio
  446. }),
  447. (menuItem2 = new MenuItem
  448. {
  449. GroupName = "A", IsChecked = true, ToggleType = MenuItemToggleType.Radio
  450. }),
  451. (menuItem3 = new MenuItem
  452. {
  453. GroupName = "A", IsChecked = false, ToggleType = MenuItemToggleType.Radio
  454. })
  455. }
  456. };
  457. var window = new Window { Content = menu };
  458. window.Show();
  459. Assert.False(menuItem1.IsChecked);
  460. Assert.True(menuItem2.IsChecked);
  461. Assert.False(menuItem3.IsChecked);
  462. menuItem3.IsChecked = true;
  463. Assert.False(menuItem1.IsChecked);
  464. Assert.False(menuItem2.IsChecked);
  465. Assert.True(menuItem3.IsChecked);
  466. }
  467. [Fact]
  468. public void Radio_Menu_Group_Can_Be_Changed_In_Runtime()
  469. {
  470. using var app = Application();
  471. MenuItem menuItem1, menuItem2, menuItem3;
  472. var menu = new Menu
  473. {
  474. Items =
  475. {
  476. (menuItem1 = new MenuItem
  477. {
  478. GroupName = "A", IsChecked = false, ToggleType = MenuItemToggleType.Radio
  479. }),
  480. (menuItem2 = new MenuItem
  481. {
  482. GroupName = "A", IsChecked = true, ToggleType = MenuItemToggleType.Radio
  483. }),
  484. (menuItem3 = new MenuItem
  485. {
  486. GroupName = null, IsChecked = false, ToggleType = MenuItemToggleType.Radio
  487. })
  488. }
  489. };
  490. var window = new Window { Content = menu };
  491. window.Show();
  492. Assert.False(menuItem1.IsChecked);
  493. Assert.True(menuItem2.IsChecked);
  494. Assert.False(menuItem3.IsChecked);
  495. menuItem3.GroupName = "A";
  496. menuItem3.IsChecked = true;
  497. Assert.False(menuItem1.IsChecked);
  498. Assert.False(menuItem2.IsChecked);
  499. Assert.True(menuItem3.IsChecked);
  500. menuItem3.GroupName = null;
  501. menuItem1.IsChecked = true;
  502. Assert.True(menuItem1.IsChecked);
  503. Assert.False(menuItem2.IsChecked);
  504. Assert.True(menuItem3.IsChecked);
  505. }
  506. [Fact]
  507. public void Radio_MenuItem_In_Same_Group_But_Submenu_Is_Unchecked()
  508. {
  509. using var app = Application();
  510. MenuItem menuItem1, menuItem2, menuItem3, menuItem4;
  511. var menu = new Menu
  512. {
  513. Items =
  514. {
  515. (menuItem1 = new MenuItem
  516. {
  517. GroupName = "A", IsChecked = false, ToggleType = MenuItemToggleType.Radio
  518. }),
  519. (menuItem2 = new MenuItem
  520. {
  521. GroupName = "A", IsChecked = false, ToggleType = MenuItemToggleType.Radio
  522. }),
  523. (menuItem3 = new MenuItem
  524. {
  525. GroupName = "A",
  526. IsChecked = true,
  527. ToggleType = MenuItemToggleType.Radio,
  528. Items =
  529. {
  530. (menuItem4 = new MenuItem
  531. {
  532. GroupName = "A",
  533. IsChecked = true,
  534. ToggleType = MenuItemToggleType.Radio
  535. })
  536. }
  537. }),
  538. }
  539. };
  540. var window = new Window { Content = menu };
  541. window.Show();
  542. Assert.False(menuItem1.IsChecked);
  543. Assert.False(menuItem2.IsChecked);
  544. Assert.True(menuItem3.IsChecked);
  545. Assert.True(menuItem4.IsChecked);
  546. menuItem2.IsChecked = true;
  547. Assert.False(menuItem1.IsChecked);
  548. Assert.True(menuItem2.IsChecked);
  549. Assert.False(menuItem3.IsChecked);
  550. Assert.False(menuItem4.IsChecked);
  551. }
  552. [Fact]
  553. public void Radio_MenuItem_In_Same_Group_But_Submenu_Is_Checked()
  554. {
  555. using var app = Application();
  556. MenuItem menuItem1, menuItem2, menuItem3, menuItem4;
  557. var menu = new Menu
  558. {
  559. Items =
  560. {
  561. (menuItem1 = new MenuItem
  562. {
  563. GroupName = "A", IsChecked = false, ToggleType = MenuItemToggleType.Radio
  564. }),
  565. (menuItem2 = new MenuItem
  566. {
  567. GroupName = "A", IsChecked = true, ToggleType = MenuItemToggleType.Radio
  568. }),
  569. (menuItem3 = new MenuItem
  570. {
  571. GroupName = "A",
  572. IsChecked = false,
  573. ToggleType = MenuItemToggleType.Radio,
  574. Items =
  575. {
  576. (menuItem4 = new MenuItem
  577. {
  578. GroupName = "A",
  579. IsChecked = false,
  580. ToggleType = MenuItemToggleType.Radio
  581. })
  582. }
  583. }),
  584. }
  585. };
  586. var window = new Window { Content = menu };
  587. window.Show();
  588. Assert.False(menuItem1.IsChecked);
  589. Assert.True(menuItem2.IsChecked);
  590. Assert.False(menuItem3.IsChecked);
  591. Assert.False(menuItem4.IsChecked);
  592. menuItem4.IsChecked = true;
  593. Assert.False(menuItem1.IsChecked);
  594. Assert.False(menuItem2.IsChecked);
  595. Assert.True(menuItem3.IsChecked);
  596. Assert.True(menuItem4.IsChecked);
  597. }
  598. [Fact]
  599. public void Radio_MenuItem_Empty_GroupName_Not_Influence_Other_Groups()
  600. {
  601. using var app = Application();
  602. MenuItem menuItem1, menuItem2, menuItem3, menuItem4;
  603. var menu = new Menu
  604. {
  605. Items =
  606. {
  607. (menuItem1 = new MenuItem
  608. {
  609. GroupName = "A", IsChecked = true, ToggleType = MenuItemToggleType.Radio
  610. }),
  611. (menuItem2 = new MenuItem
  612. {
  613. GroupName = "A", IsChecked = false, ToggleType = MenuItemToggleType.Radio
  614. }),
  615. (menuItem3 = new MenuItem
  616. {
  617. GroupName = null, IsChecked = false, ToggleType = MenuItemToggleType.Radio
  618. }),
  619. (menuItem4 = new MenuItem
  620. {
  621. GroupName = null, IsChecked = true, ToggleType = MenuItemToggleType.Radio
  622. })
  623. }
  624. };
  625. var window = new Window { Content = menu };
  626. window.Show();
  627. Assert.True(menuItem1.IsChecked);
  628. Assert.False(menuItem2.IsChecked);
  629. Assert.False(menuItem3.IsChecked);
  630. Assert.True(menuItem4.IsChecked);
  631. menuItem3.IsChecked = true;
  632. Assert.True(menuItem1.IsChecked);
  633. Assert.False(menuItem2.IsChecked);
  634. Assert.True(menuItem3.IsChecked);
  635. Assert.False(menuItem4.IsChecked);
  636. }
  637. [Fact]
  638. public void Radio_Menus_With_Empty_Group_On_Different_Levels_Can_Be_Checked_Simultaneously()
  639. {
  640. using var app = Application();
  641. MenuItem menuItem1, menuItem2, menuItem3, menuItem4;
  642. var menu = new Menu
  643. {
  644. Items =
  645. {
  646. (menuItem1 = new MenuItem
  647. {
  648. GroupName = null, IsChecked = true, ToggleType = MenuItemToggleType.Radio
  649. }),
  650. (menuItem2 = new MenuItem
  651. {
  652. GroupName = null,
  653. IsChecked = false,
  654. ToggleType = MenuItemToggleType.Radio,
  655. Items =
  656. {
  657. (menuItem3 = new MenuItem
  658. {
  659. GroupName = null,
  660. IsChecked = false,
  661. ToggleType = MenuItemToggleType.Radio
  662. }),
  663. (menuItem4 = new MenuItem
  664. {
  665. GroupName = null,
  666. IsChecked = false,
  667. ToggleType = MenuItemToggleType.Radio
  668. }),
  669. }
  670. })
  671. }
  672. };
  673. var window = new Window { Content = menu };
  674. window.Show();
  675. Assert.True(menuItem1.IsChecked);
  676. Assert.False(menuItem2.IsChecked);
  677. Assert.False(menuItem3.IsChecked);
  678. Assert.False(menuItem4.IsChecked);
  679. menuItem3.IsChecked = true;
  680. Assert.True(menuItem1.IsChecked);
  681. Assert.False(menuItem2.IsChecked);
  682. Assert.True(menuItem3.IsChecked);
  683. Assert.False(menuItem4.IsChecked);
  684. }
  685. [Fact]
  686. public void MenuItem_CommandParameter_Does_Not_Change_While_Execution()
  687. {
  688. var target = new MenuItem();
  689. object lastParamenter = "A";
  690. var generator = new Random();
  691. var onlyOnce = false;
  692. var command = new TestCommand(parameter =>
  693. {
  694. if (!onlyOnce)
  695. {
  696. onlyOnce = true;
  697. target.CommandParameter = generator.Next();
  698. }
  699. lastParamenter = parameter;
  700. return true;
  701. },
  702. parameter =>
  703. {
  704. Assert.Equal(lastParamenter, parameter);
  705. });
  706. target.CommandParameter = lastParamenter;
  707. target.Command = command;
  708. var root = new TestRoot { Child = target };
  709. (target as IClickableControl).RaiseClick();
  710. }
  711. private IDisposable Application()
  712. {
  713. var screen = new PixelRect(new PixelPoint(), new PixelSize(100, 100));
  714. var screenImpl = new Mock<IScreenImpl>();
  715. screenImpl.Setup(x => x.ScreenCount).Returns(1);
  716. screenImpl.Setup(X => X.AllScreens).Returns(new[] { new Screen(1, screen, screen, true) });
  717. var windowImpl = MockWindowingPlatform.CreateWindowMock();
  718. popupImpl = MockWindowingPlatform.CreatePopupMock(windowImpl.Object);
  719. popupImpl.SetupGet(x => x.RenderScaling).Returns(1);
  720. windowImpl.Setup(x => x.CreatePopup()).Returns(popupImpl.Object);
  721. windowImpl.Setup(x => x.TryGetFeature(It.Is<Type>(t => t == typeof(IScreenImpl)))).Returns(screenImpl.Object);
  722. var services = TestServices.StyledWindow.With(
  723. inputManager: new InputManager(),
  724. windowImpl: windowImpl.Object,
  725. windowingPlatform: new MockWindowingPlatform(() => windowImpl.Object, x => popupImpl.Object));
  726. return UnitTestApplication.Start(services);
  727. }
  728. private record MenuViewModel(string Header)
  729. {
  730. public IList<MenuViewModel> Children { get; set; }
  731. }
  732. }
  733. }