[[20170126135024]] 『ユーザーフォームを前面に表示』(ちか) ページの最後に飛ぶ

[ 初めての方へ | 一覧(最新更新順) | 全文検索 | 過去ログ ]

 

『ユーザーフォームを前面に表示』(ちか)

エクセル2016でユーザーフォームを前面に表示させる方法を教えて下さい。

2010では特に指定しなくても前面になっていて、Ctrl + Tabで別のエクセルに移動しても前面のままだったのですが、2016では常に背面で困っています。設定の問題でしょうか。教えて下さい。よろしくお願いします。
ちなみに、PCは64bitです。

< 使用 Excel:Excel2016、使用 OS:Windows10 >


 >>2016では常に背面で困っています

 2016は持っていなので確認できませんが、【常に背面】ということが、どういうことなのか
 理解できていません。

 2016 でも、マクロブックがあって、それが前面に出ていて、そのマクロでユーザーフォームを表示すれば
 前面に表示されると思うのですが、そういう場合も、2016 では背面(マクロブックの後ろ側)に隠れるということですか?

 それはちょっと考えられないのですが。

 それとは別に、2010までは MDI(マルチドキュメントインターフェース)でしたけど、2013からSDI(シングルドキュメントインターフェース)に
 変更されており、このために、様々な不具合(ないしは、2010までの現象と異なる現象の発現)が見られます。

 ユーザーフォームがどこに表示されるかということも、その最たるものの1つでしょうね。
 このあたりは、MSにぜひ改善してもらいたいと強く願っています。

(β) 2017/01/26(木) 14:17


今は手元に2016が無いので試せませんが、UserForm1.Show vbModal のように、モーダルである事を明記してみるとどうなりますか?
デフォルトでモーダルなので、変わらないはずですが、モーダルだと自Excel内ではトップに表示されます。(逆に、自Excel内では、フォーム以外は触れなくなります)

他Excelよりも前、という事であれば、SetWindowPosあたりをキーワードにして、APIを調べてみてください。
(???) 2017/01/26(木) 14:19


 たまたま、MDI と SDI について質問を受けたときに、自分で整理してみたメモが手元にありましたので
 以下、アップします。

 なお、SDI環境でも、常に MDI時代と同じようにユーザーフォームを最前面にだしておくには
 ??? さんもコメントしておられるように SetWindowPos と SetWindowLong を組みあわせることで
 可能になります。

 とりあえず、メモです。

MDI と SDI の違いについては、ブックを複数開いておいて、ウィンドウの左右整列をすれば
見た目で明確に確認できると思います。

MDI(xl2010まで)の場合、上のほうのリボンや数式バー、下のコマンドやステータスのバーは
横長に1つだけ、で、中身としてブックが横に複数並びます。

この上のほうと下の部分がエクセルウィンドウ、中身のブックの部分が ブックウィンドウです。
つまり、1つのエクセルウィンドウ上に複数(Multi)のブック(Document)ウィンドウが、のっかっている構造です。

一方 SDI(xl2013以降)の場合、ブックごとに、上のほうと下のほうが、別物として表示されています。
つまり、1つのブックに、それぞれ1つのエクセルウィンドウが存在。
エクセルウィンドウというものから見た場合、その上には1つ(Single)のみブック(Document)が存在するということになっています。

一般論として、ウィンドウというポイントで眺めた場合、上(最前面)から下(最背面)にかけて

ユーザーフォームウィンドウ階層
ブックウィンドウ階層
エクセルウィンドウ

こうなっていますが、この状態は MDI でも SDI でも同じです。
ただ、違っているのは、SDI では エクセルウィンドウとブックウィンドウが
1:1になっているということです。

イメージで書きますと

●MDI


ユーザーフォームA ウィンドウ
ユーザーフォームB ウィンドウ
ユーザーフォームC ウィンドウ
ユーザーフォームD ウィンドウ
ユーザーフォームE ウィンドウ
ユーザーフォームF ウィンドウ

ブック1 ウィンドウ
ブック2 ウィンドウ
ブック3 ウィンドウ
ブック4 ウィンドウ

エクセルウィンドウ
============================
エクセル区画

こんな階層です。
ブックウィンドウの zオーダを変更しても、ブックウィンドウ階層内でのいれかえで
前面のユーザーフォームウィンドウ階層には影響がなく、結果 つねに なにかしらの
ユーザーフォームがエクセル区画(MDIですから エクセルウィンドウと同義)の最前面に表示されています。

●SDI

一方、SDI は以下のようになっています。


ユーザーフォームF ウィンドウ
ユーザーフォームE ウィンドウ

