포커스를 훔치지 않고 양식을 표시 하시겠습니까?


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를 원한다면 다른 답변을 참조하십시오 .
Roman Starkov

2
의 값 WS_EX_NOACTIVATEWS_EX_TOOLWINDOW있다 0x080000000x00000080각각.
Juan Juan

69

PInvoke.netShowWindow 메소드 에서 도난당했습니다 .

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 Lyman이 대답했습니다. 코드를 직접 붙여 넣어 코드를 확장하고 있습니다. 편집 권한을 가진 사람이 코드를 복사하여 삭제할 수 있습니다.)


궁금합니다. 화면 왼쪽 하단에 표시되는 양식이 다른 스레드에 있어야합니까?
Patrick Desjardins

50
양식과 상호 작용하려면 여전히 외부 DLL 파일에 연결해야한다는 것이 믿어지지 않습니다. 우리는 .NET framework 버전 4에 있습니다! 그것을 감싸는 시간.
Maltrap

9
수락 된 답변이 잘못되었습니다. ShowWithoutActivation를 찾으십시오
mklein

frm.Hide ()를 추가하십시오 . 폼의 초점을 직접 맞추지 않으려면 ShowInactiveTopmost 함수의 시작 부분에 잊지 마십시오 : using 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;
       }
    }

Visual Studio 디자이너 또는 다른 곳에서 TopMost 설정을 생략해야합니다.

이것은 여기에서 도난 당하고, 실수로 차용되었습니다 (해결 방법을 클릭하십시오).

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


1
최상위 + 집중되지 않은 작업으로 모든 답변 중 가장 깨끗한 것처럼 보입니다.
feos

Microsoft는 Windows 8을 사용할 때 사용자를 처벌하는 Windows 8부터 더 이상 사용되지 않습니다. 결과적으로 최상위 창을 연 다음 닫은 후 Windows는 응용 프로그램의 다른 창을 배경으로 이동합니다. 이것은 반드시 응용 프로그램에서 원하는 동작이 아닙니다. 마이크로 소프트는 과거에 많은 프로그래머들이 가장 방해가되는 것을 남용했기 때문에이를 구현했습니다. 최상위는 매우 공격적입니다. 나는 그것을 사용하지 않습니다.
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

편집 : 참고로, 초점을 훔치지 않고 이미 만든 양식을 올릴 수 있습니다.


이 "알림 양식"이 다른 스레드에서 열렸 기 때문에 여기서 작동하지 않는 것 같습니다.
Matías

1
아마도이 경우 메소드를 올바른 스레드로 다시 호출하려면 this.Invoke () 호출을 수행해야합니다. 일반적으로 잘못된 스레드에서 양식을 사용하면 예외가 발생합니다.
Matthew Scharley

이것이 효과가 있지만 언급 된 것처럼 해킹 된 방법이며 특정 조건에서 BSOD를 유발했기 때문에 이것을 조심하십시오.
Raphael Smit

Microsoft는 Windows 8을 사용할 때 사용자를 처벌하는 Windows 8부터 더 이상 사용되지 않습니다. 결과적으로 최상위 창을 연 다음 닫은 후 Windows는 응용 프로그램의 다른 창을 배경으로 이동합니다. 이것은 반드시 응용 프로그램에서 원하는 동작이 아닙니다. 마이크로 소프트는 과거에 많은 프로그래머들이 가장 방해가되는 것을 남용했기 때문에이를 구현했습니다. 최상위는 매우 공격적입니다. 나는 그것을 사용하지 않습니다.
Elmue

9

Alex Lyman / TheSoftwareJedi의 답변에있는 pinvoke.net의 샘플 코드는 창을 "맨 위"창으로 만들며, 팝업 된 후에는 일반 창 뒤에 놓을 수 없습니다. 그가 사용하고자하는 것에 대한 Matias의 설명이 주어지면, 그것은 그가 원하는 것일 수 있습니다. 그러나 팝업 후 다른 창 뒤에 사용자가 창을 놓을 수있게하려면 샘플에서 HWND_TOPMOST (-1) 대신 HWND_TOP (0)을 사용하십시오.


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

나는 비슷한 것을 가지고 있으며 단순히 알림 양식을 보여준 다음에

this.Focus();

다시 주 양식에 초점을 맞 춥니 다.


간단하지만 효과적인!
Tom

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
}

또한 주 양식 ( frm.Close()) 으로 프로그래밍 방식으로 닫을 수 있도록 NotifcationForm 객체에 대한 핸들을 유지할 수 있습니다 .

일부 세부 사항이 누락되었지만 희망적으로 올바른 방향으로 나아갈 수 있기를 바랍니다.


양식이 원래 활성 양식 인 경우에만 작동합니다. 이런 종류의 알림은 이런 종류의 알림의 주요 목적에 위배됩니다.
Alex Lyman

1
응? 이것이 알림의 목적입니다. 알림을 설정하고 원래 활성화 된 양식으로 다시 초점을 다시 맞추는 것입니다.
Bob Nadler

2
이것은 응용 프로그램의 양식에만 초점을 맞 춥니 다. 당시에 다른 프로그램이 활성화되어 있다면 어떨까요? 알림 창을 표시하면 (일반적으로 사용자에게 응용 프로그램 상태를 업데이트하도록) 응용 프로그램을보고 있지 않을 때만 유용합니다.
알렉스 리먼

3

어떤 종류의 알림을 표시 할 것인지 고려할 수 있습니다.

사용자에게 일부 이벤트에 대해 알리는 것이 절대적으로 중요한 경우 사용자가 확인할 때까지 Messagebox.Show를 사용하면 다른 이벤트를 기본 창에 차단할 수 있으므로 권장되는 방법입니다. 그러나 팝업 실명에 유의하십시오.

중요하지 않은 경우 다른 방법을 사용하여 창의 맨 아래에있는 도구 모음과 같은 알림을 표시 할 수 있습니다. 화면 오른쪽 하단에 알림을 표시한다고 썼습니다. 표준 트레이시스템 트레이 아이콘 과 함께 풍선 도움말을 사용하는 것입니다 .


2
-풍선 도움말은 비활성화 할 수 있기 때문에 옵션이 아닙니다.-추천 프로그램 덕분에 프로그램을 최소화 한 경우 상태 표시 줄이 숨겨 질 수 있습니다
Matías

3

이것은 잘 작동합니다.

참조 : OpenIcon-MSDNSetForegroundWindow-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 메서드로 끝나는 위의 제안이 가장 우아하다는 것을 인정해야하지만 논리로만 처리 할 수 있습니다 .

어쨌든, 나는 이미 최근에 전화를 한 경우 더 이상의 BringToFront 호출을 허용하지 않기 위해 DateTime 속성을 사용 하여이 문제를 해결했습니다.

예를 들어 'Form1, 2 및 3'의 세 가지 양식을 처리하는 핵심 클래스 'Core'를 가정하십시오. 각 양식에는 DateTime 속성과 Core를 호출하여 창을 앞으로 가져 오는 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 파일을 피하려면 제안으로 사용됩니다.


1

이것이 네크로 포스팅으로 간주되는지는 모르겠지만 user32의 "ShowWindow"및 "SetWindowPos"메소드로 작동하지 않기 때문에 내가 한 일입니다. 새 창은 항상 맨 위에 있어야하므로이 경우 "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 ()는 예외입니다. 함수 내에서 선언하면 함수가 종료되는 즉시 스레드가 종료되고 양식이 사라지기 때문에 스레드가 전역 적으로 표시되는지 확인하십시오.


당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.