フォーカスを盗まずにフォームを表示しますか?


140

フォームを使用して通知を表示しています(画面の右下に表示されます)が、このフォームを表示すると、メインフォームからフォーカスが奪われます。フォーカスを盗まずにこの「通知」フォームを表示する方法はありますか?

回答:


165

うーん、単にForm.ShowWithoutActivationをオーバーライドするだけでは十分ではありませんか?

protected override bool ShowWithoutActivation
{
  get { return true; }
}

また、ユーザーにこの通知ウィンドウをクリックさせたくない場合は、CreateParamsをオーバーライドできます。

protected override CreateParams CreateParams
{
  get
  {
    CreateParams baseParams = base.CreateParams;

    const int WS_EX_NOACTIVATE = 0x08000000;
    const int WS_EX_TOOLWINDOW = 0x00000080;
    baseParams.ExStyle |= ( int )( WS_EX_NOACTIVATE | WS_EX_TOOLWINDOW );

    return baseParams;
  }
}

3
ShowWithoutActivation、私がそれを見つけられなかったなんて信じられません。
Deerchao

2
またform1.Enabled = false、内部コントロールがフォーカスを盗むのを防ぐための設定も必要でした
Jader Dias

23
そして、TopMostをオフのままにします。
mklein

4
また、TopMostが必要な場合は、他の回答を参照してください。
ローマン・スターコフ2013

2
WS_EX_NOACTIVATEおよびの値WS_EX_TOOLWINDOW0x08000000および0x00000080です。
フアン

69

PInvoke.netShowWindowメソッドから盗まれた:

private const int SW_SHOWNOACTIVATE = 4;
private const int HWND_TOPMOST = -1;
private const uint SWP_NOACTIVATE = 0x0010;

[DllImport("user32.dll", EntryPoint = "SetWindowPos")]
static extern bool SetWindowPos(
     int hWnd,             // Window handle
     int hWndInsertAfter,  // Placement-order handle
     int X,                // Horizontal position
     int Y,                // Vertical position
     int cx,               // Width
     int cy,               // Height
     uint uFlags);         // Window positioning flags

[DllImport("user32.dll")]
static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);

static void ShowInactiveTopmost(Form frm)
{
     ShowWindow(frm.Handle, SW_SHOWNOACTIVATE);
     SetWindowPos(frm.Handle.ToInt32(), HWND_TOPMOST,
     frm.Left, frm.Top, frm.Width, frm.Height,
     SWP_NOACTIVATE);
}

(Alex Lymanがこれに答えました、私はコードを直接貼り付けることによってそれを拡張しています。編集権限を持つ誰かがそこにコピーして、私が気にするすべてのためにこれを削除できます;))


画面の左下に表示されているフォームが別のスレッドにある場合、本当に必要なのでしょうか。
Patrick Desjardins、

50
フォームとやり取りするために外部DLLファイルにリンクする必要があるのは信じられないことです。.NET Frameworkバージョン4です。マイクロソフトをラップする時間です。
Maltrap 2009年

9
受け入れられた答えは正しくありません。ShowWithoutActivationを探します
mklein

frm.Hide();を追加するだけです。フォームを直接フォーカスしない場合は、ShowInactiveTopmost関数の先頭。忘れないでください。System.Runtime.InteropServicesを使用します。このコードを実行するには
Zitun

1
@Talhaこのコードは、Loadイベントとは関係ありません。Loadイベントは、フォームが表示されているときではなく、ロードされているときに発生します。
TheSoftwareJedi 2014年


12

これは私のために働いたものです。TopMostを提供しますが、フォーカスを盗むことはありません。

    protected override bool ShowWithoutActivation
    {
       get { return true; }
    }

    private const int WS_EX_TOPMOST = 0x00000008;
    protected override CreateParams CreateParams
    {
       get
       {
          CreateParams createParams = base.CreateParams;
          createParams.ExStyle |= WS_EX_TOPMOST;
          return createParams;
       }
    }

Visual Studioデザイナまたは他の場所でTopMostを設定することを忘れてください。

これは、ここから盗まれ、誤って、借用されています(回避策をクリックしてください):

https://connect.microsoft.com/VisualStudio/feedback/details/401311/showwithoutactivation-is-not-supported-with-topmost


1
最上位+焦点を当てていない作品。すべての回答の中で最もクリーンなようです。
feos 2017年

Topmostは、Windows 8から廃止されました。Windows8を使用すると、Microsoftから罰せられます。その効果は、最上位のウィンドウを開いてから閉じた後、アプリケーションの他のウィンドウを背景に移動することです。これは確かにアプリケーションにとって望ましい動作ではありません。Microsoftがこれを実装したのは、これまで、多くのプログラマーが非常に煩わしい最上部を乱用したためです。一番上は非常に攻撃的です。私はそれを使用することはありません。
Elmue

