TreeComboBox.cs
上传用户:eleven0727
上传日期:2022-04-14
资源大小:271k
文件大小:11k
源码类别:

组合框控件

开发平台:

C#

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