2008-10-01 4 views
125

Я использую форму для отображения уведомлений (она появляется в правом нижнем углу экрана), но когда я показываю эту форму, она крадет фокус с основной формы. Есть ли способ показать эту форму «уведомления» без кражи фокуса?Показать форму без кражи фокуса?

ответ

148

Хммм, не просто переопределяет форму. Показывает, что недостаточно?

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

Edit: Обратите внимание, это только поднимает уже созданную форму без кражи фокуса.

+0

, похоже, не работает здесь ... может быть, потому что эта «уведомляющая форма» открывается в другом потоке? – 2008-10-01 13:04:32

+1

Возможно, в этом случае вам нужно выполнить вызов this.Invoke(), чтобы снова вызвать метод как правый поток. Обычно работа с формами из неправильного потока вызывает исключение, которое может быть выбрано. – 2008-10-02 04:34:17

+0

Хотя это действительно работает, это хакерский метод, как упоминалось, и вызвал BSOD для меня при определенных условиях, поэтому остерегайтесь этого. – 2014-02-11 06:12:34

14

Если вы хотите использовать Win32P/Invoke, то вы можете использовать метод ShowWindow (первый пример кода делает именно то, что вы хотите).

-3

При создании новой формы с помощью

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

он крадет фокус, потому что ваш код не может продолжать выполнение на главной форме, пока эта форма не будет закрыта.

Исключением является использование потоков для создания новой формы, а затем Form.Show(). Убедитесь, что поток глобально виден, потому что, если вы объявите его в функции, как только ваша функция выйдет, ваш поток закончится, и форма исчезнет.

3

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

Если абсолютно необходимо сообщить пользователю о каком-либо событии, используя Messagebox.Show будет рекомендованным способом, поскольку его природа блокирует любые другие события в главном окне, пока пользователь не подтвердит это. Однако помните о всплывающей слепоте.

Если это критическое значение, вы можете использовать альтернативный способ отображения уведомлений, например панели инструментов в нижней части окна. Вы написали, что вы показываете уведомления в правом нижнем углу экрана - стандартный способ сделать это будет использовать balloon tip с комбинацией значка system tray.

+2

- подсказки с шариками не являются опцией, потому что могут быть отключены. - Панель состояния может быть скрыта, если у вас есть программа сведена к минимуму. В любом случае спасибо за ваши рекомендации – 2008-10-01 12:59:22

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 вокруг так, что он может быть программно закрыт основной формой (frm.Close()).

Некоторые детали отсутствуют, но, надеюсь, это поможет вам двигаться в правильном направлении.

+0

Это будет работать, только если ваша форма была изначально активной формы. Такой подход противоречит основной цели такого уведомления. – 2008-10-01 03:35:28

+1

А? Такова цель уведомления - поднять его и вернуть фокус обратно в первоначально активную форму. – 2008-10-01 03:39:25

+2

Это только дает фокус форме в вашем приложении - что, если какая-либо другая программа активна в то время? Отображение окна уведомления (как правило, чтобы дать пользователю обновление статуса вашего приложения) действительно полезно, когда они не смотрят ваше приложение. – 2008-10-01 15:05:57

65

Похищенные из PInvoke.net «ы 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); 
} 

(Alex Лиман ответил на этот вопрос, я просто расширение его непосредственно вставить код Кто-то с правами редактирования можно скопировать его там и удалить это для. все, что мне очень интересно;))

+0

Мне было интересно, ему действительно нужно, чтобы, если форма, которую он отображает в левом нижнем углу экрана, находится в другой теме? – 2008-10-01 14:23:49

+0

резьба не имеет отношения к получению фокуса. – TheSoftwareJedi 2008-10-01 16:01:52

+46

Мне кажется невероятным, что нам все равно нужно связать внешние файлы DLL для взаимодействия с формами. Мы в .NET Framework версии 4! Время обернуть его Microsoft. – Maltrap 2009-06-22 03:21:30

8

Образец кода от pinvoke.net в ответах Alex Lyman/TheSoftwareJedi сделает окно «самым верхним» окном, что означает, что после его появления вы не сможете поместить его в нормальные окна. Учитывая описание Матиаса того, что он хочет использовать, это может быть то, что он хочет.Но если вы хотите, чтобы пользователь мог поместить ваше окно за другие окна после того, как вы его всплыли, просто используйте HWND_TOP (0) вместо HWND_TOPMOST (-1) в образце.

2

Это хорошо работает:

[System.Runtime.InteropServices.DllImport("user32")] 

public static extern long OpenIcon(long hwnd); 

[System.Runtime.InteropServices.DllImport("user32")] 

public static extern long SetForegroundWindow(long hwnd); 


public static void ActivateInstance() 
{ 

    long MyHndl = 0; 

    long result = 0; 

    Process objProcess = Process.GetCurrentProcess(); 

    MyHndl = objProcess.MainWindowHandle.ToInt32(); 

    result = OpenIcon(MyHndl); // Restore the program. 

    result = SetForegroundWindow(MyHndl); // Activate the application. 

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

} 
0

Я знаю, что это может звук был глупым, но это сработало:

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

у меня есть что-то подобное, и я просто показать форму уведомления, а затем сделать

this.Focus(); 

довести фокус обратно на основной форме.

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> 

Последний один атрибут является один вам нужно ShowActivated = «False».

-4

Показанный: window.WindowState = WindowState.Minimized;.

2

может обрабатывать его только по логике, хотя я должен признать, что предложения, приведенные выше, где вы заканчиваете метод BringToFront без фактического кражи фокуса, являются самыми изящными.

Как бы то ни было, я столкнулся с этим и решил его с помощью свойства DateTime, чтобы не допустить дальнейших вызовов BringToFront, если вызовы были сделаны уже недавно.

Предположим, что основной класс «Core», который обрабатывает, например, три формы: «Form1, 2 и 3». Каждая форма должна свойство DateTime и событие Activate, что называют основной принести окна на фронт:

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

А затем создать работу в Основной класс:

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

На стороне записки, если вы чтобы восстановить свернутое окно в исходное состояние (не развернуто), использование:

inForm.WindowState = FormWindowState.Normal; 

Опять же, я знаю, что это просто патч решение в отсутствие BringToFrontWithoutFocus. Это означает, что вы хотите избежать DLL-файла.

8

Это то, что сработало для меня. Он обеспечивает 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

Я не знаю, если это рассматривается как некро-проводка, но это то, что я сделал так Я не могу заставить его работать с методами «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

Мне нужно было сделать это с моим окном 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 
-1

Если это не имеет значения, если первая форма теряет фокус менее 1 секунды, вы можете попробовать это.

private void Form2_Load(object sender, EventArgs e) 
{ 
Form1 f1 = new Form1(); 
f1.focus(); 
} 

Так что, когда form2 показывает, он будет сфокусирован сразу на form1.