Làm thế nào để làm cho một cửa sổ luôn ở trên cùng trong .Net?


93

Tôi có một ứng dụng C # winforms chạy macro trong một chương trình khác. Chương trình khác sẽ liên tục bật lên các cửa sổ và nói chung là khiến mọi thứ trông như điên rồ. Tôi muốn triển khai nút hủy để ngăn quá trình chạy, nhưng dường như tôi không thể làm cho cửa sổ ở trên cùng. Làm cách nào để thực hiện việc này trong C #?

Chỉnh sửa: Tôi đã thử TopMost = true; , nhưng chương trình khác vẫn tiếp tục bật lên các cửa sổ của chính nó ở phía trên. Có cách nào để đưa cửa sổ của tôi lên đầu mỗi n mili giây không?

Chỉnh sửa: Cách tôi giải quyết vấn đề này là thêm một biểu tượng khay hệ thống sẽ hủy quá trình bằng cách nhấp đúp vào nó. Biểu tượng khay hệ thống không bị che đi. Cảm ơn tất cả những người đã trả lời. Tôi đã đọc bài báo về lý do tại sao không có cửa sổ 'siêu trên đầu' ... nó không hoạt động một cách hợp lý.


62
Có, hãy đặt bộ hẹn giờ cho mỗi vài mili giây sẽ đặt Biểu mẫu của bạn thành true. Sau đó, để tạo sự thú vị, khi chương trình "điên rồ" tải, hãy phát đoạn âm thanh từ Mortal Kombat "FIGHT!" :-P
BFree

2
Bạn có thể nghĩ rằng bình luận của bạn là vui nhộn, bạn có thể nghĩ rằng bạn có thể chế giễu hành động xấu. Vấn đề của tôi là tạo một menu ngữ cảnh nổi trên một biểu mẫu có bảng điều khiển luồng. Chỉ có thể cuộn bảng điều khiển luồng nếu bạn gọi đó là phương thức Activate (), Focus () là KHÔNG đủ trong một số trường hợp nhất định. Bạn sẽ không thể cuộn nó. Điều đó đánh cắp tiêu điểm từ menu ngữ cảnh ngay cả khi nó có độc quyền trên cùng = true! Như bất kỳ người nào nhạy cảm biết đó là thánh practive để cho các ứng dụng Winform của bạn chạy trong chế độ MTAThread và cung cấp cho mọi hình thức đó là chủ đề riêng mà làm cho giải pháp đơn giản:
Traubenfuchs

1
Kìa, một con quỷ: pastebin.com/sMJX0Yav Nó hoạt động hoàn hảo mà không nhấp nháy và chế độ ngủ (1) đủ để giữ cho nó không làm mất hiệu suất nghiêm trọng. Ai luôn nhìn vào trình quản lý tác vụ của mình trong khi anh ta tập trung vào menu ngữ cảnh? Khi menu ngữ cảnh đóng lại, nó hy vọng sẽ chạy vào trình xử lý ngoại lệ trống và chết. Tuy nhiên, bạn có thể xây dựng trong một break isDisposed.
Traubenfuchs

Tôi vừa đăng giải pháp của mình cho vấn đề này ở đây: stackoverflow.com/questions/2546566/…
kfn

@Traubenfuchs Điều đó sẽ không thành công vì ngoại lệ hoạt động Chéo. Điều này sẽ hoạt động.
mekb

Câu trả lời:


171

Form.TopMost sẽ hoạt động trừ khi chương trình khác đang tạo cửa sổ trên cùng.

Không có cách nào để tạo một cửa sổ không bị che bởi các cửa sổ trên cùng mới của quá trình khác. Raymond Chen giải thích tại sao.


10
Trong mọi trường hợp người mới hoàn toàn khác thấy điều này vào năm 2016 và xa hơn nữa, hãy thửForm.ActiveForm.TopMost
Advocate Devil

1
@ScottBeeson: Điều này tốt. Thông tin cập nhật là rất cần thiết trong thế giới năng động công nghệ này. Cảm ơn:).
Sandeep Kushwah

47

Tôi đã tìm cách đặt ứng dụng WinForms của mình "Luôn ở trên cùng" nhưng việc đặt "TopMost" không làm được gì đối với tôi. Tôi biết điều đó là có thể vì WinAmp thực hiện điều này (cùng với một loạt các ứng dụng khác).

Những gì tôi đã làm là thực hiện cuộc gọi tới "user32.dll." Tôi không hề e ngại khi làm như vậy và nó hoạt động rất tốt. Dù sao thì đó cũng là một lựa chọn.

