Windows 可以将多个显示器映射为虚拟桌面,使我们可以利用这一点设计出方便工作的应用程序。例如 PowerPoint 就充分发挥了双显示器的优势(大多数的笔记本电脑都支持),它可以在一个显示器上播放幻灯片,而在另一个显示器上显示备注,可以控制播放的进程,使使用者做商务演说的时候非常等心应手。那么我们怎么开发这种应用程序呢?这篇文章将向你展示如果用 Delphi 实现使用多显示器的应用程序。
Windows 还支持克隆显示方式,每个显示器输出同样的内容,这对某些应用也是有意义的。还有些显卡虽然也支持两个显示器,不过他们并不是真正意义上的多显示器,而是虚拟高分辨率显示模式(如 2048 × 768 或者 1024 × 1536 ),通过显卡将画面分别显示到两个显示器上。这两种显示模式都不是本文介绍的 zhongdian ,而且也非常简单,所以我们也就不再赘述了。
Windows 最多支持 10 个显示器, Windows 将所有显示器映射为一个大的虚拟桌面。可以将显示器理解为桌面某个局部的视图。在显示属性中可以根据显示器的物理位置任意排布这些显示器。如果显示器的排列不规则,虚拟桌面上的某些部分可能无法显示在任何一个显示器上。为了不使一个窗体显示在两个显示器之间等原因的考虑, Windows 将一个显示器作为主显示器。启动计算机时,登录对话框就显示在主显示器中。绝大多数程序启动示,都会显示在主监视器中。
根据上述介绍,不难发现几个重要的概念:桌面、显示器、主显示器等。首先必须先弄清楚这些概念以及他们之前的关系。这是掌握多显示器应用程序开发方法的重点。理解了这些概念,其他的部分就非常好理解了。
桌面实际上是指 Windows 可显示的逻辑区域。实际上是可以将一个窗体显示到桌面之外的。然而这并不是说桌面的所有部分都会显示在某台显示器上(原因如前所述);但反过来说,任何一个显示器显示的内容都必然是桌面的一部分。
桌面是一个矩形区域,可以通过顶点坐标( Top , Left )和宽高来描述桌面的尺寸。为什么还需要顶点坐标呢?因为顶点坐标不是想当然的( 0,0 )。那么( 0,0 )在哪里呢?说来话长,还是让我们先来回顾一下刚才提到地一个概念——主显示器吧。 Windows 希望一般的程序初始的时候显示到主显示器,因为人们习惯于关注一个离自己最近的显示器。而 Windows 也不可能强制用户把最左边一个显示器作为主显示器,这样一来应用程序为了把自己显示到主显示器,就需要费脑筋的计算。然而,多数用户都只有一个显示器(两个显示器实在太占地方了),而一般的应用程序也不希望大费周章的去计算主显示器在哪里,自己应该显示在什么位置。所以 Windows 提出了一个合理的解决方案:以主显示器的顶点坐标作为坐标系的原点。这样一来,普通的程序之需要想在单显示器环境中一样考虑问题就可以了。
显示器是桌面的局部视图。就好像透过窗户看窗外的风景,站在不同的窗前就可以看到不同的画面。同样的,显示器也是一个矩形区域,同样可以通过顶点坐标( Top , Left )和宽高来描述它的尺寸。顶点坐标是相对于桌面坐标系原点的,也就是相对于主显示器的顶点。
工作区的概念比较简单,它是指显示器中除了任务条和其他停靠在桌面上的窗体之外的矩形区域。
Windows 为多显示器应用程序的开发提供了一组 API 。 VCL 将这些 API 封装起来,非常自然的融入整个 Framework 之中,使得开发多显示器应用程序变得非常简单。下面就介绍与之相关的内容。
在 VCL 之中大家最熟悉的恐怕非 TCustomForm 莫属了,它是所有窗体的基类。 TCustomForm 的 Position 属性用来设置窗体的现实位置,其可选值中有两个是值得关心的:一个是 poScreenCenter ,当 Position 属性被设置成 poScreenCenter 时,窗体会显示到主显示器的中央;另一个是 poDesktopCenter ,当 Position 属性被设置成 poDesktopCenter 时,窗体显示在整个桌面的中央。如果把这个属性设成 poDesktopCenter ,程序又运行在一个有多台显示器的系统上,那么这个窗口就会显示在两个显示器之间,会给用户带来不必要的麻烦。因此即使我们的程序不是针对多显示器而设计的,也应该细心处理这个值。另外一个属性是 DefaultMonitor ,它的作用与 Position 有些类似,决定窗口最初显示在哪个显示器内。它有四个备选值: dmDesktop , dmPrimary , dmMainForm 和 dmActiveForm 。他们的含义如下:
Value
|
Meaning
|
dmDesktop
|
不特别处理
|
dmPrimary
|
将窗体显示到第一个显示器上。这又是一个陷阱,字面上理解是主显示器,而事实上它是指 Screen.Monitor[0] 这个显示器。
|
dmMainForm
|
将窗体显示到主窗体所在的显示器
|
dmActiveForm
|
将窗体显示到桌面上活动窗体所在的显示器
|
TCustomForm 还有一个只读的共有属性(没有 Published ) Monitor ,它提供了访问窗体所在显示器实例的引用,这个值与 DefaultMonitor 是有紧密的关联的。
那么怎么在使窗体在不同的显示器之间移动呢?这并不困难,估计你也想到了。这里介绍两种方法:
第一, 可以设置 TCustomForm 的 Top 和 Left 使窗体显示在桌面的任意位置。正如前面所述,桌面是由所有显示器组成的。它们有共同的坐标系,所以可以根据显示器的逻辑位置决定窗体的位置。现在的问题是如何获得每个显示器的逻辑位置和尺寸,后面就会介绍。
第二, 可以调用 TCustomForm 的 MakeFullyVisible 方法将窗体完全显示到指定的显示器之中。可以通过这个方法避免窗口在两个显示器上各显示一部分。
刚才我们提出了一个问题:如何获得每个显示器的逻辑位置和尺寸。为了解答这个问题,需要再介绍连个类: TScreen 和 TMonitor 。
TScreen 描述与显示设备有关的一些信息,我们主要关心与显示器逻辑位置和尺寸有关的信息。其他方面的内容可以在 Delphi 的文档中获知。在程序运行的时候 VCL 自动创建一个 TScreen 的实例——全局变量,所以通常情况下程序是不需要实例化 TScreen 的。
TScreen 有一组形如 Desktop* 的属性,这些属性描述了整个桌面的尺寸和各顶点坐标。还有对开发多显示器应用程序有重要意义的连个属性: MonitorCount 和 Monitors 。通过这两个属性我们可以枚举出系统中所有的显示器( TMonitor )的实例,每个实例都反映了相应显示器的相对位置和分辨率等信息(后文会详细说明)。
在 TScreen 的众多属性之中,我们会找到 Height 和 Width 这两个属性。要特别警惕它们不是指整个桌面的尺寸,而是指主显示器的高度和宽度。这非常容易让人产生错觉,无以为是整个桌面的尺寸。与之类似的还有形如 WorkArea* 的一组属性,它们描述了主显示器的工作区域的尺寸和各顶点坐标。是不是觉得少了什么?为什么没有获取主显示器相对位置的属性?原因就像前面所说的: Windows 是以主显示器的左上角为坐标系原点的,所以主显示器的相对位置必然是( 0 , 0 )。
除了这些属性之外,还要介绍 TScreen 的三个成员函数: MonitorFromPoint , MonitorFromRect 和 MonitorFromWindow 。顾名思义,他们分别是获取个坐标、某个区域和某个窗口所在的显示器的实例。在实际的开发中可能也会用到。
最好,再来看看 TMonitor 类。它封装了物理显示器的有关属性——这些属性都是只读的。下表简单介绍了这些属性的含义,它们对编写多显示器应用程序非常有用:
属性
|
说明
|
Handle
|
获取该显示器的 Windows 句柄
|
MonitorNum
|
获取显示器的编号
|
Primary
|
获取该显示器是否是主显示器。又且仅有一个显示器的 Primary 是 True 。
|
Top
|
获取显示器的上边界
|
Left
|
获取显示器的左边界
|
Height
|
获取显示器的高度
|
Width
|
获取显示器的宽度
|
BoundsRect
|
获取显示器的对应桌面的区域,它与上面四个属性是等价的
|
WorkareaRect
|
获取显示器的工作区对应桌面的区域。
|
清楚地了解了 TScreen 和 TMonitor 之后,前面的问题也就自然解决了。到这里,本文已经介绍了开发多显示器应用程序所需的全部知识。相信你可以利用这些知识开发出非常实用的软件产品。
附:你可以下载一个 DEMO ,帮助理解本文。
分享到:
相关推荐
Delphi实现使用多显示器的应用程序。在启动程序的时候进行了是否多显示器的判断,如果是多显示器则进入程序。
实例229 在程序中使用多线程 293 实例230 怎样调用外部的EXE文件 295 实例231 关闭外部已开启的程序 296 6.9 程序运行 297 实例232 监测是否运行多个程序 297 实例233 防止程序多次运行 298 实例234 ...
对于刚出的delphi8,由于它是基于net框架的,主要用于网页编写,编写应用程序的方式不一样,而且delphi7的很多控件都没有,所以一般不能在delphi8中开发运行(除非重新编写所有的代码)。 (六)注意事项: 本系统...
0055 在Delphi中使用汇编 35 0056 为程序设置版本和帮助信息 36 0057 动态链接库的创建与调用 36 0058 String类型和Pchar类型的用法和区别 36 0059 如何捕获异常 37 0060 TStrings与TStringList的使用 ...
第4章通过24个实例介绍了如何在程序中对一些系统功能进行控制,主要内容包括设置桌面墙纸、动态修改显示器分辨率、重启动和关闭计算机、建立应用程序快捷方式、列举进程、模拟鼠标和键盘操作等。 第5章通过19...
0055 在Delphi中使用汇编 35 0056 为程序设置版本和帮助信息 36 0057 动态链接库的创建与调用 36 0058 String类型和Pchar类型的用法和区别 36 0059 如何捕获异常 37 0060 TStrings与TStringList的使用 ...
实现应用程序的文件拖放功能 将EXE文件转换成SWF文件 将SWF文件转换成EXE文件 文件捆绑 统计中英文字个数 06图形图像多媒体 实例一--艺术字效果 实例七---photoshop图层效果 实例三---...
Windows提供了应用程序丰富的可呼叫函数,大多数用于实作其使用者接口和在视讯显示器上显示文字和图形。这些函数采用动态链接库(Dynamic Linking Library,DLL)的方式撰写。这些动态链接库是些具有.DLL或者有时是....
RAD Studio 11 提供了使用 Delphi 和 C++Builder 的视觉丰富的用户界面构建和更新数据密集型和高度交互的应用程序的最快方法。此处提供了广泛的 Windows 10 支持功能,包括适用于 Windows 10 的新 VCL 组件、适用于 ...
第9章 Windows相关技术 337 9.1 磁盘信息 338 0520 获取驱动器的卷标 338 0521 检测软驱是否有软盘 ...0631 替换应用程序中对话框资源 419 0632 可导出的动态链接库函数 420 0633 动态链接库动态加载 420
第9章 Windows相关技术 337 9.1 磁盘信息 338 0520 获取驱动器的卷标 338 0521 检测软驱是否有软盘 ...0631 替换应用程序中对话框资源 419 0632 可导出的动态链接库函数 420 0633 动态链接库动态加载 420
DXUT框架剖析(9) 摘要: 下面列出允许改变DXUT行为和获取内部变量的函数,这些函数在使用DXUT框架的Direct3D程序中是非常实用的。 DXUT框架剖析(8) 摘要: Direct3D API的设计使程序能比较容易地处理各种错误,...
ActiveX 组件能给你带来可重复使用的、... 对每个控件,我们将提供帮助文件(PDF),使用例程(VB6, VC6, HTML, C#, VB.NET,VBA(Excel))等来指导你如何使用这些控件到你的应用程序中去,了解这些控件提供了什么功能。
Java 3DMenu 界面源码,有人说用到游戏中不错,其实平时我信编写Java应用程序时候也能用到吧,不一定非要局限于游戏吧,RES、SRC资源都有,都在压缩包内。 Java zip压缩包查看程序源码 1个目标文件 摘要:Java...
Java 3DMenu 界面源码,有人说用到游戏中不错,其实平时我信编写Java应用程序时候也能用到吧,不一定非要局限于游戏吧,RES、SRC资源都有,都在压缩包内。 Java zip压缩包查看程序源码 1个目标文件 摘要:Java源码...
Java 3DMenu 界面源码,有人说用到游戏中不错,其实平时我信编写Java应用程序时候也能用到吧,不一定非要局限于游戏吧,RES、SRC资源都有,都在压缩包内。 Java zip压缩包查看程序源码 1个目标文件 摘要:Java源码...
Java 3DMenu 界面源码,有人说用到游戏中不错,其实平时我信编写Java应用程序时候也能用到吧,不一定非要局限于游戏吧,RES、SRC资源都有,都在压缩包内。 Java zip压缩包查看程序源码 1个目标文件 摘要:Java源码...