准确、轻松、快速的窗体设计,借助 Excel 的一点帮助






3.47/5 (8投票s)
一个 Microsoft Excel 工作表,用于准确、轻松、快速地设计和应用 Windows 窗体及资源字符串
引言
本文介绍的 Microsoft Excel 工作表是我多年来用来加速开发显示窗体或使用字符串资源(无论是托管的还是非托管的)的程序的“加速器”设计的一种变体。换句话说,这种设计可用于 C、C++ 和 C# 项目,并且我已经在所有这三种语言的项目中使用过它。除其他优点外,它还能在设计阶段及时查明三个关键问题,这时最容易纠正。
- 标签中嵌入的重复助记符(快捷键分配)。据我所知,开发环境中没有内置工具能做到这一点。尽管如此,此工作表甚至能为您跟踪这些分配。
- 重复的控件名称。虽然编辑器会警告并纠正这些问题,但纠正发生在事后,并且您的设计文档会立即不同步。此工具会在控件名称很容易纠正时(即在将它们输入窗体设计器附加的属性表中之前)将其提请您注意。
- 重复的字符串名称。同样,尽管编辑器会警告,但如果您曾在编辑过程中尝试纠正一个重复项,您会知道这至少是很令人恼火的,而且您的代码会与您的规划文档不同步。}
然而,以上都不是我开始使用 Excel 来组织 C、C++ 和 C# 程序字符串的原因。相反,我最初是在去年一篇题为《Collision Proof Shared String Resources》的文章中写到的,关于开发一个高效的工具来生成大量的 Windows 字符串资源,用于错误消息和项目之间的共享。
背景
人们很容易认为 Microsoft Excel 是数字计算的首选工具,它确实如此。尽管如此,软件设计人员和其他人员也可以很好地利用 Excel,而这是我发现的关于 Windows 电子表格软件的无冕之王的一个众多应用中的一个。不过,我已尽力以您无论对 Excel 的熟悉程度如何都能理解的方式进行写作。
此工作表利用了许多超越其数字计算能力的功能。
- 文本解析。工作表公式通过搜索
&
信号字符并提取其后的字符,来识别并显示字符串中标记的快捷键。 - 字符串连接。其他单元格构造变量名,甚至完整的、有效的 C# 语句,您可以将其放入窗体后面的代码中,以从字符串填充窗体上的标签。为什么要做所有那些易出错、乏味的输入工作,当 Excel 可以每次完美地为您完成呢?
- 条件格式。围绕命名范围构建的三个条件格式公式完成了将引言中枚举的三个关键功能变为现实的大部分工作。
使用代码
随本文提供的 Microsoft Excel 工作簿是一个实际项目中的工作文档。事实上,在我撰写本文时,该项目仍在进行中,并且工作表中描述的三个单选按钮尚未创建。
.
图 1 是工作表在没有错误时显示的样子。
在开始介绍工作表之前,先进行一下简要的背景介绍。
- 左上角包含一个图例,解释了条件格式规则应用于单元格以引起您注意的错误的三种颜色代码。
- 单元格从左到右的顺序是按设计排列的,以方便输入,特别是关键的字符串资源名称和值。幸运的是,Visual C++ 工具链中的 Windows 资源编辑器和 Visual C# 工具链中的托管资源编辑器都使用相同的基本布局。
- 除了努力使用非常鲜明的颜色外,为三种错误类型选择的颜色是任意的。如果您喜欢其他颜色,请随时在条件格式规则中编辑格式。
- 带有浅交叉影线图案的单元格是空的,或者包含返回空字符串的公式,以引起您对应该填充但被忽略的单元格的注意。
- 带有浅绿色背景的单元格是输入单元格。任何其他颜色的单元格都包含公式或标签,在正常操作期间不应更改。
- 每个表的下方是一行深棕色单元格,包含黄色文本。这些单元格标记着对条件格式规则正确运行至关重要的命名范围的结束,这些规则会突出显示需要您注意的错误。这些受保护的行已合并,以防止意外输入属于这些范围的单元格,这会损害它们的有效性。另外,合并它们还可以防止最左侧单元格中的长文本干扰格式自动调整功能。如有需要,请在这些行上方插入额外的行。
以下是上面图 1 中可见的功能的快速概述。
- A 列,标记为类型,标识其右侧单元格描述的控件的类型。出于多种原因,其中最重要的是数量庞大,即使忽略自定义控件,以及它们的名称在公式中不起作用的事实,类型都未经验证。(我开发过其他使用已验证类型名称的工作表,当它们在生成对象名称时起作用。)
- B 列,标记为名称,是输入控件名称的地方。此工作表在活动工作表的左半部分列出标签,在右半部分列出它们适用的对象。我稍后将详细介绍我这样设计的理由。
- C 列,标记为快捷键,是许多计算列中的第一个。其单元格中的公式以大致相同的方式识别同一行 E 列文本中出现的快捷键,运行时引擎也是如此。
- D 列,标记为资源字符串名称,使用公式从将应用于标签的名称(B 列)派生字符串的名称。
- E 列,标记为文本,应该是不言自明的,但值得评论。如果标签的文本包含嵌入的
&
字符,紧随其后的字符就被指定为其助记符。除非禁用(默认情况下,此功能处于开启状态),当您按住ALT
键同时按下助记符字母时,一条消息会发送到窗体,窗体预期会做出响应,例如选择相邻的文本框,或者在按钮的情况下,触发其 Click 事件。 - F 列和 G 列,分别标记为绝对资源字符串名称和窗体加载事件的赋值语句,使用字符串连接公式构造表示字符串资源的应用程序属性的完全限定名称,并由此构造一个语法正确的 C# 语句,用于使用该字符串设置标签的
Text
属性。我知道这些方法有效,因为我将整个列粘贴到窗体的构造函数中,编译了程序集,并运行了它。这超过一百行我不必写的 C# 代码,而且我在具有更多标签的窗体上使用了这种技术。 - H、I 和 J 列,标记为
TabIndex
、LocationX
和LocationY
,是从标签的同名属性表中填充的。我使用这些来快速查看所有控件相对于彼此的位置,这一点我将在本节的末尾更详细地解释。 - K 列,标记为
TopToTop
,使用一个简单的公式计算控件顶部与其上方直接控件的距离,这让我能够快速了解控件是否均匀间隔。 - L 列,标记为
Between
,使用另一个简单的公式计算控件顶部与其上方直接控件底部之间的距离。此列和左侧列中的值不能直接从开发环境中获得,因为它们不对应属性。 - M 列和 N 列,标记为宽度和高度,是从设计器中属性表的同名字段填充的。
- O 到 Y 列重复输入控件的相同信息,这些控件是组合框、文本框和单选按钮的混合体。然而,在快捷键和资源字符串名称列的替代位置是一个
true/false
值列,标记为ReadOnly
。我使用此列来跟踪我标记为只读的文本框,因为它们是仅显示字段。 - 在工作表的左半部分,主表正下方,有一个区域从 B 列开始,有一列合并单元格,标记为消息助记符。它与上面的名称列对齐,并且与相邻的快捷键列重叠并非偶然,因为它的目的是接受输入,从中构造具有公共前缀的消息字符串的名称,这是 D 列单元格中公式的作用。
- 右侧合并单元格列的单元格的命名和用途与上方更宽表的行相同。只有两处略有不同。
- D 列单元格中的公式应用的名称前缀与其正上方单元格中的公式应用的前缀不同,这样,标签上的字符串和用户提示文本框中的消息就通过其前缀区分开来。虽然我曾考虑为前缀设置一个单元格,但我放弃了,因为如果需要更改,只需更改顶部单元格中的公式,然后将其复制下来即可。
- G 列中的公式用于构造语法正确的 C# 赋值语句,它从名为
txtMessageForUser
的范围获取要设置其Text
属性的对象的名称。这是一个单单元格命名范围,指向包含为程序操作员指定的信息消息的只读文本框的单元格。重命名该文本框,您仍然可以获得有效的代码。
您可能会问,为什么我花费如此多的精力输入控件的制表符顺序、位置和大小,这是一个合理的问题。
- 放松,这并不像看起来那么难。当您保存
Form1
的设计时,Visual Studio 会将属性存储在Form1.Designer.cs
中,您可以在 Visual Studio 代码编辑器或您喜欢的任何 C# 识别的文本编辑器(如 Notepad++,或我最喜欢的 UltraEdit)中打开并查看它。从文本文件中读取和复制值比浏览属性表要容易得多。 - 尽管设计器提供了不错的对齐对象到网格的工具,但我发现它们并不总是能令我满意,尤其是当一行包含多种高度各异的控件时。因此,我经常查看文本编辑器中的代码,并且经常使用编辑器来调整控件的位置和尺寸(天哪!)。我发现,在文本编辑器中进行调整所花费的时间可能比在设计器中导航属性所花费的时间要少得多。
我将工作表布局为左半部分描述标签,右半部分描述关联的控件,以便轻松地来回查看控件及其标签的详细信息。这比在设计器中完成同样的事情要方便得多!
在我们返回大本营之前,您应该看看当控件名称和快捷键分配出现错误时会发生什么。
图 2 显示了工作表如何引起对两个已分配相同名称的控件的注意。更改其中任何一个,错误标志就会消失。
图 3 显示了当控件与其标签被分配相同名称时工作表的外观。由于重复项出现在同一行中,因此只高亮显示一行,这表明重复项位于同一行的右半部分。对于其他项目,我使用各种公式来生成唯一的控件名称。此项目中的控件太少,不值得为此付出努力。
图 4 显示了工作表如何警告重复的快捷键。要纠正它,请将 &
字符移到任一高亮行 E 列单元格的另一个字母前面。
图 5 显示了工作表如何提醒您注意重复的字符串名称。请注意,重复项位于工作表的下部;我不得故意损坏一个公式才能实现这一点,但我想展示验证跨越了两个表。
关注点
欢迎回到大本营;是时候享用美味的饼干桶了。来点奶酪、饼干和新鲜热咖啡吧!
尽管此工作簿中的大多数公式都是直接、日常的电子表格公式,但有几个公式足够晦涩,需要解释。
识别您的快捷键助记符的公式相当复杂。
=IF(ISBLANK(E5),"",IFERROR(UPPER(MID(E5,FIND("&",E5)+1,1)),""))
- 此公式的最终效果如下。
- 如果其紧邻右侧的单元格为空,则此单元格留空。
- 如果其紧邻右侧的单元格中不包含
&
字符,或者最后一个字符是&
,则该单元格留空。 - 否则,单元格将包含大写的加速键,以保持一致性。
- 最外层的
IF
函数很简单,但它的存在允许此列中的每个行都可以填充,而不会在工作表中出现未使用的行中的杂乱文本。 - 错误时返回的值子句以有些令人困惑的
IFERROR
函数开始,该函数返回计算函数的结果,除非该结果是错误代码(#ERROR!
、#N/A!
、#DIV/0!
和#VALUE!
),在这种情况下,它返回第二个参数(在此例中为空字符串)。将其视为工作表函数的捕获块;当找不到预期的子字符串时,FIND
函数返回#VALUE!
。除此之外,FIND
函数很简单;它扫描第二个参数(单元格E5
)以查找第一个或唯一的&
。否则,其返回值将使整个表达式无效。FIND
返回的值为MID
函数提供了三个参数中的第二个,该函数提取紧邻第一个&
字符右侧的字符。当成功时,FIND
返回找到第一个&
的位置,从 1 开始计数,其中 1 是被扫描字符串(在第二个参数中命名)的第一个字符。MID
的第一个参数是从中提取子字符串的字符串,而该字符串恰好是FIND
扫描的字符串,第二个参数是要包含在返回的子字符串中的第一个字符的位置。将FIND
返回的值加 1,得到其后紧邻字符的位置,即指定的加速键。MID
的第三个参数是要从字符串中提取的字符数;指定 1 即可获得所需的加速键,不多不少。- 整个
MID
函数嵌套在UPPER
中,其含义应该很明显。
- 由于没有要分配的内容,最后一个字符是
&
并不定义加速键。由于这种情况导致MID
失败,因此达到了预期结果,并且不显示加速键。
我不知道为什么 Microsoft Excel 程序员设计 FIND
在找到不存在的字符串时返回错误值,而不是简单的零,除非背后有一些古老的历史(也许是 Lotus 1-2-3 或 MultiPlan 的类似功能)。距离我上次使用 Lotus 1-2-3 已经太久了,而且我只用过一次 MultiPlan,大概一个小时,时间更早。无论如何,现在为时已晚,无法做任何事情,因为有太多工作表依赖于其既定、有据可查的行为。此时,一个返回零的查找函数必须是一个新函数,具有自己的名称和怪癖。
另一个值得注意的函数是高亮显示重复控件名称的条件格式规则中的公式,它非常复杂。
如果您不知道,条件公式必须返回布尔值(True 或 False),值为 True 会导致应用规则中定义的条件格式,并且应用是累加的。累加的意思是,规则中未指定的任何属性都将从底层格式继承。因此,如果您指定了背景颜色但未指定文本颜色,则单元格将继承底层单元格格式的文本颜色。此外,虽然您可以应用样式属性(如粗体、斜体和颜色)到字体,但您无法更改其字体或大小。所以,如果底层单元格格式为 10 点 Consolas,您就只能用它了。
首先,我将展示它在公式编辑器中的样子,然后我会像嵌套的 C/C++/C# 函数一样布局它。
=AND(OR(LEN($B3)>0,LEN($Q3)>0),OR(COUNTIF(LabelNames,$B3)+COUNTIF(InputControlNames,$B3)>1,COUNTIF(LabelNames,$Q3)+COUNTIF(InputControlNames,$Q3)>1))
AND ( OR ( LEN ( $B3 ) > 0 , LEN ( $Q3 ) > 0 ) , OR ( COUNTIF ( LabelNames , $B3 ) + COUNTIF ( InputControlNames , $B3 ) > 1 , COUNTIF ( LabelNames , $Q3 ) + COUNTIF ( InputControlNames , $Q3 ) > 1 ) )
在我解释这个公式之前,我应该解释一下,如果 COUNTIF
可以处理非连续范围,那么它可以简化。不幸的是,我已确信非连续范围会导致 COUNTIF
返回可怕的 #VALUE!
,正如在与 IFERROR
相关时讨论的那样。据我所知,直到今天,这种行为从未被公开记录。当我发现它时,我向 4 年前的 BBS 帖子(http://www.mrexcel.com/forum/excel-questions/623620-countif-value-error.html)添加了一个注释,该帖子引导我进行了证明该结论的实验。
每行 B 列和 Q 列中的单元格都用于存放控件名称。
- B 列保留用于标签控件的名称。
- Q 列保留用于标签所应用控件的名称。
由于目标是使用相同的公式来高亮显示整行单元格,因此引用当前行中单元格的地址的列部分通过在列字母前加上 $
来绝对化。相反,行引用未加修饰,以便公式可以向下复制到表中。我假设您知道,当一个单元格向下复制时,未锚定的引用会根据目标单元格相对于源单元格的位置进行调整。这些规则也适用于条件格式公式。
- 首先,两个条件都必须为真。
- 当前行中用于存放控件名称的两个单元格中,至少一个必须包含文本。
- 至少一个非空控件名称必须是重复项。
- 使用
LEN
函数测试单元格内容长度比传统的ISBLANK
测试更健壮,后者对于包含公式的任何单元格都返回FALSE
。由于它包含一个公式,所以该单元格技术上不是空白的,尽管大多数人会认为它是空白的。在我极少数使用ISBLANK
的情况下,我几乎总是用长度测试来支持它,而且我的许多工作表完全依赖LEN
,因为当我发现ISBLANK
的实际行为时,它就已经从我的脑海中消失了。 COUNTIF
是另一个有些晦涩的函数;它的第一个参数是要计数的单元格范围,而第二个参数是计数的标准。也就是说,计算第一个参数命名的范围内的所有单元格,如果它们满足第二个参数指定的标准。使其晦涩的还有一个因素是标准的指定方式多样;它可以是一个字面量,如 10 或“Fred”,或一个关系表达式,如“>=10”,或一个单元格引用。此公式使用了第三种,也是最简单的选项;如果单元格的值等于指定单元格的值,则计算该单元格。- 另一个棘手的地方在于,我们真正想要计数的范围不是连续的。然而,通过将两个范围对同一标准单元格的计数相加,可以获得所需的结果。如果单元格
b3
包含FooBar
,而单元格Q3
为空或包含BarFoo
,则第一个计数返回 1,而第二个计数返回零,除非任一列中的另一个行包含FooBar
或BarFoo
。
工作表受保护,以防止意外更改。但是,如果您需要这样做,可以随时解锁;密码是 TheCodeProject
。
历史
本文发布于 2016 年 6 月 18 日星期六。
在文章最初发布时丢失的图片应该已经恢复。