WinForms按钮:绘制为默认按钮,而不设置窗体的AcceptButton属性(自定义绘制,IsDefault属性,BS_DEFPUSHBUTTON)

s4chpxco  于 2022-11-17  发布在  其他
关注(0)|答案(1)|浏览(111)

假设在WinForms. NET中有下列建构。WinForms表单包含具有数个按钮的自订控件,这些按钮是传统Button类别的执行严修。其中一个按钮是表单的预设按钮。当按下ENTER时,自订控件会执行与预设按钮相关的动作。这是在重新定义的ProcessCmdKey方法中完成的:

protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
{
    if (keyData == Keys.Return)
    {
        buttonOK_Click(null, EventArgs.Empty);
        return true;
    }
    return base.ProcessCmdKey(ref msg, keyData);
}

默认按钮必须有额外的视觉提示,告知用户这是默认按钮(按钮内的额外边框)。如果我们在普通窗体中这样做,我们将设置它的AcceptButton属性。但是,这种方法在这里不适用。即使我们使用Control.FindForm方法或(this.Parent as Form)之类的表达式找到父窗体,我们无法设置主机表单的AcceptButton属性,然后以正确的方式清除它,而不会出现资源泄漏或类似的问题(在这里放置了很多技术细节,使问题变得冗长)。
解决这个任务的第一个可能的方法是重新定义或增强按钮的绘制。有没有一种相对简单的方法可以绘制一个按钮作为默认按钮,并带有相应的可视提示,而无需实现完全自定义绘制?在我的理解中,我们可以基于以下核心为默认按钮编写一个特殊的类:

internal class DefaultButton : Button
{
    protected override void OnPaint(PaintEventArgs pevent)
    {
        Rectangle rc = new Rectangle(0, 0, this.Width, this.Height);
        ButtonRenderer.DrawButton(pevent.Graphics, rc, System.Windows.Forms.VisualStyles.PushButtonState.Default);
    }
}

然而,它应该考虑到焦点状态,窗体上的另一个按钮是否是焦点(在本例中,默认按钮没有用可视提示绘制),等等。我找不到一个很好的例子来作为我开发的基础。
解决我的问题的另一个可能的方法是设置protected IsDefault属性或/和在继承自Button类的类中的重写CreateParams方法中指定BS_DEFPUSHBUTTON标志,例如:

internal class DefaultButton : Button
{
    public DefaultButton() : base()
    {
        IsDefault = true;
    }

    protected override CreateParams CreateParams
    {
        get
        {
            const int BS_DEFPUSHBUTTON = 1;
            CreateParams cp = base.CreateParams;
            cp.Style |= BS_DEFPUSHBUTTON;
            return cp;
        }
    }
}

但是我无法使这个代码工作。基于这个类的按钮总是被绘制为没有默认按钮视觉提示的普通按钮。

vhmi4jdf

vhmi4jdf1#

我不确定原来的要求;例如,我不知道为什么UserControl本身应该设置窗体的AcceptButton,或者如果窗体上有多个此类控件的示例,预期的行为是什么。设置窗体的AcceptButton似乎不是UserControl的责任,可能有更好的解决方案,例如依赖事件和设置AcceptButton。
无论如何,下面的代码示例演示如何设置窗体的AcceptButton;也许它能帮助你找到一个解决方案。代码的亮点:

  • 代码使用dispose将AcceptButton设置为null。
  • 程式码实作ISupportInitialize,以在控件初始化完成后设定接受按钮。如果您在执行阶段使用程式码建立控件执行严修,请不要忘记呼叫EndInit,如下所示:((System.ComponentModel.ISupportInitialize)(userControl11)).EndInit();,但如果您使用设计器,则设计器会处理此问题。
  • 代码调用NotifyDefault(true)只是为了在窗体上承载它时在设计时获得视觉效果。

下面是一个例子:

using System;
using System.ComponentModel;
using System.Windows.Forms;
namespace WindowsFormsApp1
{
    public class UserControl1 : UserControl, ISupportInitialize
    {
        /// <summary> 
        /// Required designer variable.
        /// </summary>
        private System.ComponentModel.IContainer components = null;

        #region Component Designer generated code

        /// <summary> 
        /// Required method for Designer support - do not modify 
        /// the contents of this method with the code editor.
        /// </summary>
        private void InitializeComponent()
        {
            this.button1 = new System.Windows.Forms.Button();
            this.button2 = new System.Windows.Forms.Button();
            this.textBox1 = new System.Windows.Forms.TextBox();
            this.SuspendLayout();
            // 
            // button1
            // 
            this.button1.Location = new System.Drawing.Point(15, 57);
            this.button1.Name = "button1";
            this.button1.Size = new System.Drawing.Size(75, 23);
            this.button1.TabIndex = 0;
            this.button1.Text = "button1";
            this.button1.UseVisualStyleBackColor = true;
            this.button1.Click += new System.EventHandler(this.button1_Click);
            // 
            // button2
            // 
            this.button2.Location = new System.Drawing.Point(96, 57);
            this.button2.Name = "button2";
            this.button2.Size = new System.Drawing.Size(75, 23);
            this.button2.TabIndex = 1;
            this.button2.Text = "button2";
            this.button2.UseVisualStyleBackColor = true;
            this.button2.Click += new System.EventHandler(this.button2_Click);
            // 
            // textBox1
            // 
            this.textBox1.Location = new System.Drawing.Point(15, 17);
            this.textBox1.Name = "textBox1";
            this.textBox1.Size = new System.Drawing.Size(100, 20);
            this.textBox1.TabIndex = 2;
            // 
            // UserControl1
            // 
            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.Controls.Add(this.textBox1);
            this.Controls.Add(this.button2);
            this.Controls.Add(this.button1);
            this.Name = "UserControl1";
            this.Size = new System.Drawing.Size(236, 106);
            this.ResumeLayout(false);
            this.PerformLayout();

        }

        #endregion
        private System.Windows.Forms.TextBox textBox1;
        public System.Windows.Forms.Button button1;
        public System.Windows.Forms.Button button2;
        public UserControl1()
        {
            InitializeComponent();
            //Just for visual effect in design time when it's hosted on a form
            button2.NotifyDefault(true); 
        }
        private void button1_Click(object sender, EventArgs e)
        {
            MessageBox.Show("1");
        }
        private void button2_Click(object sender, EventArgs e)
        {
            MessageBox.Show("2");
        }
        public void BeginInit()
        {
        }
        public void EndInit()
        {
            var f = this.FindForm();
            if (f != null)
                f.AcceptButton = button2;
        }
        protected override void Dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.Dispose();
            }
            if (disposing)
            {
                var f = this.FindForm();
                if (f != null)
                    f.AcceptButton = null;
            }
            base.Dispose(disposing);
        }
    }
}

相关问题