MaskedTextBoxTests.cs 32 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Globalization;
  4. using System.Reactive.Linq;
  5. using System.Threading.Tasks;
  6. using Avalonia.Controls.Presenters;
  7. using Avalonia.Controls.Primitives;
  8. using Avalonia.Controls.Templates;
  9. using Avalonia.Data;
  10. using Avalonia.Input;
  11. using Avalonia.Input.Platform;
  12. using Avalonia.Layout;
  13. using Avalonia.Media;
  14. using Avalonia.Platform;
  15. using Avalonia.Rendering;
  16. using Avalonia.UnitTests;
  17. using Moq;
  18. using Xunit;
  19. namespace Avalonia.Controls.UnitTests
  20. {
  21. public class MaskedTextBoxTests
  22. {
  23. [Fact]
  24. public void Opening_Context_Menu_Does_not_Lose_Selection()
  25. {
  26. using (Start(FocusServices))
  27. {
  28. var target1 = new MaskedTextBox
  29. {
  30. Template = CreateTemplate(),
  31. Text = "1234",
  32. ContextMenu = new TestContextMenu()
  33. };
  34. var target2 = new MaskedTextBox
  35. {
  36. Template = CreateTemplate(),
  37. Text = "5678"
  38. };
  39. var sp = new StackPanel();
  40. sp.Children.Add(target1);
  41. sp.Children.Add(target2);
  42. target1.ApplyTemplate();
  43. target2.ApplyTemplate();
  44. var root = new TestRoot() { Child = sp };
  45. target1.SelectionStart = 0;
  46. target1.SelectionEnd = 3;
  47. target1.Focus();
  48. Assert.False(target2.IsFocused);
  49. Assert.True(target1.IsFocused);
  50. target2.Focus();
  51. Assert.Equal("123", target1.SelectedText);
  52. }
  53. }
  54. [Fact]
  55. public void Opening_Context_Flyout_Does_not_Lose_Selection()
  56. {
  57. using (Start(FocusServices))
  58. {
  59. var target1 = new MaskedTextBox
  60. {
  61. Template = CreateTemplate(),
  62. Text = "1234",
  63. ContextFlyout = new MenuFlyout
  64. {
  65. Items = new List<MenuItem>
  66. {
  67. new MenuItem { Header = "Item 1" },
  68. new MenuItem {Header = "Item 2" },
  69. new MenuItem {Header = "Item 3" }
  70. }
  71. }
  72. };
  73. target1.ApplyTemplate();
  74. var root = new TestRoot() { Child = target1 };
  75. target1.SelectionStart = 0;
  76. target1.SelectionEnd = 3;
  77. target1.Focus();
  78. Assert.True(target1.IsFocused);
  79. target1.ContextFlyout.ShowAt(target1);
  80. Assert.Equal("123", target1.SelectedText);
  81. }
  82. }
  83. [Fact]
  84. public void DefaultBindingMode_Should_Be_TwoWay()
  85. {
  86. Assert.Equal(
  87. BindingMode.TwoWay,
  88. TextBox.TextProperty.GetMetadata(typeof(MaskedTextBox)).DefaultBindingMode);
  89. }
  90. [Fact]
  91. public void CaretIndex_Can_Moved_To_Position_After_The_End_Of_Text_With_Arrow_Key()
  92. {
  93. using (Start(TestServices.StyledWindow))
  94. {
  95. var target = new MaskedTextBox
  96. {
  97. Template = CreateTemplate(),
  98. Text = "1234"
  99. };
  100. target.ApplyTemplate();
  101. target.CaretIndex = 3;
  102. target.Measure(Size.Infinity);
  103. RaiseKeyEvent(target, Key.Right, 0);
  104. Assert.Equal(4, target.CaretIndex);
  105. }
  106. }
  107. [Fact]
  108. public void Press_Ctrl_A_Select_All_Text()
  109. {
  110. using (Start())
  111. {
  112. var target = new MaskedTextBox
  113. {
  114. Template = CreateTemplate(),
  115. Text = "1234"
  116. };
  117. target.ApplyTemplate();
  118. RaiseKeyEvent(target, Key.A, KeyModifiers.Control);
  119. Assert.Equal(0, target.SelectionStart);
  120. Assert.Equal(4, target.SelectionEnd);
  121. }
  122. }
  123. [Fact]
  124. public void Press_Ctrl_A_Select_All_Null_Text()
  125. {
  126. using (Start())
  127. {
  128. var target = new MaskedTextBox
  129. {
  130. Template = CreateTemplate()
  131. };
  132. RaiseKeyEvent(target, Key.A, KeyModifiers.Control);
  133. Assert.Equal(0, target.SelectionStart);
  134. Assert.Equal(0, target.SelectionEnd);
  135. }
  136. }
  137. [Fact]
  138. public void Press_Ctrl_Z_Will_Not_Modify_Text()
  139. {
  140. using (Start())
  141. {
  142. var target = new MaskedTextBox
  143. {
  144. Template = CreateTemplate(),
  145. Text = "1234"
  146. };
  147. RaiseKeyEvent(target, Key.Z, KeyModifiers.Control);
  148. Assert.Equal("1234", target.Text);
  149. }
  150. }
  151. [Fact]
  152. public void Control_Backspace_Should_Remove_The_Word_Before_The_Caret_If_There_Is_No_Selection()
  153. {
  154. using (Start(TestServices.StyledWindow))
  155. {
  156. MaskedTextBox textBox = new MaskedTextBox
  157. {
  158. Template = CreateTemplate(),
  159. Text = "First Second Third Fourth",
  160. CaretIndex = 5
  161. };
  162. textBox.ApplyTemplate();
  163. // (First| Second Third Fourth)
  164. RaiseKeyEvent(textBox, Key.Back, KeyModifiers.Control);
  165. Assert.Equal(" Second Third Fourth", textBox.Text);
  166. // ( Second |Third Fourth)
  167. textBox.CaretIndex = 8;
  168. RaiseKeyEvent(textBox, Key.Back, KeyModifiers.Control);
  169. Assert.Equal(" Third Fourth", textBox.Text);
  170. // ( Thi|rd Fourth)
  171. textBox.CaretIndex = 4;
  172. RaiseKeyEvent(textBox, Key.Back, KeyModifiers.Control);
  173. Assert.Equal(" rd Fourth", textBox.Text);
  174. // ( rd F[ou]rth)
  175. textBox.SelectionStart = 5;
  176. textBox.SelectionEnd = 7;
  177. RaiseKeyEvent(textBox, Key.Back, KeyModifiers.Control);
  178. Assert.Equal(" rd Frth", textBox.Text);
  179. // ( |rd Frth)
  180. textBox.CaretIndex = 1;
  181. RaiseKeyEvent(textBox, Key.Back, KeyModifiers.Control);
  182. Assert.Equal("rd Frth", textBox.Text);
  183. }
  184. }
  185. [Fact]
  186. public void Control_Delete_Should_Remove_The_Word_After_The_Caret_If_There_Is_No_Selection()
  187. {
  188. using (Start(TestServices.StyledWindow))
  189. {
  190. var textBox = new MaskedTextBox
  191. {
  192. Template = CreateTemplate(),
  193. Text = "First Second Third Fourth",
  194. CaretIndex = 19
  195. };
  196. textBox.ApplyTemplate();
  197. // (First Second Third |Fourth)
  198. RaiseKeyEvent(textBox, Key.Delete, KeyModifiers.Control);
  199. Assert.Equal("First Second Third ", textBox.Text);
  200. // (First Second |Third )
  201. textBox.CaretIndex = 13;
  202. RaiseKeyEvent(textBox, Key.Delete, KeyModifiers.Control);
  203. Assert.Equal("First Second ", textBox.Text);
  204. // (First Sec|ond )
  205. textBox.CaretIndex = 9;
  206. RaiseKeyEvent(textBox, Key.Delete, KeyModifiers.Control);
  207. Assert.Equal("First Sec", textBox.Text);
  208. // (Fi[rs]t Sec )
  209. textBox.SelectionStart = 2;
  210. textBox.SelectionEnd = 4;
  211. RaiseKeyEvent(textBox, Key.Delete, KeyModifiers.Control);
  212. Assert.Equal("Fit Sec", textBox.Text);
  213. // (Fit Sec| )
  214. textBox.Text += " ";
  215. textBox.CaretIndex = 7;
  216. RaiseKeyEvent(textBox, Key.Delete, KeyModifiers.Control);
  217. Assert.Equal("Fit Sec", textBox.Text);
  218. }
  219. }
  220. [Fact]
  221. public void Setting_SelectionStart_To_SelectionEnd_Sets_CaretPosition_To_SelectionStart()
  222. {
  223. using (Start())
  224. {
  225. var textBox = new MaskedTextBox
  226. {
  227. Text = "0123456789"
  228. };
  229. textBox.SelectionStart = 2;
  230. textBox.SelectionEnd = 2;
  231. Assert.Equal(2, textBox.CaretIndex);
  232. }
  233. }
  234. [Fact]
  235. public void Setting_Text_Updates_CaretPosition()
  236. {
  237. using (Start())
  238. {
  239. var target = new MaskedTextBox
  240. {
  241. Text = "Initial Text",
  242. CaretIndex = 11
  243. };
  244. var invoked = false;
  245. target.GetObservable(TextBox.TextProperty).Skip(1).Subscribe(_ =>
  246. {
  247. // Caret index should be set before Text changed notification, as we don't want
  248. // to notify with an invalid CaretIndex.
  249. Assert.Equal(7, target.CaretIndex);
  250. invoked = true;
  251. });
  252. target.Text = "Changed";
  253. Assert.True(invoked);
  254. }
  255. }
  256. [Fact]
  257. public void Press_Enter_Does_Not_Accept_Return()
  258. {
  259. using (Start())
  260. {
  261. var target = new MaskedTextBox
  262. {
  263. Template = CreateTemplate(),
  264. AcceptsReturn = false,
  265. Text = "1234"
  266. };
  267. RaiseKeyEvent(target, Key.Enter, 0);
  268. Assert.Equal("1234", target.Text);
  269. }
  270. }
  271. [Fact]
  272. public void Press_Enter_Add_Default_Newline()
  273. {
  274. using (Start())
  275. {
  276. var target = new MaskedTextBox
  277. {
  278. Template = CreateTemplate(),
  279. AcceptsReturn = true
  280. };
  281. target.ApplyTemplate();
  282. RaiseKeyEvent(target, Key.Enter, 0);
  283. Assert.Equal(Environment.NewLine, target.Text);
  284. }
  285. }
  286. [Theory]
  287. [InlineData("00/00/0000", "12102000", "12/10/2000")]
  288. [InlineData("LLLL", "дбs", "____")]
  289. [InlineData("AA", "Ü1", "__")]
  290. public void AsciiOnly_Should_Not_Accept_Non_Ascii(string mask, string textEventArg, string expected)
  291. {
  292. using (Start())
  293. {
  294. var target = new MaskedTextBox
  295. {
  296. Template = CreateTemplate(),
  297. Mask = mask,
  298. AsciiOnly = true
  299. };
  300. RaiseTextEvent(target, textEventArg);
  301. Assert.Equal(expected, target.Text);
  302. }
  303. }
  304. [Fact]
  305. public void Programmatically_Set_Text_Should_Not_Be_Removed_On_Key_Press()
  306. {
  307. using (Start())
  308. {
  309. var target = new MaskedTextBox
  310. {
  311. Template = CreateTemplate(),
  312. Mask = "00:00:00.000",
  313. Text = "12:34:56.000"
  314. };
  315. target.CaretIndex = target.Text.Length;
  316. RaiseKeyEvent(target, Key.Back, 0);
  317. Assert.Equal("12:34:56.00_", target.Text);
  318. }
  319. }
  320. [Fact]
  321. public void Invalid_Programmatically_Set_Text_Should_Be_Rejected()
  322. {
  323. using (Start())
  324. {
  325. var target = new MaskedTextBox
  326. {
  327. Template = CreateTemplate(),
  328. Mask = "00:00:00.000",
  329. Text = "12:34:560000"
  330. };
  331. Assert.Equal("__:__:__.___", target.Text);
  332. }
  333. }
  334. [Theory]
  335. [InlineData("00/00/0000", "12102000", "**/**/****")]
  336. [InlineData("LLLL", "дбs", "***_")]
  337. [InlineData("AA#00", "S2 33", "**_**")]
  338. public void PasswordChar_Should_Hide_User_Input(string mask, string textEventArg, string expected)
  339. {
  340. using (Start())
  341. {
  342. var target = new MaskedTextBox
  343. {
  344. Template = CreateTemplate(),
  345. Mask = mask,
  346. PasswordChar = '*'
  347. };
  348. RaiseTextEvent(target, textEventArg);
  349. Assert.Equal(expected, target.Text);
  350. }
  351. }
  352. [Theory]
  353. [InlineData("00/00/0000", "12102000", "12/10/2000")]
  354. [InlineData("LLLL", "дбs", "дбs_")]
  355. [InlineData("AA#00", "S2 33", "S2_33")]
  356. public void Mask_Should_Work_Correctly(string mask, string textEventArg, string expected)
  357. {
  358. using (Start())
  359. {
  360. var target = new MaskedTextBox
  361. {
  362. Template = CreateTemplate(),
  363. Mask = mask
  364. };
  365. RaiseTextEvent(target, textEventArg);
  366. Assert.Equal(expected, target.Text);
  367. }
  368. }
  369. [Fact]
  370. public void Press_Enter_Add_Custom_Newline()
  371. {
  372. using (Start())
  373. {
  374. var target = new MaskedTextBox
  375. {
  376. Template = CreateTemplate(),
  377. AcceptsReturn = true,
  378. NewLine = "Test"
  379. };
  380. target.ApplyTemplate();
  381. RaiseKeyEvent(target, Key.Enter, 0);
  382. Assert.Equal("Test", target.Text);
  383. }
  384. }
  385. [Theory]
  386. [InlineData(new object[] { false, TextWrapping.NoWrap, ScrollBarVisibility.Hidden })]
  387. [InlineData(new object[] { false, TextWrapping.Wrap, ScrollBarVisibility.Disabled })]
  388. [InlineData(new object[] { true, TextWrapping.NoWrap, ScrollBarVisibility.Auto })]
  389. [InlineData(new object[] { true, TextWrapping.Wrap, ScrollBarVisibility.Disabled })]
  390. public void Has_Correct_Horizontal_ScrollBar_Visibility(
  391. bool acceptsReturn,
  392. TextWrapping wrapping,
  393. ScrollBarVisibility expected)
  394. {
  395. using (Start())
  396. {
  397. var target = new MaskedTextBox
  398. {
  399. AcceptsReturn = acceptsReturn,
  400. TextWrapping = wrapping,
  401. };
  402. Assert.Equal(expected, ScrollViewer.GetHorizontalScrollBarVisibility(target));
  403. }
  404. }
  405. [Fact]
  406. public void SelectionEnd_Doesnt_Cause_Exception()
  407. {
  408. using (Start())
  409. {
  410. var target = new MaskedTextBox
  411. {
  412. Template = CreateTemplate(),
  413. Text = "0123456789"
  414. };
  415. target.SelectionStart = 0;
  416. target.SelectionEnd = 9;
  417. target.Text = "123";
  418. RaiseTextEvent(target, "456");
  419. Assert.True(true);
  420. }
  421. }
  422. [Fact]
  423. public void SelectionStart_Doesnt_Cause_Exception()
  424. {
  425. using (Start())
  426. {
  427. var target = new MaskedTextBox
  428. {
  429. Template = CreateTemplate(),
  430. Text = "0123456789"
  431. };
  432. target.SelectionStart = 8;
  433. target.SelectionEnd = 9;
  434. target.Text = "123";
  435. RaiseTextEvent(target, "456");
  436. Assert.True(true);
  437. }
  438. }
  439. [Fact]
  440. public void SelectionStartEnd_Are_Valid_AterTextChange()
  441. {
  442. using (Start())
  443. {
  444. var target = new MaskedTextBox
  445. {
  446. Template = CreateTemplate(),
  447. Text = "0123456789"
  448. };
  449. target.SelectionStart = 8;
  450. target.SelectionEnd = 9;
  451. target.Text = "123";
  452. Assert.True(target.SelectionStart <= "123".Length);
  453. Assert.True(target.SelectionEnd <= "123".Length);
  454. }
  455. }
  456. [Fact]
  457. public void SelectedText_Changes_OnSelectionChange()
  458. {
  459. using (Start())
  460. {
  461. var target = new MaskedTextBox
  462. {
  463. Template = CreateTemplate(),
  464. Text = "0123456789"
  465. };
  466. Assert.True(target.SelectedText == "");
  467. target.SelectionStart = 2;
  468. target.SelectionEnd = 4;
  469. Assert.True(target.SelectedText == "23");
  470. }
  471. }
  472. [Fact]
  473. public void SelectedText_EditsText()
  474. {
  475. using (Start())
  476. {
  477. var target = new MaskedTextBox
  478. {
  479. Template = CreateTemplate(),
  480. Text = "0123"
  481. };
  482. target.SelectedText = "AA";
  483. Assert.True(target.Text == "AA0123");
  484. target.SelectionStart = 1;
  485. target.SelectionEnd = 3;
  486. target.SelectedText = "BB";
  487. Assert.True(target.Text == "ABB123");
  488. }
  489. }
  490. [Fact]
  491. public void SelectedText_CanClearText()
  492. {
  493. using (Start())
  494. {
  495. var target = new MaskedTextBox
  496. {
  497. Template = CreateTemplate(),
  498. Text = "0123"
  499. };
  500. target.SelectionStart = 1;
  501. target.SelectionEnd = 3;
  502. target.SelectedText = "";
  503. Assert.True(target.Text == "03");
  504. }
  505. }
  506. [Fact]
  507. public void SelectedText_NullClearsText()
  508. {
  509. using (Start())
  510. {
  511. var target = new MaskedTextBox
  512. {
  513. Template = CreateTemplate(),
  514. Text = "0123"
  515. };
  516. target.SelectionStart = 1;
  517. target.SelectionEnd = 3;
  518. target.SelectedText = null;
  519. Assert.True(target.Text == "03");
  520. }
  521. }
  522. [Fact]
  523. public void CoerceCaretIndex_Doesnt_Cause_Exception_with_malformed_line_ending()
  524. {
  525. using (Start())
  526. {
  527. var target = new MaskedTextBox
  528. {
  529. Template = CreateTemplate(),
  530. Text = "0123456789\r"
  531. };
  532. target.CaretIndex = 11;
  533. Assert.True(true);
  534. }
  535. }
  536. [Theory]
  537. [InlineData(Key.Up)]
  538. [InlineData(Key.Down)]
  539. [InlineData(Key.Home)]
  540. [InlineData(Key.End)]
  541. public void Textbox_doesnt_crash_when_Receives_input_and_template_not_applied(Key key)
  542. {
  543. using (Start(FocusServices))
  544. {
  545. var target1 = new MaskedTextBox
  546. {
  547. Template = CreateTemplate(),
  548. Text = "1234",
  549. IsVisible = false
  550. };
  551. var root = new TestRoot { Child = target1 };
  552. target1.Focus();
  553. Assert.True(target1.IsFocused);
  554. RaiseKeyEvent(target1, key, KeyModifiers.None);
  555. }
  556. }
  557. [Fact]
  558. public void TextBox_GotFocus_And_LostFocus_Work_Properly()
  559. {
  560. using (Start(FocusServices))
  561. {
  562. var target1 = new MaskedTextBox
  563. {
  564. Template = CreateTemplate(),
  565. Text = "1234"
  566. };
  567. var target2 = new MaskedTextBox
  568. {
  569. Template = CreateTemplate(),
  570. Text = "5678"
  571. };
  572. var sp = new StackPanel();
  573. sp.Children.Add(target1);
  574. sp.Children.Add(target2);
  575. target1.ApplyTemplate();
  576. target2.ApplyTemplate();
  577. var root = new TestRoot { Child = sp };
  578. var gfcount = 0;
  579. var lfcount = 0;
  580. target1.GotFocus += (s, e) => gfcount++;
  581. target2.LostFocus += (s, e) => lfcount++;
  582. target2.Focus();
  583. Assert.False(target1.IsFocused);
  584. Assert.True(target2.IsFocused);
  585. target1.Focus();
  586. Assert.False(target2.IsFocused);
  587. Assert.True(target1.IsFocused);
  588. Assert.Equal(1, gfcount);
  589. Assert.Equal(1, lfcount);
  590. }
  591. }
  592. [Fact]
  593. public void TextBox_CaretIndex_Persists_When_Focus_Lost()
  594. {
  595. using (Start(FocusServices))
  596. {
  597. var target1 = new MaskedTextBox
  598. {
  599. Template = CreateTemplate(),
  600. Text = "1234"
  601. };
  602. var target2 = new MaskedTextBox
  603. {
  604. Template = CreateTemplate(),
  605. Text = "5678"
  606. };
  607. var sp = new StackPanel();
  608. sp.Children.Add(target1);
  609. sp.Children.Add(target2);
  610. target1.ApplyTemplate();
  611. target2.ApplyTemplate();
  612. var root = new TestRoot { Child = sp };
  613. target2.Focus();
  614. target2.CaretIndex = 2;
  615. Assert.False(target1.IsFocused);
  616. Assert.True(target2.IsFocused);
  617. target1.Focus();
  618. Assert.Equal(2, target2.CaretIndex);
  619. }
  620. }
  621. [Fact]
  622. public void TextBox_Reveal_Password_Reset_When_Lost_Focus()
  623. {
  624. using (Start(FocusServices))
  625. {
  626. var target1 = new MaskedTextBox
  627. {
  628. Template = CreateTemplate(),
  629. Text = "1234",
  630. PasswordChar = '*'
  631. };
  632. var target2 = new MaskedTextBox
  633. {
  634. Template = CreateTemplate(),
  635. Text = "5678"
  636. };
  637. var sp = new StackPanel();
  638. sp.Children.Add(target1);
  639. sp.Children.Add(target2);
  640. target1.ApplyTemplate();
  641. target2.ApplyTemplate();
  642. var root = new TestRoot { Child = sp };
  643. target1.Focus();
  644. target1.RevealPassword = true;
  645. target2.Focus();
  646. Assert.False(target1.RevealPassword);
  647. }
  648. }
  649. [Fact]
  650. public void Setting_Bound_Text_To_Null_Works()
  651. {
  652. using (Start())
  653. {
  654. var source = new Class1 { Bar = "bar" };
  655. var target = new MaskedTextBox { DataContext = source };
  656. target.Bind(TextBox.TextProperty, new Binding("Bar"));
  657. Assert.Equal("bar", target.Text);
  658. source.Bar = null;
  659. Assert.Null(target.Text);
  660. }
  661. }
  662. [Theory]
  663. [InlineData("abc", "d", 3, 0, 0, false, "abc")]
  664. [InlineData("abc", "dd", 4, 3, 3, false, "abcd")]
  665. [InlineData("abc", "ddd", 3, 0, 2, true, "ddc")]
  666. [InlineData("abc", "dddd", 4, 1, 3, true, "addd")]
  667. [InlineData("abc", "ddddd", 5, 3, 3, true, "abcdd")]
  668. public void MaxLength_Works_Properly(
  669. string initalText,
  670. string textInput,
  671. int maxLength,
  672. int selectionStart,
  673. int selectionEnd,
  674. bool fromClipboard,
  675. string expected)
  676. {
  677. using (Start(TestServices.StyledWindow))
  678. {
  679. var target = new MaskedTextBox
  680. {
  681. Template = CreateTemplate(),
  682. Text = initalText,
  683. MaxLength = maxLength,
  684. SelectionStart = selectionStart,
  685. SelectionEnd = selectionEnd
  686. };
  687. var impl = CreateMockTopLevelImpl();
  688. var topLevel = new TestTopLevel(impl.Object)
  689. {
  690. Template = CreateTopLevelTemplate()
  691. };
  692. topLevel.Content = target;
  693. topLevel.ApplyTemplate();
  694. topLevel.LayoutManager.ExecuteInitialLayoutPass();
  695. target.ApplyTemplate();
  696. if (fromClipboard)
  697. {
  698. topLevel.Clipboard?.SetTextAsync(textInput).GetAwaiter().GetResult();
  699. RaiseKeyEvent(target, Key.V, KeyModifiers.Control);
  700. topLevel.Clipboard?.ClearAsync().GetAwaiter().GetResult();
  701. }
  702. else
  703. {
  704. RaiseTextEvent(target, textInput);
  705. }
  706. Assert.Equal(expected, target.Text);
  707. }
  708. }
  709. [Theory]
  710. [InlineData(Key.X, KeyModifiers.Control)]
  711. [InlineData(Key.Back, KeyModifiers.None)]
  712. [InlineData(Key.Delete, KeyModifiers.None)]
  713. [InlineData(Key.Tab, KeyModifiers.None)]
  714. [InlineData(Key.Enter, KeyModifiers.None)]
  715. public void Keys_Allow_Undo(Key key, KeyModifiers modifiers)
  716. {
  717. using (Start())
  718. {
  719. var target = new MaskedTextBox
  720. {
  721. Template = CreateTemplate(),
  722. Text = "0123",
  723. AcceptsReturn = true,
  724. AcceptsTab = true
  725. };
  726. var impl = CreateMockTopLevelImpl();
  727. var topLevel = new TestTopLevel(impl.Object)
  728. {
  729. Template = CreateTopLevelTemplate()
  730. };
  731. topLevel.Content = target;
  732. topLevel.ApplyTemplate();
  733. topLevel.LayoutManager.ExecuteInitialLayoutPass();
  734. target.SelectionStart = 1;
  735. target.SelectionEnd = 3;
  736. RaiseKeyEvent(target, key, modifiers);
  737. RaiseKeyEvent(target, Key.Z, KeyModifiers.Control); // undo
  738. Assert.True(target.Text == "0123");
  739. }
  740. }
  741. private static TestServices FocusServices => TestServices.MockThreadingInterface.With(
  742. focusManager: new FocusManager(),
  743. keyboardDevice: () => new KeyboardDevice(),
  744. keyboardNavigation: new KeyboardNavigationHandler(),
  745. inputManager: new InputManager(),
  746. renderInterface: new MockPlatformRenderInterface(),
  747. fontManagerImpl: new MockFontManagerImpl(),
  748. textShaperImpl: new MockTextShaperImpl(),
  749. standardCursorFactory: Mock.Of<ICursorFactory>());
  750. private static TestServices Services => TestServices.MockThreadingInterface.With(
  751. standardCursorFactory: Mock.Of<ICursorFactory>(),
  752. textShaperImpl: new MockTextShaperImpl(),
  753. fontManagerImpl: new MockFontManagerImpl());
  754. private static IControlTemplate CreateTemplate()
  755. {
  756. return new FuncControlTemplate<MaskedTextBox>((control, scope) =>
  757. new TextPresenter
  758. {
  759. Name = "PART_TextPresenter",
  760. [!!TextPresenter.TextProperty] = new Binding
  761. {
  762. Path = nameof(TextPresenter.Text),
  763. Mode = BindingMode.TwoWay,
  764. Priority = BindingPriority.Template,
  765. RelativeSource = new RelativeSource(RelativeSourceMode.TemplatedParent),
  766. },
  767. [!!TextPresenter.CaretIndexProperty] = new Binding
  768. {
  769. Path = nameof(TextPresenter.CaretIndex),
  770. Mode = BindingMode.TwoWay,
  771. Priority = BindingPriority.Template,
  772. RelativeSource = new RelativeSource(RelativeSourceMode.TemplatedParent),
  773. }
  774. }.RegisterInNameScope(scope));
  775. }
  776. private static void RaiseKeyEvent(MaskedTextBox textBox, Key key, KeyModifiers inputModifiers)
  777. {
  778. textBox.RaiseEvent(new KeyEventArgs
  779. {
  780. RoutedEvent = InputElement.KeyDownEvent,
  781. KeyModifiers = inputModifiers,
  782. Key = key
  783. });
  784. }
  785. private void RaiseTextEvent(MaskedTextBox textBox, string text)
  786. {
  787. textBox.RaiseEvent(new TextInputEventArgs
  788. {
  789. RoutedEvent = InputElement.TextInputEvent,
  790. Text = text
  791. });
  792. }
  793. private static IDisposable Start(TestServices services = null)
  794. {
  795. CultureInfo.CurrentCulture = CultureInfo.CurrentUICulture = CultureInfo.GetCultureInfo("en-US");
  796. return UnitTestApplication.Start(services ?? Services);
  797. }
  798. private class Class1 : NotifyingBase
  799. {
  800. private int _foo;
  801. private string _bar;
  802. public int Foo
  803. {
  804. get { return _foo; }
  805. set { _foo = value; RaisePropertyChanged(); }
  806. }
  807. public string Bar
  808. {
  809. get { return _bar; }
  810. set { _bar = value; RaisePropertyChanged(); }
  811. }
  812. }
  813. internal class ClipboardStub : IClipboard // in order to get tests working that use the clipboard
  814. {
  815. private string _text;
  816. public Task<string> GetTextAsync() => Task.FromResult(_text);
  817. public Task SetTextAsync(string text)
  818. {
  819. _text = text;
  820. return Task.CompletedTask;
  821. }
  822. public Task ClearAsync()
  823. {
  824. _text = null;
  825. return Task.CompletedTask;
  826. }
  827. public Task SetDataObjectAsync(IDataObject data) => Task.CompletedTask;
  828. public Task<string[]> GetFormatsAsync() => Task.FromResult(Array.Empty<string>());
  829. public Task<object> GetDataAsync(string format) => Task.FromResult((object)null);
  830. }
  831. private class TestTopLevel : TopLevel
  832. {
  833. private readonly ILayoutManager _layoutManager;
  834. public TestTopLevel(ITopLevelImpl impl, ILayoutManager layoutManager = null)
  835. : base(impl)
  836. {
  837. _layoutManager = layoutManager ?? new LayoutManager(this);
  838. }
  839. protected override ILayoutManager CreateLayoutManager() => _layoutManager;
  840. }
  841. private static Mock<ITopLevelImpl> CreateMockTopLevelImpl()
  842. {
  843. var clipboard = new Mock<ITopLevelImpl>();
  844. clipboard.Setup(r => r.CreateRenderer(It.IsAny<IRenderRoot>()))
  845. .Returns(RendererMocks.CreateRenderer().Object);
  846. clipboard.Setup(r => r.TryGetFeature(typeof(IClipboard)))
  847. .Returns(new ClipboardStub());
  848. clipboard.SetupGet(x => x.RenderScaling).Returns(1);
  849. return clipboard;
  850. }
  851. private static FuncControlTemplate<TestTopLevel> CreateTopLevelTemplate()
  852. {
  853. return new FuncControlTemplate<TestTopLevel>((x, scope) =>
  854. new ContentPresenter
  855. {
  856. Name = "PART_ContentPresenter",
  857. [!ContentPresenter.ContentProperty] = x[!ContentControl.ContentProperty],
  858. }.RegisterInNameScope(scope));
  859. }
  860. private class TestContextMenu : ContextMenu
  861. {
  862. public TestContextMenu()
  863. {
  864. IsOpen = true;
  865. }
  866. }
  867. }
  868. }