Tampilkan Formulir tanpa mencuri fokus?


140

Saya menggunakan Formulir untuk menampilkan notifikasi (muncul di kanan bawah layar), tetapi ketika saya menunjukkan formulir ini ia mencuri fokus dari Formulir utama. Apakah ada cara untuk menampilkan formulir "pemberitahuan" ini tanpa mencuri fokus?

Jawaban:


165

Hmmm, bukankah hanya mengungguli Form.ShowWithoutActivation cukup?

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

Dan jika Anda tidak ingin pengguna mengklik jendela pemberitahuan ini juga, Anda dapat mengesampingkan 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, Tidak percaya saya tidak menemukannya, terbuang satu sore penuh!
deerchao

2
Saya juga perlu mengatur form1.Enabled = falseuntuk mencegah kontrol dalam mencuri fokus
Jader Dias

23
Dan tinggalkan TopMost.
mklein

4
Dan jika Anda ingin TopMost, lihat jawaban lainnya .
Roman Starkov

2
Nilai WS_EX_NOACTIVATEdan WS_EX_TOOLWINDOWadalah 0x08000000dan 0x00000080masing - masing.
Juan

69

Dicuri dari PInvoke.net 's ShowWindow metode:

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 menjawab ini, saya hanya mengembangkannya dengan langsung menempelkan kode. Seseorang dengan hak edit dapat menyalinnya di sana dan menghapus ini untuk semua yang saya pedulikan;))


Saya bertanya-tanya, apakah dia benar-benar membutuhkannya jika formulir yang ditampilkan di kiri bawah layarnya ada di utas lain?
Patrick Desjardins

50
Saya merasa sulit dipercaya bahwa kita masih perlu menautkan ke file DLL eksternal untuk berinteraksi dengan formulir. Kami berada di .NET framework versi 4 !! Saatnya membungkusnya Microsoft.
Maltrap

9
Jawaban yang diterima salah. Carilah ShowWithoutActivation
mklein

Cukup tambahkan frm. Sembunyikan (); di awal fungsi ShowInactiveTopmost jika Anda ingin formulir Anda tidak fokus secara langsung. Jangan lupa: menggunakan System.Runtime.InteropServices; untuk membuat kode ini berjalan
Zitun

1
@ Talha Kode ini tidak ada hubungannya dengan acara Load. Acara Muat menyala saat formulir dimuat, bukan saat ditampilkan.
TheSoftwareJedi


12

Inilah yang bekerja untuk saya. Ini memberikan TopMost tetapi tanpa mencuri fokus.

    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;
       }
    }

Ingatlah untuk menghilangkan pengaturan TopMost di perancang Visual Studio, atau di tempat lain.

Ini dicuri, salah, dipinjam, dari sini (klik Workarounds):

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


1
+ Karya paling tidak fokus, dan sepertinya jawaban terbersih dari semua jawaban.
feos

Paling atas sudah tidak digunakan lagi sejak Windows 8 di mana Microsoft menghukum Anda saat menggunakannya. Efeknya adalah bahwa setelah membuka jendela paling atas dan kemudian menutupnya, Windows memindahkan jendela lain dari aplikasi Anda ke latar belakang. Ini tentu bukan perilaku yang diinginkan untuk aplikasi Anda. Microsoft menerapkan ini karena di masa lalu banyak programmer menyalahgunakan yang paling intrusif. Paling atas sangat agresif. Saya tidak pernah menggunakannya.
Elmue

9

Melakukan ini tampaknya seperti peretasan, tetapi tampaknya berhasil:

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

Sunting: Catatan, ini hanya memunculkan formulir yang sudah dibuat tanpa mencuri fokus.


sepertinya tidak berfungsi di sini ... mungkin karena "formulir pemberitahuan" ini dibuka di utas lain?
Matías

1
Mungkin, dalam hal ini Anda perlu melakukan ini. Panggilan () panggilan untuk memanggil metode lagi sebagai utas yang tepat. Umumnya bekerja dengan bentuk-bentuk dari utas yang salah menyebabkan pengecualian untuk dilemparkan.
Matthew Scharley

Walaupun ini berfungsi, ini adalah metode hacky seperti yang disebutkan dan telah menyebabkan BSOD untuk saya dalam kondisi tertentu, jadi waspadalah terhadap hal ini.
Raphael Smit

Paling atas sudah tidak digunakan lagi sejak Windows 8 di mana Microsoft menghukum Anda saat menggunakannya. Efeknya adalah bahwa setelah membuka jendela paling atas dan kemudian menutupnya, Windows memindahkan jendela lain dari aplikasi Anda ke latar belakang. Ini tentu bukan perilaku yang diinginkan untuk aplikasi Anda. Microsoft menerapkan ini karena di masa lalu banyak programmer menyalahgunakan yang paling intrusif. Paling atas sangat agresif. Saya tidak pernah menggunakannya.
Elmue

9

Kode sampel dari pinvoke.net pada jawaban Alex Lyman / TheSoftwareJedi akan menjadikan jendela tersebut sebagai jendela "paling atas", artinya Anda tidak dapat meletakkannya di belakang jendela normal setelah muncul. Mengingat deskripsi Matias tentang apa yang dia ingin gunakan untuk ini, itu bisa menjadi apa yang dia inginkan. Tetapi jika Anda ingin pengguna dapat menempatkan jendela Anda di belakang jendela lain setelah Anda muncul, cukup gunakan HWND_TOP (0) alih-alih HWND_TOPMOST (-1) dalam sampel.


6

Di WPF Anda dapat menyelesaikannya seperti ini:

Di jendela letakkan atribut ini:

<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>

Atribut yang terakhir adalah yang Anda butuhkan ShowActivated = "False".


