Як змусити вікно завжди залишатися вгорі в .Net?


93

У мене є програма C # winforms, яка запускає макрос в іншій програмі. Інша програма постійно з’являтиметься у вікнах і загалом змушуватиме речі виглядати, через відсутність кращого слова, шаленими. Я хочу застосувати кнопку скасування, яка зупинить процес, але не здається, що вікно залишається вгорі. Як це зробити в C #?

Редагувати: я спробував TopMost = true; , але інша програма постійно вискакує власні вікна зверху. Чи є спосіб відправити своє вікно вгору кожні n мілісекунд?

Редагувати: Я вирішив це шляхом додавання значка в системній області, який скасує процес подвійним клацанням на ньому. Значок системного лотка не закривається. Дякую усім, хто відгукнувся. Я прочитав статтю про те, чому немає вікна "зверху зверху" ... це логічно не працює.


62
Так, встановіть таймер на кожні кілька мілісекунд, який встановлюватиме для Form.TopMost значення true. Потім, щоб було цікаво, коли завантажується "божевільна" програма, відтворіть аудіокліп із Mortal Kombat "БОЙ!" :-P
BFree

2
Вам може здатися, що ваш коментар був веселим, ви могли подумати, що можете висміяти погану практику. Моя проблема полягала в створенні контекстного меню, яке плаває над формою за допомогою панелі потоку. Панель потокової розгортки можна прокрутити, лише якщо ви викликаєте її метод Activate (), Focus () НЕ достатньо за певних обставин. Ви просто не зможете прокрутити його. Це викрадає фокус із контекстного меню, навіть якщо воно має ексклюзивний topmost = true! Як будь-яка розсудлива людина знає, благочестиво дозволити вашим програмам winform працювати в режимі MTAThread і надавати кожній формі власну тему, що робить рішення простим:
Traubenfuchs

1
Ось диявол: pastebin.com/sMJX0Yav Він працює бездоганно, не мерехтить, і сну (1) достатньо, щоб уникнути серйозних результатів. Хто все одно продовжує дивитись у своєму диспетчері завдань, поки він зосереджується на контекстному меню? Після закриття контекстного меню воно, сподіваємось, запускається до порожнього обробника винятків і вмирає. Однак ви можете вбудувати перерву isDisposed.
Траубенфукс

Я тільки що відправив моє рішення цієї проблеми тут: stackoverflow.com/questions/2546566 / ...
к.ф.н.

@Traubenfuchs Це не вдасться через виняток міжпотокової роботи. Це має спрацювати.
mekb

Відповіді:


171

Form.TopMost буде працювати, якщо інша програма не створює найвищі вікна.

Немає можливості створити вікно, яке не охоплюється новими верхніми вікнами іншого процесу. Раймонд Чен пояснив, чому.


10
Якщо будь-який інший повний новачок побачить це у 2016 році та пізніше, спробуйтеForm.ActiveForm.TopMost
адвокат диявола

1
@ScottBeeson: Це добре. Оновлена ​​інформація дуже необхідна в цьому техно-динамічному світі. Дякую:).
Сандіп Кушва

47

Я шукав, щоб зробити свою програму 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/…
Кевін Вюльєм'є

23

Якщо під "збожеволінням" ви маєте на увазі, що кожне вікно продовжує красти фокус у іншого, TopMost не вирішить проблему.

Натомість спробуйте:

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

Це покаже форму "дочірній", не викрадаючи фокус. Дочірня форма також залишатиметься над батьківською, навіть якщо батьківська активована або зосереджена. Цей код працює легко, лише якщо ви створили екземпляр дочірньої форми з форми власника. В іншому випадку, можливо, доведеться встановити власника за допомогою API.


1
Щиро дякую, я використав саме це, і це спрацювало чудово!
AvetisG

1
Дякую .. саме те, що я шукав
Самеера Кумарасінга

Встановлення CalledForm.Ownerдля себе ( CalledForm) спричинить System.ArgumentException: "Зроблено кругове контрольне посилання. Елемент керування не може належати самому собі або бути переданим йому у батьківство.
mekb

2
Ось чому ви використовуєте CallerForm замість CalledForm :)
Jesper

16

Встановити форму. TopMost


Я спробував, це ... мені потрібно постійно це робити? "Божевільна програма" починає негайно ...
jle

2
Ні - якщо ви встановите форму. TopMost = true, це має спрацювати. У "божевільній" програмі також повинні бути встановлені діалогові вікна TopMost, і в цьому випадку ви не можете їх замінити.
Рід Копсі,

Не чесна боротьба. Дякую.
jle

11

У мене був щомісячний 5-хвилинний проміжок часу, і я забув вказати форму повністю так:

  myformName.ActiveForm.TopMost = true;

Але що я справді хотів, це ЦЕ!

  this.TopMost = true;

Працював ідеально для мене. if (checkBox1.Checked == true) {this.TopMost = true; } else {this.TopMost = false; }
йош

6

Встановіть для .TopMostвластивості форми значення true.

Ви, мабуть, не хочете залишати це так постійно: встановіть його, коли починається ваш зовнішній процес, і поверніть його назад, коли він закінчиться.


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

Погодьтеся з Алексаном - що робить вашу програму найвищою? Здається, що це насправді лише твердження "topmost = true", яке не працює у багатьох випадках. Решта коду насправді не відповідає на проблему.
Fhaab

4

Яка ще програма, яку ви намагаєтеся придушити видимість? Чи досліджували ви інші способи досягнення бажаного ефекту? Будь ласка, зробіть це, перш ніж піддавати своїх користувачів такій неправдивій поведінці, яку ви описуєте: те, що ви намагаєтесь зробити, схоже на те, що певні неслухняні сайти роблять з вікнами браузера ...

Принаймні, спробуйте дотримуватися правила найменшого сюрпризу . Користувачі очікують, що зможуть самостійно визначити z-порядок більшості програм. Ви не знаєте, що для них найважливіше, тому, якщо ви щось змінюєте, вам слід зосередитись на тому, щоб підштовхувати іншу програму за все, а не рекламувати свою.

Це, звичайно, складніше, оскільки Windows не має особливо складного диспетчера вікон. Два підходи пропонують самі:

  1. перераховуючи вікна верхнього рівня та перевіряючи, до якого процесу вони належать , відмовляючись від їх z-порядку . (Я не впевнений, чи існують фреймворкові методи для цих функцій 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

1

На підставі clamum в відповідь, і Кевін 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;
        }
    }
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.