Показати форму без крадіжки фокусу?


140

Я використовую Форму для показу сповіщень (вона з’являється в нижній правій частині екрана), але коли я показую цю форму, вона викрадає фокус з головної Форми. Чи є спосіб показати цю форму "сповіщення" без крадіжки фокусу?

Відповіді:


165

Хммм, чи не просто переосмислити Form.ShowWithoutActivation досить?

protected override bool ShowWithoutActivation
{
  get { return true; }
}

Якщо ви також не хочете, щоб користувач натискав це вікно сповіщень, ви можете змінити команду CreateParams:

protected override CreateParams CreateParams
{
  get
  {
    CreateParams baseParams = base.CreateParams;

    const int WS_EX_NOACTIVATE = 0x08000000;
    const int WS_EX_TOOLWINDOW = 0x00000080;
    baseParams.ExStyle |= ( int )( WS_EX_NOACTIVATE | WS_EX_TOOLWINDOW );

    return baseParams;
  }
}

3
ShowWithoutActivation, Не можу повірити, я його не знайшов, витратив цілий день!
deerchao

2
Мені також потрібно було встановити, form1.Enabled = falseщоб внутрішні органи контролю не могли вкрасти фокус
Jader Dias

23
І залиште TopMost вимкненим.
mklein

4
А якщо ви хочете TopMost, дивіться іншу відповідь .
Роман Старков

2
Значення WS_EX_NOACTIVATEі WS_EX_TOOLWINDOWє, 0x08000000і 0x00000080відповідно.
Хуан

69

Викрадений з Pinvoke.net «s ShowWindow методи:

private const int SW_SHOWNOACTIVATE = 4;
private const int HWND_TOPMOST = -1;
private const uint SWP_NOACTIVATE = 0x0010;

[DllImport("user32.dll", EntryPoint = "SetWindowPos")]
static extern bool SetWindowPos(
     int hWnd,             // Window handle
     int hWndInsertAfter,  // Placement-order handle
     int X,                // Horizontal position
     int Y,                // Vertical position
     int cx,               // Width
     int cy,               // Height
     uint uFlags);         // Window positioning flags

[DllImport("user32.dll")]
static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);

static void ShowInactiveTopmost(Form frm)
{
     ShowWindow(frm.Handle, SW_SHOWNOACTIVATE);
     SetWindowPos(frm.Handle.ToInt32(), HWND_TOPMOST,
     frm.Left, frm.Top, frm.Width, frm.Height,
     SWP_NOACTIVATE);
}

(Алекс Лайман відповів на це, я просто розширюю його, безпосередньо вставляючи код. Хтось із правами редагування може скопіювати його там і видалити все, що мені цікаво;))


Мені було цікаво, чи він йому справді потрібен, якщо форма, яку він відображається в нижній лівій частині екрана, це в іншій нитці?
Патрік Дежардінс

50
Мені здається неймовірним, що нам все одно потрібно посилатися на зовнішні файли DLL, щоб взаємодіяти з формами. Ми в .NET Framework версії 4 !! Час обернути це Microsoft.
Мальтрап

9
Прийнята відповідь невірна. Шукайте ShowWithoutActivation
mklein

Просто додайте frm.Hide (); на початку функції ShowInactiveTopmost, якщо ви хочете, щоб форма не була зосереджена безпосередньо. Не забудьте: використання System.Runtime.InteropServices; щоб запустити цей код
Zitun

1
@Talha Цей код не має ніякого відношення до події Load. Подія Load запускається під час завантаження форми, а не під час її показу.
TheSoftwareJedi


12

Це те, що працювало для мене. Він забезпечує TopMost, але без крадіжки фокусу.

    protected override bool ShowWithoutActivation
    {
       get { return true; }
    }

    private const int WS_EX_TOPMOST = 0x00000008;
    protected override CreateParams CreateParams
    {
       get
       {
          CreateParams createParams = base.CreateParams;
          createParams.ExStyle |= WS_EX_TOPMOST;
          return createParams;
       }
    }

Не забудьте пропустити налаштування TopMost у дизайнера Visual Studio або в іншому місці.

Це вкрадено, помилково, запозичено звідси (натисніть на Обхідні шляхи):

https://connect.microsoft.com/VisualStudio/feedback/details/401311/showwithoutactivation-is-not-supported-with-topmost


1
Топ + нефокусований працює, і це виглядає як найчистіша з усіх відповідей.
феос

