Окно уведомлений - предотвращение фокусировки окна - PullRequest
6 голосов
/ 13 октября 2011

У меня возникли некоторые проблемы при получении окна уведомлений для правильного поведения в c #.В основном я показываю форму без границ в правой нижней части экрана, которая отображает сообщение в течение нескольких секунд, а затем исчезает.Проблема в том, что мне нужно, чтобы он появлялся поверх других окон, чтобы он не смог украсть фокус.В идеале я хочу, чтобы это был чисто управляемый код, хотя, просматривая похожие примеры, сомневаюсь, что это будет возможно.

В настоящий момент я не даю ему украсть фокус при вызове Form.Show () с переопределением:

protected override bool ShowWithoutActivation // stops the window from stealing focus
{
    get { return true; }
}

и последующее игнорирование щелчков мыши с помощью:

    private const int WM_MOUSEACTIVATE = 0x0021;
    private const int MA_NOACTIVATEANDEAT = 0x0004;

    protected override void WndProc(ref Message m)
    {
        if (m.Msg == WM_MOUSEACTIVATE)
        {
            m.Result = (IntPtr)MA_NOACTIVATEANDEAT;
            return;
        }
        base.WndProc(ref m);
    }

Однако я обнаружил, что если я использую их в сочетании с TopMost = true (что мне нужно), он все равно получает фокуси, если все остальные окна свернуты, он также получает фокус.

Итак, есть ли способ выровнять, чтобы форма не получала фокус (будь то щелчок мыши, alt-tab и т. д.), в то время какпо-прежнему будучи самой верхней / второй по величине формой?Даже простое возвращение фокуса к окну, из которого оно было украдено, сработало бы (хотя и с мерцанием).

Буду очень признателен за любые предложения, я действительно застрял с этим.

РЕДАКТИРОВАТЬ:

Хорошо, так что мне наконец удалось получить эту работу, используя:

protected override bool ShowWithoutActivation // stops the window from stealing focus
{
    get { return true; }
}

// and

const int WS_EX_NOACTIVATE = 0x08000000;
const int WS_EX_TOPMOST = 0x00000008;

protected override CreateParams CreateParams
{
    get
    {
        CreateParams param = base.CreateParams;
        param.ExStyle |= WS_EX_TOPMOST; // make the form topmost
        param.ExStyle |= WS_EX_NOACTIVATE; // prevent the form from being activated
        return param;
    }
}

// and

[DllImport("user32.dll")]
private extern static IntPtr SetActiveWindow(IntPtr handle);
private const int WM_ACTIVATE = 6;
private const int WA_INACTIVE = 0;

private const int WM_MOUSEACTIVATE = 0x0021;
private const int MA_NOACTIVATEANDEAT = 0x0004;

