วิธีสร้างหน้าต่างให้อยู่ด้านบนเสมอใน. Net?


95

ฉันมีแอป C # winforms ที่เรียกใช้มาโครในโปรแกรมอื่น โปรแกรมอื่น ๆ จะแสดงหน้าต่างขึ้นมาอย่างต่อเนื่องและโดยทั่วไปจะทำให้สิ่งต่างๆดูไม่มีคำที่ดีกว่าบ้า ฉันต้องการใช้ปุ่มยกเลิกที่จะหยุดกระบวนการทำงาน แต่ดูเหมือนว่าฉันจะไม่สามารถให้หน้าต่างอยู่ด้านบนได้ ฉันจะทำสิ่งนี้ใน C # ได้อย่างไร

แก้ไข: ฉันได้ลอง TopMost = true; แต่โปรแกรมอื่น ๆ จะเปิดหน้าต่างของตัวเองขึ้นด้านบน มีวิธีส่งหน้าต่างของฉันไปด้านบนทุก ๆ มิลลิวินาทีหรือไม่?

แก้ไข: วิธีที่ฉันแก้ปัญหานี้คือการเพิ่มไอคอนถาดระบบซึ่งจะยกเลิกกระบวนการโดยดับเบิลคลิกที่ไอคอน ไอคอนถาดระบบไม่ได้รับการปกปิด ขอบคุณทุกคนที่ตอบกลับ ฉันอ่านบทความเกี่ยวกับสาเหตุที่ไม่มีหน้าต่าง 'super-on-top' ... มันไม่ทำงานตามเหตุผล


63
ใช่ตั้งเวลาทุกๆสองสามมิลลิวินาทีซึ่งจะตั้งค่า Form.TopMost ของคุณเป็น true จากนั้นเพื่อให้น่าสนใจเมื่อโหลดโปรแกรม "บ้า" ให้เล่นคลิปเสียงจาก Mortal Kombat "FIGHT!" :-P
BFree

2
คุณอาจคิดว่าความคิดเห็นของคุณเป็นเรื่องตลกคุณอาจคิดว่าคุณสามารถเยาะเย้ยการปฏิบัติที่ไม่ดี ปัญหาของฉันคือการสร้างเมนูบริบทที่ลอยอยู่เหนือแบบฟอร์มด้วยโฟลว์เลย์เอาต์พาเนล Flowlayoutpanel สามารถเลื่อนได้ก็ต่อเมื่อคุณเรียกมันว่า Activate () method, Focus () ไม่เพียงพอในบางสถานการณ์ คุณจะไม่สามารถเลื่อนได้ ที่ขโมยโฟกัสจากเมนูบริบทแม้ว่าจะมีเอกสิทธิ์สูงสุด = true! ในฐานะที่เป็นบุคคลที่มีสติสัมปชัญญะรู้ดีว่าการปล่อยให้แอปพลิเคชัน winform ของคุณทำงานในโหมด MTAThread และให้ทุกรูปแบบเป็นเธรดของตัวเองซึ่งทำให้การแก้ปัญหาง่ายขึ้น:
Traubenfuchs

1
ดูเถิดปีศาจ: pastebin.com/sMJX0Yavมันทำงานได้อย่างไม่มีที่ติโดยไม่มีการกะพริบและการนอนหลับ (1) ก็เพียงพอที่จะป้องกันไม่ให้มันหมดประสิทธิภาพอย่างจริงจัง ใครยังคงมองหาคนงานในขณะที่เขามุ่งเน้นไปที่เมนูบริบท เมื่อเมนูบริบทปิดลงหวังว่าจะเข้าสู่ตัวจัดการข้อยกเว้นที่ว่างเปล่าและตาย คุณอาจสร้างในตัวแบ่ง isDisposed
Traubenfuchs

ฉันเพิ่งโพสต์วิธีแก้ปัญหานี้ไว้ที่นี่: stackoverflow.com/questions/2546566/…
kfn

@Traubenfuchs ที่จะล้มเหลวเนื่องจากข้อยกเว้นการดำเนินการข้ามเธรด สิ่งนี้ควรใช้งานได้
mekb

คำตอบ:


172