Найвище застаріло з часу Windows 8, де Microsoft карає вас за його використання. Ефект полягає в тому, що після відкриття самого верхнього вікна і після його закриття Windows переміщує інші вікна вашої програми на другий план. Це, звичайно, не бажана поведінка для вашої програми. Microsoft реалізувала це тому, що в минулому багато програмістів зловживали дуже настирливими вершинами. Верхній дуже агресивний. Я ніколи цим не користуюся.
Elmue

9

Робити це, здається, хак, але, здається, працює:

this.TopMost = true;  // as a result the form gets thrown to the front
this.TopMost = false; // but we don't actually want our form to always be on top

Редагувати: Зауважте, це просто піднімає вже створену форму без крадіжки фокусу.


Здається, тут не працює ... може бути, тому що ця "форма сповіщення" відкрита в іншому потоці?
Матіас

1
Можливо, у такому випадку вам потрібно зробити this.Invoke () виклик, щоб викликати метод ще раз як правильний потік. Як правило, робота з формами з неправильної нитки призводить до викиду виключень.
Меттью Шарлі

Хоча це і працює, це згадуваний метод, який викликав BSOD для мене за певних умов, тому остерігайтесь цього.
Рафаель Сміт

Найвище застаріло з часу Windows 8, де Microsoft карає вас за його використання. Ефект полягає в тому, що після відкриття самого верхнього вікна і після його закриття Windows переміщує інші вікна вашої програми на другий план. Це, звичайно, не бажана поведінка для вашої програми. Microsoft реалізувала це тому, що в минулому багато програмістів зловживали дуже настирливими вершинами. Верхній дуже агресивний. Я ніколи цим не користуюся.
Елмуе

9

Зразок коду з pinvoke.net у відповідях Алекса Лимана / TheSoftwareJedi зробить вікно "найвищим" вікном, тобто, ви не зможете помістити його за звичайні вікна після того, як воно спливе. Враховуючи опис Матіаса, для чого він хоче це використовувати, це може бути те, що він хоче. Але якщо ви хочете, щоб користувач міг поставити ваше вікно за іншими вікнами після того, як ви його вискочили, просто використовуйте HWND_TOP (0) замість HWND_TOPMOST (-1) у зразку.


6

У WPF ви можете вирішити це так:

У вікно помістіть ці атрибути:

<Window
    x:Class="myApplication.winNotification"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  Title="Notification Popup" Width="300" SizeToContent="Height"
  WindowStyle="None" AllowsTransparency="True" Background="Transparent" ShowInTaskbar="False" Topmost="True" Focusable="False" ShowActivated="False" >
</Window>

Останній атрибут - це той, який вам потрібен ShowActivate = "False".


4

У мене є щось подібне, і я просто показую форму сповіщення, а потім роблю

this.Focus();

щоб повернути фокус на основну форму.


Просто, але ефективно!
Том

3

Створіть та запустіть форму сповіщення в окремому потоці та поверніть фокус назад до основної форми після відкриття форми. Запропонуйте Формі сповіщення надати подію OnFormOpened, яку звільняють від Form.Shownподії. Щось на зразок цього:

private void StartNotfication()
{
  Thread th = new Thread(new ThreadStart(delegate
  {
    NotificationForm frm = new NotificationForm();
    frm.OnFormOpen += NotificationOpened;
    frm.ShowDialog();
  }));
  th.Name = "NotificationForm";
  th.Start();
} 

private void NotificationOpened()
{
   this.Focus(); // Put focus back on the original calling Form
}

Ви також можете тримати ручку об’єкта NotifcationForm навколо, щоб він міг бути програмно закритий головним Form ( frm.Close()).

Деякі деталі відсутні, але, сподіваємось, це приведе вас у правильне русло.


Це буде працювати лише в тому випадку, якщо ваша форма була початково активною формою. Цей вид суперечить основній меті такого роду повідомлення.
Алекс Лиман

1
Так? Це і є мета сповіщення - поставити його та відновити фокус назад до початково активної форми.
Боб Надлер

2
Це лише фокусує форму у вашій програмі - що робити, якщо якась інша програма на той час активна? Показати вікно сповіщень (як правило, щоб повідомити користувачеві про стан вашої програми), корисно лише тоді, коли він не переглядає вашу програму.
Алекс Лиман

3

Ви можете розглянути, яке саме повідомлення ви хочете відобразити.

Якщо абсолютно важливо повідомити користувачеві про якусь подію, використовуючи Messagebox.Показати рекомендованим способом було б через блокування будь-яких інших подій у головному вікні, поки користувач не підтвердить це. Однак пам’ятайте про спливаючу сліпоту.

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


