ImageNameStrip.cs 45 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453
  1. using Resources;
  2. using SmartCoalApplication.Base;
  3. using SmartCoalApplication.SystemLayer;
  4. using System;
  5. using System.Collections.Generic;
  6. using System.ComponentModel;
  7. using System.Drawing;
  8. using System.Drawing.Drawing2D;
  9. using System.Linq;
  10. using System.Text;
  11. using System.Threading.Tasks;
  12. using System.Windows.Forms;
  13. using System.Windows.Forms.VisualStyles;
  14. namespace SmartCoalApplication.Core
  15. {
  16. public class ImageNameStrip : Control
  17. {
  18. public enum ItemPart
  19. {
  20. None,
  21. Image,
  22. CloseButton
  23. }
  24. public sealed class Item
  25. {
  26. private Rectangle rectangle;
  27. private PushButtonState imageRenderState;
  28. private Image image;
  29. private string name;
  30. private bool selected;
  31. private PushButtonState checkRenderState;
  32. private CheckState checkState;
  33. private PushButtonState closeRenderState;
  34. private bool dirty;
  35. private bool lockedDirtyValue;
  36. private int dirtyValueLockCount = 0;
  37. private object tag;
  38. public event EventHandler Changed;
  39. private void OnChanged()
  40. {
  41. if (Changed != null)
  42. {
  43. Changed(this, EventArgs.Empty);
  44. }
  45. }
  46. public Rectangle Rectangle
  47. {
  48. get
  49. {
  50. return this.rectangle;
  51. }
  52. set
  53. {
  54. this.rectangle = value;
  55. }
  56. }
  57. public string Name
  58. {
  59. get
  60. {
  61. return this.name;
  62. }
  63. set
  64. {
  65. this.name = value;
  66. OnChanged();
  67. }
  68. }
  69. public Image Image
  70. {
  71. get
  72. {
  73. return this.image;
  74. }
  75. set
  76. {
  77. this.image = value;
  78. OnChanged();
  79. }
  80. }
  81. public PushButtonState ImageRenderState
  82. {
  83. get
  84. {
  85. return this.imageRenderState;
  86. }
  87. set
  88. {
  89. if (this.imageRenderState != value)
  90. {
  91. this.imageRenderState = value;
  92. OnChanged();
  93. }
  94. }
  95. }
  96. public bool Selected
  97. {
  98. get
  99. {
  100. return this.selected;
  101. }
  102. set
  103. {
  104. if (this.selected != value)
  105. {
  106. this.selected = value;
  107. OnChanged();
  108. }
  109. }
  110. }
  111. public bool Dirty
  112. {
  113. get
  114. {
  115. if (this.dirtyValueLockCount > 0)
  116. {
  117. return this.lockedDirtyValue;
  118. }
  119. else
  120. {
  121. return this.dirty;
  122. }
  123. }
  124. set
  125. {
  126. if (this.dirty != value)
  127. {
  128. this.dirty = value;
  129. if (this.dirtyValueLockCount <= 0)
  130. {
  131. OnChanged();
  132. }
  133. }
  134. }
  135. }
  136. public void LockDirtyValue(bool forceValue)
  137. {
  138. ++this.dirtyValueLockCount;
  139. if (this.dirtyValueLockCount == 1)
  140. {
  141. this.lockedDirtyValue = forceValue;
  142. }
  143. }
  144. public void UnlockDirtyValue()
  145. {
  146. --this.dirtyValueLockCount;
  147. if (this.dirtyValueLockCount == 0)
  148. {
  149. OnChanged();
  150. }
  151. else if (this.dirtyValueLockCount < 0)
  152. {
  153. throw new InvalidOperationException("Calls to UnlockDirtyValue() must be matched by a preceding call to LockDirtyValue()");
  154. }
  155. }
  156. public bool Checked
  157. {
  158. get
  159. {
  160. return (CheckState == CheckState.Checked);
  161. }
  162. set
  163. {
  164. if (value)
  165. {
  166. CheckState = CheckState.Checked;
  167. }
  168. else
  169. {
  170. CheckState = CheckState.Unchecked;
  171. }
  172. }
  173. }
  174. public CheckState CheckState
  175. {
  176. get
  177. {
  178. return this.checkState;
  179. }
  180. set
  181. {
  182. if (this.checkState != value)
  183. {
  184. this.checkState = value;
  185. OnChanged();
  186. }
  187. }
  188. }
  189. public PushButtonState CheckRenderState
  190. {
  191. get
  192. {
  193. return this.checkRenderState;
  194. }
  195. set
  196. {
  197. if (this.checkRenderState != value)
  198. {
  199. this.checkRenderState = value;
  200. OnChanged();
  201. }
  202. }
  203. }
  204. public PushButtonState CloseRenderState
  205. {
  206. get
  207. {
  208. return this.closeRenderState;
  209. }
  210. set
  211. {
  212. if (this.closeRenderState != value)
  213. {
  214. this.closeRenderState = value;
  215. OnChanged();
  216. }
  217. }
  218. }
  219. public void SetPartRenderState(ItemPart itemPart, PushButtonState renderState)
  220. {
  221. switch (itemPart)
  222. {
  223. case ItemPart.None:
  224. break;
  225. case ItemPart.CloseButton:
  226. CloseRenderState = renderState;
  227. break;
  228. case ItemPart.Image:
  229. ImageRenderState = renderState;
  230. break;
  231. default:
  232. throw new InvalidEnumArgumentException();
  233. }
  234. }
  235. public object Tag
  236. {
  237. get
  238. {
  239. return this.tag;
  240. }
  241. set
  242. {
  243. this.tag = value;
  244. OnChanged();
  245. }
  246. }
  247. public void Update()
  248. {
  249. OnChanged();
  250. }
  251. public Item()
  252. {
  253. }
  254. public Item(Image image)
  255. {
  256. this.image = image;
  257. }
  258. }
  259. private bool managedFocus = false;
  260. private bool showScrollButtons = false;
  261. private ArrowButton leftScrollButton;
  262. private ArrowButton rightScrollButton;
  263. private int scrollOffset = 0;
  264. private bool showCloseButtons = false;
  265. private const int closeButtonLength = 13;
  266. private int imagePadding = 2;
  267. private int closeButtonPadding = 2;
  268. private int mouseOverIndex = -1;
  269. private ItemPart mouseOverItemPart = ItemPart.None;
  270. private bool mouseOverApplyRendering = false;
  271. private int mouseDownIndex = -1;
  272. private MouseButtons mouseDownButton = MouseButtons.None;
  273. private ItemPart mouseDownItemPart = ItemPart.None;
  274. private bool mouseDownApplyRendering = false;
  275. private bool drawDirtyOverlay = true;
  276. public bool DrawDirtyOverlay
  277. {
  278. get
  279. {
  280. return this.drawDirtyOverlay;
  281. }
  282. set
  283. {
  284. if (this.drawDirtyOverlay != value)
  285. {
  286. this.drawDirtyOverlay = value;
  287. Refresh();
  288. }
  289. }
  290. }
  291. // This is done as an optimization: otherwise we're getting flooded with MouseMove events
  292. // and constantly refreshing our rendering. So CPU usage goes to heck.
  293. private Point lastMouseMovePt = new Point(-32000, -32000);
  294. private List<Item> items = new List<Item>();
  295. protected ArrowButton LeftScrollButton
  296. {
  297. get
  298. {
  299. return this.leftScrollButton;
  300. }
  301. }
  302. protected ArrowButton RightScrollButton
  303. {
  304. get
  305. {
  306. return this.rightScrollButton;
  307. }
  308. }
  309. private void MouseStatesToItemStates()
  310. {
  311. UI.SuspendControlPainting(this);
  312. for (int i = 0; i < this.items.Count; ++i)
  313. {
  314. this.items[i].CheckRenderState = PushButtonState.Normal;
  315. this.items[i].CloseRenderState = PushButtonState.Normal;
  316. this.items[i].ImageRenderState = PushButtonState.Normal;
  317. this.items[i].Selected = false;
  318. }
  319. if (this.mouseDownApplyRendering)
  320. {
  321. if (this.mouseDownIndex < 0 || this.mouseDownIndex >= this.items.Count)
  322. {
  323. this.mouseDownApplyRendering = false;
  324. }
  325. else
  326. {
  327. this.items[this.mouseDownIndex].SetPartRenderState(this.mouseDownItemPart, PushButtonState.Pressed);
  328. this.items[this.mouseDownIndex].Selected = true;
  329. }
  330. }
  331. else if (this.mouseOverApplyRendering)
  332. {
  333. if (this.mouseOverIndex < 0 || this.mouseOverIndex >= this.items.Count)
  334. {
  335. this.mouseOverApplyRendering = false;
  336. }
  337. else
  338. {
  339. this.items[this.mouseOverIndex].SetPartRenderState(this.mouseOverItemPart, PushButtonState.Hot);
  340. this.items[this.mouseOverIndex].Selected = true;
  341. }
  342. }
  343. UI.ResumeControlPainting(this);
  344. Invalidate();
  345. }
  346. public ImageNameStrip()
  347. {
  348. SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint, true);
  349. SetStyle(ControlStyles.Selectable, false);
  350. DoubleBuffered = true;
  351. ResizeRedraw = true;
  352. InitializeComponent();
  353. }
  354. private void InitializeComponent()
  355. {
  356. this.leftScrollButton = new ArrowButton();
  357. this.rightScrollButton = new ArrowButton();
  358. SuspendLayout();
  359. //
  360. // leftScrollButton
  361. //
  362. this.leftScrollButton.Name = "leftScrollButton";
  363. this.leftScrollButton.ArrowDirection = ArrowDirection.Left;
  364. this.leftScrollButton.ArrowOutlineWidth = 1.0f;
  365. this.leftScrollButton.Click += new EventHandler(LeftScrollButton_Click);
  366. this.leftScrollButton.DrawWithGradient = true;
  367. //
  368. // rightScrollButton
  369. //
  370. this.rightScrollButton.Name = "rightScrollButton";
  371. this.rightScrollButton.ArrowDirection = ArrowDirection.Right;
  372. this.rightScrollButton.ArrowOutlineWidth = 1.0f;
  373. this.rightScrollButton.Click += new EventHandler(RightScrollButton_Click);
  374. this.rightScrollButton.DrawWithGradient = true;
  375. //
  376. // ImageStrip
  377. //
  378. this.Name = "ImageStrip";
  379. this.TabStop = false;
  380. this.Controls.Add(this.leftScrollButton);
  381. this.Controls.Add(this.rightScrollButton);
  382. ResumeLayout();
  383. PerformLayout();
  384. }
  385. public event EventHandler<EventArgs<ArrowDirection>> ScrollArrowClicked;
  386. protected virtual void OnScrollArrowClicked(ArrowDirection arrowDirection)
  387. {
  388. if (ScrollArrowClicked != null)
  389. {
  390. ScrollArrowClicked(this, new EventArgs<ArrowDirection>(arrowDirection));
  391. }
  392. }
  393. private void LeftScrollButton_Click(object sender, EventArgs e)
  394. {
  395. Focus();
  396. OnScrollArrowClicked(ArrowDirection.Left);
  397. }
  398. private void RightScrollButton_Click(object sender, EventArgs e)
  399. {
  400. Focus();
  401. OnScrollArrowClicked(ArrowDirection.Right);
  402. }
  403. /// <summary>
  404. /// This event is raised when this control wishes to relinquish focus.
  405. /// </summary>
  406. public event EventHandler RelinquishFocus;
  407. private void OnRelinquishFocus()
  408. {
  409. if (RelinquishFocus != null)
  410. {
  411. RelinquishFocus(this, EventArgs.Empty);
  412. }
  413. }
  414. /// <summary>
  415. /// Gets or sets whether the control manages focus.
  416. /// </summary>
  417. /// <remarks>
  418. /// If this is true, the toolstrip will capture focus when the mouse enters its client area. It will then
  419. /// relinquish focus (via the RelinquishFocus event) when the mouse leaves. It will not capture or
  420. /// attempt to relinquish focus if MenuStripEx.IsAnyMenuActive returns true.
  421. /// </remarks>
  422. public bool ManagedFocus
  423. {
  424. get
  425. {
  426. return this.managedFocus;
  427. }
  428. set
  429. {
  430. this.managedFocus = value;
  431. }
  432. }
  433. public void AddItem(Item newItem)
  434. {
  435. if (this.items.Contains(newItem))
  436. {
  437. throw new ArgumentException("newItem was already added to this control");
  438. }
  439. //在这里计算名称的宽度开始 zyh
  440. //需要保证名称不能为空
  441. Size textSize = TextRenderer.MeasureText(newItem.Name, new Font(SystemFonts.DefaultFont.Name, 10, FontStyle.Regular));
  442. newItem.Rectangle = new Rectangle(0, 0, textSize.Width + 10, ClientSize.Height);
  443. //在这里计算名称的宽度技术 zyh
  444. newItem.Changed += Item_Changed;
  445. this.items.Add(newItem);
  446. PerformLayout();
  447. Invalidate();
  448. }
  449. public void RemoveItem(Item item)
  450. {
  451. if (!this.items.Contains(item))
  452. {
  453. throw new ArgumentException("item was never added to this control");
  454. }
  455. item.Changed -= Item_Changed;
  456. this.items.Remove(item);
  457. PerformLayout();
  458. Invalidate();
  459. }
  460. public void ClearItems()
  461. {
  462. SuspendLayout();
  463. UI.SuspendControlPainting(this);
  464. while (this.items.Count > 0)
  465. {
  466. RemoveItem(this.items[this.items.Count - 1]);
  467. }
  468. UI.ResumeControlPainting(this);
  469. ResumeLayout(true);
  470. Invalidate();
  471. }
  472. private void Item_Changed(object sender, EventArgs e)
  473. {
  474. Invalidate();
  475. }
  476. /// <summary>
  477. /// Raised when an item is clicked on.
  478. /// </summary>
  479. /// <remarks>
  480. /// e.Data.First is a reference to the Item.
  481. /// e.Data.Second is the ItemPart.
  482. /// e.Data.Third is the MouseButtons that was used to click on the ItemPart.
  483. /// </remarks>
  484. public event EventHandler<EventArgs<Triple<Item, ItemPart, MouseButtons>>> ItemClicked;
  485. protected virtual void OnItemClicked(Item item, ItemPart itemPart, MouseButtons mouseButtons)
  486. {
  487. if (ItemClicked != null)
  488. {
  489. ItemClicked(this, new EventArgs<Triple<Item, ItemPart, MouseButtons>>(
  490. Triple.Create(item, itemPart, mouseButtons)));
  491. }
  492. }
  493. public void PerformItemClick(int itemIndex, ItemPart itemPart, MouseButtons mouseButtons)
  494. {
  495. PerformItemClick(this.items[itemIndex], itemPart, mouseButtons);
  496. }
  497. public void PerformItemClick(Item item, ItemPart itemPart, MouseButtons mouseButtons)
  498. {
  499. OnItemClicked(item, itemPart, mouseButtons);
  500. }
  501. public Item[] Items
  502. {
  503. get
  504. {
  505. return this.items.ToArray();
  506. }
  507. }
  508. public int ItemCount
  509. {
  510. get
  511. {
  512. return this.items.Count;
  513. }
  514. }
  515. public bool ShowScrollButtons
  516. {
  517. get
  518. {
  519. return this.showScrollButtons;
  520. }
  521. set
  522. {
  523. if (this.showScrollButtons != value)
  524. {
  525. this.showScrollButtons = value;
  526. PerformLayout();
  527. Invalidate(true);
  528. }
  529. }
  530. }
  531. public int MinScrollOffset
  532. {
  533. get
  534. {
  535. return 0;
  536. }
  537. }
  538. public int MaxScrollOffset
  539. {
  540. get
  541. {
  542. int itemsLength = GetAllItemWidth();//itemSize.Width * this.items.Count;
  543. int viewLength = itemsLength - ClientSize.Width;
  544. int maxScrollOffset = Math.Max(0, viewLength);
  545. return maxScrollOffset;
  546. }
  547. }
  548. public int ScrollOffset
  549. {
  550. get
  551. {
  552. return this.scrollOffset;
  553. }
  554. set
  555. {
  556. int clampedValue = Utility.Clamp(value, MinScrollOffset, MaxScrollOffset);
  557. if (this.scrollOffset != clampedValue)
  558. {
  559. this.scrollOffset = clampedValue;
  560. OnScrollOffsetChanged();
  561. Invalidate(true);
  562. }
  563. }
  564. }
  565. public event EventHandler ScrollOffsetChanged;
  566. protected virtual void OnScrollOffsetChanged()
  567. {
  568. PerformLayout();
  569. if (ScrollOffsetChanged != null)
  570. {
  571. ScrollOffsetChanged(this, EventArgs.Empty);
  572. }
  573. }
  574. /// <summary>
  575. /// 获取所有item的宽度和,
  576. /// 因为每个item和原来不一样了
  577. /// 原本item是等宽
  578. /// </summary>
  579. /// <returns></returns>
  580. public int GetAllItemWidth()
  581. {
  582. int widths = 0;
  583. for (int y = 0; y < items.Count; y++)
  584. {
  585. widths += items[y].Rectangle.Width;
  586. }
  587. return widths;
  588. }
  589. /// <summary>
  590. /// Gets the viewable area, in View coordinate space.
  591. /// </summary>
  592. public Rectangle ViewRectangle
  593. {
  594. get
  595. {
  596. Size itemSize = ItemSize;
  597. return new Rectangle(0, 0, GetAllItemWidth(), itemSize.Height);
  598. }
  599. }
  600. protected override void OnLayout(LayoutEventArgs levent)
  601. {
  602. int arrowWidth = UI.ScaleWidth(16);
  603. ScrollOffset = Utility.Clamp(this.scrollOffset, MinScrollOffset, MaxScrollOffset);
  604. // Determine arrow visibility / position
  605. this.leftScrollButton.Size = new Size(arrowWidth, ClientSize.Height);
  606. this.leftScrollButton.Location = new Point(0, 0);
  607. this.rightScrollButton.Size = new Size(arrowWidth, ClientSize.Height);
  608. this.rightScrollButton.Location = new Point(ClientSize.Width - this.rightScrollButton.Width, 0);
  609. bool showEitherButton = this.showScrollButtons && (this.ViewRectangle.Width > ClientRectangle.Width);
  610. bool showRightButton = (this.scrollOffset < MaxScrollOffset) && showEitherButton;
  611. bool showLeftButton = (this.scrollOffset > MinScrollOffset) && showEitherButton;
  612. this.rightScrollButton.Enabled = showRightButton;
  613. this.rightScrollButton.Visible = showRightButton;
  614. this.leftScrollButton.Enabled = showLeftButton;
  615. this.leftScrollButton.Visible = showLeftButton;
  616. base.OnLayout(levent);
  617. }
  618. public bool ShowCloseButtons
  619. {
  620. get
  621. {
  622. return this.showCloseButtons;
  623. }
  624. set
  625. {
  626. if (this.showCloseButtons != value)
  627. {
  628. this.showCloseButtons = value;
  629. PerformLayout();
  630. Invalidate();
  631. }
  632. }
  633. }
  634. public int PreferredMinClientWidth
  635. {
  636. get
  637. {
  638. if (this.items.Count == 0)
  639. {
  640. return 0;
  641. }
  642. int minWidth = ItemSize.Width;
  643. if (this.leftScrollButton.Visible || this.rightScrollButton.Visible)
  644. {
  645. minWidth += this.leftScrollButton.Width;
  646. minWidth += this.rightScrollButton.Width;
  647. }
  648. minWidth = Math.Min(minWidth, ViewRectangle.Width);
  649. return minWidth;
  650. }
  651. }
  652. public Size PreferredImageSize
  653. {
  654. get
  655. {
  656. Rectangle itemRect;
  657. MeasureItemPartRectangles(out itemRect, null);
  658. return new Size(itemRect.Width - imagePadding * 2, itemRect.Height - imagePadding * 2);
  659. }
  660. }
  661. public Size ItemSize
  662. {
  663. get
  664. {
  665. Rectangle itemRect;
  666. MeasureItemPartRectangles(out itemRect, null);
  667. return itemRect.Size;
  668. }
  669. }
  670. protected virtual void DrawItemBackground(Graphics g, Item item, Rectangle itemRect)
  671. {
  672. }
  673. protected virtual void DrawItemHighlight(
  674. Graphics g,
  675. Item item,
  676. Rectangle itemRect,
  677. Rectangle highlightRect)
  678. {
  679. Color backFillColor;
  680. Color outlineColor;
  681. Color textColor;
  682. if (item.Checked)
  683. {
  684. backFillColor = Color.FromArgb(192, SystemColors.Highlight);
  685. outlineColor = backFillColor;
  686. textColor = Color.White;
  687. }
  688. else if (item.Selected)
  689. {
  690. backFillColor = Color.FromArgb(64, SystemColors.HotTrack);
  691. outlineColor = Color.FromArgb(64, SystemColors.HotTrack);
  692. textColor = Color.Black;
  693. }
  694. else
  695. {
  696. backFillColor = Color.Transparent;
  697. outlineColor = Color.Transparent;
  698. textColor = Color.Black;
  699. }
  700. using (SolidBrush backFillBrush = new SolidBrush(backFillColor))
  701. {
  702. g.FillRectangle(backFillBrush, highlightRect);
  703. }
  704. using (Pen outlinePen = new Pen(outlineColor))
  705. {
  706. g.DrawRectangle(outlinePen, highlightRect.X, highlightRect.Y, highlightRect.Width - 1, highlightRect.Height - 1);
  707. }
  708. g.DrawString(item.Name, new Font(SystemFonts.DefaultFont.Name, 10, FontStyle.Regular), new SolidBrush(textColor), new PointF(itemRect.X + 5, itemRect.Y + 5));
  709. g.DrawLine(new Pen(Color.FromArgb(189, 189, 189)), itemRect.X + itemRect.Width - 1, itemRect.Y, itemRect.X + itemRect.Width - 1, itemRect.Y + 25);
  710. }
  711. protected virtual void DrawItemCloseButton(
  712. Graphics graphics,
  713. Item item,
  714. Rectangle itemRect,
  715. Rectangle closeButtonRect)
  716. {
  717. if (item.Checked && item.Selected)
  718. {
  719. const string resourceNamePrefix = "Images.ImageStrip.CloseButton.";
  720. const string resourceNameSuffix = ".png";
  721. string resourceNameInfix = item.CloseRenderState.ToString();
  722. string resourceName = resourceNamePrefix + resourceNameInfix + resourceNameSuffix;
  723. Image image = PdnResources.GetImageResource(resourceName).Reference;
  724. graphics.SmoothingMode = SmoothingMode.AntiAlias;
  725. graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
  726. graphics.DrawImage(image, closeButtonRect, new Rectangle(0, 0, image.Width, image.Width), GraphicsUnit.Pixel);
  727. }
  728. }
  729. protected virtual void DrawItemDirtyOverlay(
  730. Graphics graphics,
  731. Item item,
  732. Rectangle itemRect,
  733. Rectangle dirtyOverlayRect)
  734. {
  735. Color outerPenColor = Color.White;
  736. Color innerPenColor = Color.Orange;
  737. const int xInset = 2;
  738. int scaledXInset = UI.ScaleWidth(xInset);
  739. const float outerPenWidth = 4.0f;
  740. const float innerPenWidth = 2.0f;
  741. float scaledOuterPenWidth = UI.ScaleWidth(outerPenWidth);
  742. float scaledInnerPenWidth = UI.ScaleWidth(innerPenWidth);
  743. SmoothingMode oldSM = graphics.SmoothingMode;
  744. graphics.SmoothingMode = SmoothingMode.AntiAlias;
  745. int left = dirtyOverlayRect.Left + scaledXInset;
  746. int top = dirtyOverlayRect.Top + scaledXInset;
  747. int right = dirtyOverlayRect.Right - scaledXInset;
  748. int bottom = dirtyOverlayRect.Bottom - scaledXInset;
  749. float r = Math.Min((right - left) / 2.0f, (bottom - top) / 2.0f);
  750. PointF centerPt = new PointF((left + right) / 2.0f, (top + bottom) / 2.0f);
  751. float twoPiOver5 = (float)(Math.PI * 0.4);
  752. PointF a = new PointF(centerPt.X + r * (float)Math.Sin(twoPiOver5), centerPt.Y - r * (float)Math.Cos(twoPiOver5));
  753. PointF b = new PointF(centerPt.X + r * (float)Math.Sin(2 * twoPiOver5), centerPt.Y - r * (float)Math.Cos(2 * twoPiOver5));
  754. PointF c = new PointF(centerPt.X + r * (float)Math.Sin(3 * twoPiOver5), centerPt.Y - r * (float)Math.Cos(3 * twoPiOver5));
  755. PointF d = new PointF(centerPt.X + r * (float)Math.Sin(4 * twoPiOver5), centerPt.Y - r * (float)Math.Cos(4 * twoPiOver5));
  756. PointF e = new PointF(centerPt.X + r * (float)Math.Sin(5 * twoPiOver5), centerPt.Y - r * (float)Math.Cos(5 * twoPiOver5));
  757. PointF[] lines =
  758. new PointF[]
  759. {
  760. centerPt, a,
  761. centerPt, b,
  762. centerPt, c,
  763. centerPt, d,
  764. centerPt, e
  765. };
  766. using (Pen outerPen = new Pen(outerPenColor, scaledOuterPenWidth))
  767. {
  768. for (int i = 0; i < lines.Length; i += 2)
  769. {
  770. graphics.DrawLine(outerPen, lines[i], lines[i + 1]);
  771. }
  772. }
  773. using (Pen innerPen = new Pen(innerPenColor, scaledInnerPenWidth))
  774. {
  775. for (int i = 0; i < lines.Length; i += 2)
  776. {
  777. graphics.DrawLine(innerPen, lines[i], lines[i + 1]);
  778. }
  779. }
  780. graphics.SmoothingMode = oldSM;
  781. }
  782. private void DrawItem(Graphics graphics, Item item, Point offset)
  783. {
  784. Rectangle itemRect;
  785. Rectangle closeButtonRect;
  786. Rectangle dirtyOverlayRect;
  787. MeasureItemPartRectangles(
  788. item,
  789. out itemRect,
  790. out closeButtonRect,
  791. out dirtyOverlayRect);
  792. itemRect.X += offset.X;
  793. itemRect.Y += offset.Y;
  794. closeButtonRect.X += offset.X;
  795. closeButtonRect.Y += offset.Y;
  796. dirtyOverlayRect.X += offset.X;
  797. dirtyOverlayRect.Y += offset.Y;
  798. DrawItemBackground(graphics, item, itemRect);
  799. Rectangle highlightRect = itemRect;
  800. DrawItemHighlight(graphics, item, itemRect, highlightRect);
  801. if (this.showCloseButtons)
  802. {
  803. DrawItemCloseButton(graphics, item, itemRect, closeButtonRect);
  804. }
  805. if (this.drawDirtyOverlay && item.Dirty)
  806. {
  807. DrawItemDirtyOverlay(graphics, item, itemRect, dirtyOverlayRect);
  808. }
  809. }
  810. public Point ClientPointToViewPoint(Point clientPt)
  811. {
  812. int viewX = clientPt.X + this.scrollOffset;
  813. return new Point(viewX, clientPt.Y);
  814. }
  815. public Point ViewPointToClientPoint(Point viewPt)
  816. {
  817. int clientX = viewPt.X - this.scrollOffset;
  818. return new Point(clientX, viewPt.Y);
  819. }
  820. private Point ViewPointToItemPoint(int itemIndex, Point viewPt)
  821. {
  822. Rectangle itemRect = ItemIndexToItemViewRectangle(itemIndex);
  823. Point itemPt = new Point(viewPt.X - itemRect.X, viewPt.Y);
  824. return itemPt;
  825. }
  826. private Rectangle ItemIndexToItemViewRectangle(int itemIndex)
  827. {
  828. return items[itemIndex].Rectangle;
  829. }
  830. public int ViewPointToItemIndex(Point viewPt)
  831. {
  832. if (!ViewRectangle.Contains(viewPt))
  833. {
  834. return -1;
  835. }
  836. int index = -1;// viewPt.X / itemSize.Width;
  837. int width = 0;
  838. //循环所有item,判断是第几个
  839. for (int i = 0; i < items.Count; i++)
  840. {
  841. if (viewPt.X >= items[i].Rectangle.X + width &&
  842. viewPt.X < items[i].Rectangle.X + width + items[i].Rectangle.Width)
  843. {
  844. index = i;
  845. break;
  846. }
  847. width += items[i].Rectangle.Width;
  848. }
  849. return index;
  850. }
  851. private void MeasureItemPartRectangles(
  852. out Rectangle itemRect,
  853. Item item)
  854. {
  855. Size textSize = new Size(0, 0);
  856. if (item != null)
  857. textSize = TextRenderer.MeasureText(item.Name, new Font(SystemFonts.DefaultFont.Name, 10, FontStyle.Regular));
  858. itemRect = new Rectangle(
  859. 0,
  860. 0,
  861. textSize.Width == 0 ? 120 : textSize.Width + 10,//120,//ClientSize.Height
  862. ClientSize.Height);
  863. if (item != null)
  864. item.Rectangle = itemRect;
  865. }
  866. /// <summary>
  867. /// 计算位置,关闭按钮、修改标记
  868. /// </summary>
  869. /// <param name="item"></param>
  870. /// <param name="itemRect"></param>
  871. /// <param name="closeButtonRect"></param>
  872. /// <param name="dirtyOverlayRect"></param>
  873. private void MeasureItemPartRectangles(
  874. Item item,
  875. out Rectangle itemRect,
  876. out Rectangle closeButtonRect,
  877. out Rectangle dirtyOverlayRect)
  878. {
  879. MeasureItemPartRectangles(out itemRect, item);
  880. int scaledCloseButtonLength = UI.ScaleWidth(closeButtonLength);
  881. int scaledCloseButtonPadding = UI.ScaleWidth(closeButtonPadding);
  882. closeButtonRect = new Rectangle(
  883. itemRect.Width - scaledCloseButtonLength - scaledCloseButtonPadding,
  884. scaledCloseButtonPadding,
  885. scaledCloseButtonLength,
  886. scaledCloseButtonLength);
  887. dirtyOverlayRect = new Rectangle(
  888. scaledCloseButtonPadding,
  889. scaledCloseButtonPadding,
  890. scaledCloseButtonLength,
  891. scaledCloseButtonLength);
  892. }
  893. private ItemPart ItemPointToItemPart(Item item, Point pt)
  894. {
  895. Rectangle itemRect;
  896. Rectangle closeButtonRect;
  897. Rectangle dirtyOverlayRect;
  898. MeasureItemPartRectangles(
  899. item,
  900. out itemRect,
  901. out closeButtonRect,
  902. out dirtyOverlayRect);
  903. //判断是否是点击了关闭按钮
  904. int dhud = 0;
  905. int index = items.FindIndex(a => a == item);
  906. if (index > 0)
  907. {
  908. dhud = GetZeroToIndexWidth(index - 1);
  909. }
  910. closeButtonRect.X += dhud;
  911. if (closeButtonRect.Contains(pt))
  912. {
  913. return ItemPart.CloseButton;
  914. }
  915. int width = 0;
  916. bool isPart = false;
  917. //循环所有item,判断是第几个
  918. for (int i = 0; i < items.Count; i++)
  919. {
  920. if (pt.X >= items[i].Rectangle.X + width &&
  921. pt.X < items[i].Rectangle.X + width + items[i].Rectangle.Width)
  922. {
  923. isPart = true;
  924. break;
  925. }
  926. width += items[i].Rectangle.Width;
  927. }
  928. if (isPart)
  929. {
  930. return ItemPart.Image;
  931. }
  932. return ItemPart.None;
  933. }
  934. private int GetZeroToIndexWidth(int index)
  935. {
  936. int m = 0;
  937. for (int i = 0; i < items.Count; i++)
  938. {
  939. if (i <= index)
  940. m += items[i].Rectangle.Width;
  941. }
  942. return m;
  943. }
  944. private Rectangle ItemIndexToClientRect(int itemIndex)
  945. {
  946. Size itemSize = ItemSize;
  947. Rectangle clientRect = new Rectangle(
  948. GetZeroToIndexWidth(itemIndex),//itemSize.Width * itemIndex,
  949. 0,
  950. itemIndex < 0 ? itemSize.Width : items[itemIndex].Rectangle.Width,//itemSize.Width,
  951. itemSize.Height);
  952. return clientRect;
  953. }
  954. /// <summary>
  955. /// 计算偏移量
  956. /// </summary>
  957. /// <param name="itemIndex">下标</param>
  958. /// <param name="minOffset">最小偏移</param>
  959. /// <param name="maxOffset">最大偏移</param>
  960. /// <param name="minFullyShownOffset"></param>
  961. /// <param name="maxFullyShownOffset"></param>
  962. private void CalculateVisibleScrollOffsets(
  963. int itemIndex,
  964. out int minOffset,
  965. out int maxOffset,
  966. out int minFullyShownOffset,
  967. out int maxFullyShownOffset)
  968. {
  969. Rectangle itemClientRect = ItemIndexToClientRect(itemIndex);
  970. minOffset = itemClientRect.Left + 1 - ClientSize.Width;
  971. maxOffset = itemClientRect.Right - 1;
  972. minFullyShownOffset = itemClientRect.Right - ClientSize.Width;
  973. maxFullyShownOffset = itemClientRect.Left;
  974. if (this.leftScrollButton.Visible)
  975. {
  976. maxOffset -= this.leftScrollButton.Width;
  977. maxFullyShownOffset -= this.leftScrollButton.Width;
  978. }
  979. if (this.rightScrollButton.Visible)
  980. {
  981. minOffset += this.rightScrollButton.Width;
  982. minFullyShownOffset += this.rightScrollButton.Width;
  983. }
  984. }
  985. public Rectangle ScrolledViewRect
  986. {
  987. get
  988. {
  989. return new Rectangle(this.scrollOffset, 0, ClientSize.Width, ClientSize.Height);
  990. }
  991. }
  992. /// <summary>
  993. /// 判断item是否显示
  994. /// </summary>
  995. /// <param name="index"></param>
  996. /// <returns></returns>
  997. public bool IsItemVisible(int index)
  998. {
  999. Rectangle itemRect = ItemIndexToClientRect(index);
  1000. Rectangle intersect = Rectangle.Intersect(itemRect, ScrolledViewRect);
  1001. return (intersect.Width > 0 || intersect.Height > 0);
  1002. }
  1003. public bool IsItemFullyVisible(int index)
  1004. {
  1005. Rectangle itemRect = ItemIndexToClientRect(index);
  1006. Rectangle svRect = ScrolledViewRect;
  1007. if (this.leftScrollButton.Visible)
  1008. {
  1009. svRect.X += this.leftScrollButton.Width;
  1010. svRect.Width -= this.leftScrollButton.Width;
  1011. }
  1012. if (this.rightScrollButton.Visible)
  1013. {
  1014. svRect.Width -= this.rightScrollButton.Width;
  1015. }
  1016. Rectangle intersect = Rectangle.Intersect(itemRect, svRect);
  1017. return (intersect == itemRect);
  1018. }
  1019. public void EnsureItemFullyVisible(Item item)
  1020. {
  1021. int index = this.items.IndexOf(item);
  1022. EnsureItemFullyVisible(index);
  1023. }
  1024. public void EnsureItemFullyVisible(int index)
  1025. {
  1026. if (IsItemFullyVisible(index))
  1027. {
  1028. return;
  1029. }
  1030. int minOffset;
  1031. int maxOffset;
  1032. int minFullyShownOffset;
  1033. int maxFullyShownOffset;
  1034. CalculateVisibleScrollOffsets(index, out minOffset, out maxOffset,
  1035. out minFullyShownOffset, out maxFullyShownOffset);
  1036. // Pick the offset that moves the image the fewest number of pixels
  1037. int oldOffset = this.scrollOffset;
  1038. int dxMin = Math.Abs(oldOffset - minFullyShownOffset);
  1039. int dxMax = Math.Abs(oldOffset - maxFullyShownOffset);
  1040. if (dxMin <= dxMax)
  1041. {
  1042. this.ScrollOffset = minFullyShownOffset;
  1043. }
  1044. else
  1045. {
  1046. this.ScrollOffset = maxFullyShownOffset;
  1047. }
  1048. }
  1049. protected override void OnPaint(PaintEventArgs e)
  1050. {
  1051. if (UI.IsControlPaintingEnabled(this))
  1052. {
  1053. if (this.items != null && this.items.Count > 0)
  1054. {
  1055. Size itemSize = ItemSize;
  1056. Rectangle firstItemRect = new Rectangle(-this.scrollOffset, 0, items[0].Rectangle.Width, itemSize.Height);
  1057. for (int i = 0; i < this.items.Count; ++i)
  1058. {
  1059. //if (IsItemVisible(i)) //暂时注释掉,在观察
  1060. {
  1061. Point itemOffset = new Point(firstItemRect.X + GetZeroToIndexWidth(i - 1), firstItemRect.Y);
  1062. //Point itemOffset = new Point(firstItemRect.X + itemSize.Width * i, firstItemRect.Y);
  1063. DrawItem(e.Graphics, this.items[i], itemOffset);
  1064. }
  1065. }
  1066. }
  1067. }
  1068. base.OnPaint(e);
  1069. }
  1070. protected override void OnMouseDown(MouseEventArgs e)
  1071. {
  1072. if (this.mouseDownButton == MouseButtons.None)
  1073. {
  1074. Point clientPt = new Point(e.X, e.Y);
  1075. Point viewPt = ClientPointToViewPoint(clientPt);
  1076. int itemIndex = ViewPointToItemIndex(viewPt);
  1077. if (itemIndex >= 0 && itemIndex < this.items.Count)
  1078. {
  1079. Item item = this.items[itemIndex];
  1080. Point itemPt = ViewPointToItemPoint(itemIndex, viewPt);
  1081. ItemPart itemPart = ItemPointToItemPart(item, itemPt);
  1082. if (itemPart == ItemPart.Image)
  1083. {
  1084. OnItemClicked(item, itemPart, e.Button);
  1085. this.mouseDownApplyRendering = false;
  1086. this.mouseOverIndex = itemIndex;
  1087. this.mouseOverItemPart = itemPart;
  1088. this.mouseOverApplyRendering = true;
  1089. }
  1090. else
  1091. {
  1092. this.mouseDownIndex = itemIndex;
  1093. this.mouseDownItemPart = itemPart;
  1094. this.mouseDownButton = e.Button;
  1095. this.mouseDownApplyRendering = true;
  1096. this.mouseOverApplyRendering = false;
  1097. }
  1098. MouseStatesToItemStates();
  1099. Refresh();
  1100. }
  1101. }
  1102. base.OnMouseDown(e);
  1103. }
  1104. protected override void OnMouseMove(MouseEventArgs e)
  1105. {
  1106. GetFocus();
  1107. Point clientPt = new Point(e.X, e.Y);
  1108. if (clientPt != this.lastMouseMovePt)
  1109. {
  1110. Point viewPt = ClientPointToViewPoint(clientPt);
  1111. int itemIndex = ViewPointToItemIndex(viewPt);
  1112. if (this.mouseDownButton == MouseButtons.None)
  1113. {
  1114. if (itemIndex >= 0 && itemIndex < this.items.Count)
  1115. {
  1116. Item item = this.items[itemIndex];
  1117. Point itemPt = ViewPointToItemPoint(itemIndex, viewPt);
  1118. ItemPart itemPart = ItemPointToItemPart(item, itemPt);
  1119. this.mouseOverIndex = itemIndex;
  1120. this.mouseOverItemPart = itemPart;
  1121. this.mouseOverApplyRendering = true;
  1122. }
  1123. else
  1124. {
  1125. this.mouseOverApplyRendering = false;
  1126. }
  1127. }
  1128. else
  1129. {
  1130. this.mouseOverApplyRendering = false;
  1131. if (itemIndex != this.mouseDownIndex)
  1132. {
  1133. this.mouseDownApplyRendering = false;
  1134. }
  1135. else if (itemIndex < 0 || itemIndex >= this.items.Count)
  1136. {
  1137. this.mouseDownApplyRendering = false;
  1138. }
  1139. else
  1140. {
  1141. Item item = this.Items[itemIndex];
  1142. Point itemPt = ViewPointToItemPoint(itemIndex, viewPt);
  1143. ItemPart itemPart = ItemPointToItemPart(item, itemPt);
  1144. if (itemPart != this.mouseDownItemPart)
  1145. {
  1146. this.mouseDownApplyRendering = false;
  1147. }
  1148. }
  1149. }
  1150. MouseStatesToItemStates();
  1151. Refresh();
  1152. }
  1153. this.lastMouseMovePt = clientPt;
  1154. base.OnMouseMove(e);
  1155. }
  1156. protected override void OnMouseUp(MouseEventArgs e)
  1157. {
  1158. bool raisedClickEvent = false;
  1159. if (this.mouseDownButton == e.Button)
  1160. {
  1161. Point clientPt = new Point(e.X, e.Y);
  1162. Point viewPt = ClientPointToViewPoint(clientPt);
  1163. int itemIndex = ViewPointToItemIndex(viewPt);
  1164. if (itemIndex >= 0 && itemIndex < this.items.Count)
  1165. {
  1166. Item item = this.items[itemIndex];
  1167. Point itemPt = ViewPointToItemPoint(itemIndex, viewPt);
  1168. ItemPart itemPart = ItemPointToItemPart(item, itemPt);
  1169. if (itemIndex == this.mouseDownIndex && itemPart == this.mouseDownItemPart)
  1170. {
  1171. if (itemPart == ItemPart.CloseButton && !item.Checked)
  1172. {
  1173. // Can only close 'checked' images, just like how tab switching+closing works in IE7
  1174. itemPart = ItemPart.Image;
  1175. }
  1176. OnItemClicked(item, itemPart, this.mouseDownButton);
  1177. raisedClickEvent = true;
  1178. }
  1179. this.mouseOverApplyRendering = true;
  1180. this.mouseOverItemPart = itemPart;
  1181. this.mouseOverIndex = itemIndex;
  1182. }
  1183. this.mouseDownApplyRendering = false;
  1184. this.mouseDownButton = MouseButtons.None;
  1185. MouseStatesToItemStates();
  1186. Refresh();
  1187. }
  1188. if (raisedClickEvent)
  1189. {
  1190. ForceMouseMove();
  1191. }
  1192. base.OnMouseUp(e);
  1193. }
  1194. protected override void OnMouseWheel(MouseEventArgs e)
  1195. {
  1196. float count = (float)e.Delta / SystemInformation.MouseWheelScrollDelta;
  1197. int pixels = GetAllItemWidth();//(int)(count * ItemSize.Width);
  1198. int newSO = ScrollOffset - pixels;
  1199. ScrollOffset = newSO;
  1200. ForceMouseMove();
  1201. base.OnMouseWheel(e);
  1202. }
  1203. private void ForceMouseMove()
  1204. {
  1205. Point clientPt = PointToClient(Control.MousePosition);
  1206. this.lastMouseMovePt = new Point(this.lastMouseMovePt.X + 1, this.lastMouseMovePt.Y + 1);
  1207. MouseEventArgs me = new MouseEventArgs(MouseButtons.None, 0, clientPt.X, clientPt.Y, 0);
  1208. OnMouseMove(me);
  1209. }
  1210. protected override void OnMouseEnter(EventArgs e)
  1211. {
  1212. GetFocus();
  1213. base.OnMouseEnter(e);
  1214. }
  1215. private void GetFocus()
  1216. {
  1217. if (this.managedFocus && !MenuStripEx.IsAnyMenuActive && UI.IsOurAppActive)
  1218. {
  1219. this.Focus();
  1220. }
  1221. }
  1222. protected override void OnMouseLeave(EventArgs e)
  1223. {
  1224. this.mouseDownApplyRendering = false;
  1225. this.mouseOverApplyRendering = false;
  1226. MouseStatesToItemStates();
  1227. Refresh();
  1228. if (this.managedFocus && !MenuStripEx.IsAnyMenuActive && UI.IsOurAppActive)
  1229. {
  1230. OnRelinquishFocus();
  1231. }
  1232. base.OnMouseLeave(e);
  1233. }
  1234. }
  1235. }