Form.TopMost จะทำงานได้เว้นแต่โปรแกรมอื่นจะสร้างหน้าต่างที่อยู่บนสุด

ไม่มีวิธีใดในการสร้างหน้าต่างที่ไม่ได้ถูกปิดทับด้วยหน้าต่างใหม่บนสุดของกระบวนการอื่น Raymond Chen อธิบายว่าทำไม


11
ในกรณีที่มือใหม่คนอื่น ๆ เห็นสิ่งนี้ในปี 2559 และหลังจากนั้นให้ลองForm.ActiveForm.TopMost
Devil's Advocate

1
@ScottBeeson: นี่คือสิ่งที่ดี ข้อมูลที่อัปเดตเป็นสิ่งที่จำเป็นมากในโลกแห่งเทคโนไดนามิกนี้ ขอบคุณ :).
Sandeep Kushwah

49

ฉันกำลังค้นหาเพื่อสร้างแอปพลิเคชัน WinForms ของฉัน "อยู่ด้านบนเสมอ" แต่การตั้งค่า "TopMost" ไม่ได้ทำอะไรให้ฉันเลย ฉันรู้ว่ามันเป็นไปได้เพราะ WinAmp ทำสิ่งนี้ (พร้อมกับโฮสต์ของแอปพลิเคชันอื่น ๆ )

สิ่งที่ฉันทำคือโทรไปที่ "user32.dll" ฉันไม่มีความมั่นใจเกี่ยวกับการทำเช่นนั้นและได้ผลดี มันเป็นตัวเลือกอย่างไรก็ตาม

ขั้นแรกให้นำเข้าเนมสเปซต่อไปนี้:

using System.Runtime.InteropServices;

เพิ่มตัวแปรสองสามตัวในการประกาศคลาสของคุณ:

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;

เพิ่มต้นแบบสำหรับฟังก์ชัน 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);

จากนั้นในรหัสของคุณ (ฉันเพิ่มการโทรใน Form_Load ()) ให้เพิ่มการโทร:

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

หวังว่าจะช่วยได้ ข้อมูลอ้างอิง


2
งานนี้ไม่เพียง แต่สำหรับการใช้งาน WinForms แต่ยังสำหรับคอนโซลหน้าต่าง พบดี!
rojo

ดีสามารถยืนยันการทำงานนี้ แต่ฉันจะเปลี่ยนกลับไม่ให้เป็นอันดับต้น ๆ ได้อย่างไร มีการตั้งค่าสถานะ HWND_BOTTOMMOST ที่คุณสามารถแบ่งปันได้หรือไม่
มาร์ค

เป็นคำถามที่ดีหากคุณต้องการสลับระหว่างความสามารถสูงสุดนี้กับพฤติกรรมเริ่มต้น (ตัวอย่างเช่นคุณมีช่องทำเครื่องหมาย "อยู่ด้านบนเสมอ" สำหรับลักษณะการทำงานของหน้าต่าง) ฉันเดาว่าอาจจะมีธงที่เหมาะสมก็เป็นไปได้ หากคุณมีแฟล็กที่ถูกต้องซึ่งอธิบายลักษณะการทำงานของหน้าต่างเริ่มต้น (พูด SWP_DEFAULT = 0x0003) คุณสามารถเรียก "SetWindowPos ()" อีกครั้งโดยใช้แฟล็กเหล่านี้ ฉันไม่แน่ใจ ฉันยังไม่ได้ดู ขอให้โชคดีและถ้ามีใครทำกรุณาเพิ่มที่นี่!
clamum

สิ่งนี้ไม่ทำงานในโหมดเกมแบบเต็มหน้าจอตามปกติ
Sajitha Rathnayake

2
@Mark ใช่มีธง HWND_NOTOPMOST (= -2) ดูdocs.microsoft.com/en-us/windows/win32/api/winuser/…
Kevin Vuilleumier

23

หาก "บ้า" หมายความว่าแต่ละหน้าต่างยังคงขโมยโฟกัสจากอีกหน้าต่าง TopMost จะไม่สามารถแก้ปัญหาได้

ให้ลอง:

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

