在WinForms中,ToolStripSplitButton可以有一个“Checked”状态吗?

3
有没有办法在ToolStripSplitButton上设置选中状态?我想要一个ToolStripButton,它具有Checked属性以确定是否在我的应用程序窗口中显示叠加层。我的用户希望直接交互来控制叠加层的不透明度,为此我想要一个分裂按钮,其中次要按钮打开一个滑块条以控制不透明度。(当然,这不是标准功能,但我有信心能够将其组合在一起)。
然而,ToolStripSplitButton没有Checked属性,因此我不能直接这样做。我尝试使用标准ToolStripButton和单独的ToolStripSplitButton,DropDownButtonWidth = 11,Margin = -5, 1, 0, 2,使它看起来像属于ToolStripButton一样。它看起来很好,但为了完成它们成为单个按钮的幻觉,我需要让它们都使用相同的VisualStyles.PushButtonState绘制——所以当鼠标悬停在它们之一上时,它们都变为热状态。
首先我尝试在两个按钮上使用MouseEnter和MouseLeave事件来尝试更改另一个按钮的背景颜色,但是没有效果。然后我尝试使用每个按钮的Paint事件来尝试使用ButtonRenderer.DrawButton以PushButtonState = Hot渲染它们,但这似乎也不起作用,并且变得非常混乱。看起来一定有更好的方法!
有没有简单或实用的解决方案?
编辑:我想要的效果类似于这样:

Combined Check button with split drop-down

2个回答

1
我想到的解决方案是通过继承ToolStripSplitButton创建自己的按钮,添加自己的Checked属性,当按钮被选中时手动绘制覆盖它:
/// <summary>
/// ToolStripSplitCheckButton adds a Check property to a ToolStripSplitButton.
/// </summary>
public partial class ToolStripSplitCheckButton : ToolStripSplitButton
{
    //==============================================================================
    // Inner class: ToolBarButonSplitCheckButtonEventArgs
    //==============================================================================

    /// <summary>
    /// The event args for the check button click event. To be able to use the OnCheckedChanged
    /// event, we must also record a dummy button as well as this one (hack).
    /// </summary>
    public class ToolBarButonSplitCheckButtonEventArgs : ToolBarButtonClickEventArgs
    {
        /// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="split_button">The sender split check button</param>
        public ToolBarButonSplitCheckButtonEventArgs(ToolStripSplitCheckButton split_button)
            : base(new ToolBarButton("Dummy Button"))       // Hack - Dummy Button is not used
        {
            SplitCheckButton = split_button;
        }

        /// <summary>
        /// The ToolStripSplitCheckButton to be sent as an argument.
        /// </summary>
        public ToolStripSplitCheckButton SplitCheckButton { get; set; }
    }


    //==========================================================================
    // Construction

    public ToolStripSplitCheckButton()
    {
        m_checked = false;
        m_mouse_over = false;
    }


    //==========================================================================
    // Properties

    /// <summary>
    /// Indicates whether the button should toggle its Checked state on click.
    /// </summary>
    [Category("Behavior"),
    Description("Indicates whether the item should toggle its selected state when clicked."),
    DefaultValue(true)]
    public bool CheckOnClick { get; set; }

    /// <summary>
    /// Indictates the Checked state of the button.
    /// </summary>
    [Category("Behavior"),
    Description("Indicates whether the ToolStripSplitCheckButton is pressed in or not pressed in."),
    DefaultValue(false)]
    public bool Checked { get { return m_checked; } set { m_checked = value; } }


    //==========================================================================
    // Methods

    /// <summary>
    /// Toggle the click state on button click.
    /// </summary>
    protected override void OnButtonClick(EventArgs e)
    {
        if (CheckOnClick) {
            m_checked = !m_checked;
            // Raise the OnCheckStateChanged event when the button is clicked
            if (OnCheckChanged != null) {
                ToolBarButonSplitCheckButtonEventArgs args = new ToolBarButonSplitCheckButtonEventArgs(this);
                OnCheckChanged(this, args);
            }
        }
        base.OnButtonClick(e);
    }

