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



想象一下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);
}
}

然而,它应该考虑聚焦状态、表单上的另一个按钮是否聚焦(在这种情况下,默认按钮不是用视觉提示绘制的(等等。我找不到一个好的例子来作为我发展的基础。

解决我的问题的另一种可能方法是设置受保护的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;
}
}
}

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

我不确定最初的需求;例如,我不知道为什么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);
}
}
}

最新更新