Bagaimana cara membuat jendela selalu berada di atas di .Net?

95

Saya memiliki aplikasi C # winforms yang menjalankan makro di program lain. Program lain akan terus-menerus memunculkan jendela dan secara umum membuat segala sesuatunya terlihat, karena tidak ada kata yang lebih baik, gila. Saya ingin menerapkan tombol batal yang akan menghentikan proses agar tidak berjalan, tetapi saya tidak bisa membuat jendela tetap di atas. Bagaimana saya melakukan ini di C #?

Sunting: Saya telah mencoba TopMost = true; , tetapi program lain terus memunculkan jendelanya sendiri di atasnya. Apakah ada cara untuk mengirim jendela saya ke atas setiap n milidetik?

Sunting: Cara saya menyelesaikan ini adalah dengan menambahkan ikon baki sistem yang akan membatalkan proses dengan mengklik dua kali di atasnya. Ikon baki sistem tidak tertutupi. Terima kasih untuk semua yang menanggapi. Saya membaca artikel tentang mengapa tidak ada jendela 'super-on-top' ... secara logis tidak berfungsi.

jle
sumber
63
Ya, setel pengatur waktu untuk setiap beberapa milidetik yang akan menyetel Form.TopMost Anda ke true. Kemudian, untuk membuatnya menarik, saat program "gila" dimuat, putar klip audio dari Mortal Kombat "FIGHT!" :-P
BFree
2
Anda mungkin mengira komentar Anda lucu, Anda mungkin berpikir Anda bisa mengejek praktik yang buruk. Masalah saya adalah membuat menu konteks yang mengapung di atas formulir dengan panel alur keluar. Flowlayoutpanel hanya dapat di-scroll jika Anda menyebutnya metode Activate (), Focus () TIDAK cukup dalam keadaan tertentu. Anda tidak akan bisa menggulirnya. Itu mencuri fokus dari menu konteks meskipun memiliki topmost eksklusif = true! Seperti yang diketahui oleh orang yang berakal sehat, adalah tindakan yang saleh untuk membiarkan aplikasi winform Anda berjalan dalam mode MTAThread dan memberikan setiap bentuk utasnya sendiri yang membuat solusinya sederhana:
Traubenfuchs
1
Lihatlah, iblis: pastebin.com/sMJX0Yav Ia bekerja dengan sempurna tanpa berkedip dan tidur (1) cukup untuk mencegahnya menguras kinerja serius. Siapa yang terus mencari di taskmanagernya saat dia fokus pada menu konteks? Setelah menu konteks ditutup, semoga berjalan ke penangan pengecualian kosong dan mati. Anda mungkin membangun istirahat isDisposed sekalipun.
Traubenfuchs
Saya baru saja memposting solusi saya untuk masalah ini di sini: stackoverflow.com/questions/2546566/…
kfn
@Traubenfuchs Itu akan gagal karena pengecualian operasi Cross-thread. Ini seharusnya berhasil.
mekb

Jawaban:

172

Form.TopMost akan berfungsi kecuali jika program lain membuat jendela paling atas.

Tidak ada cara untuk membuat jendela yang tidak tercakup oleh jendela paling atas baru dari proses lain. Raymond Chen menjelaskan alasannya.

RossFabricant
sumber
11
Jika ada pemula lain yang melihat ini pada tahun 2016 dan seterusnya, cobalahForm.ActiveForm.TopMost
Devil's Advocate
1
@ ScottBeeson: Ini bagus. Informasi terkini sangat diperlukan di dunia yang dinamis tekno ini. Terima kasih:).
Sandeep Kushwah
48

Saya sedang mencari untuk membuat aplikasi WinForms saya "Selalu di Atas" tetapi pengaturan "Paling Top" tidak melakukan apa-apa untuk saya. Saya tahu itu mungkin karena WinAmp melakukan ini (bersama dengan sejumlah aplikasi lain).

Apa yang saya lakukan adalah membuat panggilan ke "user32.dll." Saya tidak ragu melakukannya dan hasilnya bagus. Itu adalah pilihan.

Pertama, impor namespace berikut:

using System.Runtime.InteropServices;