Đầu tiên, nhập không gian tên sau:

using System.Runtime.InteropServices;

Thêm một vài biến vào khai báo lớp của bạn:

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;

Thêm nguyên mẫu cho hàm 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);

Sau đó, trong mã của bạn (tôi đã thêm cuộc gọi trong Form_Load ()), hãy thêm lệnh gọi:

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

Hy vọng rằng sẽ giúp. Tài liệu tham khảo


2
Điều này không chỉ hoạt động cho các ứng dụng WinForms mà còn cho các cửa sổ bảng điều khiển . Rất vui!
rojo

Tốt, có thể xác nhận điều này hoạt động. Nhưng làm thế nào tôi có thể thay đổi nó trở lại để không đứng đầu nhất? có cờ HWND_BOTTOMMOST mà bạn có thể chia sẻ không?
Đánh dấu

Câu hỏi hay, nếu bạn muốn hoán đổi giữa khả năng hàng đầu này và hành vi mặc định (ví dụ: bạn có hộp kiểm "Luôn ở trên cùng" cho hành vi cửa sổ). Tôi đoán rằng có thể với những lá cờ thích hợp, điều đó có thể xảy ra. Nếu bạn có các cờ phù hợp mô tả hành vi cửa sổ mặc định (giả sử SWP_DEFAULT = 0x0003), thì bạn có thể gọi lại "SetWindowPos ()" với các cờ này. Tôi không chắc lắm; Tôi đã không nhìn vào nó. Chúc may mắn nếu bạn làm, và nếu ai đó có, hãy thêm nó vào đây!
ngaoum

Đây không phải là làm việc trên chế độ màn hình trò chơi đầy đủ như bình thường
Sajitha Rathnayake

2
@Mark Có, có một cờ HWND_NOTOPMOST (= -2). Xem docs.microsoft.com/en-us/windows/win32/api/winuser/…
Kevin Vuilleumier

23

Nếu bạn "phát điên" có nghĩa là mỗi cửa sổ tiếp tục đánh cắp tiêu điểm từ cửa sổ khác, TopMost sẽ không giải quyết được vấn đề.

Thay vào đó, hãy thử:

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

Điều này sẽ hiển thị dạng 'con' mà nó không đánh cắp tiêu điểm. Biểu mẫu con cũng sẽ ở trên cha mẹ của nó ngay cả khi biểu mẫu cha được kích hoạt hoặc tập trung. Mã này chỉ hoạt động dễ dàng nếu bạn đã tạo một phiên bản của biểu mẫu con từ trong biểu mẫu chủ sở hữu. Nếu không, bạn có thể phải đặt chủ sở hữu bằng cách sử dụng API.


1
Cảm ơn bạn rất nhiều, tôi đã sử dụng chính xác cái này và nó hoạt động hoàn hảo!
AvetisG

1
Cảm ơn .. chính xác những gì tôi đang tìm kiếm
Sameera Kumarasingha

Đặt CalledForm.Ownerthành chính nó ( CalledForm) sẽ gây ra System.ArgumentException: 'Một tham chiếu điều khiển vòng tròn đã được thực hiện. Một điều khiển không thể được sở hữu bởi hoặc cha mẹ của chính nó. '
mekb

2
Đó là lý do tại sao bạn sử dụng CallerForm thay vì CalledForm :)
Jesper

16

Đặt Form.TopMost


Tôi đã thử, điều này ... tôi có cần phải liên tục làm không? 'Chương trình điên rồ' tiếp quản ngay lập tức ...
jle

2
Không - nếu bạn đặt biểu mẫu của mình.TopMost = true, nó sẽ hoạt động. Chương trình "điên rồ" cũng phải đặt hộp thoại của nó thành TopMost, trong trường hợp đó, bạn không thể ghi đè nó.
Reed Copsey

Không phải là một cuộc chiến công bằng. Cảm ơn bạn.
jle

11

Tôi đã có khoảng thời gian 5 phút trôi qua trong chốc lát và tôi đã quên chỉ định đầy đủ biểu mẫu như sau:

  myformName.ActiveForm.TopMost = true;

Nhưng những gì tôi thực sự muốn là NÀY!

  this.TopMost = true;

Làm việc hoàn hảo cho tôi. if (checkBox1.Checked == true) {this.TopMost = true; } else {this.TopMost = false; }
yosh

6