    /// <summary>
    /// On mouse enter, record that we are over the button.
    /// </summary>
    protected override void OnMouseEnter(EventArgs e)
    {
        m_mouse_over = true;
        base.OnMouseEnter(e);
        this.Invalidate();
    }

    /// <summary>
    /// On mouse leave, record that we are no longer over the button.
    /// </summary>
    protected override void OnMouseLeave(EventArgs e)
    {
        m_mouse_over = false;
        base.OnMouseLeave(e);
        this.Invalidate();
    }

    /// <summary>
    /// Paint the check highlight when required.
    /// </summary>
    protected override void OnPaint(PaintEventArgs e)
    {
        base.OnPaint(e);
        if (m_checked) {
            // I can't get the check + mouse over to render properly, so just give the button a colour fill - Hack
            if (m_mouse_over) {
                using (Brush brush = new SolidBrush(Color.FromArgb(64, SystemColors.MenuHighlight))) {
                    e.Graphics.FillRectangle(brush, ButtonBounds);
                }
            }
            ControlPaint.DrawBorder(
                e.Graphics,
                e.ClipRectangle,            // To draw around the button + drop-down
                //this.ButtonBounds,        // To draw only around the button 
                SystemColors.MenuHighlight,
                ButtonBorderStyle.Solid);
        }
    }


    //==========================================================================
    // Member Variables

    // The delegate that acts as a signature for the function that is ultimately called 
    // when the OnCheckChanged event is triggered.
    public delegate void SplitCheckButtonEventHandler(object source, EventArgs e);
    public event SplitCheckButtonEventHandler OnCheckChanged;

    private bool m_checked;
    private bool m_mouse_over;
}

要使用此功能,请处理此按钮的OnCheckChanged事件。
但是,这里有几个笨拙的技巧。为了能够为复选框按钮引发OnCheckedChanged事件,事件参数必须除了实际按钮之外还包括对未使用的虚拟按钮的引用。即使在尝试各种渲染器后,我也无法使选中+鼠标悬停的渲染与普通复选框按钮完全相同,因此我只绘制了一个颜色叠加层,接近但不完全正确。

1
这个解决方案很好,但是在Checked属性的setter中需要调用Invalidate()方法,否则当属性改变时,控件将无法显示更新后的状态。 - undefined

1
我建议您创建自己的UserControl,并将其用作ToolStripItem
toolStripMenuItem.DropDownItems.Add(new ToolStripControlHost(new OverlayControl()));

这是一个简单的示例,展示了OverlayControl的用法(建议您使用Designer创建一个真正的UserControl)。
public class OverlayControl : UserControl
{
    private readonly CheckBox _checkBox = new CheckBox();
    private readonly TrackBar _trackBar = new TrackBar();

    public ToolStripExtended()
    {
        _checkBox.Text = "Overlay";
        BackColor = SystemColors.Window;
        _checkBox.AutoSize = true;
        _trackBar.AutoSize = true;
        var flowLayoutPanel = new FlowLayoutPanel {AutoSize = true, AutoSizeMode = AutoSizeMode.GrowAndShrink};
        flowLayoutPanel.Controls.AddRange( new Control[] { _checkBox, _trackBar });
        Controls.Add(flowLayoutPanel);
        AutoSize = true;
        AutoSizeMode = AutoSizeMode.GrowAndShrink;
        PerformLayout();
    }
} 

谢谢,@Bioukh,这似乎是一个不错的方法。实际上,我想要的是按钮的选中状态,而不是下拉框中的复选框,并且我已经添加了一张图片来更清楚地说明这一点。我的想法是让按钮本身切换覆盖层(选中或取消选中),而下拉控件控制透明度。我会继续研究你的建议,看看能否修改它以满足我的需求。再次感谢。 - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接