TreeComboBox.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Text;
  4. using System.Windows.Forms;
  5. using System.Drawing;
  6. namespace Core.StlMes.Client.SaleBase
  7. {
  8. public class TreeComboBox : ComboBox
  9. {
  10. #region 自定义成员变量
  11. private TextBox m_TextBox;
  12. private TreeView m_TreeView;
  13. private Button m_DropdownButton;
  14. private int m_MaxDropDownItems = 8;
  15. //是否正在显示下拉列表
  16. private bool b_Dropdown = false;
  17. //使能
  18. private bool b_Enabled = true;
  19. //事件
  20. public event EventHandler DropDown;
  21. public event EventHandler DropDownClosed;
  22. public event EventHandler EnableChanged;
  23. public event TreeViewCancelEventHandler BeforeExpand;
  24. public event TreeViewCancelEventHandler BeforeCollapse;
  25. public event TreeViewEventHandler AfterExpand;
  26. public event TreeViewEventHandler AfterCollapse;
  27. #endregion
  28. /// <summary>
  29. /// 构造函数
  30. /// </summary>
  31. public TreeComboBox()
  32. {
  33. this.InitControls();
  34. this.LayoutControls();
  35. }
  36. #region 内部辅助方法
  37. /// <summary>
  38. /// 创建并初始化所有控件,包括添加事件处理函数
  39. /// </summary>
  40. private void InitControls()
  41. {
  42. //TextBox
  43. this.m_TextBox = new TextBox();
  44. this.m_TextBox.KeyDown += new KeyEventHandler(m_TextBox_KeyDown);
  45. this.m_TextBox.Parent = this;
  46. this.m_TextBox.ReadOnly = true;
  47. //Button
  48. this.m_DropdownButton = new Button();
  49. //Assembly asm = Assembly.GetExecutingAssembly();
  50. //System.IO.Stream stream = asm.GetManifestResourceStream(asm.GetName().Name + ".BM_dropdown.bmp");
  51. Bitmap bm = new Bitmap(this.GetType(), "BM_dropdown.bmp");
  52. this.m_DropdownButton.Image = bm;
  53. this.m_DropdownButton.Width = 16;
  54. this.m_DropdownButton.Height = this.m_TextBox.Height-2;
  55. this.m_DropdownButton.Location = new Point(this.m_TextBox.Right-18, 1);
  56. this.m_DropdownButton.Click += new EventHandler(m_DropdownButton_Click);
  57. this.m_DropdownButton.TextChanged += new EventHandler(m_DropdownButton_TextChanged);
  58. this.m_DropdownButton.FlatStyle = FlatStyle.Flat;
  59. this.m_DropdownButton.Parent = this;
  60. this.m_DropdownButton.BringToFront();
  61. //TreeView
  62. this.m_TreeView = new TreeView();
  63. this.m_TreeView.Visible = false;
  64. this.m_TreeView.DoubleClick += new EventHandler(m_TreeView_DoubleClick);
  65. this.m_TreeView.KeyDown += new KeyEventHandler(m_TreeView_KeyDown);
  66. this.m_TreeView.LostFocus += new EventHandler(m_TreeView_LostFocus);
  67. this.m_TreeView.BeforeExpand += new TreeViewCancelEventHandler(m_TreeView_BeforeExpand);
  68. this.m_TreeView.BeforeCollapse += new TreeViewCancelEventHandler(TreeComboBox_BeforeCollapse);
  69. this.m_TreeView.AfterExpand += new TreeViewEventHandler(m_TreeView_AfterExpand);
  70. this.m_TreeView.AfterCollapse += new TreeViewEventHandler(TreeComboBox_AfterCollapse);
  71. this.m_TreeView.Location = new Point(0, 0);
  72. this.m_TreeView.Parent = null;
  73. this.LostFocus += new EventHandler(TreeComboBox_LostFocus);
  74. }
  75. /// <summary>
  76. /// 布局所有控件,让TextBox尺寸适应容器尺寸
  77. /// </summary>
  78. private void LayoutControls()
  79. {
  80. this.m_TextBox.Width = this.Width;
  81. this.Height = this.m_TextBox.Height;
  82. this.m_DropdownButton.Left = this.m_TextBox.Right - 18;
  83. this.m_DropdownButton.Height = this.m_TextBox.Height - 2;
  84. this.m_TreeView.Width = this.Width * 2;
  85. this.m_TreeView.Height = (int)((this.Font.Height+3) * this.m_MaxDropDownItems);
  86. }
  87. /// <summary>
  88. /// 显示下拉列表
  89. /// </summary>
  90. private void ShowDropDown()
  91. {
  92. if (this.Parent == null)
  93. return;
  94. // 智能计算显示的位置,尝试在下方显示,如果没有足够空间,则在上方显示
  95. // 尝试在下方的位置(现在只是相对父窗口的相对位置)
  96. Point pos = new Point(this.Left, this.Bottom-1);
  97. // 把位置映射到顶层窗口,获取父窗口的屏幕坐标
  98. Point parentPos = this.Parent.PointToScreen(this.Parent.Location);
  99. // 获取顶层窗口的屏幕坐标
  100. Point topParentPos = this.TopLevelControl.PointToScreen(this.Parent.Location);
  101. // 把相对父窗口的位置变换为相对顶级窗口的位置,因为popup的父是顶级窗口
  102. pos.Offset(parentPos.X - topParentPos.X, parentPos.Y - topParentPos.Y);
  103. // 检查是否有足够空间用于在label下方显示day picker
  104. if ((pos.Y + this.m_TreeView.Height) > this.TopLevelControl.ClientRectangle.Height)
  105. {
  106. // 没有足够的空间(超出了顶级窗口客户区),尝试在上方显示将Y方向,向上平移
  107. pos.Y -= (this.Height + this.m_TreeView.Height);
  108. if (pos.Y < 0)
  109. {
  110. // 如果上方仍然没有空间显示,则显示在顶级窗口的底部
  111. pos.Y = (this.TopLevelControl.ClientRectangle.Height -this.m_TreeView.Height);
  112. }
  113. }
  114. // 尝试停靠,如果右边超过顶级窗口的客户区,则将控件向左移动,并紧靠在顶级窗口右侧
  115. if ((pos.X + this.m_TreeView.Width) > this.TopLevelControl.ClientRectangle.Width)
  116. pos.X = (this.TopLevelControl.ClientRectangle.Width - this.m_TreeView.Width);
  117. this.m_TreeView.Location = pos;// this.Parent.PointToScreen(pt);
  118. this.m_TreeView.Visible = true;
  119. this.m_TreeView.Parent = this.TopLevelControl;
  120. this.m_TreeView.BringToFront();
  121. this.b_Dropdown = true;
  122. //raise event
  123. if (this.DropDown != null)
  124. this.DropDown(this, EventArgs.Empty);
  125. this.m_TreeView.Focus();
  126. }
  127. /// <summary>
  128. /// 隐藏下拉列表
  129. /// </summary>
  130. private void HideDropDown()
  131. {
  132. if (this.DropDownClosed != null)
  133. this.DropDownClosed(this,EventArgs.Empty);
  134. this.m_TreeView.Parent = null;
  135. this.m_TreeView.Visible = false;
  136. this.b_Dropdown = false;
  137. }
  138. #endregion
  139. #region 事件处理 - TextBox
  140. /// <summary>
  141. /// 在编辑框中按下按键
  142. /// </summary>
  143. /// <param name="sender"></param>
  144. /// <param name="e"></param>
  145. private void m_TextBox_KeyDown(object sender, KeyEventArgs e)
  146. {
  147. if (e.KeyCode == Keys.Enter)
  148. {
  149. if (this.b_Dropdown)
  150. this.HideDropDown();
  151. else
  152. this.ShowDropDown();
  153. }
  154. else if (e.KeyCode == Keys.Down)
  155. {
  156. this.ShowDropDown();
  157. this.m_TreeView.Focus();
  158. }
  159. }
  160. /// <summary>
  161. /// 在编辑框改变Text
  162. /// </summary>
  163. /// <param name="sender"></param>
  164. /// <param name="e"></param>
  165. private void m_DropdownButton_TextChanged(object sender, EventArgs e)
  166. {
  167. throw new NotImplementedException();
  168. }
  169. #endregion
  170. #region 事件处理 - TreeView
  171. /// <summary>
  172. /// 在下拉列表中选择了一个节点的事件处理!
  173. /// </summary>
  174. /// <param name="sender"></param>
  175. /// <param name="e"></param>
  176. private void m_TreeView_KeyDown(object sender, KeyEventArgs e)
  177. {
  178. //如果按下回车表示要选中当前节点
  179. if (e.KeyCode == Keys.Enter)
  180. {
  181. this.m_TreeView_DoubleClick(sender, EventArgs.Empty);
  182. }
  183. }
  184. /// <summary>
  185. /// 失去焦点时,如果不是被下拉按钮夺取的焦点,则隐藏它!
  186. /// </summary>
  187. /// <param name="sender"></param>
  188. /// <param name="e"></param>
  189. void m_TreeView_LostFocus(object sender, EventArgs e)
  190. {
  191. if(!this.m_DropdownButton.Focused)
  192. this.HideDropDown();
  193. }
  194. /// <summary>
  195. /// 在下拉列表中双击
  196. /// </summary>
  197. /// <param name="sender"></param>
  198. /// <param name="e"></param>
  199. private void m_TreeView_DoubleClick(object sender, EventArgs e)
  200. {
  201. TreeNode node = this.m_TreeView.SelectedNode;
  202. if (node != null)
  203. this.m_TextBox.Text = node.Text;
  204. if (this.b_Dropdown)
  205. {
  206. this.HideDropDown();
  207. }
  208. }
  209. void TreeComboBox_AfterCollapse(object sender, TreeViewEventArgs e)
  210. {
  211. if (this.AfterCollapse != null)
  212. this.AfterCollapse(this, e);
  213. }
  214. void TreeComboBox_BeforeCollapse(object sender, TreeViewCancelEventArgs e)
  215. {
  216. if (this.BeforeCollapse != null)
  217. this.BeforeCollapse(this, e);
  218. }
  219. void m_TreeView_AfterExpand(object sender, TreeViewEventArgs e)
  220. {
  221. if (this.AfterExpand != null)
  222. this.AfterExpand(this,e);
  223. }
  224. void m_TreeView_BeforeExpand(object sender, TreeViewCancelEventArgs e)
  225. {
  226. if (this.BeforeExpand != null)
  227. this.BeforeExpand(this, e);
  228. }
  229. #endregion
  230. #region 事件处理 - Button
  231. private void m_DropdownButton_Click(object sender, EventArgs e)
  232. {
  233. //throw new Exception("The method or operation is not implemented.");
  234. if (this.b_Dropdown)
  235. this.HideDropDown();
  236. else
  237. this.ShowDropDown();
  238. }
  239. #endregion
  240. #region 事件处理 - 自身
  241. /// <summary>
  242. /// 失去焦点
  243. /// </summary>
  244. /// <param name="sender"></param>
  245. /// <param name="e"></param>
  246. private void TreeComboBox_LostFocus(object sender, EventArgs e)
  247. {
  248. if (this.b_Dropdown)
  249. this.HideDropDown();
  250. }
  251. /// <summary>
  252. /// 重设尺寸
  253. /// </summary>
  254. /// <param name="e"></param>
  255. protected override void OnResize(EventArgs e)
  256. {
  257. base.OnResize(e);
  258. this.LayoutControls();
  259. }
  260. protected override void OnSizeChanged(EventArgs e)
  261. {
  262. base.OnSizeChanged(e);
  263. this.LayoutControls();
  264. }
  265. #endregion
  266. #region 外部属性封装
  267. /// <summary>
  268. /// 获取节点集合
  269. /// </summary>
  270. public TreeNodeCollection Nodes
  271. {
  272. get { return this.m_TreeView.Nodes; }
  273. }
  274. /// <summary>
  275. /// 设置或者获取节点的图片列表
  276. /// </summary>
  277. public ImageList ImageList
  278. {
  279. get
  280. {
  281. return this.m_TreeView.ImageList;
  282. }
  283. set
  284. {
  285. this.m_TreeView.ImageList = value;
  286. }
  287. }
  288. /// <summary>
  289. /// 重写enabled属性
  290. /// </summary>
  291. public new bool Enabled
  292. {
  293. get { return this.b_Enabled; }
  294. set
  295. {
  296. if (this.b_Enabled != value)
  297. {
  298. this.b_Enabled = value;
  299. this.m_DropdownButton.Enabled = value;
  300. this.b_Enabled = value;
  301. //当灰掉时,隐藏下拉列表
  302. if (!this.b_Enabled && this.b_Dropdown)
  303. this.HideDropDown();
  304. if (this.EnableChanged != null)
  305. this.EnableChanged(this, EventArgs.Empty);
  306. }
  307. }
  308. }
  309. /// <summary>
  310. /// 字体
  311. /// </summary>
  312. public override Font Font
  313. {
  314. get
  315. {
  316. return base.Font;
  317. }
  318. set
  319. {
  320. base.Font = value;
  321. this.m_TextBox.Font = value;
  322. this.m_TreeView.Font = value;
  323. //调整布局
  324. this.LayoutControls();
  325. }
  326. }
  327. /// <summary>
  328. /// 重写Text
  329. /// </summary>
  330. public override string Text
  331. {
  332. get
  333. {
  334. return this.m_TextBox.Text;
  335. }
  336. set
  337. {
  338. if (this.Text != value)
  339. {
  340. this.m_TextBox.Text = value;
  341. }
  342. }
  343. }
  344. /// <summary>
  345. /// 是否显示lines
  346. /// </summary>
  347. public bool ShowLines
  348. {
  349. get { return this.m_TreeView.ShowLines; }
  350. set { this.m_TreeView.ShowLines = value; }
  351. }
  352. /// <summary>
  353. /// 是否显示+ -按钮
  354. /// </summary>
  355. public bool ShowPlusMinus
  356. {
  357. get { return this.m_TreeView.ShowPlusMinus; }
  358. set { this.m_TreeView.ShowPlusMinus = value; }
  359. }
  360. /// <summary>
  361. /// 是否显示root lines
  362. /// </summary>
  363. public bool ShowRootLines
  364. {
  365. get { return this.m_TreeView.ShowRootLines; }
  366. set { this.m_TreeView.ShowRootLines = value; }
  367. }
  368. /// <summary>
  369. /// 是否显示root lines
  370. /// </summary>
  371. public bool ShowNodeToolTips
  372. {
  373. get { return this.m_TreeView.ShowNodeToolTips; }
  374. set { this.m_TreeView.ShowNodeToolTips = value; }
  375. }
  376. /// <summary>
  377. /// 获取或者设置选中的节点!
  378. /// </summary>
  379. public TreeNode SelectedNode
  380. {
  381. get
  382. {
  383. return this.m_TreeView.SelectedNode;
  384. }
  385. set
  386. {
  387. this.m_TreeView.SelectedNode = value;
  388. if (value != null)
  389. this.Text = value.Text;
  390. }
  391. }
  392. public int MaxDropDownItems
  393. {
  394. get
  395. {
  396. return this.m_MaxDropDownItems;
  397. }
  398. set
  399. {
  400. if (this.m_MaxDropDownItems != value)
  401. {
  402. this.m_MaxDropDownItems = value;
  403. this.m_TreeView.Height = this.m_TextBox.Height * value;
  404. }
  405. }
  406. }
  407. #endregion
  408. }
  409. }