NET'te bir pencerenin her zaman zirvede kalması nasıl sağlanır?


97

Başka bir programda bir makro çalıştıran bir C # winforms uygulamam var. Diğer program sürekli olarak pencereler açacak ve genellikle daha iyi bir kelime olmadığı için her şeyi çılgınca gösterecektir. İşlemin çalışmasını durduracak bir iptal düğmesi uygulamak istiyorum, ancak pencerenin üstte kalmasını sağlayamıyorum. Bunu C # ile nasıl yaparım?

Düzenleme: TopMost = true; , ancak diğer program üstte kendi pencerelerini açmaya devam ediyor. Her n milisaniyede bir penceremi en üste göndermenin bir yolu var mı?

Düzenleme: Bunu çözme şeklim, üzerine çift tıklayarak işlemi iptal edecek bir sistem tepsisi simgesi eklemekti. Sistem tepsisi simgesinin üzeri örtülmez. Cevap veren herkese teşekkür ederim. Neden 'süper üstte' bir pencere olmadığına dair makaleyi okudum ... mantıksal olarak çalışmıyor.


65
Evet, birkaç milisaniyede bir Form.TopMost'unuzu true olarak ayarlayacak bir zamanlayıcı ayarlayın. Sonra, sadece ilginç kılmak için, "çılgın" program yüklendiğinde, Mortal Kombat "FIGHT!" :-P
BFree

2
Yorumunuzun komik olduğunu düşünebilir, kötü uygulamalarla alay edebileceğinizi düşünebilirsiniz. Benim sorunum, bir akış düzen paneliyle bir form üzerinde kayan bir bağlam menüsü oluşturmaktı. Bir akış düzeni paneli yalnızca Activate () yöntemini çağırırsanız kaydırılabilir, Focus () belirli durumlarda yeterli DEĞİLDİR. Sadece kaydıramayacaksın. Bu, özel en üste sahip olsa bile bağlam menüsünden odağı çalıyor = true! Mantıklı herhangi bir kişi, winform uygulamalarınızın MTAThread modunda çalışmasına izin vermenin ve çözümü basitleştiren her forma kendi iş parçacığını vermenin tanrısal bir uygulama olduğunu bildiği gibi:
Traubenfuchs

1
Bakın, bir şeytan: pastebin.com/sMJX0Yav Titremeden kusursuz çalışır ve uyku (1) onu ciddi performansı tüketmekten alıkoymak için yeterlidir. Bir bağlam menüsüne odaklanırken yine de görev yöneticisine kim bakar ki? Bağlam menüsü kapandığında, umarız boş istisna işleyicisine gider ve ölür. Yine de isDisposed bir mola oluşturabilirsiniz.
Traubenfuchs

Bu soruna çözümümü az önce burada yayınladım: stackoverflow.com/questions/2546566/…
kfn

@Traubenfuchs Yani çünkü Çapraz iplik operasyonu istisna başarısız olur. Bu çalışmalı.
mekb

Yanıtlar:


172

Form.TopMost diğer program en üstteki pencereleri oluşturmadıkça çalışacaktır.

Başka bir işlemin en üstteki yeni pencereleri tarafından kapatılmayan bir pencere yaratmanın bir yolu yoktur. Raymond Chen nedenini açıkladı .


12
2016 ve sonrasında yeni başlayanların bunu görmesi durumunda, deneyinForm.ActiveForm.TopMost
Devil's Advocate

1
@ScottBeeson: Bu iyi. Bu tekno dinamik dünyada güncellenmiş bilgi çok gereklidir. Teşekkürler:).
Sandeep Kushwah

50

WinForms uygulamamı "Her Zaman En Üstte" yapmak için arıyordum, ancak "TopMost" u ayarlamak benim için hiçbir şey yapmadı. Bunun mümkün olduğunu biliyordum çünkü WinAmp bunu yapıyor (diğer birçok uygulama ile birlikte).

