DropDownCellEditor.cs 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713
  1. //#########################################################################################
  2. //★★★★★★★ http://www.cnpopsoft.com [华普软件] ★★★★★★★
  3. //★★★★★★★ 华普软件 - VB & C#.NET 专业论文与源码荟萃! ★★★★★★★
  4. //#########################################################################################
  5. /*
  6. * Copyright ?2005, Mathew Hall
  7. * All rights reserved.
  8. *
  9. * DropDownCellEditor.ActivationListener, DropDownCellEditor.ShowDropDown() and
  10. * DropDownCellEditor.HideDropDown() contains code based on Steve McMahon's
  11. * PopupWindowHelper (see http://www.vbaccelerator.com/home/NET/Code/Controls/Popup_Windows/Popup_Windows/article.asp)
  12. *
  13. * Redistribution and use in source and binary forms, with or without modification,
  14. * are permitted provided that the following conditions are met:
  15. *
  16. * - Redistributions of source code must retain the above copyright notice,
  17. * this list of conditions and the following disclaimer.
  18. *
  19. * - Redistributions in binary form must reproduce the above copyright notice,
  20. * this list of conditions and the following disclaimer in the documentation
  21. * and/or other materials provided with the distribution.
  22. *
  23. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
  24. * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  25. * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
  26. * IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
  27. * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  28. * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
  29. * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
  30. * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  31. * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
  32. * OF SUCH DAMAGE.
  33. */
  34. using System;
  35. using System.ComponentModel;
  36. using System.Drawing;
  37. using System.Windows.Forms;
  38. using XPTable.Events;
  39. using XPTable.Models;
  40. using XPTable.Renderers;
  41. using XPTable.Win32;
  42. namespace XPTable.Editors
  43. {
  44. /// <summary>
  45. /// A base class for editing Cells that contain drop down buttons
  46. /// </summary>
  47. public abstract class DropDownCellEditor : CellEditor, IEditorUsesRendererButtons
  48. {
  49. #region Class Data
  50. /// <summary>
  51. /// The container that holds the Control displayed when editor is dropped down
  52. /// </summary>
  53. private DropDownContainer dropDownContainer;
  54. /// <summary>
  55. /// Specifies whether the DropDownContainer is currently displayed
  56. /// </summary>
  57. private bool droppedDown;
  58. /// <summary>
  59. /// Specifies the DropDown style
  60. /// </summary>
  61. private DropDownStyle dropDownStyle;
  62. /// <summary>
  63. /// The user defined width of the DropDownContainer
  64. /// </summary>
  65. private int dropDownWidth;
  66. /// <summary>
  67. /// Listener for WM_NCACTIVATE and WM_ACTIVATEAPP messages
  68. /// </summary>
  69. private ActivationListener activationListener;
  70. /// <summary>
  71. /// The Form that will own the DropDownContainer
  72. /// </summary>
  73. private Form parentForm;
  74. /// <summary>
  75. /// Specifies whether the mouse is currently over the
  76. /// DropDownContainer
  77. /// </summary>
  78. private bool containsMouse;
  79. #endregion
  80. #region Constructor
  81. /// <summary>
  82. /// Initializes a new instance of the DropDownCellEditor class with default settings
  83. /// </summary>
  84. public DropDownCellEditor() : base()
  85. {
  86. TextBox textbox = new TextBox();
  87. textbox.AutoSize = false;
  88. textbox.BackColor = SystemColors.Window;
  89. textbox.BorderStyle = BorderStyle.None;
  90. textbox.MouseEnter += new EventHandler(textbox_MouseEnter);
  91. this.Control = textbox;
  92. this.dropDownContainer = new DropDownContainer(this);
  93. this.droppedDown = false;
  94. this.DropDownStyle = DropDownStyle.DropDownList;
  95. this.dropDownWidth = -1;
  96. this.parentForm = null;
  97. this.activationListener = new ActivationListener(this);
  98. this.containsMouse = false;
  99. }
  100. #endregion
  101. #region Methods
  102. /// <summary>
  103. /// Prepares the CellEditor to edit the specified Cell
  104. /// </summary>
  105. /// <param name="cell">The Cell to be edited</param>
  106. /// <param name="table">The Table that contains the Cell</param>
  107. /// <param name="cellPos">A CellPos representing the position of the Cell</param>
  108. /// <param name="cellRect">The Rectangle that represents the Cells location and size</param>
  109. /// <param name="userSetEditorValues">Specifies whether the ICellEditors
  110. /// starting value has already been set by the user</param>
  111. /// <returns>true if the ICellEditor can continue editing the Cell, false otherwise</returns>
  112. public override bool PrepareForEditing(Cell cell, Table table, CellPos cellPos, Rectangle cellRect, bool userSetEditorValues)
  113. {
  114. if (!(table.ColumnModel.Columns[cellPos.Column] is DropDownColumn))
  115. {
  116. throw new InvalidOperationException("Cannot edit Cell as DropDownCellEditor can only be used with a DropDownColumn");
  117. }
  118. return base.PrepareForEditing (cell, table, cellPos, cellRect, userSetEditorValues);
  119. }
  120. /// <summary>
  121. /// Starts editing the Cell
  122. /// </summary>
  123. public override void StartEditing()
  124. {
  125. this.TextBox.KeyPress += new KeyPressEventHandler(OnKeyPress);
  126. this.TextBox.LostFocus += new EventHandler(OnLostFocus);
  127. base.StartEditing();
  128. this.parentForm = this.EditingTable.FindForm();
  129. if (this.DroppedDown)
  130. {
  131. this.ShowDropDown();
  132. }
  133. this.TextBox.Focus();
  134. }
  135. /// <summary>
  136. /// Stops editing the Cell and commits any changes
  137. /// </summary>
  138. public override void StopEditing()
  139. {
  140. this.TextBox.KeyPress -= new KeyPressEventHandler(OnKeyPress);
  141. this.TextBox.LostFocus -= new EventHandler(OnLostFocus);
  142. base.StopEditing();
  143. this.DroppedDown = false;
  144. this.parentForm = null;
  145. }
  146. /// <summary>
  147. /// Stops editing the Cell and ignores any changes
  148. /// </summary>
  149. public override void CancelEditing()
  150. {
  151. this.TextBox.KeyPress -= new KeyPressEventHandler(OnKeyPress);
  152. this.TextBox.LostFocus -= new EventHandler(OnLostFocus);
  153. base.CancelEditing();
  154. this.DroppedDown = false;
  155. this.parentForm = null;
  156. }
  157. /// <summary>
  158. /// Displays the drop down portion to the user
  159. /// </summary>
  160. protected virtual void ShowDropDown()
  161. {
  162. Point p = this.EditingTable.PointToScreen(this.TextBox.Location);
  163. p.Y += this.TextBox.Height + 1;
  164. Rectangle screenBounds = Screen.GetBounds(p);
  165. if (p.Y + this.dropDownContainer.Height > screenBounds.Bottom)
  166. {
  167. p.Y -= this.TextBox.Height + this.dropDownContainer.Height + 1;
  168. }
  169. if (p.X + this.dropDownContainer.Width > screenBounds.Right)
  170. {
  171. ICellRenderer renderer = this.EditingTable.ColumnModel.GetCellRenderer(this.EditingCellPos.Column);
  172. int buttonWidth = ((DropDownCellRenderer) renderer).ButtonWidth;
  173. p.X = p.X + this.TextBox.Width + buttonWidth - this.dropDownContainer.Width;
  174. }
  175. this.dropDownContainer.Location = p;
  176. this.parentForm.AddOwnedForm(this.dropDownContainer);
  177. this.activationListener.AssignHandle(this.parentForm.Handle);
  178. this.dropDownContainer.ShowDropDown();
  179. this.dropDownContainer.Activate();
  180. // A little bit of fun. We've shown the popup,
  181. // but because we've kept the main window's
  182. // title bar in focus the tab sequence isn't quite
  183. // right. This can be fixed by sending a tab,
  184. // but that on its own would shift focus to the
  185. // second control in the form. So send a tab,
  186. // followed by a reverse-tab.
  187. // Send a Tab command:
  188. NativeMethods.keybd_event((byte) Keys.Tab, 0, 0, 0);
  189. NativeMethods.keybd_event((byte) Keys.Tab, 0, KeyEventFFlags.KEYEVENTF_KEYUP, 0);
  190. // Send a reverse Tab command:
  191. NativeMethods.keybd_event((byte) Keys.ShiftKey, 0, 0, 0);
  192. NativeMethods.keybd_event((byte) Keys.Tab, 0, 0, 0);
  193. NativeMethods.keybd_event((byte) Keys.Tab, 0, KeyEventFFlags.KEYEVENTF_KEYUP, 0);
  194. NativeMethods.keybd_event((byte) Keys.ShiftKey, 0, KeyEventFFlags.KEYEVENTF_KEYUP, 0);
  195. }
  196. /// <summary>
  197. /// Conceals the drop down portion from the user
  198. /// </summary>
  199. protected virtual void HideDropDown()
  200. {
  201. this.dropDownContainer.HideDropDown();
  202. this.parentForm.RemoveOwnedForm(this.dropDownContainer);
  203. this.activationListener.ReleaseHandle();
  204. this.parentForm.Activate();
  205. }
  206. /// <summary>
  207. /// Gets whether the editor should stop editing if a mouse click occurs
  208. /// outside of the DropDownContainer while it is dropped down
  209. /// </summary>
  210. /// <param name="target">The Control that will receive the message</param>
  211. /// <param name="cursorPos">The current position of the mouse cursor</param>
  212. /// <returns>true if the editor should stop editing, false otherwise</returns>
  213. protected virtual bool ShouldStopEditing(Control target, Point cursorPos)
  214. {
  215. return true;
  216. }
  217. /// <summary>
  218. /// Filters out a mouse message before it is dispatched
  219. /// </summary>
  220. /// <param name="target">The Control that will receive the message</param>
  221. /// <param name="msg">A WindowMessage that represents the message to process</param>
  222. /// <param name="wParam">Specifies the WParam field of the message</param>
  223. /// <param name="lParam">Specifies the LParam field of the message</param>
  224. /// <returns>true to filter the message and prevent it from being dispatched;
  225. /// false to allow the message to continue to the next filter or control</returns>
  226. public override bool ProcessMouseMessage(Control target, WindowMessage msg, int wParam, int lParam)
  227. {
  228. if (this.DroppedDown)
  229. {
  230. if (msg == WindowMessage.WM_LBUTTONDOWN || msg == WindowMessage.WM_RBUTTONDOWN ||
  231. msg == WindowMessage.WM_MBUTTONDOWN || msg == WindowMessage.WM_XBUTTONDOWN ||
  232. msg == WindowMessage.WM_NCLBUTTONDOWN || msg == WindowMessage.WM_NCRBUTTONDOWN ||
  233. msg == WindowMessage.WM_NCMBUTTONDOWN || msg == WindowMessage.WM_NCXBUTTONDOWN)
  234. {
  235. Point cursorPos = Cursor.Position;
  236. if (!this.DropDown.Bounds.Contains(cursorPos))
  237. {
  238. if (target != this.EditingTable && target != this.TextBox)
  239. {
  240. if (this.ShouldStopEditing(target, cursorPos))
  241. {
  242. this.EditingTable.StopEditing();
  243. }
  244. }
  245. }
  246. }
  247. else if (msg == WindowMessage.WM_MOUSEMOVE)
  248. {
  249. Point cursorPos = Cursor.Position;
  250. if (this.DropDown.Bounds.Contains(cursorPos))
  251. {
  252. if (!this.containsMouse)
  253. {
  254. this.containsMouse = true;
  255. this.EditingTable.RaiseCellMouseLeave(this.EditingCellPos);
  256. }
  257. }
  258. else
  259. {
  260. this.containsMouse = true;
  261. }
  262. }
  263. }
  264. return false;
  265. }
  266. /// <summary>
  267. /// Filters out a key message before it is dispatched
  268. /// </summary>
  269. /// <param name="target">The Control that will receive the message</param>
  270. /// <param name="msg">A WindowMessage that represents the message to process</param>
  271. /// <param name="wParam">Specifies the WParam field of the message</param>
  272. /// <param name="lParam">Specifies the LParam field of the message</param>
  273. /// <returns>true to filter the message and prevent it from being dispatched;
  274. /// false to allow the message to continue to the next filter or control</returns>
  275. public override bool ProcessKeyMessage(Control target, WindowMessage msg, int wParam, int lParam)
  276. {
  277. if (msg == WindowMessage.WM_KEYDOWN)
  278. {
  279. if (((Keys) wParam) == Keys.F4)
  280. {
  281. if (this.TextBox.Focused || this.DropDown.ContainsFocus)
  282. {
  283. this.DroppedDown = !this.DroppedDown;
  284. return true;
  285. }
  286. }
  287. }
  288. return false;
  289. }
  290. #endregion
  291. #region Properties
  292. /// <summary>
  293. /// Gets the TextBox used to edit the Cells contents
  294. /// </summary>
  295. protected TextBox TextBox
  296. {
  297. get
  298. {
  299. return this.Control as TextBox;
  300. }
  301. }
  302. /// <summary>
  303. /// Gets the container that holds the Control displayed when editor is dropped down
  304. /// </summary>
  305. protected DropDownContainer DropDown
  306. {
  307. get
  308. {
  309. return this.dropDownContainer;
  310. }
  311. }
  312. /// <summary>
  313. /// Gets or sets whether the editor is displaying its drop-down portion
  314. /// </summary>
  315. public bool DroppedDown
  316. {
  317. get
  318. {
  319. return this.droppedDown;
  320. }
  321. set
  322. {
  323. if (this.droppedDown != value)
  324. {
  325. this.droppedDown = value;
  326. if (value)
  327. {
  328. this.ShowDropDown();
  329. }
  330. else
  331. {
  332. this.HideDropDown();
  333. }
  334. }
  335. }
  336. }
  337. /// <summary>
  338. /// Gets or sets the width of the of the drop-down portion of the editor
  339. /// </summary>
  340. public int DropDownWidth
  341. {
  342. get
  343. {
  344. if (this.dropDownWidth != -1)
  345. {
  346. return this.dropDownWidth;
  347. }
  348. return this.dropDownContainer.Width;
  349. }
  350. set
  351. {
  352. this.dropDownWidth = value;
  353. this.dropDownContainer.Width = value;
  354. }
  355. }
  356. /// <summary>
  357. /// Gets the user defined width of the of the drop-down portion of the editor
  358. /// </summary>
  359. internal int InternalDropDownWidth
  360. {
  361. get
  362. {
  363. return this.dropDownWidth;
  364. }
  365. }
  366. /// <summary>
  367. /// Gets or sets a value specifying the style of the drop down editor
  368. /// </summary>
  369. public DropDownStyle DropDownStyle
  370. {
  371. get
  372. {
  373. return this.dropDownStyle;
  374. }
  375. set
  376. {
  377. if (!Enum.IsDefined(typeof(DropDownStyle), value))
  378. {
  379. throw new InvalidEnumArgumentException("value", (int) value, typeof(DropDownStyle));
  380. }
  381. if (this.dropDownStyle != value)
  382. {
  383. this.dropDownStyle = value;
  384. this.TextBox.ReadOnly = (value == DropDownStyle.DropDownList);
  385. }
  386. }
  387. }
  388. /// <summary>
  389. /// Gets or sets the text that is selected in the editable portion of the editor
  390. /// </summary>
  391. public string SelectedText
  392. {
  393. get
  394. {
  395. if (this.DropDownStyle == DropDownStyle.DropDownList)
  396. {
  397. return "";
  398. }
  399. return this.TextBox.SelectedText;
  400. }
  401. set
  402. {
  403. if (this.DropDownStyle != DropDownStyle.DropDownList && value != null)
  404. {
  405. this.TextBox.SelectedText = value;
  406. }
  407. }
  408. }
  409. /// <summary>
  410. /// Gets or sets the number of characters selected in the editable portion
  411. /// of the editor
  412. /// </summary>
  413. public int SelectionLength
  414. {
  415. get
  416. {
  417. return this.TextBox.SelectionLength;
  418. }
  419. set
  420. {
  421. this.TextBox.SelectionLength = value;
  422. }
  423. }
  424. /// <summary>
  425. /// Gets or sets the starting index of text selected in the editor
  426. /// </summary>
  427. public int SelectionStart
  428. {
  429. get
  430. {
  431. return this.TextBox.SelectionStart;
  432. }
  433. set
  434. {
  435. this.TextBox.SelectionStart = value;
  436. }
  437. }
  438. /// <summary>
  439. /// Gets or sets the text associated with the editor
  440. /// </summary>
  441. public string Text
  442. {
  443. get
  444. {
  445. return this.TextBox.Text;
  446. }
  447. set
  448. {
  449. this.TextBox.Text = value;
  450. }
  451. }
  452. #endregion
  453. #region Events
  454. /// <summary>
  455. /// Handler for the editors TextBox.KeyPress event
  456. /// </summary>
  457. /// <param name="sender">The object that raised the event</param>
  458. /// <param name="e">A KeyPressEventArgs that contains the event data</param>
  459. protected virtual void OnKeyPress(object sender, KeyPressEventArgs e)
  460. {
  461. if (e.KeyChar == AsciiChars.CarriageReturn /*Enter*/)
  462. {
  463. if (this.EditingTable != null)
  464. {
  465. this.EditingTable.StopEditing();
  466. }
  467. }
  468. else if (e.KeyChar == AsciiChars.Escape)
  469. {
  470. if (this.EditingTable != null)
  471. {
  472. this.EditingTable.CancelEditing();
  473. }
  474. }
  475. }
  476. /// <summary>
  477. /// Handler for the editors TextBox.LostFocus event
  478. /// </summary>
  479. /// <param name="sender">The object that raised the event</param>
  480. /// <param name="e">An EventArgs that contains the event data</param>
  481. protected virtual void OnLostFocus(object sender, EventArgs e)
  482. {
  483. if (this.TextBox.Focused || this.DropDown.ContainsFocus)
  484. {
  485. return;
  486. }
  487. if (this.EditingTable != null)
  488. {
  489. this.EditingTable.StopEditing();
  490. }
  491. }
  492. /// <summary>
  493. /// Handler for the editors drop down button MouseDown event
  494. /// </summary>
  495. /// <param name="sender">The object that raised the event</param>
  496. /// <param name="e">A CellMouseEventArgs that contains the event data</param>
  497. public virtual void OnEditorButtonMouseDown(object sender, CellMouseEventArgs e)
  498. {
  499. this.DroppedDown = !this.DroppedDown;
  500. }
  501. /// <summary>
  502. /// Handler for the editors drop down button MouseUp event
  503. /// </summary>
  504. /// <param name="sender">The object that raised the event</param>
  505. /// <param name="e">A CellMouseEventArgs that contains the event data</param>
  506. public virtual void OnEditorButtonMouseUp(object sender, CellMouseEventArgs e)
  507. {
  508. }
  509. /// <summary>
  510. /// Handler for the editors textbox MouseEnter event
  511. /// </summary>
  512. /// <param name="sender">The object that raised the event</param>
  513. /// <param name="e">An EventArgs that contains the event data</param>
  514. private void textbox_MouseEnter(object sender, EventArgs e)
  515. {
  516. this.EditingTable.RaiseCellMouseLeave(this.EditingCellPos);
  517. }
  518. #endregion
  519. #region ActivationListener
  520. /// <summary>
  521. /// Listener for WM_NCACTIVATE and WM_ACTIVATEAPP messages
  522. /// </summary>
  523. internal class ActivationListener : XPTable.Win32.NativeWindow
  524. {
  525. /// <summary>
  526. /// The DropDownCellEditor that owns the listener
  527. /// </summary>
  528. private DropDownCellEditor owner;
  529. /// <summary>
  530. /// Initializes a new instance of the DropDownCellEditor class with the
  531. /// specified DropDownCellEditor owner
  532. /// </summary>
  533. /// <param name="owner">The DropDownCellEditor that owns the listener</param>
  534. public ActivationListener(DropDownCellEditor owner) : base()
  535. {
  536. this.owner = owner;
  537. }
  538. /// <summary>
  539. /// Gets or sets the DropDownCellEditor that owns the listener
  540. /// </summary>
  541. public DropDownCellEditor Editor
  542. {
  543. get
  544. {
  545. return this.owner;
  546. }
  547. set
  548. {
  549. this.owner = value;
  550. }
  551. }
  552. /// <summary>
  553. /// Processes Windows messages
  554. /// </summary>
  555. /// <param name="m">The Windows Message to process</param>
  556. protected override void WndProc(ref Message m)
  557. {
  558. base.WndProc(ref m);
  559. if (this.owner != null && this.owner.DroppedDown)
  560. {
  561. if (m.Msg == (int) WindowMessage.WM_NCACTIVATE)
  562. {
  563. if (((int) m.WParam) == 0)
  564. {
  565. NativeMethods.SendMessage(this.Handle, (int) WindowMessage.WM_NCACTIVATE, 1, 0);
  566. }
  567. }
  568. else if (m.Msg == (int) WindowMessage.WM_ACTIVATEAPP)
  569. {
  570. if ((int)m.WParam == 0)
  571. {
  572. this.owner.DroppedDown = false;
  573. NativeMethods.PostMessage(this.Handle, (int) WindowMessage.WM_NCACTIVATE, 0, 0);
  574. }
  575. }
  576. }
  577. }
  578. }
  579. #endregion
  580. }
  581. }