Đặt thuộc tính của biểu mẫu thành .TopMosttrue.

Bạn có thể không muốn để nó theo cách này mọi lúc: đặt nó khi quá trình bên ngoài của bạn bắt đầu và đặt nó trở lại khi nó kết thúc.


5

Cách tôi giải quyết vấn đề này là tạo biểu tượng khay hệ thống có tùy chọn hủy.


5

Đoạn mã sau làm cho cửa sổ luôn ở trên cùng cũng như làm cho nó không có khung.

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

Đồng ý với Alexan - điều gì khiến chương trình của bạn đứng đầu? Có vẻ như nó thực sự chỉ là câu lệnh "topmost = true", không hoạt động trong nhiều trường hợp. Tất cả phần còn lại của mã không thực sự trả lời được vấn đề.
Fhaab

4

Ứng dụng khác mà bạn đang cố gắng ngăn chặn khả năng hiển thị của nó là gì? Bạn đã tìm hiểu những cách khác để đạt được hiệu quả mong muốn của mình chưa? Vui lòng làm như vậy trước khi đẩy người dùng của bạn vào hành vi lừa đảo như bạn đang mô tả: những gì bạn đang cố gắng làm giống như những gì một số trang web nghịch ngợm làm với cửa sổ trình duyệt ...

Ít nhất hãy cố gắng tuân thủ quy tắc Ít bất ngờ . Người dùng mong đợi có thể tự xác định thứ tự z của hầu hết các ứng dụng. Bạn không biết điều gì là quan trọng nhất đối với họ, vì vậy nếu bạn thay đổi bất cứ điều gì, bạn nên tập trung vào việc thúc đẩy ứng dụng khác đằng sau mọi thứ hơn là quảng bá của riêng bạn.

Điều này tất nhiên là phức tạp hơn, vì Windows không có trình quản lý cửa sổ đặc biệt phức tạp. Hai cách tiếp cận tự gợi ý:

  1. liệt kê các cửa sổ cấp cao nhất và kiểm tra xem chúng thuộc quy trình nào , bỏ thứ tự z của chúng nếu có . (Tôi không chắc liệu có các phương thức khung cho các hàm WinAPI này hay không.)
  2. Đang loay hoay với các quyền của quy trình con để ngăn nó truy cập vào màn hình ... nhưng tôi sẽ không thử điều này cho đến khi cách tiếp cận này không thành công, vì quy trình con có thể kết thúc ở trạng thái zombie trong khi yêu cầu sự tương tác của người dùng.

4

Tại sao không đặt biểu mẫu của bạn thành một hộp thoại:

myForm.ShowDialog();

1
Đúng! Đây là những gì tôi muốn. Cài đặt TopMost = truebuộc biểu mẫu của tôi phải ở trên mọi thứ, bao gồm cả chrome, trong khi thực tế nó chỉ là một hộp cài đặt và tôi cần nó ở trên biểu mẫu chính. Kudos cho bạn internet người.
MDMoore313

3

Đây là tương đương SetForegroundWindow:

form.Activate();

Tôi đã thấy mọi người làm những điều kỳ lạ như:

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

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


Điều gì sẽ xảy ra nếu tôi không muốn cửa sổ của mình hoạt động, tôi chỉ muốn nó ở trên cùng (nhiều thông tin, không tương tác)? Tôi hỏi chỉ vì thực sự việc phát hành "topmost = True" không hoạt động trong trường hợp của tôi (nó hoạt động trên hệ thống, không phải trên hệ thống khác).
Fhaab

Tìm thấy điều này để làm việc cho chúng tôi: this.Show(); this.Activate(); this.BringToFront(); Nhưng câu trả lời này đã đưa chúng tôi đến giải pháp đó. Cảm ơn!
jibbs

1

Tôi biết điều này đã cũ, nhưng tôi không thấy phản hồi này.

Trong cửa sổ (xaml) thêm:

Deactivated="Window_Deactivated"

Trong mã đằng sau cho Window_Deactiised:

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

Điều này sẽ giữ cho cửa sổ của bạn ở trên cùng.


1
Bạn không thấy câu trả lời này vì câu hỏi là về winform.
Kinetic

1

Dựa trên câu trả lời của ngaoum và nhận xét của Kevin Vuilleumier về lá cờ khác chịu trách nhiệm cho hành vi, tôi đã thực hiện chuyển đổi này để chuyển đổi giữa ở trênkhông ở trên bằng một lần nhấn nút.

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;
        }
    }
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.