สิ่งนี้จะแสดงรูปแบบ "เด็ก" โดยไม่ต้องขโมยโฟกัส แบบฟอร์มย่อยจะยังคงอยู่ด้านบนสุดของผู้ปกครองแม้ว่าผู้ปกครองจะเปิดใช้งานหรือเน้น รหัสนี้จะใช้งานได้ง่ายก็ต่อเมื่อคุณได้สร้างอินสแตนซ์ของฟอร์มลูกจากในฟอร์มเจ้าของ มิฉะนั้นคุณอาจต้องตั้งค่าเจ้าของโดยใช้ API


1
ขอบคุณมากที่ได้ใช้สิ่งนี้และทำงานได้อย่างสมบูรณ์แบบ!
AvetisG

1
ขอบคุณ .. สิ่งที่ฉันกำลังมองหา
Sameera Kumarasingha

การตั้งค่าCalledForm.Ownerเป็นตัวมันเอง ( CalledForm) จะทำให้เกิด a System.ArgumentException: 'มีการอ้างอิงการควบคุมแบบวงกลม การควบคุมไม่สามารถเป็นเจ้าของหรือเลี้ยงดูตัวเองได้ '
mekb

2
นั่นคือเหตุผลที่คุณใช้ CallerForm แทน CallingForm :)
Jesper

16

ตั้งค่าForm.TopMost


ฉันพยายามแล้วนี่ ... ฉันต้องทำอย่างต่อเนื่องหรือไม่? 'โปรแกรมบ้าๆ' เข้าครอบงำทันที ...
jle

2
ไม่ - ถ้าคุณตั้งค่าแบบฟอร์ม TopMost = true ก็ควรใช้งานได้ โปรแกรม "บ้า" จะต้องมีการตั้งค่ากล่องโต้ตอบเป็น TopMost ด้วยซึ่งในกรณีนี้คุณจะไม่สามารถแทนที่ได้
Reed Copsey

ไม่ใช่การต่อสู้ที่ยุติธรรม ขอบคุณ.
jle

11

ฉันมีเวลา 5 นาทีผ่านไปชั่วขณะและฉันลืมระบุแบบฟอร์มแบบเต็มดังนี้:

  myformName.ActiveForm.TopMost = true;

แต่สิ่งที่ฉันต้องการจริงๆคือสิ่งนี้!

  this.TopMost = true;

ทำงานได้สมบูรณ์แบบสำหรับฉัน ถ้า (checkBox1.Checked == จริง) {this.TopMost = true; } else {this.TopMost = false; }
yosh

6

ตั้งค่า.TopMostคุณสมบัติของฟอร์มเป็นจริง

คุณอาจไม่ต้องการปล่อยไว้แบบนี้ตลอดเวลา: ตั้งค่าเมื่อกระบวนการภายนอกของคุณเริ่มต้นและนำกลับมาใช้ใหม่เมื่อเสร็จสิ้น


5

วิธีที่ฉันแก้ไขคือการสร้างไอคอนถาดระบบที่มีตัวเลือกยกเลิก


5

รหัสต่อไปนี้ทำให้หน้าต่างอยู่ด้านบนเสมอและทำให้เป็นแบบไร้กรอบ

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

เห็นด้วยกับ Alexan - อะไรที่ทำให้โปรแกรมของคุณอยู่ในอันดับต้น ๆ ดูเหมือนว่าจริงๆแล้วเป็นเพียงคำสั่ง "topmost = true" ซึ่งไม่ได้ผลในหลาย ๆ กรณี โค้ดที่เหลือทั้งหมดไม่สามารถตอบปัญหาได้อย่างแท้จริง
Fhaab

4

แอปพลิเคชันอื่นที่คุณพยายามระงับการมองเห็นคืออะไร? คุณได้ตรวจสอบวิธีอื่น ๆ ในการบรรลุผลตามที่คุณต้องการหรือไม่? โปรดทำเช่นนั้นก่อนที่จะทำให้ผู้ใช้ของคุณมีพฤติกรรมโกงดังที่คุณกำลังอธิบาย: สิ่งที่คุณพยายามทำให้ดูเหมือนว่าไซต์ซนบางอย่างทำกับหน้าต่างเบราว์เซอร์ ...