Tambahkan beberapa variabel ke deklarasi kelas Anda:

private static readonly IntPtr HWND_TOPMOST = new IntPtr(-1);
private const UInt32 SWP_NOSIZE = 0x0001;
private const UInt32 SWP_NOMOVE = 0x0002;
private const UInt32 TOPMOST_FLAGS = SWP_NOMOVE | SWP_NOSIZE;

Tambahkan prototipe untuk fungsi user32.dll:

[DllImport("user32.dll")] 
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags);

Kemudian di kode Anda (saya menambahkan panggilan di Form_Load ()), tambahkan panggilan:

SetWindowPos(this.Handle, HWND_TOPMOST, 0, 0, 0, 0, TOPMOST_FLAGS);

Semoga membantu. Referensi

kerang
sumber
2
Ini berfungsi tidak hanya untuk aplikasi WinForms, tetapi juga untuk jendela konsol . Temuan bagus!
rojo
Bagus, dapat mengonfirmasi bahwa ini berfungsi. Tapi bagaimana saya bisa mengubahnya kembali menjadi tidak paling top? apakah ada panji HWND_BOTTOMMOST yang dapat Anda bagikan?
Tandai
Pertanyaan bagus, jika Anda ingin menukar antara kemampuan paling atas ini dan perilaku default (misalnya, Anda memiliki kotak centang "Selalu di atas" untuk perilaku jendela). Saya rasa mungkin dengan bendera yang tepat hal itu bisa dilakukan. Jika Anda memiliki flag yang tepat yang menjelaskan perilaku jendela default (katakanlah SWP_DEFAULT = 0x0003), maka Anda dapat memanggil "SetWindowPos ()" lagi dengan flag ini. Saya hanya tidak yakin; Saya belum memeriksanya. Semoga berhasil jika Anda melakukannya, dan jika seseorang melakukannya, tambahkan di sini!
clamum
Ini tidak berfungsi pada mode permainan layar penuh seperti biasa
Sajitha Rathnayake
2
@Tanda Ya, ada bendera HWND_NOTOPMOST (= -2). Lihat docs.microsoft.com/en-us/windows/win32/api/winuser/…
Kevin Vuilleumier
23

Jika yang Anda maksud dengan "menjadi gila" adalah setiap jendela terus mencuri fokus dari yang lain, TopMost tidak akan menyelesaikan masalah.

Sebagai gantinya, coba:

CalledForm.Owner = CallerForm;
CalledForm.Show();

Ini akan menunjukkan bentuk 'anak' tanpa mencuri fokus. Bentuk anak juga akan tetap berada di atas induknya meskipun induknya diaktifkan atau difokuskan. Kode ini hanya berfungsi dengan mudah jika Anda telah membuat instance dari bentuk anak dari dalam formulir pemilik. Jika tidak, Anda mungkin harus menyetel pemilik menggunakan API.

Victor Stoddard
sumber
1
Terima kasih banyak, saya telah menggunakan ini dan bekerja dengan sempurna!
AvetisG
1
Terima kasih .. persis apa yang saya cari
Sameera Kumarasingha
Menyetel CalledForm.Ownerke dirinya sendiri ( CalledForm) akan menyebabkan System.ArgumentException: 'Referensi kontrol melingkar telah dibuat. Kontrol tidak dapat dimiliki oleh atau diasuh untuk dirinya sendiri. '
mekb
2
Itulah mengapa Anda menggunakan CallerForm daripada CalledForm :)
Jesper
16

Atur Form.TopMost

Reed Copsey
sumber
Saya mencoba, ini ... apakah saya perlu terus melakukannya? 'Program gila' segera mengambil alih ...
jle
2
Tidak - jika Anda menyetel formulir Anda.TopMost = true, seharusnya berfungsi. Program "gila" harus memiliki dialognya yang disetel ke TopMost juga, dalam hal ini, Anda tidak dapat menimpanya.
Reed Copsey
Bukan pertarungan yang adil. Terima kasih.
jle
11

Saya mengalami selang waktu 5 menit dan saya lupa untuk menentukan formulir secara lengkap seperti ini:

  myformName.ActiveForm.TopMost = true;

