| File: System\ComponentModel\Design\CollectionEditor.SplitButton.cs | Web Access |
| Project: src\winforms\src\System.Windows.Forms.Design\src\System.Windows.Forms.Design.csproj (System.Windows.Forms.Design) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Drawing; using System.Windows.Forms; using System.Windows.Forms.VisualStyles; namespace System.ComponentModel.Design; public partial class CollectionEditor { internal class SplitButton : Button { private PushButtonState _state; private const int PushButtonWidth = 14; private Rectangle _dropDownRectangle; private bool _showSplit; private static bool s_isScalingInitialized; private const int Offset2Pixels = 2; private static int s_offset2X = Offset2Pixels; private static int s_offset2Y = Offset2Pixels; public SplitButton() : base() { if (!s_isScalingInitialized) { s_offset2X = ScaleHelper.ScaleToInitialSystemDpi(Offset2Pixels); s_offset2Y = ScaleHelper.ScaleToInitialSystemDpi(Offset2Pixels); s_isScalingInitialized = true; } } public bool ShowSplit { set { if (value != _showSplit) { _showSplit = value; Invalidate(); } } } private PushButtonState State { get => _state; set { if (!_state.Equals(value)) { _state = value; Invalidate(); } } } public override Size GetPreferredSize(Size proposedSize) { Size preferredSize = base.GetPreferredSize(proposedSize); if (_showSplit && !string.IsNullOrEmpty(Text) && TextRenderer.MeasureText(Text, Font).Width + PushButtonWidth > preferredSize.Width) { return preferredSize + new Size(PushButtonWidth, 0); } return preferredSize; } protected override bool IsInputKey(Keys keyData) { if (keyData is Keys.Down && _showSplit) { return true; } return base.IsInputKey(keyData); } protected override void OnGotFocus(EventArgs e) { if (!_showSplit) { base.OnGotFocus(e); return; } if (State is not (PushButtonState.Pressed or PushButtonState.Disabled)) { State = PushButtonState.Default; } } protected override void OnKeyDown(KeyEventArgs kevent) { if (kevent.KeyCode is Keys.Down && _showSplit) { ShowContextMenuStrip(); } else { // We need to pass the unhandled characters (including Keys.Space) on // to base.OnKeyDown when it's not to drop the split menu base.OnKeyDown(kevent); } } protected override void OnLostFocus(EventArgs e) { if (!_showSplit) { base.OnLostFocus(e); return; } if (State is not (PushButtonState.Pressed or PushButtonState.Disabled)) { State = PushButtonState.Normal; } } protected override void OnMouseDown(MouseEventArgs e) { if (!_showSplit) { base.OnMouseDown(e); return; } if (_dropDownRectangle.Contains(e.Location)) { ShowContextMenuStrip(); } else { State = PushButtonState.Pressed; } } protected override void OnMouseEnter(EventArgs e) { if (!_showSplit) { base.OnMouseEnter(e); return; } if (State is not (PushButtonState.Pressed or PushButtonState.Disabled)) { State = PushButtonState.Hot; } } protected override void OnMouseLeave(EventArgs e) { if (!_showSplit) { base.OnMouseLeave(e); return; } if (State is not (PushButtonState.Pressed or PushButtonState.Disabled)) { State = Focused ? PushButtonState.Default : PushButtonState.Normal; } } protected override void OnMouseUp(MouseEventArgs mevent) { if (!_showSplit) { base.OnMouseUp(mevent); return; } if (ContextMenuStrip is not { Visible: true }) { SetButtonDrawState(); if (Parent is not null && Bounds.Contains(Parent.PointToClient(Cursor.Position)) && !_dropDownRectangle.Contains(mevent.Location)) { OnClick(EventArgs.Empty); } } } protected override void OnPaint(PaintEventArgs pevent) { base.OnPaint(pevent); if (!_showSplit) { return; } Graphics g = pevent.Graphics; Rectangle bounds = new(0, 0, Width, Height); TextFormatFlags formatFlags = TextFormatFlags.HorizontalCenter | TextFormatFlags.VerticalCenter; ButtonRenderer.DrawButton(g, bounds, State); _dropDownRectangle = new Rectangle(bounds.Right - PushButtonWidth - 1, 4, PushButtonWidth, bounds.Height - 8); if (RightToLeft == RightToLeft.Yes) { _dropDownRectangle.X = bounds.Left + 1; g.DrawLine(SystemPens.ButtonHighlight, bounds.Left + PushButtonWidth, 4, bounds.Left + PushButtonWidth, bounds.Bottom - 4); g.DrawLine(SystemPens.ButtonHighlight, bounds.Left + PushButtonWidth + 1, 4, bounds.Left + PushButtonWidth + 1, bounds.Bottom - 4); bounds.Offset(PushButtonWidth, 0); bounds.Width -= PushButtonWidth; } else { g.DrawLine(SystemPens.ButtonHighlight, bounds.Right - PushButtonWidth, 4, bounds.Right - PushButtonWidth, bounds.Bottom - 4); g.DrawLine(SystemPens.ButtonHighlight, bounds.Right - PushButtonWidth - 1, 4, bounds.Right - PushButtonWidth - 1, bounds.Bottom - 4); bounds.Width -= PushButtonWidth; } PaintArrow(g, _dropDownRectangle); // If we don't use mnemonic, set formatFlag to NoPrefix as this will show ampersand. if (!UseMnemonic) { formatFlags |= TextFormatFlags.NoPrefix; } else if (!ShowKeyboardCues) { formatFlags |= TextFormatFlags.HidePrefix; } if (!string.IsNullOrEmpty(Text)) { TextRenderer.DrawText(pevent, Text, Font, bounds, SystemColors.ControlText, formatFlags); } if (Focused) { bounds.Inflate(-4, -4); } } private static void PaintArrow(IDeviceContext deviceContext, Rectangle dropDownRect) { Point middle = new( Convert.ToInt32(dropDownRect.Left + dropDownRect.Width / 2), Convert.ToInt32(dropDownRect.Top + dropDownRect.Height / 2)); // If the width is odd - favor pushing it over one pixel right. middle.X += (dropDownRect.Width % 2); Point[] arrow = [ new(middle.X - s_offset2X, middle.Y - 1), new(middle.X + s_offset2X + 1, middle.Y - 1), new(middle.X, middle.Y + s_offset2Y) ]; deviceContext.TryGetGraphics(create: true)?.FillPolygon(SystemBrushes.ControlText, arrow); } private void ShowContextMenuStrip() { State = PushButtonState.Pressed; if (ContextMenuStrip is not null) { ContextMenuStrip.Closed += ContextMenuStrip_Closed; ContextMenuStrip.Show(this, 0, Height); } } private void ContextMenuStrip_Closed(object? sender, ToolStripDropDownClosedEventArgs e) { if (sender is ContextMenuStrip cms) { cms.Closed -= ContextMenuStrip_Closed; } SetButtonDrawState(); } private void SetButtonDrawState() { if (Parent is not null && Bounds.Contains(Parent.PointToClient(Cursor.Position))) { State = PushButtonState.Hot; } else if (Focused) { State = PushButtonState.Default; } else { State = PushButtonState.Normal; } } } }