อย่างน้อยก็พยายามที่จะยึดมั่นในกฎของความประหลาดใจน้อยที่สุด ผู้ใช้คาดหวังว่าจะสามารถกำหนดลำดับ z ของแอปพลิเคชันส่วนใหญ่ได้ด้วยตนเอง คุณไม่รู้ว่าอะไรสำคัญที่สุดสำหรับพวกเขาดังนั้นหากคุณเปลี่ยนแปลงสิ่งใดคุณควรมุ่งเน้นไปที่การผลักดันแอปพลิเคชันอื่น ๆ ที่อยู่เบื้องหลังทุกสิ่งแทนที่จะโปรโมตของคุณเอง

แน่นอนว่านี่เป็นเรื่องยากกว่าเนื่องจาก Windows ไม่มีตัวจัดการหน้าต่างที่ซับซ้อนเป็นพิเศษ สองแนวทางแนะนำตัวเอง:

  1. ระบุหน้าต่างระดับบนสุด และตรวจสอบว่าเป็นของกระบวนการใดปล่อย z-order ถ้าเป็นเช่นนั้น (ฉันไม่แน่ใจว่ามีวิธีการเฟรมเวิร์กสำหรับฟังก์ชัน WinAPI เหล่านี้หรือไม่)
  2. เล่นซอกับสิทธิ์กระบวนการย่อยเพื่อป้องกันไม่ให้เข้าถึงเดสก์ท็อป ... แต่ฉันจะไม่ลองทำเช่นนี้จนกว่าวิธีการอื่นจะล้มเหลวเนื่องจากกระบวนการย่อยอาจอยู่ในสถานะซอมบี้ในขณะที่ต้องการการโต้ตอบกับผู้ใช้

4

ทำไมไม่สร้างแบบฟอร์มของคุณเป็นกล่องโต้ตอบ:

myForm.ShowDialog();

1
ใช่ นี่คือสิ่งที่ฉันต้องการ การตั้งค่าTopMost = trueบังคับให้ฟอร์มของฉันอยู่เหนือทุกสิ่งรวมถึงโครเมี่ยมเมื่อความเป็นจริงมันเป็นเพียงกล่องการตั้งค่าและฉันต้องการให้มันอยู่ด้านบนของฟอร์มหลัก ขอชื่นชมคุณคนอินเทอร์เน็ต
MDMoore313

3

นี่คือค่าเทียบเท่า SetForegroundWindow:

form.Activate();

ฉันเคยเห็นคนทำอะไรแปลก ๆ เช่น:

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

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


จะเกิดอะไรขึ้นถ้าฉันไม่ต้องการให้หน้าต่างของฉันใช้งานได้ฉันแค่ต้องการให้หน้าต่างนั้นอยู่บนสุด (ให้ข้อมูลไม่ใช่แบบโต้ตอบ) ฉันถามเพียงเพราะจริงๆแล้วการออก "topmost = True" นั้นใช้ไม่ได้ในกรณีของฉัน (ใช้ได้กับระบบไม่ใช่กับระบบอื่น)
Fhaab

พบว่าสิ่งนี้ใช้ได้ผลสำหรับเรา: this.Show(); this.Activate(); this.BringToFront(); แต่คำตอบนี้ทำให้เราได้รับคำตอบ ขอบคุณ!
jibbs

1

ฉันรู้ว่านี่เก่า แต่ฉันไม่เห็นคำตอบนี้

ในหน้าต่าง (xaml) เพิ่ม:

Deactivated="Window_Deactivated"

ในโค้ดด้านหลังสำหรับ Window_Deactivated:

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

เพื่อให้หน้าต่างอยู่ด้านบน


1
คุณไม่เห็นคำตอบนี้เนื่องจากคำถามเกี่ยวกับ winform
Kinetic

0

จากคำตอบของ clamumและความคิดเห็นของ Kevin Vuilleumierเกี่ยวกับการตั้งค่าสถานะอื่น ๆ ที่รับผิดชอบต่อพฤติกรรมนี้ฉันได้ทำการสลับที่สลับระหว่างด้านบนและด้านบนด้วยการกดปุ่ม

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;
        }
    }
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.