protected override void WndProc(ref Message m)
{
    if (m.Msg == WM_MOUSEACTIVATE)
    {
        m.Result = (IntPtr)MA_NOACTIVATEANDEAT; // prevent the form from being clicked and gaining focus
        return;
    }
    if (m.Msg == WM_ACTIVATE) // if a message gets through to activate the form somehow
    {
        if (((int)m.WParam & 0xFFFF) != WA_INACTIVE)
        {

            if (m.LParam != IntPtr.Zero)
            {
                SetActiveWindow(m.LParam);
            }
            else
            {
                // Could not find sender, just in-activate it.
                SetActiveWindow(IntPtr.Zero);
            }

        }
    }

Я также добавил Form.Hide () к событию GotFocus, чтобы дажеесли он каким-то образом получает фокус, он просто закрывается и удаляется от пользователя как можно скорее.

Кроме того, если кому-то интересно, константы для всех стилей окон и т. Д. Можно найти в WINUSER.H, он находится по адресу http://www.woodmann.com/fravia/sources/WINUSER.H, если вы не можете его найти.

Однако, если кто-нибудь увидит более элегантный способ сделать это, это будет оценено.

Ответы [ 3 ]

3 голосов
/ 21 февраля 2012

В WPF попробуйте это:

ShowActivated="False"
2 голосов
/ 13 октября 2011

Возможно, WS_EX_NOACTIVATE расширенный стиль окна - это то, что вы ищете. Окно с этим стилем не активируется при нажатии. Например, окно «Виртуальная клавиатура» имеет этот стиль.

Чтобы применить этот стиль к окну, переопределите функцию CreateParams и измените baseParams.ExStyle.

0 голосов
/ 10 ноября 2015

Я не ищу здесь баллов, так как оригинальный постер уже опубликовал решение, которое им помогло, но я хотел поделиться своим опытом с этой проблемой.Использование приведенного выше решения (которое находится внизу вопроса, а не в форме ответа) дает мне Win32Exception: Error creating window handle error. при использовании кода WndProc, как он там опубликован.Компоненты ShowWithoutActivation и CreateParams прекрасно работают для предотвращения активации формы и сохранения ее наивысшего уровня.

Я предложил альтернативное решение для предотвращения нажатия на форму с помощью SetWindowLong, которое делаетформа прозрачна, и, следовательно, по ней можно щелкнуть, и SetLayeredWindowAttributes, которая возвращает прозрачность в нормальное состояние, чтобы вы снова могли видеть форму, но при этом сохраняете возможность щелкать по форме.1012 * Вы-НЕ МОЖЕТЕ взаимодействовать с формой вообще в этом состоянии, и даже пытаясь нажать кнопку 'X', вы просто нажмете все, что находится позади формы в этой позиции, поэтому вам нужно будет использовать код для закрытия формы:

public partial class Form1 : Form
{
    private enum GWL : int
    {
        ExStyle = -20
    }

    private enum WS_EX : int
    {
        Transparent = 0x20,
        Layered = 0x80000
    }

    public enum LWA : int
    {
        ColorKey = 0x1,
        Alpha = 0x2
    }

    [DllImport("user32.dll")]
    static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong);

    [DllImport("user32.dll")]
    static extern bool SetLayeredWindowAttributes(IntPtr hwnd, uint crKey, byte bAlpha, uint dwFlags);

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

    const int WS_EX_NOACTIVATE = 0x08000000;
    const int WS_EX_TOPMOST = 0x00000008;

    protected override CreateParams CreateParams
    {
        get
        {
            CreateParams param = base.CreateParams;
            param.ExStyle |= WS_EX_TOPMOST; // make the form topmost
            param.ExStyle |= WS_EX_NOACTIVATE; // prevent the form from being activated
            return param;
        }
    }

    public Form1()
    {
        InitializeComponent();
    }

    private void Form1_Load(object sender, EventArgs e)
    {
        // Prevent form from showing up in taskbar which also prevents activation by Alt+Tab 

        this.ShowInTaskbar = false;

        // Allow the form to be clicked through so that the message never physically interferes with work being done on the computer 

        SetWindowLong(this.Handle, (int)GWL.ExStyle, (int)WS_EX.Layered | (int)WS_EX.Transparent);

        // Set the opacity of the form

        byte nOpacity = 255;    // 0 = invisible, 255 = solid, anything inbetween will show the form with transparency
        SetLayeredWindowAttributes(this.Handle, 0, nOpacity, (uint)LWA.Alpha);
    }
}

Я также смог заставить оригинальный подход работать, внеся небольшое изменение в код WndProc, приведенный выше.

ПРИМЕЧАНИЕ: Это также делает форму неприкосновеннойно поведение отличается тем, что вы можете нажимать кнопки min, max и 'X', но ничего не происходит, когда вы это делаете.Курсор мыши также изменяется, когда вы находитесь на краю формы, как будто хотите изменить размер, но это не позволяет изменять размер:

public partial class Form1 : Form
{
    protected override bool ShowWithoutActivation
    {
        get { return true; }
    }

    const int WS_EX_NOACTIVATE = 0x08000000;
    const int WS_EX_TOPMOST = 0x00000008;

    protected override CreateParams CreateParams
    {
        get
        {
            CreateParams param = base.CreateParams;
            param.ExStyle |= WS_EX_TOPMOST; // make the form topmost
            param.ExStyle |= WS_EX_NOACTIVATE; // prevent the form from being activated
            return param;
        }
    }

    [DllImport("user32.dll")]
    private extern static IntPtr SetActiveWindow(IntPtr handle);
    private const int WM_ACTIVATE = 6;
    private const int WA_INACTIVE = 0;

    private const int WM_MOUSEACTIVATE = 0x0021;
    private const int MA_NOACTIVATEANDEAT = 0x0004;

    protected override void WndProc(ref Message m)
    {
        if (m.Msg == WM_MOUSEACTIVATE)
        {
            m.Result = (IntPtr)MA_NOACTIVATEANDEAT; // prevent the form from being clicked and gaining focus
            return;
        }
        if (m.Msg == WM_ACTIVATE) // if a message gets through to activate the form somehow
        {
            if (((int)m.WParam & 0xFFFF) != WA_INACTIVE)
            {

                if (m.LParam != IntPtr.Zero)
                {
                    SetActiveWindow(m.LParam);
                }
                else
                {
                    // Could not find sender, just in-activate it.
                    SetActiveWindow(IntPtr.Zero);
                }

            }
        }
        else
        {
            base.WndProc(ref m);
        }
    }

    public Form1()
    {
        InitializeComponent();
    }

    private void Form1_Load(object sender, EventArgs e)
    {
        // Prevent form from showing up in taskbar which also prevents activation by Alt+Tab 

        this.ShowInTaskbar = false;
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...