ฉันใช้แบบฟอร์มเพื่อแสดงการแจ้งเตือน (ปรากฏที่ด้านล่างขวาของหน้าจอ) แต่เมื่อฉันแสดงแบบฟอร์มนี้จะเป็นการขโมยโฟกัสจากแบบฟอร์มหลัก มีวิธีแสดงแบบฟอร์ม "แจ้งเตือน" นี้โดยไม่ขโมยโฟกัสหรือไม่
ฉันใช้แบบฟอร์มเพื่อแสดงการแจ้งเตือน (ปรากฏที่ด้านล่างขวาของหน้าจอ) แต่เมื่อฉันแสดงแบบฟอร์มนี้จะเป็นการขโมยโฟกัสจากแบบฟอร์มหลัก มีวิธีแสดงแบบฟอร์ม "แจ้งเตือน" นี้โดยไม่ขโมยโฟกัสหรือไม่
คำตอบ:
อืมไม่ได้มีเพียงแค่เอาชนะแบบฟอร์มแสดงโดยไม่มีการเปิดใช้งานเพียงพอ
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;
}
}
form1.Enabled = false
เพื่อป้องกันการควบคุมภายในจากการขโมยโฟกัส
WS_EX_NOACTIVATE
และWS_EX_TOOLWINDOW
เป็น0x08000000
และ0x00000080
ตามลำดับ
ถูกขโมยจาก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 ตอบคำถามนี้ฉันแค่ขยายมันโดยการวางรหัสโดยตรงคนที่มีสิทธิ์แก้ไขสามารถคัดลอกได้ที่นั่นและลบสิ่งนี้สำหรับสิ่งที่ฉันสนใจ;)
หากคุณยินดีที่จะใช้Win32 P / 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;
}
}
อย่าลืมตั้งค่า TopMost ในตัวออกแบบ Visual Studio หรือที่อื่น ๆ
สิ่งนี้ถูกขโมยทำผิดยืมไปจากที่นี่ (คลิกที่การแก้ไขปัญหา):
การทำเช่นนี้ดูเหมือนว่าจะเป็นการแฮก แต่ดูเหมือนว่าจะทำงาน:
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
แก้ไข: โปรดทราบนี่เป็นเพียงการยกแบบฟอร์มที่สร้างไว้แล้วโดยไม่ขโมยโฟกัส
โค้ดตัวอย่างจาก pinvoke.net ในคำตอบของ Alex Lyman / TheSoftwareJedi จะทำให้หน้าต่างเป็นหน้าต่าง "สุดยอด" ซึ่งหมายความว่าคุณไม่สามารถวางไว้ด้านหลังหน้าต่างปกติหลังจากที่มันโผล่ขึ้นมา จากคำอธิบายของ Matias ว่าเขาต้องการใช้สิ่งนี้เพื่ออะไรนั่นอาจเป็นสิ่งที่เขาต้องการ แต่ถ้าคุณต้องการให้ผู้ใช้สามารถวางหน้าต่างไว้ด้านหลังหน้าต่างอื่นหลังจากที่คุณเปิดมันขึ้นมาให้ใช้ HWND_TOP (0) แทน HWND_TOPMOST (-1) ในตัวอย่าง
ใน 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>
แอตทริบิวต์สุดท้ายคือสิ่งที่คุณต้องการ ShowActivated = "เท็จ"
ฉันมีสิ่งที่คล้ายกันและฉันเพียงแค่แสดงแบบฟอร์มการแจ้งเตือนแล้วทำ
this.Focus();
เพื่อนำโฟกัสกลับมาที่ฟอร์มหลัก
สร้างและเริ่มฟอร์มการแจ้งเตือนในเธรดแยกต่างหากและรีเซ็ตโฟกัสกลับไปที่ฟอร์มหลักของคุณหลังจากที่เปิดแบบฟอร์ม มีแบบฟอร์มการแจ้งเตือนให้เหตุการณ์ OnFormOpened ที่ถูกไล่ออกจากForm.Shown
เหตุการณ์ บางสิ่งเช่นนี้
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()
)
รายละเอียดบางอย่างหายไป แต่หวังว่าจะทำให้คุณไปในทิศทางที่ถูกต้อง
คุณอาจต้องการพิจารณาประเภทของการแจ้งเตือนที่คุณต้องการแสดง
หากจำเป็นอย่างยิ่งที่จะต้องให้ผู้ใช้ทราบเกี่ยวกับเหตุการณ์บางอย่างการใช้ Messagebox แสดงให้เห็นว่าเป็นวิธีที่แนะนำเนื่องจากมีลักษณะในการบล็อกกิจกรรมอื่น ๆ ในหน้าต่างหลักจนกว่าผู้ใช้จะยืนยัน ระวังการตาบอดแบบผุดขึ้น
หากไม่สำคัญคุณอาจต้องการใช้วิธีอื่นในการแสดงการแจ้งเตือนเช่นแถบเครื่องมือที่ด้านล่างของหน้าต่าง คุณเขียนว่าคุณแสดงการแจ้งเตือนที่ด้านล่างขวาของหน้าจอ - วิธีมาตรฐานในการทำเช่นนี้คือการใช้ปลายบอลลูนกับการรวมกันของไอคอนถาดระบบ
มันใช้งานได้ดี
ดู: 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);
}
คุณสามารถจัดการด้วยตรรกะเพียงอย่างเดียวเช่นกันแม้ว่าฉันต้องยอมรับว่าคำแนะนำข้างต้นที่คุณจบลงด้วยวิธีการ BringToFront โดยไม่ต้องขโมยโฟกัสจริง ๆ เป็นสิ่งที่งดงามที่สุด
อย่างไรก็ตามฉันพบปัญหานี้และแก้ไขโดยใช้คุณสมบัติ DateTime เพื่อไม่อนุญาตให้มีการเรียกใช้ BringToFront อีกต่อไปหากมีการโทรออกไปแล้ว
สมมติคลาสหลัก 'Core' ซึ่งจัดการตัวอย่างเช่นสามฟอร์ม 'Form1, 2 และ 3' แต่ละแบบฟอร์มต้องการคุณสมบัติ DateTime และกิจกรรมเปิดใช้งานที่เรียกคอร์เพื่อให้หน้าต่างอยู่ด้านหน้า:
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;
}
จากนั้นสร้างงานใน Core Class:
internal static void BringAllToFront(Form inForm)
{
Form1.BringToFront();
Form2.BringToFront();
Form3.BringToFront();
inForm.Focus();
}
ในบันทึกย่อด้านข้างหากคุณต้องการคืนค่าหน้าต่างย่อเล็กสุดให้กลับสู่สถานะดั้งเดิม (ไม่ขยายใหญ่สุด) ให้ใช้:
inForm.WindowState = FormWindowState.Normal;
อีกครั้งฉันรู้ว่านี่เป็นเพียงวิธีแก้ไขในการขาด BringToFrontWithoutFocus มันมีความหมายเป็นข้อเสนอแนะถ้าคุณต้องการหลีกเลี่ยงไฟล์ DLL
ฉันไม่ทราบว่านี่เป็นการโพสต์แบบ necro หรือไม่ แต่นี่คือสิ่งที่ฉันทำเพราะฉันไม่สามารถทำงานกับวิธี "ShowWindow" และ "SetWindowPos" ของ user32 ได้ และไม่การแทนที่ "ShowWithoutActivati on" จะไม่ทำงานในกรณีนี้เนื่องจากหน้าต่างใหม่ควรอยู่ด้านบนเสมอ อย่างไรก็ตามฉันสร้างเมธอดผู้ช่วยเหลือที่ใช้ฟอร์มเป็นพารามิเตอร์ เมื่อเรียกใช้จะแสดงแบบฟอร์มนำมาไว้ด้านหน้าและทำให้เป็น 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);
}
}
ฉันรู้ว่ามันอาจจะฟังดูงี่เง่า แต่สิ่งนี้ได้ผล:
this.TopMost = true;
this.TopMost = false;
this.TopMost = true;
this.SendToBack();
ฉันต้องทำสิ่งนี้กับหน้าต่าง TopMost ฉันใช้วิธี PInvoke ด้านบน แต่พบว่าเหตุการณ์การโหลดของฉันไม่ได้รับการเรียกเหมือน Talha ด้านบน ในที่สุดฉันก็ประสบความสำเร็จ บางทีนี่อาจช่วยใครซักคน นี่คือทางออกของฉัน:
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
เมื่อคุณสร้างฟอร์มใหม่โดยใช้
Form f = new Form();
f.ShowDialog();
มันขโมยโฟกัสเพราะรหัสของคุณไม่สามารถดำเนินการต่อในรูปแบบหลักจนกว่าจะปิดแบบฟอร์มนี้
ข้อยกเว้นคือการใช้เธรดเพื่อสร้างแบบฟอร์มใหม่แล้ว Form.Show () ตรวจสอบให้แน่ใจว่าเธรดนั้นสามารถมองเห็นได้ทั่วโลกเพราะถ้าคุณประกาศภายในฟังก์ชั่นทันทีที่ฟังก์ชั่นของคุณออกเธรดของคุณจะจบลงและรูปแบบจะหายไป
คิดออก: window.WindowState = WindowState.Minimized;
.