web-dev-qa-db-ja.com

フォーカスを盗むことなくフォームを表示しますか?

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

134
Matías

うーん、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;
  }
}
158
Martin Plante

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がこれに答えました、私はコードを直接貼り付けることでそれを拡張しています。編集権限を持つ誰かがそれをそこにコピーし、私が気にするすべてのためにこれを削除できます;))

67
TheSoftwareJedi

Win32P/Invoke を使用する場合は、 ShowWindow メソッドを使用できます(最初のコードサンプルは、望みどおりに動作します)。

14
Alex Lyman

これは私のために働いたものです。 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

12
RenniePet

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

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

編集:注意、これはフォーカスを盗むことなく、すでに作成されたフォームを上げるだけです。

9

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

8
Micah

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"が必要な属性です。

6
Ziketo

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

this.Focus();

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

3
pkr298

表示する通知の種類を検討することをお勧めします。

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

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

3
Silver Dragon

別のスレッドで通知フォームを作成して開始し、フォームが開いたらメインフォームにフォーカスをリセットします。 _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())によってプログラムで閉じることができるようにすることもできます。

一部の詳細が欠落していますが、うまくいけば正しい方向に進むことができます。

3
Bob Nadler

これはうまく機能します。

参照: 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);    
}
2
Aerik

これがネクロ投稿と見なされるかどうかはわかりませんが、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);
        }
    }
1
domi1819

canロジックのみで処理することもできますが、実際にフォーカスを奪うことなくBringToFrontメソッドに到達する上記の提案が最もエレガントであることを認めなければなりません。

とにかく、私はこれに遭遇し、呼び出しがすでに最近行われた場合、さらにBringToFront呼び出しを許可しないようにDateTimeプロパティを使用することでそれを解決しました。

たとえば、3つのフォーム「Form1、2、3」を処理するコアクラス「Core」を想定します。各フォームには、コアを呼び出してウィンドウを前面に表示するDateTimeプロパティと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
Meta

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

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

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
0
Steven Cvetko