2
- Підказки на повітряній кулі - це не варіант, тому що їх можна відключити - Панель стану може бути прихованою, якщо програма все одно зведена до мінімуму завдяки вашим рекомендаціям
Matías

3

Це добре працює.

Дивіться: OpenIcon - MSDN та SetForegroundWindow - MSDN

using System.Runtime.InteropServices;

[DllImport("user32.dll")]
static extern bool OpenIcon(IntPtr hWnd);

[DllImport("user32.dll")]
static extern bool SetForegroundWindow(IntPtr hWnd);

public static void ActivateInstance()
{
    IntPtr hWnd = IntPtr hWnd = Process.GetCurrentProcess().MainWindowHandle;

    // Restore the program.
    bool result = OpenIcon(hWnd); 
    // Activate the application.
    result = SetForegroundWindow(hWnd);

    // End the current instance of the application.
    //System.Environment.Exit(0);    
}

1

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

У всякому разі, я зіткнувся з цим і вирішив це, використовуючи властивість DateTime, щоб не дозволяти подальші дзвінки BringToFront, якщо дзвінки були зроблені недавно.

Припустимо базовий клас, 'Core', який обробляє, наприклад, три форми, 'Form1, 2 і 3'. Кожна форма потребує властивості DateTime та події Activate, що викликає Core, щоб вивести вікна на передній план:

internal static DateTime LastBringToFrontTime { get; set; }

private void Form1_Activated(object sender, EventArgs e)
{
    var eventTime = DateTime.Now;
    if ((eventTime - LastBringToFrontTime).TotalMilliseconds > 500)
        Core.BringAllToFront(this);
    LastBringToFrontTime = eventTime;
}

А потім створіть роботу в Core Class:

internal static void BringAllToFront(Form inForm)
{
    Form1.BringToFront();
    Form2.BringToFront();
    Form3.BringToFront();
    inForm.Focus();
}

У бічній примітці, якщо ви хочете відновити мінімізоване вікно до його початкового стану (не максимізованого), використовуйте:

inForm.WindowState = FormWindowState.Normal;

Знову ж таки, я знаю, що це лише патч-рішення у відсутності BringToFrontWithoutFocus. Це означає, як пропозиція, якщо ви хочете уникнути файлу DLL.


1

Я не знаю, чи розглядають це як некро-опублікування, але це те, що я робив з тих пір, як я не можу це працювати з методами "ShowWindow" та "SetWindowPos" user32. І ні, переорієнтування "ShowWithoutActivation" не працює в цьому випадку, оскільки нове вікно має бути завжди вгорі. У будь-якому випадку я створив хелперний метод, який приймає форму як параметр; коли дзвонить, він показує форму, виводить її на передню частину і робить її TopMost, не крадучи фокус поточного вікна (мабуть, так, але користувач цього не помітить).

    [DllImport("user32.dll")]
    static extern IntPtr GetForegroundWindow();

    [DllImport("user32.dll")]
    static extern IntPtr SetForegroundWindow(IntPtr hWnd);

    public static void ShowTopmostNoFocus(Form f)
    {
        IntPtr activeWin = GetForegroundWindow();

        f.Show();
        f.BringToFront();
        f.TopMost = true;

        if (activeWin.ToInt32() > 0)
        {
            SetForegroundWindow(activeWin);
        }
    }

0

Я знаю, що це може здатися дурним, але це спрацювало:

this.TopMost = true;
this.TopMost = false;
this.TopMost = true;
this.SendToBack();

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

0

Мені потрібно було це зробити зі своїм вікном TopMost. Я реалізував вищевказаний метод PInvoke, але виявив, що моя подія Load не називається вище, як Talha. Нарешті мені це вдалося. Можливо, це комусь допоможе. Ось моє рішення:

        form.Visible = false;
        form.TopMost = false;
        ShowWindow(form.Handle, ShowNoActivate);
        SetWindowPos(form.Handle, HWND_TOPMOST,
            form.Left, form.Top, form.Width, form.Height,
            NoActivate);
        form.Visible = true;    //So that Load event happens

-4

Коли ви створюєте нову форму, використовуючи

Form f = new Form();
f.ShowDialog();

він краде фокус, оскільки ваш код не може продовжувати виконувати основну форму, поки ця форма не буде закрита.

Виняток - використання нитки для створення нової форми, а потім Form.Show (). Переконайтеся, що нитка є всесвітньо видимою, тому що якщо ви оголосите її в межах функції, як тільки ваша функція завершиться, ваш потік закінчиться і форма зникне.


Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.