Yaptığım şey "user32.dll" e bir çağrı yapmaktı. Bunu yapmaktan hiç çekinmedim ve harika çalışıyor. Zaten bu bir seçenek.

Önce, aşağıdaki ad alanını içe aktarın:

using System.Runtime.InteropServices;

Sınıf bildiriminize birkaç değişken ekleyin:

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 işlevi için prototip ekleyin:

[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);

Sonra kodunuzda (çağrıyı Form_Load () 'a ekledim), çağrıyı ekleyin:

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

Umarım yardımcı olur. Referans


2
Bu sadece WinForms uygulamaları için değil, aynı zamanda konsol pencereleri için de işe yarar . Güzel bul!
rojo

Güzel, bunun çalıştığını doğrulayabilirim Ama onu en üstte olmayacak şekilde nasıl değiştirebilirim? paylaşabileceğiniz bir HWND_BOTTOMMOST bayrağı var mı?
Mark

Bu en iyi yetenek ile varsayılan davranış arasında geçiş yapmak istiyorsanız iyi bir soru (örneğin, pencere davranışı için "Her zaman en üstte" onay kutunuz var). Tahminimce uygun bayraklarla bu mümkün olabilirdi. Varsayılan pencere davranışını tanımlayan doğru bayraklara sahipseniz (örneğin, SWP_DEFAULT = 0x0003), o zaman bu bayraklarla tekrar "SetWindowPos ()" çağırabilirsiniz. Ben sadece emin değilim; Bakmadım. Eğer yaparsanız bol şans ve birisi gelirse lütfen buraya ekleyin!
clamum

Bu her zamanki gibi tam ekran oyun modunda çalışmıyor
Sajitha Rathnayake

2
@Mark Evet, bir HWND_NOTOPMOST (= -2) bayrağı var. Bkz. Docs.microsoft.com/en-us/windows/win32/api/winuser/…
Kevin Vuilleumier

23

"Delirmek" derken, her pencerenin diğerinden odaklanmayı sürdürdüğünü kastediyorsanız, TopMost sorunu çözmeyecektir.

Bunun yerine şunu deneyin:

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

Bu, odak çalmadan 'çocuk' formunu gösterecektir. Çocuk formu, ebeveyn etkinleştirilse veya odaklansa bile üst formunun üstünde kalacaktır. Bu kod yalnızca sahip formunun içinden alt formun bir örneğini oluşturduysanız kolayca çalışır. Aksi takdirde, sahibi API'yi kullanarak ayarlamanız gerekebilir.


1
Çok teşekkür ederim tam olarak bunu kullandım ve mükemmel çalıştı!
AvetisG

1
Teşekkürler .. tam olarak aradığım şey
Sameera Kumarasingha

CalledForm.OwnerKendisine ( CalledForm) ayarlamak şunlara neden olur System.ArgumentException: 'Dairesel bir kontrol referansı yapılmıştır. Bir denetim kendisine ait olamaz veya kendisine üstlenilemez. '
mekb

2
Bu yüzden CalledForm yerine CallerForm kullanıyorsunuz :)
Jesper

16

Form.TopMost Ayarla


Denedim, bunu ... sürekli yapmak zorunda mıyım? 'Çılgın program' hemen
devreye giriyor

2
Hayır - formunuzu ayarlarsanız. TopMost = true, çalışmalıdır. "Çılgın" programın diyalogları da TopMost olarak ayarlanmış olmalıdır, bu durumda onu geçersiz kılamazsınız.
Reed Copsey

Adil bir dövüş değil. Teşekkür ederim.
jle

11

5 dakikalık anlık bir gecikme yaşadım ve formu tam olarak şu şekilde belirtmeyi unuttum:

  myformName.ActiveForm.TopMost = true;

Ama gerçekten istediğim şey BU!

  this.TopMost = true;

Benim için mükemmel çalıştı. eğer (checkBox1.Checked == true) {this.TopMost = true; } else {this.TopMost = false; }
yosh

6

Formları ayarlayın .TopMost özelliğini true olarak .