4

Saya memiliki sesuatu yang serupa, dan saya hanya menunjukkan formulir pemberitahuan dan kemudian melakukannya

this.Focus();

untuk mengembalikan fokus pada formulir utama.


Sederhana tetapi efektif!
Tom

3

Buat dan mulai Formulir pemberitahuan di utas terpisah dan setel ulang fokus kembali ke formulir utama Anda setelah Formulir dibuka. Minta Formulir pemberitahuan menyediakan acara OnFormOpened yang dipecat dari Form.Shownacara tersebut. Sesuatu seperti ini:

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
}

Anda juga bisa menjaga pegangan ke objek NotifcationForm Anda di sekitar sehingga bisa ditutup secara terprogram oleh Formulir utama ( frm.Close()).

Beberapa detail hilang, tetapi mudah-mudahan ini akan membuat Anda pergi ke arah yang benar.


Ini hanya akan berfungsi jika formulir Anda adalah formulir yang semula aktif. Hal semacam itu bertentangan dengan tujuan utama pemberitahuan semacam ini.
Alex Lyman

1
Hah? Itulah tujuan dari pemberitahuan - untuk memasangnya dan mendapatkan kembali fokus ke bentuk yang semula aktif.
Bob Nadler

2
Ini hanya memberi fokus pada formulir di aplikasi Anda - bagaimana jika beberapa program lain aktif saat itu? Menampilkan jendela pemberitahuan (biasanya untuk memberi pengguna pembaruan tentang status aplikasi Anda) hanya sangat berguna ketika mereka tidak menonton aplikasi Anda.
Alex Lyman

3

Anda mungkin ingin mempertimbangkan jenis pemberitahuan apa yang ingin Anda tampilkan.

Jika benar-benar penting untuk memberi tahu pengguna tentang suatu peristiwa, menggunakan Messagebox. Pertunjukan akan menjadi cara yang disarankan, karena sifatnya untuk memblokir acara lain ke jendela utama, hingga pengguna mengonfirmasinya. Waspadai kebutaan pop-up.

Jika kurang dari kritis, Anda mungkin ingin menggunakan cara alternatif untuk menampilkan pemberitahuan, seperti bilah alat di bagian bawah jendela. Anda menulis, bahwa Anda menampilkan pemberitahuan di kanan bawah layar - cara standar untuk melakukan ini adalah menggunakan ujung balon dengan kombinasi ikon baki sistem .


2
- Kiat balon bukan opsi karena dapat dinonaktifkan - Bilah status dapat disembunyikan jika Anda memiliki program yang diperkecil Bagaimanapun, terima kasih atas rekomendasi Anda
Matías

3

Ini bekerja dengan baik.

Lihat: OpenIcon - MSDN dan 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

Anda dapat mengatasinya hanya dengan logika saja, walaupun saya harus mengakui bahwa saran di atas di mana Anda berakhir dengan metode BringToFront tanpa benar-benar mencuri fokus adalah yang paling elegan.

Bagaimanapun, saya mengalami ini dan menyelesaikannya dengan menggunakan properti DateTime untuk tidak memungkinkan panggilan BringToFront lebih lanjut jika panggilan sudah dilakukan baru-baru ini.

Asumsikan kelas inti, 'Inti', yang menangani misalnya tiga bentuk, 'Form1, 2, dan 3'. Setiap formulir membutuhkan properti DateTime dan acara Activate yang memanggil Core untuk membawa windows ke depan:

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;
}

Dan kemudian buat karya di Kelas Inti:

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

Sebagai catatan, jika Anda ingin mengembalikan jendela yang diperkecil ke keadaan semula (tidak dimaksimalkan), gunakan:

inForm.WindowState = FormWindowState.Normal;

Sekali lagi, saya tahu ini hanyalah solusi patch karena kurangnya BringToFrontWithoutFocus. Ini dimaksudkan sebagai saran jika Anda ingin menghindari file DLL.


1

Saya tidak tahu apakah ini dianggap sebagai posting necro, tapi ini yang saya lakukan karena saya tidak bisa menggunakannya dengan metode "ShowWindow" dan "SetWindowPos" user32. Dan tidak, mengganti "ShowWithoutActivation" tidak berfungsi dalam kasus ini karena jendela baru harus selalu di atas. Lagi pula, saya membuat metode pembantu yang mengambil formulir sebagai parameter; ketika dipanggil, ia menunjukkan formulir, membawanya ke depan dan membuatnya TopMost tanpa mencuri fokus jendela saat ini (tampaknya memang demikian, tetapi pengguna tidak akan memperhatikan).

    [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

Saya tahu ini terdengar bodoh, tetapi ini berhasil:

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

Jika Anda mengirim jendela depan ke belakang, itu mungkin tidak lagi ditampilkan jika jendela latar belakang tumpang tindih dengan yang baru.
TamusJRoyce

0

Saya perlu melakukan ini dengan jendela saya TopMost. Saya menerapkan metode PInvoke di atas tetapi menemukan bahwa acara Load saya tidak dipanggil seperti Talha di atas. Saya akhirnya berhasil. Mungkin ini akan membantu seseorang. Ini solusinya:

        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

Saat Anda membuat formulir baru menggunakan

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

itu mencuri fokus karena kode Anda tidak dapat melanjutkan mengeksekusi pada formulir utama sampai formulir ini ditutup.

Pengecualian adalah dengan menggunakan threading untuk membuat formulir baru lalu Form.Show (). Pastikan utas tersebut terlihat secara global, karena jika Anda mendeklarasikannya dalam suatu fungsi, segera setelah fungsi Anda keluar, utas Anda akan berakhir dan formulir itu akan hilang.


Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.