Tapi yang saya inginkan adalah INI!

  this.TopMost = true;
Dave
sumber
Bekerja sempurna untuk saya. jika (checkBox1.Checked == true) {this.TopMost = true; } lain {this.TopMost = false; }
yosh
6

Set .TopMostproperti formulir menjadi benar.

Anda mungkin tidak ingin membiarkannya seperti ini sepanjang waktu: atur saat proses eksternal Anda dimulai dan kembalikan saat selesai.

Joel Coehoorn
sumber
5

Cara saya menyelesaikannya adalah dengan membuat ikon baki sistem yang memiliki opsi batal.

jle
sumber
5

Kode berikut membuat jendela selalu berada di atas sekaligus menjadikannya tanpa bingkai.

using System;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace StayOnTop
{
    public partial class Form1 : Form
    {
        private static readonly IntPtr HWND_TOPMOST = new IntPtr(-1);
        private const UInt32 SWP_NOSIZE = 0x0001;
        private const UInt32 SWP_NOMOVE = 0x0002;
        private const UInt32 TOPMOST_FLAGS = SWP_NOMOVE | SWP_NOSIZE;

        [DllImport("user32.dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags);

        public Form1()
        {
            InitializeComponent();
            FormBorderStyle = FormBorderStyle.None;
            TopMost = true;
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            SetWindowPos(this.Handle, HWND_TOPMOST, 100, 100, 300, 300, TOPMOST_FLAGS);
        }

        protected override void WndProc(ref Message m)
        {
            const int RESIZE_HANDLE_SIZE = 10;

            switch (m.Msg)
            {
                case 0x0084/*NCHITTEST*/ :
                    base.WndProc(ref m);

                    if ((int)m.Result == 0x01/*HTCLIENT*/)
                    {
                        Point screenPoint = new Point(m.LParam.ToInt32());
                        Point clientPoint = this.PointToClient(screenPoint);
                        if (clientPoint.Y <= RESIZE_HANDLE_SIZE)
                        {
                            if (clientPoint.X <= RESIZE_HANDLE_SIZE)
                                m.Result = (IntPtr)13/*HTTOPLEFT*/ ;
                            else if (clientPoint.X < (Size.Width - RESIZE_HANDLE_SIZE))
                                m.Result = (IntPtr)12/*HTTOP*/ ;
                            else
                                m.Result = (IntPtr)14/*HTTOPRIGHT*/ ;
                        }
                        else if (clientPoint.Y <= (Size.Height - RESIZE_HANDLE_SIZE))
                        {
                            if (clientPoint.X <= RESIZE_HANDLE_SIZE)
                                m.Result = (IntPtr)10/*HTLEFT*/ ;
                            else if (clientPoint.X < (Size.Width - RESIZE_HANDLE_SIZE))
                                m.Result = (IntPtr)2/*HTCAPTION*/ ;
                            else
                                m.Result = (IntPtr)11/*HTRIGHT*/ ;
                        }
                        else
                        {
                            if (clientPoint.X <= RESIZE_HANDLE_SIZE)
                                m.Result = (IntPtr)16/*HTBOTTOMLEFT*/ ;
                            else if (clientPoint.X < (Size.Width - RESIZE_HANDLE_SIZE))
                                m.Result = (IntPtr)15/*HTBOTTOM*/ ;
                            else
                                m.Result = (IntPtr)17/*HTBOTTOMRIGHT*/ ;
                        }
                    }
                    return;
            }
            base.WndProc(ref m);
        }

        protected override CreateParams CreateParams
        {
            get
            {
                CreateParams cp = base.CreateParams;
                cp.Style |= 0x20000; // <--- use 0x20000
                return cp;
            }
        }
    }
}
BK Krish
sumber
Setuju dengan Alexan - apa yang membuat program Anda menjadi yang teratas? Sepertinya itu sebenarnya hanya pernyataan "paling atas = benar", yang tidak berfungsi di banyak kasus. Semua kode lainnya tidak benar-benar menjawab masalah.
Fhaab
4

Apa aplikasi lain yang Anda coba untuk tutupi visibilitasnya? Sudahkah Anda menyelidiki cara lain untuk mencapai efek yang Anda inginkan? Harap lakukan sebelum mengarahkan pengguna Anda pada perilaku nakal seperti yang Anda gambarkan: apa yang Anda coba lakukan terdengar seperti apa yang dilakukan situs nakal tertentu dengan jendela browser ...

Setidaknya cobalah untuk mematuhi aturan Kejutan Terkecil . Pengguna berharap dapat menentukan sendiri urutan z dari sebagian besar aplikasi. Anda tidak tahu apa yang paling penting bagi mereka, jadi jika Anda mengubah sesuatu, Anda harus fokus untuk mendorong aplikasi lain di belakang semuanya daripada mempromosikan aplikasi Anda sendiri.

Ini tentu saja lebih rumit, karena Windows tidak memiliki pengelola jendela yang sangat canggih. Dua pendekatan menunjukkan diri mereka sendiri:

  1. enumerasi jendela tingkat atas dan memeriksa prosesnya , menghapus urutan z jika demikian . (Saya tidak yakin apakah ada metode kerangka kerja untuk fungsi WinAPI ini.)
  2. Mengotak-atik izin proses anak untuk mencegahnya mengakses desktop ... tetapi saya tidak akan mencoba ini sampai pendekatan lain gagal, karena proses anak mungkin berakhir dalam keadaan zombie sementara memerlukan interaksi pengguna.
Pontus Gagge
sumber
4

Mengapa tidak menjadikan formulir Anda sebagai kotak dialog:

myForm.ShowDialog();
Salim
sumber
1
Iya! Inilah yang saya inginkan. Pengaturan TopMost = truememaksa formulir saya di atas segalanya, termasuk chrome, padahal sebenarnya itu hanya kotak pengaturan dan saya membutuhkannya di atas formulir utama. Kudos to you internet person.
MDMoore313
3

Berikut adalah padanan SetForegroundWindow:

form.Activate();

Saya telah melihat orang-orang melakukan hal-hal aneh seperti:

this.TopMost = true;
this.Focus();
this.BringToFront();
this.TopMost = false;

http://blog.jorgearimany.com/2010/10/win32-setforegroundwindow-equivalent-in.html

pengguna2070066
sumber
Bagaimana jika saya tidak ingin jendela saya aktif, saya hanya menginginkannya paling atas (informatif, bukan interaktif)? Saya bertanya hanya karena sebenarnya mengeluarkan "paling atas = Benar" tidak berfungsi dalam kasus saya (ini berfungsi pada sistem, bukan pada orang lain).
Fhaab
Menemukan ini berhasil untuk kami: this.Show(); this.Activate(); this.BringToFront(); Tetapi jawaban ini membawa kami ke solusi itu. Terima kasih!
jibbs
1

Saya tahu ini sudah tua, tetapi saya tidak melihat tanggapan ini.

Di jendela (xaml) tambahkan:

Deactivated="Window_Deactivated"

Di kode di belakang untuk Window_Deactivated:

private void Window_Deactivated(object sender, EventArgs e)
    {
        Window window = (Window)sender;
        window.Activate();
    }

Ini akan membuat jendela Anda tetap di atas.

tunggu apa
sumber
1
Anda tidak melihat tanggapan ini karena pertanyaannya tentang winform.
Kinetik
0

Berdasarkan jawaban clamum , dan komentar Kevin Vuilleumier tentang bendera lain yang bertanggung jawab atas perilaku tersebut, saya membuat sakelar yang beralih antara on-top dan tidak on-top dengan menekan tombol.

private void button1_Click(object sender, EventArgs e)
    {
        if (on)
        {
            button1.Text = "yes on top";
            IntPtr HwndTopmost = new IntPtr(-1);
            SetWindowPos(this.Handle, HwndTopmost, 0, 0, 0, 0, TopmostFlags);
            on = false;
        }
        else
        {
            button1.Text = "not on top";
            IntPtr HwndTopmost = new IntPtr(-2);
            SetWindowPos(this.Handle, HwndTopmost, 0, 0, 0, 0, TopmostFlags);
            on = true;
        }
    }
Tóth Dániel
sumber