Как показать всплывающую подсказку systray длиной более 63 символов?

Как показать всплывающую подсказку systray длиной более 63 символов? NotifyIcon.Text имеет ограничение 63 символов, но я видел, что VNC-сервер имеет более длинную подсказку.

Как я могу сделать то, что делает VNC-сервер?

Ответы

Ответ 1

Собственно, это ошибка в свойстве свойств свойства Text. Объявление P/Invoke для NOTIFYICONDATA внутри Windows Forms использует предел 128 char. Вы можете взломать его с помощью Reflection:

using System;
using System.Windows.Forms;
using System.Reflection;

    public class Fixes {
      public static void SetNotifyIconText(NotifyIcon ni, string text) {
        if (text.Length >= 128) throw new ArgumentOutOfRangeException("Text limited to 127 characters");
        Type t = typeof(NotifyIcon);
        BindingFlags hidden = BindingFlags.NonPublic | BindingFlags.Instance;
        t.GetField("text", hidden).SetValue(ni, text);
        if ((bool)t.GetField("added", hidden).GetValue(ni))
          t.GetMethod("UpdateIcon", hidden).Invoke(ni, new object[] { true });
      }
    }

Ответ 2

В документации MSDN на Win32 структура NOTIFYICONDATA:

szTip

Строка с нулевым символом, которая указывает текст для стандартного ToolTip. Он может иметь максимум 64 символов, включая завершающий нулевой символ.

Для Windows 2000 (Shell32.dll версии 5.0) и более поздних версий szTip может иметь не более 128 символов, включая завершающий нулевой символ.

Похоже, что библиотека Windows Forms поддерживает здесь самый низкий общий знаменатель.

Ответ 3

Расширение на bk1e правильного ответа.

Под капотом значок в системном трее в WinForms реализован как значок уведомления Win32. Поэтому версия winforms имеет все ограничения, как родные. Ограничение размера всплывающей подсказки - всего лишь один пример.

Ответ 4

Недавно я столкнулся с подобной проблемой. Вместо того, чтобы взламывать back-end, я реализовал обход, который использует BalloonTipText, который может вместить довольно много символов.

Всплывающая подсказка отображается в первом событии MouseMove над значком в трее, и всплывающая подсказка отображается в течение 2 секунд. Atter всплывающая подсказка закрыта, ее можно снова открыть снова с помощью нового события MouseMove.

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

Обратите внимание, что заголовок и текст могут быть установлены в любое время в другом месте программы. Они указаны здесь только в демонстрационных целях.

EDIT: ShowBalloonTip() активирует каскадные события MouseMove, поэтому необходимо отключить это событие до тех пор, пока всплывающая подсказка не будет скрыта. Кроме того, BalloonTipClosed (в соответствии с документация) запускается только тогда, когда пользователь активно нажимает на "X", хотя я заметил, что его увольняют, когда всплывающая подсказка закрывается после таймаута. Поэтому я добавил вспомогательный таймер к reset sate вместо того, чтобы полагаться на событие BalloonTipClosed. Пересмотренный и проверенный код ниже:

    private bool balloonTipShown;
    private Timer balloonTimer;
    private void trayIcon_MouseMove(object sender, MouseEventArgs e)
    {
        if (balloonTipShown)
        {
            return;
        }
        balloonTipShown = true;
        trayIcon.MouseMove -= trayIcon_MouseMove;
        balloonTimer = new Timer();
        balloonTimer.Tick += balloonTimer_Tick;
        balloonTimer.Interval = 2005;
        balloonTimer.Start();
        trayIcon.ShowBalloonTip(2000);
    }

    void balloonTimer_Tick(object sender, EventArgs e)
    {
        balloonTipShown = false;
        balloonTimer.Stop();
        balloonTimer.Dispose();
        trayIcon.MouseMove += trayIcon_MouseMove;
    }

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

Ответ 5

bk1e здесь говорит, что ограничение составляет 128 символов, теперь, если вы используете UTF-16, который является родным форматом Unicode в Windows и особенно .NET, значит, вы ограничены 64 символами, включая NUL.

Я бы поверил, что вы используете API юникода, который ограничивает всплывающие подсказки для 64 16-битных символов (включая нуль), и что VNC-сервер использует вместо него ascii (или ANSI) api, позволяя использовать 128 8-битных символов (включая нуль).

EDIT: этот ответ неверен, вот полезный комментарий Коди Грея, объясняющий, почему:

Это рассуждение является убедительным, но на самом деле оно не является правильным. Когда в документации MSDN говорится о "символах", это фактически означает количество элементов char или wchar_t в массиве (в зависимости от того, настроен ли вы для Unicode). Таким образом, вы получаете полные 128 символов, обещанных при работе в Windows 2000+. Windows 9x ограничивалась 64 символами. - Коди Грей Июнь 19 в 4:11 "