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

Aero 摇晃

starIconstarIconstarIconstarIconemptyStarIcon

4.00/5 (3投票s)

2009 年 5 月 9 日

CPOL

7分钟阅读

viewsIcon

46447

downloadIcon

715

在 .NET WinForms 中实现 Windows 7 Aero Shake 功能。

引言

随着 Microsoft Windows 7 的预发布版本最近出现,Windows 世界也随之涌现了许多新功能。其中一些是简单的想法,可以轻松地集成到现有的 .NET 应用程序中。例如,Beta 版本中的一个便捷功能是能够将窗口停靠在屏幕侧边。在本文中,我们将用 C# 实现另一个新功能:Aero Shake。

Aero 摇晃

显然,要将该功能集成到我们自己的应用程序中,我们必须了解它的作用。Aero Shake 允许用户“摇动”一个窗口(通过鼠标移动它)。一旦窗口检测到摇动,所有其他打开的窗口都会被最小化。如果用户再次摇动窗口,则被最小化的窗口将恢复。

实现概述

将 Aero Shake 功能集成到 WinForms 中分为两部分

  • 创建一个 Windows Form,在检测到用户“摇动”时触发一个事件
  • 创建一个类来封装最小化和最大化其他应用程序的函数

请注意,代码不会非常复杂,但如果我们想将来利用这些函数,整洁地实现它们将很重要。将功能分解成小代码段的另一个优点是它为开发人员提供了灵活性。改进功能的各个方面和微调代码的行为将更容易。

检测窗口摇动

可以说,最重要的一部分将是检测 Windows Form 何时被摇动。可能存在更复杂的算法,但我选择了一个简单的三部分检查

  1. 时间。窗体测量从“摇动”开始到结束之间的时间差。这将允许我们过滤掉慢速摇动,因为它们将花费更多时间。
  2. 距离。同样重要的是测量窗体在整体上移动了多远。这允许我们微调构成“摇动”的“区域”。此外,它有助于过滤掉常规的窗体拖动。
  3. 数量。此指标确定窗口需要摇动多少次才能算作“摇动”。

一旦获得每个值,我们就会检查每个值是否在某个范围内。请注意,这为“摇动”的定义提供了很大的灵活性。[顺便说一句,这是人们在实际 Windows 7 Aero Shake 中抱怨的事情]。

要测量这些值,我们有两种选择:我们可以使用事件来检测窗体位置何时发生变化,或者我们可以通过 WndProc 检查传递给窗体的消息。对于这个特定的实现,我选择了 WndProc,因为它更容易检查何时单击标题栏。

时间

获取摇动所需的时间是最容易计算的值。我们只需使用两个 DateTime 字段变量来存储适当的时间间隔。WndProc 可以轻松检测标题栏上的鼠标按下和鼠标抬起操作(源代码显示了所有杂乱的常量)。

请注意,这不是关键的时间测量,因此 DateTime 就足够了。StopWatch 类更精确,但对于我们的目的来说似乎有点矫枉过正。

Distance

距离值稍微复杂一些。我们需要做的是在用户单击窗体标题栏时设置一个标志。然后,每当窗体在屏幕上的位置发生变化时,如果设置了标志,该位置就会存储在点的列表中。当摇动标志首次设置时,点的列表会重置。

一旦用户松开标题栏,我们将拥有一个在“摇动”或执行任何其他移动期间窗体所有位置的列表。我们可以使用点的列表来计算平均位置

private Point GetAveragePoint(List<Point> points)
{
     Point avg = new Point();
     foreach (Point p in points)
     {
          avg.X += p.X;
          avg.Y += p.Y;
     }

     avg.X /= points.Count;
     avg.Y /= points.Count;

     return avg;
}

通过将平均位置与当前窗口位置进行比较,我们可以确定窗口在整体上移动了多远。低值是好的,因为它意味着大部分移动都围绕一个中心点发生,这很好地描述了摇动窗口的外观。

金额

通过简单地检查用于测量距离的列表中存储了多少个点,可以轻松衡量窗口被摇动了多少。

摇动事件

现在我们有了检测摇动的技术细节,剩下要做的就是将其整洁地封装到一个事件中。在我看来,最简单的方法是创建一个新类 ShakeForm,它继承自常规的 Form 类。

我们的 ShakeForm 类会覆盖 WndProc 来跟踪用户与窗体在屏幕上的位置的交互。每当检测到移动时,我们都会处理遍历的位置列表,看看它们是否描述了摇动。如果它们是,我们将触发我们自己的 FormShaken 事件。

我们如何使用它?任何现有的 Windows Form 都将被修改为继承 ShakeForm 而不是 Form。然后,将一个方法绑定到 FormShaken 事件,就像任何其他 Form 事件一样。由于 ShakeForm 继承了常规的 Form 类,所有其他 Form 属性和事件都将保持不变。

最小化和最大化

现在,我们有了一种在窗口被摇动时触发代码的干净方法。实现 Aero Shake 的第二部分是相应地最小化和最大化所有其他应用程序。要做到这一点,我们将不得不依赖一些 API 调用。

更改所有打开窗口的窗口状态的常用方法是使用 SendMessageFindWindow API 调用。例如

IntPtr lHwnd = FindWindow("Shell_TrayWnd", null);
SendMessage(lHwnd, WM_COMMAND, (IntPtr)MIN_ALL, IntPtr.Zero);

然而,问题是上面的代码也会最小化被摇动的窗口。由于我们希望将该窗口保持为唯一未被最小化的窗口,因此我们必须逐一最小化每个打开的窗口。

我们将使用的 API 调用是

  • GetWindowPlacement:这将告诉我们其他应用程序的当前窗口状态。
  • ShowWindow:这将允许我们更改其他应用程序的窗口状态。
  • SetForegroundWindow:这个只是为了确保当其他窗口恢复时,我们的窗口保持在最前面。

可行的方法是使用 System.Diagnostics.Process 类获取所有当前运行进程的列表。对于每个进程,首先检查它是否有窗口(因为有些进程是后台工作进程),然后检查它是否不是对应于我们当前窗口的进程(因为我们想排除它)。

对于每个有窗口将被最小化的进程,我们将窗口的句柄存储在两个列表之一中:一个用于已最大化的窗口,一个用于正常状态的窗口。存储句柄后,我们使用 ShowWindow 最小化窗口。

稍后,当我们想将最小化的窗口恢复到其先前状态时,我们只需遍历每个列表,然后再次使用 ShowWindow 将它们恢复到正常窗口状态或最大化窗口状态。由于恢复窗口也会将它们移到屏幕顶部,因此最后调用 SetForegroundWindow 使我们可以将摇动的窗体移回前面。为什么我们不使用 this.BringToFront()?仅仅是因为在此实现中,最大化函数位于一个静态类中。

代码将像 Windows 7 中的 Aero Shake 一样工作,即如果摇动之前有内容被最小化,则它将保持不变。

这种方法的缺点是,运行的应用程序的原始窗口状态得以恢复,但原始堆叠顺序不一定保持不变。

结论

如前所述,代码的各个部分并不复杂。代码的结构对于使实现将来有用至关重要。这也使得改进 Aero Shake 的部分变得容易。例如,你可以选择重写 WndProc 代码以改用 .NET 事件,或者你可能有一个更好的检测摇动的算法,或者你可能只想调整检测的灵敏度。

© . All rights reserved.