フォームを使用して通知を表示しています(画面の右下に表示されます)が、このフォームを表示すると、メインフォームからフォーカスが奪われます。フォーカスを盗むことなく、この「通知」フォームを表示する方法はありますか?
うーん、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;
}
}
PInvoke.net の ShowWindow メソッドから盗まれた:
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がこれに答えました、私はコードを直接貼り付けることでそれを拡張しています。編集権限を持つ誰かがそれをそこにコピーし、私が気にするすべてのためにこれを削除できます;))
Win32P/Invoke を使用する場合は、 ShowWindow メソッドを使用できます(最初のコードサンプルは、望みどおりに動作します)。
これは私のために働いたものです。 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を設定するのを忘れないでください。
これは、ここから盗まれたり、エラーになったり、借用されたりします(回避策をクリックします)。
これを行うことはハックのように見えますが、うまくいくようです:
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
編集:注意、これはフォーカスを盗むことなく、すでに作成されたフォームを上げるだけです。
Alex Lyman/TheSoftwareJediの回答にあるpinvoke.netのサンプルコードは、ウィンドウを「一番上の」ウィンドウにします。つまり、ポップアップした後に通常のウィンドウの後ろに置くことはできません。 Matiasがこれを何に使いたいのかという説明を考えると、それが彼の望みかもしれません。ただし、ポップアップした後、ユーザーが他のウィンドウの後ろにウィンドウを配置できるようにするには、サンプルでHWND_TOPMOST(-1)ではなくHWND_TOP(0)を使用します。
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"が必要な属性です。
似たようなものがあり、通知フォームを表示してから
this.Focus();
メインフォームにフォーカスを戻します。
表示する通知の種類を検討することをお勧めします。
ユーザーに何らかのイベントを知らせることが絶対に重要な場合は、ユーザーが確認するまで、メインウィンドウへの他のイベントをブロックする性質があるため、Messagebox.Showを使用することをお勧めします。ただし、ポップアップ失明に注意してください。
重要度が低い場合は、ウィンドウの下部にあるツールバーなど、通知を表示する別の方法を使用できます。画面の右下に通知を表示することを書きました-これを行う標準的な方法は、 システムトレイ アイコンの組み合わせで balloon tip を使用することです。
別のスレッドで通知フォームを作成して開始し、フォームが開いたらメインフォームにフォーカスをリセットします。 _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()
)によってプログラムで閉じることができるようにすることもできます。
一部の詳細が欠落していますが、うまくいけば正しい方向に進むことができます。
これはうまく機能します。
参照: 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);
}
これがネクロ投稿と見なされるかどうかはわかりませんが、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);
}
}
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ファイルを避けたい場合の提案としての意味です。
私はそれが愚かに聞こえるかもしれませんが、これはうまくいきました:
this.TopMost = true;
this.TopMost = false;
this.TopMost = true;
this.SendToBack();
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