Muhtemelen her zaman bu şekilde bırakmak istemezsiniz: harici işleminiz başladığında ayarlayın ve bittiğinde geri koyun.


5

Bunu çözme şeklim, iptal seçeneği olan bir sistem tepsisi simgesi yapmaktı.


5

Aşağıdaki kod, pencerenin her zaman üstte kalmasını ve çerçevesiz olmasını sağlar.

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 ile aynı fikirde olun - programınızı en önde yapan nedir? Görünüşe göre bu aslında pek çok durumda işe yaramayan "en üstteki = doğru" ifadesi. Kodun geri kalanının tamamı soruna gerçekten cevap vermiyor.
Fhaab

4

Görünürlüğünü bastırmaya çalıştığınız diğer uygulama nedir? İstediğiniz etkiyi elde etmenin başka yollarını araştırdınız mı? Lütfen kullanıcılarınızı açıkladığınız gibi hileli davranışlara maruz bırakmadan önce bunu yapın: yapmaya çalıştığınız şey, bazı yaramaz sitelerin tarayıcı pencereleri ile yaptıklarına benziyor ...

En azından En Az Sürpriz kuralına uymaya çalışın . Kullanıcılar, çoğu uygulamanın z sırasını kendilerinin belirleyebilmeyi bekler. Onlar için neyin en önemli olduğunu bilmiyorsunuz, bu yüzden herhangi bir şeyi değiştirirseniz, kendi uygulamanızı tanıtmak yerine diğer uygulamayı her şeyin arkasına itmeye odaklanmalısınız.

Bu, elbette daha yanıltıcıdır, çünkü Windows özellikle karmaşık bir pencere yöneticisine sahip değildir. İki yaklaşım kendini gösteriyor:

  1. üst düzey pencereleri numaralandırmak ve hangi işleme ait olduklarını kontrol etmek , eğer öyleyse z sıralarını düşürmek . (Bu WinAPI işlevleri için çerçeve yöntemleri olup olmadığından emin değilim.)
  2. Masaüstüne erişimini engellemek için çocuk işlem izinleriyle uğraşmak ... ancak diğer yaklaşım başarısız olana kadar bunu denemem, çünkü çocuk süreç kullanıcı etkileşimi gerektirirken zombi durumuna geçebilir.

4

Neden formunuzu bir diyalog kutusu yapmıyorsunuz?

myForm.ShowDialog();

1
Evet! İstediğim buydu. Ayarlama TopMost = true, formumu krom dahil her şeyin üstüne zorladı, gerçekte sadece bir ayarlar kutusu ve ana formun üstünde ona ihtiyacım vardı. Şerefe internet insanı.
MDMoore313

3

İşte SetForegroundWindow eşdeğeri:

form.Activate();

İnsanların garip şeyler yaptığını gördüm:

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

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


Ya penceremin aktif olmasını istemiyorsam, sadece en üstte olmasını istiyorum (bilgilendirici, etkileşimli değil)? Sadece soruyorum çünkü aslında benim durumumda "en üstte = Doğru" olmak işe yaramıyor (diğerlerinde değil sistemlerde çalışıyor).
Fhaab

Bunu bizim için işe yarayacak şekilde this.Show(); this.Activate(); this.BringToFront(); buldum : Ama bu cevap bizi o çözüme ulaştırdı. Teşekkürler!
jibbs

1

Bunun eski olduğunu biliyorum ama bu yanıtı görmedim.

Pencerede (xaml) şunu ekleyin:

Deactivated="Window_Deactivated"

Window_Deactivated için arkasındaki kodda:

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

Bu, pencerenizi üstte tutacaktır.


1
Soru winform ile ilgili olduğu için bu yanıtı görmediniz.
Kinetic

0

Clamum'un cevabına ve Kevin Vuilleumier'in davranıştan sorumlu diğer bayrak hakkındaki yorumuna dayanarak, bir düğmeye basarak üstte ve üstte değil arasında geçiş yapan bu geçişi yaptım .

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;
        }
    }
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.