ブック1 ウィンドウ

エクセルウィンドウ
============================
ユーザーフォームD ウィンドウ

ブック2 ウィンドウ

エクセルウィンドウ
============================
ユーザーフォームB ウィンドウ
ユーザーフォームC ウィンドウ

ブック3 ウィンドウ

エクセルウィンドウ
============================
ユーザーフォームA ウィンドウ

ブック4 ウィンドウ

エクセルウィンドウ
============================
エクセル区画

なお、各ユーザーフォームウィンドウの場所は、そのユーザーフォームを呼び出したブックの属するウィンドウではなく
そのときに最前面になっていたブックが属するエクセルウィンドウの最上位階層になります。

で、たとえば ブック3 の zオーダを最前面にするとそのブックウインドウとエクセルウィンドウが1:1ですからペアになったエクセルウィンドウも
一緒に上に移動。そのエクセルウィンドウの配下として存在しているユーザーフォームウィンドウも引きずられて上にきます。

つまり、

============================
ユーザーフォームB ウィンドウ
ユーザーフォームC ウィンドウ


ブック3 ウィンドウ

エクセルウィンドウ
============================

この塊で、最前面になり、現象としては、ユーザーフォームFが隠れ、ユーザーフォームBが一番上に見える状態になります。
もし、ブック3の属するエクセルウィンドウ配下にユーザーフォームがなければ、現象としては
ユーザーフォームFがブック3の後ろに隠れてしまうということになります。

ただし、(繰り返しになりますが)1つのエクセルウィンドウに属する、ブックウィンドウとユーザーフォームウィンドウの関係は
MDIもSDIも同じになっています。(SDIの場合、そこには、ブックが1つしかないというだけの違いです)

その他参考URL

http://ubuntu84.blogspot.com/2014/09/excel2013.html
http://www.jkp-ads.com/Articles/keepuserformontop02.asp

(β) 2017/01/26(木) 14:28


β様

回答ありがとうございます!
非常に分かり難い書き方をしてしまいました。申し訳ございません。
マクロブックが前面に出ている時はユーザーフォーム前面に表示されます。
シート1にコマンドボタンを置き、ボタンをクリックするとユーザーフォームが起動し前面に表示されます。
ここまでは2016でも問題なく動かすことができました。
ここから、別のブックをアクティブにすると、2010ではユーザーフォームが前面に表示された状態のままブックのみ移動するのですが、2016ではユーザーフォームも背面に表示されてしまいます。
ユーザーフォームの入ったブックをアクティブにすればユーザーフォームも前面に表示させることができます。
(ちか) 2017/01/26(木) 14:29


なるほど、確かに2016だとSDIになったために、他のブックを前にする = 他のExcelが前になるので、フォームも他ブックの裏に回ってしまいますね。