9

これを行うことはハックのようですが、うまくいくようです:

this.TopMost = true;  // as a result the form gets thrown to the front
this.TopMost = false; // but we don't actually want our form to always be on top

編集:注、これは単にフォーカスを盗むことなく、既に作成されたフォームを発生させるだけです。


ここで動作しないようです...この「通知フォーム」が別のスレッドで開かれているためでしょうか?
マティアス

1
おそらく、その場合は、this.Invoke()呼び出しを実行して、メソッドを正しいスレッドとして再度呼び出す必要があります。一般に、間違ったスレッドのフォームを操作すると、例外がスローされます。
Matthew Scharley、2008年

これは機能しますが、前述のようにハッキーな方法であり、特定の条件下で私にBSODを引き起こしているので、これに注意してください。
Raphael Smit 2014

Topmostは、Windows 8から廃止されました。Windows8を使用すると、Microsoftから罰せられます。その効果は、最上位のウィンドウを開いてから閉じた後、アプリケーションの他のウィンドウを背景に移動することです。これは確かにアプリケーションにとって望ましい動作ではありません。Microsoftがこれを実装したのは、これまで、多くのプログラマーが非常に煩わしい最上部を乱用したためです。一番上は非常に攻撃的です。私はそれを使用することはありません。
Elmue

9

Alex Lyman / TheSoftwareJediの回答にあるpinvoke.netのサンプルコードは、ウィンドウを「最上位」ウィンドウにします。つまり、ポップアップした後、通常のウィンドウの背後に置くことはできません。マティアスがこれを何に使用したいかについての説明を考えると、それは彼が望んでいることかもしれません。ただし、ポップアップした後、ユーザーがウィンドウを他のウィンドウの背後に配置できるようにするには、サンプルでHWND_TOPMOST(-1)ではなくHWND_TOP(0)を使用します。


6

WPFでは、次のように解決できます。

ウィンドウに次の属性を入力します。

<Window
    x:Class="myApplication.winNotification"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  Title="Notification Popup" Width="300" SizeToContent="Height"
  WindowStyle="None" AllowsTransparency="True" Background="Transparent" ShowInTaskbar="False" Topmost="True" Focusable="False" ShowActivated="False" >
</Window>

最後の1つの属性は、ShowActivated = "False"が必要な属性です。


4

似たようなものがあり、通知フォームを表示してから

this.Focus();

メインフォームにフォーカスを戻します。


シンプルだけど効果的!
トム

3

別のスレッドで通知フォームを作成して開始し、フォームが開いたらフォーカスをメインフォームに戻します。通知フォームに、Form.Shownイベントから発生するOnFormOpenedイベントを提供させます。このようなもの:

private void StartNotfication()
{
  Thread th = new Thread(new ThreadStart(delegate
  {
    NotificationForm frm = new NotificationForm();
    frm.OnFormOpen += NotificationOpened;
    frm.ShowDialog();
  }));
  th.Name = "NotificationForm";
  th.Start();
} 

private void NotificationOpened()
{
   this.Focus(); // Put focus back on the original calling Form
}

また、NotifcationFormオブジェクトへのハンドルを保持して、メインのフォーム(frm.Close())によってプログラムで閉じることができるようにすることもできます。

いくつかの詳細は欠けていますが、うまくいけば、これで正しい方向に進むことができます。


これは、フォームが元々アクティブだった場合にのみ機能します。そのようなことは、この種の通知の主な目的に反します。
Alex Lyman、

1
えっ?それが通知の目的です。通知を入れて、元のアクティブなフォームに再びフォーカスを戻すことです。
Bob Nadler、

2
これは、アプリケーションのフォームにのみ焦点を当てます-他のプログラムがその時点でアクティブな場合はどうなりますか?通知ウィンドウの表示(通常、ユーザーにアプリケーションのステータスの更新を通知するため)は、ユーザーがアプリケーションを監視していないときにのみ非常に役立ちます。
アレックスライマン

3

表示する通知の種類を検討する必要がある場合があります。

イベントについてユーザーに知らせることが絶対に重要な場合は、ユーザーが確認するまでメインウィンドウへの他のイベントをブロックする性質があるため、Messagebox.Showを使用することをお勧めします。ただし、ポップアップ失明に注意してください。

重要度が低い場合は、ウィンドウの下部にあるツールバーなど、別の方法で通知を表示することをお勧めします。画面の右下に通知を表示することを書いた-これを行うための標準的な方法は、システムトレイアイコンの組み合わせでバルーンヒントを使用することです。


2
-無効にすることができるため、バルーンヒントはオプションではありません-あなたはプログラムがとにかくあなたのアーティスト同士のコラボレーションのための感謝を最小化した場合、ステータスバーを非表示にすることができ
マティアス

