DOBON.NET DOBON.NETプログラミング掲示板過去ログ

MessageBoxの表示位置について

環境/言語:[OS:WindowsXP、開発環境:VS2008(.NET3.5 C#)]
分類:[.NET]

以下のページを参考にMessageBoxの表示位置を指定の位置で表示させたいと思っています。

参照:
http://www.koutou-software.co.jp/junk/mynote-cs-dotnet-messagebox-position-on-center.html


フォーム上にボタンが一つあり、ボタンをクリックすることでMessageBoxを表示しているのですが
セカンダリモニタ上にフォームを移動して実行するとMessageBoxがフォームの中央に表示されてしまいます。
(プライマリモニタ上で実行する場合は指定した位置へMessageBoxが移動します)

※二つのモニタを使用して左(プライマリ)、右(セカンダリ)の構成で、二つのモニタ間を移動できるようになっています。


対処方法が全く分からないのですが、どなたかアドバイスをいただけないでしょうか。
参照先を見るに表示はSetWindowPosで行っていると思われるのでこれに与える座標が拾えればいいわけですよね。

GetMonitorInfo、GetSystemMetrics辺りを使えばモニタに関する情報を色々取得、できるみたいです。
説明不足で申し訳ありません。

たとえば、以下のようなモニタ構成がある場合に、

モニタ1(プライマリ) 1024×1280
モニタ2(セカンダリ) 1024×1280


int x = 300;
int x = 0;
SetWindowPosで(wprm, HWND_TOP, x, y, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);

とした場合、プライマリモニタ上にフォームを移動して、MessageBoxを表示する場合は指定した位置で表示されます。
しかし、セカンダリモニタ上にフォームを移動して、MessageBoxを表示するとフォームの中央に表示されてしまうのです。

確認できたのは、MessageBoxを表示前にフォームを非表示にした場合は、指定した位置へ表示されますので
アクティブなウィンドウが関係ありそうなのですが・・・
自己レスです。

散々悩んだ結果
SetWindowPosの「X,Y」のどちらかでも「0」が入っていると
セカンダリモニタの時に正しく表示できないことが分かりました。

原因は不明ですが、0を回避することで何とかなりそうなので
解決とさせていただきます。

ん〜・・・でも、気になりますね?
MSDNのSetWindowPos説明によると
X ウィンドウの左上端の新しい x 座標をクライアント座標で指定します。
Y ウィンドウの左上端の新しい y 座標をクライアント座標で指定します。
という事らしいので、wprmで渡しているウィンドウの座標位置からの相対でx=300,y=0に表示されているのではないでしょうか。

フォーム表示/非表示で取得できる親フォームの座標が違うんじゃないですかね、
恐らくですがセカンダリにフォーム移動しても最小化するとプライマリ側の座標にいるんだと思います。

あと、これは誤記だと思うのですが一応確認しといた方がよいかと
int x = 300;
int x = 0;
uni様

ご回答ありがとうございます。

座標(X)の重複については私の記述ミスです。
申し訳ありません。

フォームの表示/非表示を行っても座標は変りませんが、
フォームがメインフォームなので、アクティブなウィンドウが取れなくてプライマリを規定としているような感じです。


当方で再度確認した結果
セカンダリ上のモニタにてMessageBoxを表示した際に、座標が「0」以外のときもフォーム中央に表示されてしまう
現象が発生しました。(ある座標の7ピクセル範囲 100〜107など)

原因が全く分からないため、コードを抜粋して記載しますので
ご指導宜しくお願いします。
でも、解決済みにしちゃったから誰も見てくれないかな・・・

※最悪、MessageBoxの表示前にダミーのフォームをプライマリに表示させて対応させようかと思案中です。


-----------------------------------------------

private System.Windows.Forms.DialogResult Show(System.Windows.Forms.IWin32Window owner, string message, string title, System.Windows.Forms.MessageBoxButtons btn, System.Windows.Forms.MessageBoxIcon icon)
{
  _OwnerWindow = owner;

  //フックを設定
  System.IntPtr instance = GetWindowLong(_OwnerWindow.Handle, GWL_HINSTANCE);
  System.IntPtr threadId = GetCurrentThreadId();
  _Hook = SetWindowsHookEx(WH_CBT, new HOOKPROC(HookProc), instance, threadId);

  return System.Windows.Forms.MessageBox.Show(_OwnerWindow, message, title, btn, icon);
}

private System.IntPtr HookProc(int code, System.IntPtr wprm, System.IntPtr lprm)
{
  System.IntPtr result;

  if (code == HCBT_ACTIVATE)
  {
    try
    {
      RECT rcForm = new RECT(0, 0, 0, 0);
      RECT rcMsg = new RECT(0, 0, 0, 0);

      GetWindowRect(_OwnerWindow.Handle, out rcForm);
      GetWindowRect(wprm, out rcMsg);

      //センター位置を計算
      int x = (rcForm.Left + (rcForm.Right - rcForm.Left) / 2) - ((rcMsg.Right - rcMsg.Left) / 2);
      int y = (rcForm.Top + (rcForm.Bottom - rcForm.Top) / 2) - ((rcMsg.Bottom - rcMsg.Top) / 2);

      //座標の再設定(テスト用)
      x = 300;
      y = 0;

      //位置を移動
      SetWindowPos(wprm, HWND_TOP, x, y, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);

      result = CallNextHookEx(_Hook, code, wprm, lprm);
    }
    finally
    {
      //フックを解除する。
      UnhookWindowsHookEx(_Hook);
      _Hook = System.IntPtr.Zero;
    }
  }
  else
  {
    result = CallNextHookEx(_Hook, code, wprm, lprm);
  }

  return result;
}
今更ですが表示させたい位置というのはフォームの位置に関係なくプライマリモニタの左上の辺りですか?
言い換えるとスクリーンに対する絶対位置ということですが・・・

SetWindowPosはクライアント座標を指定して制御する関数なので
スクリーンの任意の位置に表示させたいのであれば
フォームの現在位置から差分を取得して与えてあげないと駄目なのではないかなと

DOBON.NET | プログラミング道 | プログラミング掲示板