それならば、フォームの表示はvbModelessとしておき、APIを使ってフォームだけ最前面にする手でしょうか。今は32bit版を使っているので、ちょっと64bit版の動作を試せないため、サンプルを書けないのですが。
(???) 2017/01/26(木) 14:37


 なぜそうなるかは、少しわかりにくかったかもしれませんが、アップしたメモの通りなんです。

 これを解消するコード案もいかにアップしておきます。

 複数のエクセルブックが開かれている状態で、新規ブックを作成し、そこでUserForm1 を挿入。
 ユーザーフォームモジュールに

 Private Declare Function WindowFromAccessibleObject Lib "oleacc" ( _
      ByVal pacc As Object, _
      ByRef phwnd As Long) As Long
 Private Declare Function SetWindowPos Lib "user32" _
    (ByVal hwnd As Long, ByVal hWndInsertAfter As Long, ByVal X As Long, _
    ByVal Y As Long, ByVal cx As Long, ByVal cy As Long, _
    ByVal wFlags As Long) As Long
 Private Declare Function SetWindowLong Lib "user32" Alias "SetWindowLongA" _
    (ByVal hwnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long

 Const GWL_HWNDPARENT = -8&

 Private Sub UserForm_Initialize()
      Dim hwnd As Long, Ret As Long

      WindowFromAccessibleObject Me, hwnd
      Ret = SetWindowPos(hwnd, -1, 0, 0, 0, 0, &H40 Or &H1 Or &H2)
      Ret = SetWindowLong(hwnd, GWL_HWNDPARENT, 0)

 End Sub

 で、標準モジュールに

 Sub Test()
    UserForm1.Show vbModeless
 End Sub

 こうした上で、この Test を実行してください。
 で、様々なブックを前面に出してみてください。どんなブックが前面に出ようと、ユーザーフォームは、さらに
 その前面に表示されます。

 ★ただし、弊害があります。常に前面。これが行き過ぎ(?)て、たとえば 全くエクセルとは関係のない
  アプリを前面に出しても、ユーザーフォームは、さらに、その前面にでます。

(β) 2017/01/26(木) 14:40


 ↑ もし、エクセルも 64BIT であれば API宣言の   Private Declare Function ・・・・・ をすべて
   Private Declare PtrSafe Function ・・・・・ に変えてください。

(β) 2017/01/26(木) 14:44


 下記のフリーソフトで、エクセルのUserFormを簡単に
 「最前面に保つ/解除」することができます。
http://jpn.moo0.com/?top=http://jpn.moo0.com/software/WindowMenuPlus/
 Moo0 窓メニュー拡張器 1.20 (フリーソフト)

 >(β)さん  2017/01/26(木) 14:40
 最前面にするAPI、いいですね。
 ■質問
 Form1に、togglebuttonを置いて、
 togglebuttonを押してボタンをへこませる と、「最前面が解除」、
 もう一度押すと、Form1が「最前面」みたいなことできますか?

 (マリオ) 2017/01/26(木) 15:17

???様、β様、マリオ様

皆様回答ありがとうございます!
返信が遅くなり申し訳ございませんでした。
提示していただいたコードの中身は理解できませんでしたが、使用したら望みどおりの結果を得ることが出来ました。
この度は丁寧にありがとうございました。大変助かりました。
(ちか) 2017/01/26(木) 15:57


 SetWindowPos(hwnd, -1
 の【-1】が最前面っていうことみたいですね。
 SetWindowPos(hwnd, 1
 と【1】にしたら、最前面解除できました。
(マリオ) 2017/01/26(木) 16:21

度々すみません。

2016を使用している担当者から最前面に表示されたということでお礼の返信をしたのですが、
試したのは2010のバージョンだったということで、2016で確認してみたところ前面に表示させることができませんでした。
特にエラーも出ずどこが問題なのか不明のままなのですが、どなたかご存知の方いらっしゃいませんでしょうか。
(ちか) 2017/01/26(木) 17:15


 >ちかさん
 OSが64bitでも、Officeが64bitとは限らないです。
 Officeが、何bitか、確認しましたか?
http://www.esumi.co.jp/faq/faqans.php?code=2&seq=1&no=14
 私は、Excel2013(32bit),Windows10(64bit)環境です
(マリオ) 2017/01/26(木) 20:52

 >>2016で確認してみたところ前面に表示させることができませんでした

 コメントしたように 2016 は持っていないので確認のしようがないのですが、う〜・・・ん・・・・。

 マリオさんコメントにあるエクセルバージョンですけど、おそらく PtrSafe を付け加えないでエラーになっていないのなら
 エクセルは 32Bit だと推測します。

 こちら(および、きっとマリオさんも)Win10(62Bit)+xl2013(32Bit) で実行して、問題なくユーザーフォームが
 表示されます。

 どなたか、xl2016 の環境をお持ちの回答者さんからの結果連絡があればいいですね。

(β) 2017/01/26(木) 23:29


マリオ様、β様

回答ありがとうございます。返信が遅くなり申し訳ありません。
PtrSafeを入れないとエラーが出てしまうので、64bitだと思われます。(担当者が午後から出勤の為PC起動できていません)
こちら2013のエクセルでも実行してみたところユーザーフォームが表示されたので、2016のバージョンで何か変わったのでしょうかね……。

2016をご使用の方、いらっしゃいましたらどうかお助け下さい。よろしくお願いいたします。
(ちか) 2017/01/27(金) 08:57


 本件とは関係ないのかもしれませんが、こと、ユーザーフォームに関しては xl2016 は ぼろぼろで
 いろんな不具合や怪奇現象が発生しているようです。(Office 365 に限定した話かもしれませんが)

http://www.moug.net/faq/viewtopic.php?t=74805&sid=e0d76bb4a0f575265e1c95ababbad0ab

(β) 2017/01/27(金) 21:40


http://blog.goo.ne.jp/mumbai/e/dc90cf94fc40baebf807f1913de4fc10
 こちらのサイトを参考にすると、
 EXCEL2007以前(Excel2007,Excel2003,…)は32bit版のみで、
 EXCEL2010以降から32bit版, 64bit版の二種類になった
 そして、EXCEL2010以降では、
 VBA7 と Win64 という 2 つの条件付きコンパイル定数が用意されていて、
 この2つを使うと、
 EXCELのバージョンやビット数を調べることができるらしい。
 コードを書くと次のようになります。IfやElseの前に「#」を付けて
 いるのはなんなのか、良くわかりませんが、付けないと、動作しません。
 *****************************************************************
 Sub EXCELのバージョンやビット数を調べる()
     #If VBA7 Then 'Excel2010以降なら
        #If Win64 Then 'Excelのbit数が64bitなら
           Debug.Print "EXCELは2010以上で、64bit版です。"
        #Else
           Debug.Print "EXCELは2010以上で、32bit版です。"
        #End If
     #Else 'Excel2007以前なら(64bitはない、32bitのみ)
        Debug.Print "EXCELは2007以下で、32bit版です"
     #End If
 End Sub
 *****************************************************************
 よって、APIをEXCELのバージョンやビット数に応じて宣言するときは、
 上記のような★場合分け★が必要になるとのことです。 
 PtrSafeが付いているAPIの宣言は、64bit用なんですね。
 PtrSafeが付いていないAPIの宣言をExcelの64bit版で用いたらどうなるか、
 また、Excelの32bit版でPtrSafeが付いているAPIの宣言を利用したら
 どうなるかの挙動については、不明ですな…。
 ただ、単純に、64bitなら、PtrSafeをつければよいということでも
 ないのでしょうか?
 _
 _
 _
 参考にしたサイトでは、最後にこう書いてますね。
 -----------------------------------------------------------------
 ということで、当初の目的である、
 VBAを32/64の両方に対応させるためのWin32API関数の宣言のやり方は、

http://www.jkp-ads.com/articles/apideclarations.asp

 のページを見て、必要な関数をコピペして使えばよいと言う事だ。
 -----------------------------------------------------------------
(マリオ) 2017/01/28(土) 09:32

 >>PtrSafeが付いていないAPIの宣言をExcelの64bit版で用いたらどうなるか、
 >>また、Excelの32bit版でPtrSafeが付いているAPIの宣言を利用したら
 >>どうなるかの挙動については、不明ですな…。

 64Bitエクセルで PtrSafe をつけないとコンパイルレベルでエラーになります。(ちか さんからも報告がありましたね)
 32Bitエクセルで PtrSafe をつけてもコンパイルレベルではエラーになりませんが、さて、どうなりますかね?
 わざわざ試したこともないのですが、マリオさんが参照されたサイトのように、バージョン変数でコンパイラーレベルの記述分岐を
 紹介しているページがあるということは、きっと 不具合が出るんだと思いますよ。

 さもなければ 64Bit版でも32Bit版でも 無条件に PtrSafe をつければいいということになりますから。
 そうではないということは、32Bit版で PtrSafe を付加するのは、やはり、だめ ということだろうと思いますけど?

 で、ちかさんの 悩みは PtrSafe を付加した 64Bit版でも、ユーザーフォームが前面にでない!
 ということですよね。

(β) 2017/01/28(土) 09:56


 追伸

 マリオさん紹介の

http://www.jkp-ads.com/articles/apideclarations.asp

 ここには残念ながら SetWindowPos や SetWindowLong のサンプルが掲載されていないのですが
 私が参考URLとしてアップした

http://www.jkp-ads.com/Articles/keepuserformontop02.asp

 ここでは、両方の記述サンプルが掲載されていますので、そこを参考にすればいいかもですね。

 ただ、2016がないので、こちらでは確認できませんけど。

(β) 2017/01/28(土) 10:06


 まったく別のポイントというか、的外れなレスになるかもしれませんが・・・

 少なくとも xl2013 までは、たとえ Windows が 64Bit版であったとしても エクセル(Ms.Office)は 32Bit版を使うことが推奨されています。

 膨大なエクセル資産を、すべて完全に 64Bit対応しきれていない部分が少なくないようですので。

 xl2016 で、そういったことが解消されているのかどうかはわかりませんが、もし可能なら xl2016 の 32Bit版を使うことも検討されてはいかがでしょう。

 ただ、それとは別に、コメントしたように、ユーザーフォームに関していろいろ不具合を内包しているようですが。

(β) 2017/01/28(土) 10:20


β様、マリオ様

返信が遅くなり申し訳ございません。回答ありがとうございます。
PCもエクセルのバージョンも64bitで間違いないようです。

お二方に紹介していただいたURLを参考に、これから試してみます。
何か新しいことが分かったらこちらに書き込みさせて頂きます。
(ちか) 2017/01/30(月) 13:06


コメント返信:

[ 一覧(最新更新順) ]


YukiWiki 1.6.7 Copyright (C) 2000,2001 by Hiroshi Yuki. Modified by kazu.