.NET: Как поместить мое окно рядом с областью уведомлений (systray)?

Я хотел бы отобразить небольшое всплывающее окно рядом с областью уведомлений. Это похоже на то, что Outlook/Skype/Live! Messenger/etc делает это, когда отображает уведомление о новом сообщении. В моем случае у него будут некоторые элементы управления вводом (текстовое поле, средство выбора даты и времени, кнопки...), поэтому простой пузырь не подойдет.

Хитрость заключается в том, чтобы сделать это правильно, когда у пользователя несколько мониторов и/или панель задач не расположена внизу экрана. Я не смог найти никаких функций, которые позволили бы мне определить положение и ориентацию панели задач/области уведомлений.


person Vilx-    schedule 26.04.2010    source источник
comment
любой полный образец исходного кода?   -  person Kiquenet    schedule 21.01.2011
comment
@alhambraeidos - я думаю, что принятый ответ содержит всю необходимую вам информацию. У меня нет исходного кода в сети, но сделать что-то подобное не очень сложно. У вас есть какие-то конкретные проблемы?   -  person Vilx-    schedule 21.01.2011


Ответы (2)


Используйте вызовы WinAPI, чтобы найти позицию панели задач и расположить окно в соответствии с ней.

Пример C#

class Program
{
    static void Main(string[] args)
    {
        Taskbar taskbar = new Taskbar();
        Console.WriteLine("Position: {0}, AlwaysOnTop: {1}; AutoHide: {2}; Bounds: {3}", taskbar.Position, taskbar.AlwaysOnTop, taskbar.AutoHide, taskbar.Bounds);

        Console.ReadLine();
    }
}

public enum TaskbarPosition
{
    Unknown = -1,
    Left,
    Top,
    Right,
    Bottom,
}

public sealed class Taskbar
{
    private const string ClassName = "Shell_TrayWnd";

    public Rectangle Bounds
    {
        get;
        private set;
    }
    public TaskbarPosition Position
    {
        get;
        private set;
    }
    public Point Location
    {
        get
        {
            return this.Bounds.Location;
        }
    }
    public Size Size
    {
        get
        {
            return this.Bounds.Size;
        }
    }
    //Always returns false under Windows 7
    public bool AlwaysOnTop
    {
        get;
        private set;
    }
    public bool AutoHide
    {
        get;
        private set;
    }

    public Taskbar()
    {
        IntPtr taskbarHandle = User32.FindWindow(Taskbar.ClassName, null);

        APPBARDATA data = new APPBARDATA();
        data.cbSize = (uint) Marshal.SizeOf(typeof(APPBARDATA));
        data.hWnd = taskbarHandle;
        IntPtr result = Shell32.SHAppBarMessage(ABM.GetTaskbarPos, ref data);
        if (result == IntPtr.Zero)
            throw new InvalidOperationException();

        this.Position = (TaskbarPosition) data.uEdge;
        this.Bounds = Rectangle.FromLTRB(data.rc.left, data.rc.top, data.rc.right, data.rc.bottom);

        data.cbSize = (uint) Marshal.SizeOf(typeof(APPBARDATA));
        result = Shell32.SHAppBarMessage(ABM.GetState, ref data);
        int state = result.ToInt32();
        this.AlwaysOnTop = (state & ABS.AlwaysOnTop) == ABS.AlwaysOnTop;
        this.AutoHide = (state & ABS.Autohide) == ABS.Autohide;
    }
}

public enum ABM : uint
{
    New = 0x00000000,
    Remove = 0x00000001,
    QueryPos = 0x00000002,
    SetPos = 0x00000003,
    GetState = 0x00000004,
    GetTaskbarPos = 0x00000005,
    Activate = 0x00000006,
    GetAutoHideBar = 0x00000007,
    SetAutoHideBar = 0x00000008,
    WindowPosChanged = 0x00000009,
    SetState = 0x0000000A,
}

public enum ABE : uint
{
    Left = 0,
    Top = 1,
    Right = 2,
    Bottom = 3
}

public static class ABS
{
    public const int Autohide = 0x0000001;
    public const int AlwaysOnTop = 0x0000002;
}

public static class Shell32
{
    [DllImport("shell32.dll", SetLastError = true)]
    public static extern IntPtr SHAppBarMessage(ABM dwMessage, [In] ref APPBARDATA pData);
}

public static class User32
{
    [DllImport("user32.dll", SetLastError = true)]
    public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
}

[StructLayout(LayoutKind.Sequential)]
public struct APPBARDATA
{
    public uint cbSize;
    public IntPtr hWnd;
    public uint uCallbackMessage;
    public ABE uEdge;
    public RECT rc;
    public int lParam;
}

[StructLayout(LayoutKind.Sequential)]
public struct RECT
{
    public int left;
    public int top;
    public int right;
    public int bottom;
}
person ba__friend    schedule 26.04.2010
comment
Однако имя класса панели задач жестко закодировано, что является не контрактом, а деталью реализации. А как насчет людей, использующих другую оболочку? - person Joey; 26.04.2010
comment
codeproject.com/KB/shell/trayposition.aspx Другой подход рассматривает комментарий от johannes rössel, просто для переноса на C# imo - person ba__friend; 26.04.2010
comment
@Johannes: я не видел пользовательских оболочек с тех пор, как Windows 98 вымерла. А если вы используете кастомную оболочку, то нет никаких гарантий, что вообще есть область уведомлений, так что все это становится бессмысленным. Но вы правы, так что я поставлю галочку в коде. Если окно не будет найдено, мое окно будет отображаться в правом нижнем углу основного монитора. - person Vilx-; 26.04.2010

Вам нужно получить фактическое местоположение значка уведомления и разместить всплывающее окно рядом с ним (или где угодно).

Вам нужно перевести ваши местоположения XY относительно рабочего стола (ов). Насколько я знаю, даже в Win32 API нет прямой функции, которая могла бы дать вам прямой ответ.

Эти сайты помогут вам:
1. http://forum.codecall.net/managed-c/262-dual-monitors-window-position.html
2. http://msdn.microsoft.com/en-us/magazine/cc188759.aspx

person Nayan    schedule 26.04.2010