Работа с окнами как в Windows 7: обновление

    • WinForms
    • WinAPI
    • VistaKeysExtender
  • modified:
  • reading: 4 minutes

Программа для работы с окнами в Windows, идеи и удобства были позаимствованы из Windows 7. Программа позволяет при помощи горячих клавиш передвигать окно по экрану, размещать его в стадиях максимизации, обычного окна, а так же самое основное - размещать окна максимизированные на половину экрана. Работает с Vista и WinXP (как x86, так и x64).

В новой версии программы исправлены некоторые ошибки:

а) Не минимизировались диалоговые окна (те, которые не могут менять размер).

б) Программа не работала с окном Google Chrome (об этом подробнее немного ниже).

А так же основное добавлены возможности перемещения окна между экранами.

Скачать новую версию можно отсюда.

Сначала пару слов о проблеме с Chrome. Я ее не обнаружил. Версия хрома у меня 2.0.160.0, пробовал в Vista. Но все же я сделал отдельную сборку, в которой в методе проверке окна на IsResizableWindow добавил такую проверку

StringBuilder sb = new StringBuilder(100);
if (WAWindows.GetClassName(window, sb, sb.Capacity) != IntPtr.Zero
 && sb.ToString().StartsWith("Chrome_"))
 return true;

Вроде должно работать.

Теперь о перемещении окна между экранами. Задача оказалась не такой уж сложной. Первое что нужно сделать, это определить на какой монитор все таки пользователь желает переместить окно, тут мне пришлось сделать в лоб, пробежаться по всем экранам, и те которые соприкасаются (сторона соприкосновения зависит от нажатой клавиши) туда и отслылать, вот код нахождения экрана:

private static Screen GetScreenToMove(Screen screen, Keys keys)
{
  foreach (Screen scr in Screen.AllScreens)
  {
    if (!scr.Equals(screen))
    {
      switch (keys)
      {
        case Keys.Left:
          if (scr.WorkingArea.Right == screen.WorkingArea.Left
            && scr.WorkingArea.Top == scr.WorkingArea.Top)
            return scr;
            break;
        case Keys.Right:
          if (scr.WorkingArea.Left == screen.WorkingArea.Right
            && scr.WorkingArea.Top == scr.WorkingArea.Top)
            return scr;
          break;
        case Keys.Down:
          if (scr.WorkingArea.Top == screen.WorkingArea.Bottom
            && scr.WorkingArea.Left == scr.WorkingArea.Left)
            return scr;
          break;
        case Keys.Up:
          if (scr.WorkingArea.Bottom == screen.WorkingArea.Top
            && scr.WorkingArea.Left == scr.WorkingArea.Left)
            return scr;
          break;
      }
    }
  }
  return null;
}

Дальше же нужно просто отправить окно в выбранный экран, отправляю я окно с учетом пропорций, то есть: если окно занимало 50% от экрана, оно и на новом экране будет столько же занимать.

Но возникла проблема с FullScreen приложениями, а именно, если, к примеру, фильм развернуть на весь экран и попытаться перебросить это окно на другой экран - ничего не происходило, оказалось данное окно не определяется как имеющее WsSizeBox, потому пришлось их распозновать следующим способом (тут может кто то подскажет что то более граммотное):

private static bool IsFullScreenWindow(WAWindows.Rect rect, Screen scr)
{
  return rect.Left == scr.Bounds.Left && rect.Top == scr.Bounds.Top
      && rect.Width == scr.Bounds.Width && rect.Height == scr.Bounds.Height;
}

Еще один интересный факт, что когда я перемещал FullScreen окна по экранам, то в какой то момент они просто теряются, я понял, что окно остается на старом экране, а размеры и позиция я задаю ему нового экрана. Решение: при установлении размеров мне приходится сначала установить размеры на пару пикселей в ширину и в высоту меньше экрана (чтобы окно разполагалось точно только на этом экране), а потом восстанавливать до FullScreen размеров, выглядит это, примерно, так

// Fix for full screen windows
if (isFullScreenWindow)
  WAWindows.SetWindowPos(window, WAWindows.HwndTop, x + 1, y + 1, cx - 2, cy - 2, WAWindows.SwpShowWindow);
 
WAWindows.SetWindowPos(window, WAWindows.HwndTop, x, y, cx, cy, WAWindows.SwpShowWindow);

Еще один неприятный момент был, что когда окно на одном экране максимизированное, его перемещаем на другой экран, а потом восстанавливаем в Normal, то оно летит на предыдущий экран, потому как там его и максимизировали и окно запомнило тот размер и расположение, потому пришлось для таких случаев написать такой FIX:

// Fixed problem with restore on other screen
Screen newScreen = Screen.FromHandle(window);
if (!newScreen.Equals(screen))
{
  if (WAWindows.GetWindowRect(window, out rect))
    MoveToNewScreen(window, newScreen, screen, rect, isResizableWindow, false);
}

Это вроде все неприятные моменты с которыми я столкнулся. Если есть предложения или замечания, то с удовольствием их выслушаю.

P.S. Мне еще приходило письмо с предложением и патчем от Andir R со следующими словами:

Добавил в неё возможность двойной максимизации. Суть следующая:

1) При первоначальной максимизации (с помощью хоткея), окно не максимизируется, а изменяется до заданных размеров,

2) Вторичная максимизация уже вызывает настоящую максимизацию. В обратном порядке действует аналогично. Зачем это нужно: На широких мониторах с большим разрешением полная максимизация окна, зачастую, не нужна совсем, но увеличивать окно до определённых размеров всё равно приходится.

Мне показалась данная функциональность не настолько уж и необходимой, по крайней мере я ей пользоваться точно не буду, если нужна будет большинству народа, то включу ее как нибудь в свой проект.

See Also