65.9K
CodeProject 正在变化。 阅读更多。
Home

可丢弃的对话框按钮

starIconstarIconstarIcon
emptyStarIcon
starIcon
emptyStarIcon

3.84/5 (9投票s)

2006年8月31日

5分钟阅读

viewsIcon

44103

downloadIcon

281

OK、Cancel、Yes 和 No 按钮,它们可以自行设置文本、名称、DialogResult 和 Accept/Cancel 状态

Sample Image - DropAndForgetButtons.gif

引言

如果你和我一样,你已经被 IntelliSense 惯坏了,希望在编码时一切都尽可能快。所以,当我创建对话框时(我相信你也是如此),为什么每次想要在窗体上添加 OK 或 Cancel 按钮时,我都必须设置 4-5 项内容?

每当我将一个按钮拖放到窗体上时,我总是会忘记以下 4 个步骤中的一个(除了第 3 步,这一步很明显)。

  1. 将 Name 设置为类似于“OK”或“Cancel”的内容,这样它就不会仅仅被称为“button1”。
  2. 将 DialogResult 设置为 DialogResult.OK 或 DialogResult.Cancel。
  3. 将 Text 设置为“OK”或“Cancel”。
  4. 将 Form 的 AcceptButton/CancelButton 设置为你新创建的 OK/Cancel 按钮(否则按 Enter/ESC 不会触发 OK/Cancel)。

我无法告诉你我多少次点击了 OK 或 Cancel,然后才奇怪为什么它不起作用。或者没有起正确的作用。或者只起了一半的作用——只触发了 OK 按钮事件处理程序中的逻辑,而没有触发窗体中 if (dialog.ShowDialog() == DialogResult.OK) 块中的逻辑。看来,对于 OK 和 Cancel 按钮,它们应该“开箱即用”。

为了完整起见,我还添加了“Yes”和“No”按钮,这使得我们也可以实现 Yes/No 和 Yes/No/Cancel 的场景。我将无用的 Abort/Retry/Ignore 场景留给读者练习。

背景

通过重载这些控件,可以非常接近我们想要的结果,这(相当)简单。这篇文章没有划时代的创新,只是一个很好的时间节省工具。如果你没有创建自定义控件的经验,这会是一个很好的入门教程。

使用代码

如果你只想使用这些按钮,请右键单击你的工具箱,然后从菜单中选择“Choose Items...”。然后点击“Browse”,然后导航到你保存 DLL 的位置。点击“OK”。

你的工具箱中会出现四个新按钮。它们都像内置按钮一样带有 [ab] 图标,但它们被命名为 OK、Cancel、Yes 和 No。

只需将 OK 按钮拖放到窗体上,你就会看到按钮被命名为“ok1”,文本为“OK”,DialogResult 为 OK,并且带有粗边框,这表示它是 AcceptButton。如果你的对话框只有一个 OK 按钮,那么你就完成了。

如果你有 Cancel 按钮,只需拖放一个。你会看到它被命名为“cancel1”,文本为“Cancel”,并且确实是一个有效的 Cancel 按钮(即使你按 Esc)。

你也可以对 Yes/No 按钮做同样的操作。对于 Yes/No/Cancel 场景,请确保你将 Cancel 按钮拖放到窗体的 **在** No 按钮 **之后**,这样 Esc 键才能按预期工作。

关注点

这段代码是如何工作的?嗯,这相当简单,但比我最初看起来要稍微复杂一些。

首先,我们需要继承自 `Button` 类。这很简单,但要获得工具箱中正确的 Bitmap,我们必须使用特性 `[ToolboxBitmap(typeof(Button))]`。我们想要一个能立即识别为 OK 按钮的东西,所以我只是将类命名为 `OK`。这样,它在工具箱中的标签就是“OK”,立即告诉我们它的用途。

    [ToolboxBitmap(typeof(Button))]
    public class OK : Button

基于此,Visual Studio 将创建名为 `ok1`、`ok2` 等的控件(尽管我不知道为什么你需要两个 OK 按钮)。我真的很希望它就被命名为 `OK`,但我会接受 `ok1`,因为我认为要说服 Visual Studio 在这一点上合作会非常困难。

好了,满足要求 #1 已经足够简单了。对话框中一个名为“ok1”的按钮显然就是 OK 按钮。代码阅读者在看到一个名为 `ok1_Click` 的方法时应该不会感到困惑。接下来我们需要设置 `DialogResult`,这可以在构造函数中轻松完成。

        public OK()
        {
            this.DialogResult = DialogResult.OK;
        }

这样,#2 也轻易解决了。将文本更改为在拖放时显示“OK”并不那么简单。因为在设计过程的某个阶段(在你的控件创建并显示之后),Visual Studio 会进入其默认文本设置模式,将 `Text` 设置为与 `Name` 相同。我不太确定处理这种情况的最佳方法,所以我只是做了一些看起来足够简单的事情。

我附加了一个 `TextChanged` 事件的处理器,然后检查 `Text` 是否与 `Name` 相同。如果相同,我将其更改为“OK”。这应该会使其在拖放时显示为“OK”,但仍然允许你在有人(“有人”是指一个不理解 Windows CUA 指南的公司范围内的委员会,但那是另一个故事了——是的,他们实际上让我们将所有 OK 按钮重命名为“Exit”,即使它和“Cancel”一起显示,而大多数人会认为它们是同一件事——但在此打住。)需要时将其更改为“Submit”、“Save”或“Exit”。

        public OK()
        {
            this.DialogResult = DialogResult.OK;
            this.TextChanged += OK_TextChanged;
        }

        private void OK_TextChanged(object sender, EventArgs e)
        {
            if (this.Text == this.Name)
                this.Text = "OK";
        }

好了,#3 解决了。现在,当 Visual Studio 执行其重命名操作时,按钮会立即将其自己的 `Text` 更改为“OK”。看着它拖放到那里并始终显示为“OK”很有趣。我感觉自己某种程度上在作弊。

设置窗体的 `AcceptButton` 是所有内容中最难的部分。问题在于,`OK` 按钮可能会被拖放到 `Panel` 或任何其他容器上。

我认为我用来设置 `Text` 的方法在这里也很好用。具体来说,当 `OK` 按钮被拖放到 `Form`(或任何其他地方)时,它的 `Parent` 属性会在那一刻发生变化。所以,我只是沿着世代的树(父到祖父到曾祖父,依此类推)向上查找,直到找到一个 `Form` 或 `null`。如果我找到一个 `Form`,我就会将其 `AcceptButton` 属性设置为我本身(`this`)。

        public OK()
        {
            this.DialogResult = DialogResult.OK;
            this.TextChanged += OK_TextChanged;
            this.ParentChanged += OK_ParentChanged;
        }

        private void OK_ParentChanged(object sender, EventArgs e)
        {
            Control parent = this.Parent;

            while (!(Parent is Form) && !(parent == null))
            {
                parent = parent.Parent;
            }

            if (parent is Form)
            {
                Form f = (Form)parent;
                f.AcceptButton = this;
            }
        }

Cancel、Yes 和 No 按钮的设计极其相似,对于 Cancel 和 No 按钮,会设置 `CancelButton` 属性。我可能可以使用一些继承,但代码足够短,所以我没有费心。我也不期望对此进行太多更改。

再次强调,请记住,如果你正在创建一个自定义的 Yes/No/Cancel 对话框,你必须按照这个顺序拖放按钮,这样 Cancel 按钮才能真正成为窗体的 `CancelButton`。

历史

2006-08-31 上传到 CodeProject。

© . All rights reserved.