using PaintDotNet.SystemLayer; using System; using System.Collections.Generic; using System.ComponentModel; using System.Drawing; using System.Drawing.Drawing2D; using System.Windows.Forms; using System.Windows.Forms.VisualStyles; namespace PaintDotNet { public class IBottomNameStrip : Control { public enum ItemPart { None, Image, CloseButton } public sealed class Item { private Rectangle rectangle; private PushButtonState imageRenderState; /// /// 图片 /// private Image image; /// /// /// private string name; /// /// 文字颜色 /// private Color nColor; private bool selected; private PushButtonState checkRenderState; private CheckState checkState; private PushButtonState closeRenderState; private bool dirty; private bool lockedDirtyValue; private int dirtyValueLockCount = 0; /// /// Tag /// private object tag; public event EventHandler Changed; private void OnChanged() { if (Changed != null) { Changed(this, EventArgs.Empty); } } /// /// checkbox的被选中/取消选中事件 /// 实际上没有用checkbox,而是自己画的,自己控制的逻辑 /// public event EventHandler CheckChanged; private void OnCheckChanged() { if (CheckChanged != null) { CheckChanged(this, EventArgs.Empty); } } public Rectangle Rectangle { get { return this.rectangle; } set { this.rectangle = value; } } public string Name { get { return this.name; } set { this.name = value; OnChanged(); } } public Color NColor { get { return this.nColor; } set { this.nColor = value; OnChanged(); } } public Image Image { get { return this.image; } set { this.image = value; OnChanged(); } } public PushButtonState ImageRenderState { get { return this.imageRenderState; } set { if (this.imageRenderState != value) { this.imageRenderState = value; OnChanged(); } } } public bool Selected { get { return this.selected; } set { if (this.selected != value) { this.selected = value; OnChanged(); } } } public bool Dirty { get { if (this.dirtyValueLockCount > 0) { return this.lockedDirtyValue; } else { return this.dirty; } } set { if (this.dirty != value) { this.dirty = value; if (this.dirtyValueLockCount <= 0) { OnChanged(); } } } } public void LockDirtyValue(bool forceValue) { ++this.dirtyValueLockCount; if (this.dirtyValueLockCount == 1) { this.lockedDirtyValue = forceValue; } } public void UnlockDirtyValue() { --this.dirtyValueLockCount; if (this.dirtyValueLockCount == 0) { OnChanged(); } else if (this.dirtyValueLockCount < 0) { throw new InvalidOperationException("Calls to UnlockDirtyValue() must be matched by a preceding call to LockDirtyValue()"); } } public bool Checked { get { return (CheckState == CheckState.Checked); } set { if (value) { CheckState = CheckState.Checked; } else { CheckState = CheckState.Unchecked; } } } public CheckState CheckState { get { return this.checkState; } set { if (this.checkState != value) { this.checkState = value; OnChanged(); OnCheckChanged(); } } } public PushButtonState CheckRenderState { get { return this.checkRenderState; } set { if (this.checkRenderState != value) { this.checkRenderState = value; OnChanged(); } } } public PushButtonState CloseRenderState { get { return this.closeRenderState; } set { if (this.closeRenderState != value) { this.closeRenderState = value; OnChanged(); } } } public void SetPartRenderState(ItemPart itemPart, PushButtonState renderState) { switch (itemPart) { case ItemPart.None: break; case ItemPart.CloseButton: CloseRenderState = renderState; break; case ItemPart.Image: ImageRenderState = renderState; break; default: throw new InvalidEnumArgumentException(); } } public object Tag { get { return this.tag; } set { this.tag = value; OnChanged(); } } public void Update() { OnChanged(); } public Item() { } public Item(Image image) { this.image = image; } } private bool managedFocus = false; private bool showScrollButtons = true;//false; private bool allowMultiChoise = true;//模式是多选模式 private ArrowButton leftScrollButton; private ArrowButton rightScrollButton; private int scrollOffset = 0; private bool showCheckButtons = false; private bool showCloseButtons = false; private const int closeButtonLength = 13; private int imagePadding = 2; private int closeButtonPadding = 2; private int mouseOverIndex = -1; private ItemPart mouseOverItemPart = ItemPart.None; private bool mouseOverApplyRendering = false; private int mouseDownIndex = -1; private MouseButtons mouseDownButton = MouseButtons.None; private ItemPart mouseDownItemPart = ItemPart.None; private bool mouseDownApplyRendering = false; private bool showCheckedColor = true; private bool drawDirtyOverlay = true; public bool ShowCheckedColor { get { return this.showCheckedColor; } set { if (this.showCheckedColor != value) { this.showCheckedColor = value; Refresh(); } } } public bool DrawDirtyOverlay { get { return this.drawDirtyOverlay; } set { if (this.drawDirtyOverlay != value) { this.drawDirtyOverlay = value; Refresh(); } } } // This is done as an optimization: otherwise we're getting flooded with MouseMove events // and constantly refreshing our rendering. So CPU usage goes to heck. private Point lastMouseMovePt = new Point(-32000, -32000); public List items = new List(); protected ArrowButton LeftScrollButton { get { return this.leftScrollButton; } } protected ArrowButton RightScrollButton { get { return this.rightScrollButton; } } private void MouseStatesToItemStates() { UI.SuspendControlPainting(this); for (int i = 0; i < this.items.Count; ++i) { this.items[i].CheckRenderState = PushButtonState.Normal; this.items[i].CloseRenderState = PushButtonState.Normal; this.items[i].ImageRenderState = PushButtonState.Normal; this.items[i].Selected = false; } if (this.mouseDownApplyRendering) { if (this.mouseDownIndex < 0 || this.mouseDownIndex >= this.items.Count) { this.mouseDownApplyRendering = false; } else { this.items[this.mouseDownIndex].SetPartRenderState(this.mouseDownItemPart, PushButtonState.Pressed); this.items[this.mouseDownIndex].Selected = true; } } else if (this.mouseOverApplyRendering) { if (this.mouseOverIndex < 0 || this.mouseOverIndex >= this.items.Count) { this.mouseOverApplyRendering = false; } else { this.items[this.mouseOverIndex].SetPartRenderState(this.mouseOverItemPart, PushButtonState.Hot); this.items[this.mouseOverIndex].Selected = true; } } UI.ResumeControlPainting(this); Invalidate(); } public IBottomNameStrip() { SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint, true); SetStyle(ControlStyles.Selectable, false); DoubleBuffered = true; ResizeRedraw = true; InitializeComponent(); } private void InitializeComponent() { this.leftScrollButton = new ArrowButton(); this.rightScrollButton = new ArrowButton(); SuspendLayout(); // // leftScrollButton // this.leftScrollButton.Name = "leftScrollButton"; this.leftScrollButton.drawTransparentBackColor = false; this.leftScrollButton.ArrowDirection = ArrowDirection.Left; this.leftScrollButton.ArrowOutlineWidth = 1.0f; this.leftScrollButton.Click += new EventHandler(LeftScrollButton_Click); this.leftScrollButton.DrawWithGradient = true; // // rightScrollButton // this.rightScrollButton.Name = "rightScrollButton"; this.rightScrollButton.drawTransparentBackColor = false; this.rightScrollButton.ArrowDirection = ArrowDirection.Right; this.rightScrollButton.ArrowOutlineWidth = 1.0f; this.rightScrollButton.Click += new EventHandler(RightScrollButton_Click); this.rightScrollButton.DrawWithGradient = true; // // ImageStrip // this.Name = "ImageStrip"; this.TabStop = false; this.Controls.Add(this.leftScrollButton); this.Controls.Add(this.rightScrollButton); ResumeLayout(); PerformLayout(); } public event EventHandler> ScrollArrowClicked; protected virtual void OnScrollArrowClicked(ArrowDirection arrowDirection) { if (ScrollArrowClicked != null) { ScrollArrowClicked(this, new EventArgs(arrowDirection)); } } private void LeftScrollButton_Click(object sender, EventArgs e) { Focus(); OnScrollArrowClicked(ArrowDirection.Left); } private void RightScrollButton_Click(object sender, EventArgs e) { Focus(); OnScrollArrowClicked(ArrowDirection.Right); } /// /// This event is raised when this control wishes to relinquish focus. /// public event EventHandler RelinquishFocus; private void OnRelinquishFocus() { if (RelinquishFocus != null) { RelinquishFocus(this, EventArgs.Empty); } } /// /// Gets or sets whether the control manages focus. /// /// /// If this is true, the toolstrip will capture focus when the mouse enters its client area. It will then /// relinquish focus (via the RelinquishFocus event) when the mouse leaves. It will not capture or /// attempt to relinquish focus if MenuStripEx.IsAnyMenuActive returns true. /// public bool ManagedFocus { get { return this.managedFocus; } set { this.managedFocus = value; } } public void AddItem(Item newItem) { if (this.items.Contains(newItem)) { throw new ArgumentException("newItem was already added to this control"); } //在这里计算名称的宽度开始 zyh //需要保证名称不能为空 Size textSize = TextRenderer.MeasureText(newItem.Name, new Font(SystemFonts.DefaultFont.Name, 10, FontStyle.Regular)); newItem.Rectangle = new Rectangle(0, 0, textSize.Width + 33, ClientSize.Height); //在这里计算名称的宽度结束 zyh newItem.Changed += Item_Changed; this.items.Add(newItem); PerformLayout(); Invalidate(); } public void RemoveItem(Item item) { if (!this.items.Contains(item)) { throw new ArgumentException("item was never added to this control"); } item.Changed -= Item_Changed; this.items.Remove(item); PerformLayout(); Invalidate(); } public void ClearItems() { SuspendLayout(); UI.SuspendControlPainting(this); while (this.items.Count > 0) { RemoveItem(this.items[this.items.Count - 1]); } UI.ResumeControlPainting(this); ResumeLayout(true); Invalidate(); } private void Item_Changed(object sender, EventArgs e) { Invalidate(); } /// /// Raised when an item is clicked on. /// /// /// e.Data.First is a reference to the Item. /// e.Data.Second is the ItemPart. /// e.Data.Third is the MouseButtons that was used to click on the ItemPart. /// public event EventHandler>> ItemClicked; protected virtual void OnItemClicked(Item item, ItemPart itemPart, MouseButtons mouseButtons) { if (ItemClicked != null) { ItemClicked(this, new EventArgs>( Triple.Create(item, itemPart, mouseButtons))); } } public void PerformItemClick(int itemIndex, ItemPart itemPart, MouseButtons mouseButtons) { PerformItemClick(this.items[itemIndex], itemPart, mouseButtons); } public void PerformItemClick(Item item, ItemPart itemPart, MouseButtons mouseButtons) { OnItemClicked(item, itemPart, mouseButtons); } public Item[] Items { get { return this.items.ToArray(); } } public int ItemCount { get { return this.items.Count; } } public bool AllowMultiChoise { get { return this.allowMultiChoise; } set { if (this.allowMultiChoise != value) { this.allowMultiChoise = value; PerformLayout(); Invalidate(true); } } } public int MinScrollOffset { get { return 0; } } public int MaxScrollOffset { get { int itemsLength = GetAllItemWidth();//itemSize.Width * this.items.Count; int viewLength = itemsLength - ClientSize.Width; int maxScrollOffset = Math.Max(0, viewLength); return maxScrollOffset; } } public int ScrollOffset { get { return this.scrollOffset; } set { int clampedValue = Utility.Clamp(value, MinScrollOffset, MaxScrollOffset); if (this.scrollOffset != clampedValue) { this.scrollOffset = clampedValue; OnScrollOffsetChanged(); Invalidate(true); } } } public event EventHandler ScrollOffsetChanged; protected virtual void OnScrollOffsetChanged() { PerformLayout(); if (ScrollOffsetChanged != null) { ScrollOffsetChanged(this, EventArgs.Empty); } } /// /// 获取所有item的宽度和, /// 因为每个item和原来不一样了 /// 原本item是等宽 /// /// public int GetAllItemWidth() { int widths = 0; for (int y = 0; y < items.Count; y++) { widths += items[y].Rectangle.Width; } return widths; } /// /// Gets the viewable area, in View coordinate space. /// public Rectangle ViewRectangle { get { Size itemSize = ItemSize; return new Rectangle(0, 0, GetAllItemWidth(), itemSize.Height); } } protected override void OnLayout(LayoutEventArgs levent) { int arrowWidth = UI.ScaleWidth(16); ScrollOffset = Utility.Clamp(this.scrollOffset, MinScrollOffset, MaxScrollOffset); // Determine arrow visibility / position this.leftScrollButton.Size = new Size(arrowWidth, ClientSize.Height); this.leftScrollButton.Location = new Point(0, 0); this.rightScrollButton.Size = new Size(arrowWidth, ClientSize.Height); this.rightScrollButton.Location = new Point(ClientSize.Width - this.rightScrollButton.Width, 0); bool showEitherButton = this.showScrollButtons && (this.ViewRectangle.Width > ClientRectangle.Width); bool showRightButton = (this.scrollOffset < MaxScrollOffset) && showEitherButton; bool showLeftButton = (this.scrollOffset > MinScrollOffset) && showEitherButton; this.rightScrollButton.Enabled = showRightButton; this.rightScrollButton.Visible = showRightButton; this.leftScrollButton.Enabled = showLeftButton; this.leftScrollButton.Visible = showLeftButton; base.OnLayout(levent); } public bool ShowCheckButtons { get { return this.showCheckButtons; } set { if (this.showCheckButtons != value) { this.showCheckButtons = value; PerformLayout(); Invalidate(); } } } public bool ShowCloseButtons { get { return this.showCloseButtons; } set { if (this.showCloseButtons != value) { this.showCloseButtons = value; PerformLayout(); Invalidate(); } } } public int PreferredMinClientWidth { get { if (this.items.Count == 0) { return 0; } int minWidth = ItemSize.Width; if (this.leftScrollButton.Visible || this.rightScrollButton.Visible) { minWidth += this.leftScrollButton.Width; minWidth += this.rightScrollButton.Width; } minWidth = Math.Min(minWidth, ViewRectangle.Width); return minWidth; } } public Size PreferredImageSize { get { Rectangle itemRect; MeasureItemPartRectangles(out itemRect, null); return new Size(itemRect.Width - imagePadding * 2, itemRect.Height - imagePadding * 2); } } public Size ItemSize { get { Rectangle itemRect; MeasureItemPartRectangles(out itemRect, null); return itemRect.Size; } } protected virtual void DrawItemBackground(Graphics g, Item item, Rectangle itemRect) { } protected virtual void DrawItemHighlight( Graphics g, Item item, Rectangle itemRect, Rectangle highlightRect) { Color backFillColor; Color outlineColor; Color textColor; if (item.Checked && this.showCheckedColor) { backFillColor = Color.FromArgb(192, SystemColors.Highlight); outlineColor = backFillColor; textColor = Color.White; } else if (item.Selected) { backFillColor = Color.FromArgb(64, SystemColors.HotTrack); outlineColor = Color.FromArgb(64, SystemColors.HotTrack); textColor = Color.Black; } else { backFillColor = Color.Transparent; outlineColor = Color.Transparent; textColor = Color.Black; } using (SolidBrush backFillBrush = new SolidBrush(backFillColor)) { g.FillRectangle(backFillBrush, highlightRect); } using (Pen outlinePen = new Pen(outlineColor)) { g.DrawRectangle(outlinePen, highlightRect.X, highlightRect.Y, highlightRect.Width - 1, highlightRect.Height - 1); } CheckBoxRenderer.DrawCheckBox(g, new Point(itemRect.X + 10, itemRect.Y + 10), (item.Checked && !this.showCheckedColor) ? CheckBoxState.CheckedNormal : CheckBoxState.UncheckedNormal); //CheckBoxRenderer.dr g.DrawString(item.Name, new Font(SystemFonts.DefaultFont.Name, 10, FontStyle.Regular), new SolidBrush(item.NColor != null ? item.NColor : textColor), new PointF(itemRect.X + 24, itemRect.Y + 10)); g.DrawLine(new Pen(Color.FromArgb(189, 189, 189)), itemRect.X + itemRect.Width - 1, itemRect.Y, itemRect.X + itemRect.Width - 1, itemRect.Y + 25); } protected virtual void DrawItemCloseButton( Graphics g, Item item, Rectangle itemRect, Rectangle closeButtonRect) { if (item.Checked && item.Selected) { const string resourceNamePrefix = "Images.ImageStrip.CloseButton."; const string resourceNameSuffix = ".png"; string resourceNameInfix = item.CloseRenderState.ToString(); string resourceName = resourceNamePrefix + resourceNameInfix + resourceNameSuffix; ImageResource imageResource = PdnResources.GetImageResource(resourceName); Image image = imageResource.Reference; g.SmoothingMode = SmoothingMode.AntiAlias; g.InterpolationMode = InterpolationMode.HighQualityBicubic; g.DrawImage(image, closeButtonRect, new Rectangle(0, 0, image.Width, image.Width), GraphicsUnit.Pixel); } } protected virtual void DrawItemDirtyOverlay( Graphics g, Item item, Rectangle itemRect, Rectangle dirtyOverlayRect) { Color outerPenColor = Color.White; Color innerPenColor = Color.Orange; const int xInset = 2; int scaledXInset = UI.ScaleWidth(xInset); const float outerPenWidth = 4.0f; const float innerPenWidth = 2.0f; float scaledOuterPenWidth = UI.ScaleWidth(outerPenWidth); float scaledInnerPenWidth = UI.ScaleWidth(innerPenWidth); SmoothingMode oldSM = g.SmoothingMode; g.SmoothingMode = SmoothingMode.AntiAlias; int left = dirtyOverlayRect.Left + scaledXInset; int top = dirtyOverlayRect.Top + scaledXInset; int right = dirtyOverlayRect.Right - scaledXInset; int bottom = dirtyOverlayRect.Bottom - scaledXInset; float r = Math.Min((right - left) / 2.0f, (bottom - top) / 2.0f); PointF centerPt = new PointF((left + right) / 2.0f, (top + bottom) / 2.0f); float twoPiOver5 = (float)(Math.PI * 0.4); PointF a = new PointF(centerPt.X + r * (float)Math.Sin(twoPiOver5), centerPt.Y - r * (float)Math.Cos(twoPiOver5)); PointF b = new PointF(centerPt.X + r * (float)Math.Sin(2 * twoPiOver5), centerPt.Y - r * (float)Math.Cos(2 * twoPiOver5)); PointF c = new PointF(centerPt.X + r * (float)Math.Sin(3 * twoPiOver5), centerPt.Y - r * (float)Math.Cos(3 * twoPiOver5)); PointF d = new PointF(centerPt.X + r * (float)Math.Sin(4 * twoPiOver5), centerPt.Y - r * (float)Math.Cos(4 * twoPiOver5)); PointF e = new PointF(centerPt.X + r * (float)Math.Sin(5 * twoPiOver5), centerPt.Y - r * (float)Math.Cos(5 * twoPiOver5)); PointF[] lines = new PointF[] { centerPt, a, centerPt, b, centerPt, c, centerPt, d, centerPt, e }; using (Pen outerPen = new Pen(outerPenColor, scaledOuterPenWidth)) { for (int i = 0; i < lines.Length; i += 2) { g.DrawLine(outerPen, lines[i], lines[i + 1]); } } using (Pen innerPen = new Pen(innerPenColor, scaledInnerPenWidth)) { for (int i = 0; i < lines.Length; i += 2) { g.DrawLine(innerPen, lines[i], lines[i + 1]); } } g.SmoothingMode = oldSM; } private void DrawItem(Graphics g, Item item, Point offset) { Rectangle itemRect; //Rectangle checkButtonRect; Rectangle closeButtonRect; Rectangle dirtyOverlayRect; MeasureItemPartRectangles( item, out itemRect, out closeButtonRect, out dirtyOverlayRect); itemRect.X += offset.X; itemRect.Y += offset.Y; closeButtonRect.X += offset.X; closeButtonRect.Y += offset.Y; dirtyOverlayRect.X += offset.X; dirtyOverlayRect.Y += offset.Y; DrawItemBackground(g, item, itemRect); Rectangle highlightRect = itemRect; DrawItemHighlight(g, item, itemRect, highlightRect); if (this.showCloseButtons) { DrawItemCloseButton(g, item, itemRect, closeButtonRect); } if (this.drawDirtyOverlay && item.Dirty) { DrawItemDirtyOverlay(g, item, itemRect, dirtyOverlayRect); } } public Point ClientPointToViewPoint(Point clientPt) { int viewX = clientPt.X + this.scrollOffset; return new Point(viewX, clientPt.Y); } public Rectangle ClientRectangleToViewRectangle(Rectangle clientRect) { Point viewPt = ClientPointToViewPoint(clientRect.Location); return new Rectangle(viewPt, clientRect.Size); } public Point ViewPointToClientPoint(Point viewPt) { int clientX = viewPt.X - this.scrollOffset; return new Point(clientX, viewPt.Y); } public Rectangle ViewRectangleToClientRectangle(Rectangle viewRect) { Point clientPt = ViewPointToClientPoint(viewRect.Location); return new Rectangle(clientPt, viewRect.Size); } private Point ViewPointToItemPoint(int itemIndex, Point viewPt) { Rectangle itemRect = ItemIndexToItemViewRectangle(itemIndex); Point itemPt = new Point(viewPt.X - itemRect.X, viewPt.Y); return itemPt; } private Rectangle ItemIndexToItemViewRectangle(int itemIndex) { return items[itemIndex].Rectangle; //Size itemSize = ItemSize; //return new Rectangle(itemSize.Width * itemIndex, itemSize.Height, itemSize.Width, itemSize.Height); } public int ViewPointToItemIndex(Point viewPt) { if (!ViewRectangle.Contains(viewPt)) { return -1; } int index = -1;// viewPt.X / itemSize.Width; int width = 0; //循环所有item,判断是第几个 for (int i = 0; i < items.Count; i++) { if (viewPt.X >= items[i].Rectangle.X + width && viewPt.X < items[i].Rectangle.X + width + items[i].Rectangle.Width) { index = i; break; } width += items[i].Rectangle.Width; } return index; } private void MeasureItemPartRectangles( out Rectangle itemRect, Item item) { //CheckBoxRenderer Size textSize = new Size(0, 0); if (item != null) textSize = /*CheckBoxRenderer*/TextRenderer.MeasureText(item.Name, new Font(SystemFonts.DefaultFont.Name, 10, FontStyle.Regular)); itemRect = new Rectangle( 0, 0, textSize.Width == 0 ? 120 : textSize.Width + 33,//120,//ClientSize.Height ClientSize.Height); if (item != null) item.Rectangle = itemRect; } /// /// 计算位置,关闭按钮、修改标记 /// /// /// /// /// private void MeasureItemPartRectangles( Item item, out Rectangle itemRect, out Rectangle closeButtonRect, out Rectangle dirtyOverlayRect) { MeasureItemPartRectangles(out itemRect, item); int scaledCloseButtonLength = UI.ScaleWidth(closeButtonLength); int scaledCloseButtonPadding = UI.ScaleWidth(closeButtonPadding); closeButtonRect = new Rectangle( itemRect.Width - scaledCloseButtonLength - scaledCloseButtonPadding, scaledCloseButtonPadding, scaledCloseButtonLength, scaledCloseButtonLength); dirtyOverlayRect = new Rectangle( scaledCloseButtonPadding, scaledCloseButtonPadding, scaledCloseButtonLength, scaledCloseButtonLength); } private ItemPart ItemPointToItemPart(Item item, Point pt) { Rectangle itemRect; Rectangle closeButtonRect; Rectangle dirtyOverlayRect; MeasureItemPartRectangles( item, out itemRect, out closeButtonRect, out dirtyOverlayRect); //判断是否是点击了关闭按钮 int dhud = 0; int index = items.FindIndex(a => a == item); if (index > 0) { dhud = GetZeroToIndexWidth(index - 1); } closeButtonRect.X += dhud; if (closeButtonRect.Contains(pt)) { return ItemPart.CloseButton; } int width = 0; bool isPart = false; //循环所有item,判断是第几个 for (int i = 0; i < items.Count; i++) { if (pt.X >= items[i].Rectangle.X + width && pt.X < items[i].Rectangle.X + width + items[i].Rectangle.Width) { isPart = true; break; } width += items[i].Rectangle.Width; } if (isPart) { return ItemPart.Image; } return ItemPart.None; } private int GetZeroToIndexWidth(int index) { int m = 0; for (int i = 0; i < items.Count; i++) { if (i <= index) m += items[i].Rectangle.Width; } return m; } private Rectangle ItemIndexToClientRect(int itemIndex) { Size itemSize = ItemSize; Rectangle clientRect = new Rectangle( GetZeroToIndexWidth(itemIndex),//itemSize.Width * itemIndex, 0, itemIndex < 0 ? itemSize.Width : items[itemIndex].Rectangle.Width,//itemSize.Width, itemSize.Height); return clientRect; } /// /// 计算偏移量 /// /// 下标 /// 最小偏移 /// 最大偏移 /// /// private void CalculateVisibleScrollOffsets( int itemIndex, out int minOffset, out int maxOffset, out int minFullyShownOffset, out int maxFullyShownOffset) { Rectangle itemClientRect = ItemIndexToClientRect(itemIndex); minOffset = itemClientRect.Left + 1 - ClientSize.Width; maxOffset = itemClientRect.Right - 1; minFullyShownOffset = itemClientRect.Right - ClientSize.Width; maxFullyShownOffset = itemClientRect.Left; if (this.leftScrollButton.Visible) { maxOffset -= this.leftScrollButton.Width; maxFullyShownOffset -= this.leftScrollButton.Width; } if (this.rightScrollButton.Visible) { minOffset += this.rightScrollButton.Width; minFullyShownOffset += this.rightScrollButton.Width; } } public Rectangle ScrolledViewRect { get { return new Rectangle(this.scrollOffset, 0, ClientSize.Width, ClientSize.Height); } } /// /// 判断item是否显示 /// /// /// public bool IsItemVisible(int index) { Rectangle itemRect = ItemIndexToClientRect(index); Rectangle intersect = Rectangle.Intersect(itemRect, ScrolledViewRect); return (intersect.Width > 0 || intersect.Height > 0); } public bool IsItemFullyVisible(int index) { Rectangle itemRect = ItemIndexToClientRect(index); Rectangle svRect = ScrolledViewRect; if (this.leftScrollButton.Visible) { svRect.X += this.leftScrollButton.Width; svRect.Width -= this.leftScrollButton.Width; } if (this.rightScrollButton.Visible) { svRect.Width -= this.rightScrollButton.Width; } Rectangle intersect = Rectangle.Intersect(itemRect, svRect); return (intersect == itemRect); } public void EnsureItemFullyVisible(Item item) { int index = this.items.IndexOf(item); EnsureItemFullyVisible(index); } public void EnsureItemFullyVisible(int index) { if (IsItemFullyVisible(index)) { return; } int minOffset; int maxOffset; int minFullyShownOffset; int maxFullyShownOffset; CalculateVisibleScrollOffsets(index, out minOffset, out maxOffset, out minFullyShownOffset, out maxFullyShownOffset); // Pick the offset that moves the image the fewest number of pixels int oldOffset = this.scrollOffset; int dxMin = Math.Abs(oldOffset - minFullyShownOffset); int dxMax = Math.Abs(oldOffset - maxFullyShownOffset); if (dxMin <= dxMax) { this.ScrollOffset = minFullyShownOffset; } else { this.ScrollOffset = maxFullyShownOffset; } } private void ForceMouseMove() { Point clientPt = PointToClient(Control.MousePosition); this.lastMouseMovePt = new Point(this.lastMouseMovePt.X + 1, this.lastMouseMovePt.Y + 1); MouseEventArgs me = new MouseEventArgs(MouseButtons.None, 0, clientPt.X, clientPt.Y, 0); OnMouseMove(me); } private void GetFocus() { if (this.managedFocus && !MenuStripEx.IsAnyMenuActive && UI.IsOurAppActive) { this.Focus(); } } #region 绘制、鼠标进入、离开、按下、移动、抬起、滚轮事件 protected override void OnPaint(PaintEventArgs e) { if (UI.IsControlPaintingEnabled(this)) { if (this.items != null && this.items.Count > 0) { Size itemSize = ItemSize; Rectangle firstItemRect = new Rectangle(-this.scrollOffset, 0, items[0].Rectangle.Width, itemSize.Height); for (int i = 0; i < this.items.Count; ++i) { //if (IsItemVisible(i)) //暂时注释掉,在观察 { Point itemOffset = new Point(firstItemRect.X + GetZeroToIndexWidth(i - 1), firstItemRect.Y); //Point itemOffset = new Point(firstItemRect.X + itemSize.Width * i, firstItemRect.Y); DrawItem(e.Graphics, this.items[i], itemOffset); } } } } base.OnPaint(e); } protected override void OnMouseDown(MouseEventArgs e) { if (this.mouseDownButton == MouseButtons.None) { Point clientPt = new Point(e.X, e.Y); Point viewPt = ClientPointToViewPoint(clientPt); int itemIndex = ViewPointToItemIndex(viewPt); if (itemIndex >= 0 && itemIndex < this.items.Count) { Item item = this.items[itemIndex]; Point itemPt = ViewPointToItemPoint(itemIndex, viewPt); ItemPart itemPart = ItemPointToItemPart(item, itemPt); if (itemPart == ItemPart.Image) { OnItemClicked(item, itemPart, e.Button); this.mouseDownApplyRendering = false; this.mouseOverIndex = itemIndex; this.mouseOverItemPart = itemPart; this.mouseOverApplyRendering = true; } else { this.mouseDownIndex = itemIndex; this.mouseDownItemPart = itemPart; this.mouseDownButton = e.Button; this.mouseDownApplyRendering = true; this.mouseOverApplyRendering = false; } MouseStatesToItemStates(); Refresh(); } } base.OnMouseDown(e); } protected override void OnMouseMove(MouseEventArgs e) { GetFocus(); Point clientPt = new Point(e.X, e.Y); if (clientPt != this.lastMouseMovePt) { Point viewPt = ClientPointToViewPoint(clientPt); int itemIndex = ViewPointToItemIndex(viewPt); if (this.mouseDownButton == MouseButtons.None) { if (itemIndex >= 0 && itemIndex < this.items.Count) { Item item = this.items[itemIndex]; Point itemPt = ViewPointToItemPoint(itemIndex, viewPt); ItemPart itemPart = ItemPointToItemPart(item, itemPt); this.mouseOverIndex = itemIndex; this.mouseOverItemPart = itemPart; this.mouseOverApplyRendering = true; } else { this.mouseOverApplyRendering = false; } } else { this.mouseOverApplyRendering = false; if (itemIndex != this.mouseDownIndex) { this.mouseDownApplyRendering = false; } else if (itemIndex < 0 || itemIndex >= this.items.Count) { this.mouseDownApplyRendering = false; } else { Item item = this.Items[itemIndex]; Point itemPt = ViewPointToItemPoint(itemIndex, viewPt); ItemPart itemPart = ItemPointToItemPart(item, itemPt); if (itemPart != this.mouseDownItemPart) { this.mouseDownApplyRendering = false; } } } MouseStatesToItemStates(); Refresh(); } this.lastMouseMovePt = clientPt; base.OnMouseMove(e); } protected override void OnMouseUp(MouseEventArgs e) { bool raisedClickEvent = false; if (this.mouseDownButton == e.Button) { Point clientPt = new Point(e.X, e.Y); Point viewPt = ClientPointToViewPoint(clientPt); int itemIndex = ViewPointToItemIndex(viewPt); if (itemIndex >= 0 && itemIndex < this.items.Count) { Item item = this.items[itemIndex]; Point itemPt = ViewPointToItemPoint(itemIndex, viewPt); ItemPart itemPart = ItemPointToItemPart(item, itemPt); if (itemIndex == this.mouseDownIndex && itemPart == this.mouseDownItemPart) { if (itemPart == ItemPart.CloseButton && !item.Checked) { // Can only close 'checked' images, just like how tab switching+closing works in IE7 itemPart = ItemPart.Image; } OnItemClicked(item, itemPart, this.mouseDownButton); raisedClickEvent = true; } this.mouseOverApplyRendering = true; this.mouseOverItemPart = itemPart; this.mouseOverIndex = itemIndex; } this.mouseDownApplyRendering = false; this.mouseDownButton = MouseButtons.None; MouseStatesToItemStates(); Refresh(); } if (raisedClickEvent) { ForceMouseMove(); } base.OnMouseUp(e); } protected override void OnMouseWheel(MouseEventArgs e) { float count = (float)e.Delta / SystemInformation.MouseWheelScrollDelta; int pixels = GetAllItemWidth();//(int)(count * ItemSize.Width); int newSO = ScrollOffset - pixels; ScrollOffset = newSO; ForceMouseMove(); base.OnMouseWheel(e); } protected override void OnMouseEnter(EventArgs e) { GetFocus(); base.OnMouseEnter(e); } protected override void OnMouseLeave(EventArgs e) { this.mouseDownApplyRendering = false; this.mouseOverApplyRendering = false; MouseStatesToItemStates(); Refresh(); if (this.managedFocus && !MenuStripEx.IsAnyMenuActive && UI.IsOurAppActive) { OnRelinquishFocus(); } base.OnMouseLeave(e); } #endregion } }