3

これはうまくいきます。

参照:OpenIcon-MSDN およびSetForegroundWindow-MSDN

using System.Runtime.InteropServices;

[DllImport("user32.dll")]
static extern bool OpenIcon(IntPtr hWnd);

[DllImport("user32.dll")]
static extern bool SetForegroundWindow(IntPtr hWnd);

public static void ActivateInstance()
{
    IntPtr hWnd = IntPtr hWnd = Process.GetCurrentProcess().MainWindowHandle;

    // Restore the program.
    bool result = OpenIcon(hWnd); 
    // Activate the application.
    result = SetForegroundWindow(hWnd);

    // End the current instance of the application.
    //System.Environment.Exit(0);    
}

1

ロジックだけで処理することもできますが、実際にフォーカスを盗まずにBringToFrontメソッドを使用する上記の提案が最もエレガントであることを認めざるを得ません。

とにかく、私はこれに遭遇し、DateTimeプロパティを使用して解決しました。呼び出しが最近行われた場合は、それ以上のBringToFront呼び出しを許可しません。

たとえば3つのフォーム「Form1、2、および3」を処理するコアクラス「Core」を想定します。各フォームには、DateTimeプロパティと、ウィンドウを前面に表示するためにCoreを呼び出すActivateイベントが必要です。

internal static DateTime LastBringToFrontTime { get; set; }

private void Form1_Activated(object sender, EventArgs e)
{
    var eventTime = DateTime.Now;
    if ((eventTime - LastBringToFrontTime).TotalMilliseconds > 500)
        Core.BringAllToFront(this);
    LastBringToFrontTime = eventTime;
}

次に、コアクラスで作品を作成します。

internal static void BringAllToFront(Form inForm)
{
    Form1.BringToFront();
    Form2.BringToFront();
    Form3.BringToFront();
    inForm.Focus();
}

余談ですが、最小化されたウィンドウを元の状態(最大化されていない状態)に戻したい場合は、次のようにします。

inForm.WindowState = FormWindowState.Normal;

繰り返しますが、これはBringToFrontWithoutFocusがない場合のパッチソリューションにすぎません。これは、DLLファイルを回避したい場合の提案です。


1

これがネクロポスティングと見なされるかどうかはわかりませんが、user32の「ShowWindow」メソッドと「SetWindowPos」メソッドで機能しないため、これを行いました。いいえ、この場合、新しいウィンドウを常に前面に表示する必要があるため、「ShowWithoutActivation」のオーバーライドは機能しません。とにかく、フォームをパラメーターとして取るヘルパーメソッドを作成しました。呼び出されると、フォームを表示し、前面に移動して、現在のウィンドウのフォーカスを奪うことなくTopMostにします(見かけ上はそうですが、ユーザーは気づきません)。

    [DllImport("user32.dll")]
    static extern IntPtr GetForegroundWindow();

    [DllImport("user32.dll")]
    static extern IntPtr SetForegroundWindow(IntPtr hWnd);

    public static void ShowTopmostNoFocus(Form f)
    {
        IntPtr activeWin = GetForegroundWindow();

        f.Show();
        f.BringToFront();
        f.TopMost = true;

        if (activeWin.ToInt32() > 0)
        {
            SetForegroundWindow(activeWin);
        }
    }

0

愚かに聞こえるかもしれませんが、これはうまくいきました:

this.TopMost = true;
this.TopMost = false;
this.TopMost = true;
this.SendToBack();

前面のウィンドウを背面に送ると、背景のウィンドウが新しい前景のウィンドウと重なっている場合は表示されなくなる可能性があります。
TamusJRoyce 2013

0

私は自分のウィンドウTopMostでこれを行う必要がありました。上記のPInvokeメソッドを実装しましたが、上記のTalhaのようにLoadイベントが呼び出されていません。ようやく成功しました。多分これは誰かを助けるでしょう。これが私の解決策です:

        form.Visible = false;
        form.TopMost = false;
        ShowWindow(form.Handle, ShowNoActivate);
        SetWindowPos(form.Handle, HWND_TOPMOST,
            form.Left, form.Top, form.Width, form.Height,
            NoActivate);
        form.Visible = true;    //So that Load event happens

-4

を使用して新しいフォームを作成するとき

Form f = new Form();
f.ShowDialog();

このフォームが閉じられるまで、コードはメインフォームで実行を続けることができないため、フォーカスを奪います。

例外は、スレッドを使用して新しいフォームを作成し、次にForm.Show()を使用することです。ただし、スレッドがグローバルに表示されることを確認してください。関数内で宣言すると、関数が終了するとすぐにスレッドが終了し、フォームが消えます。


弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.