Программируем Windows 7: Taskbar. Часть 8 – IconicThumbnail

Недавно я рассказал о том, каким образом можно влиять на preview окна в панели задач Windows 7. Тогда мы отображали только ту часть окна, которая наиболее важна для пользователя. Тем не менее в определенных ситуациях гораздо полезнее может быть отображение не части окна, а совершенно другая картинка, отображающая состояние приложения.

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

Давайте разберемся с этой возможностью панели задач Windows 7. Для реализации подобного поведения в .NET Interop Sample Library существует метод SetIconicThumbnail. Именно с его помощью мы можем легко и просто создать собственный preview для нашего окна. Однако, прежде чем мы начнем его использовать мы должны разрешить это поведение с помощью метода EnableCustomWindowPreview. В противном случае при выполнении метода SetIconicThumbnail мы получим соответствующее исключение. Сделать это можно прямо в конструкторе формы.

public Form1()
{
    InitializeComponent();
    WindowsFormsExtensions.EnableCustomWindowPreview(this);
}

Стоит сказать, что размеры изображения, которое мы хотим отобразить в preview ограничены. Это логично, т.к. представьте что бы было, если preview могли бы быть размером 1024x768. Эти размеры ограничены значениями 200x120. Изображение может быть меньше этого размера, в этом случае само окно preview также будет уменьшено. Если изображение больше этих размеров, то будет сгенерировано исключение.

В параметрах метода SetIconicThumbnail передается ссылка на текущую форму и изображение (Bitmap), которое нужно отобразить. Наша задача – сгенерировать этот Bitmap. И в этом случае у нас полностью развязаны руки – мы можем сгенерировать все что нам угодно. Это может быть снимок окна, с каким-то дополнительным текстом поверх. Это может быть какая-то собственная картинка, содержащая информацию. Это может быть текст с некой статистикой. Все зависит от вашей фантазии.

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

private static void SetState(Form form, string stateText, Image stateImage)
{
    // blank image
    var preview = new Bitmap(200, 120);
    var g = Graphics.FromImage(preview);

    // fill background
    g.DrawImage(Images.background, 0, 0);

    // file image of state
    if (stateImage != null)
    {
        g.DrawImage(stateImage.GetThumbnailImage(100, 100, null, IntPtr.Zero), 100, 10);
    }

    // fill image of form
    g.DrawImage(GetFormImage(form, 50, 60), 10, 10);

    // draw text
    g.DrawString(stateText, new Font("Verdana", 18), Brushes.White, 10, 70);

    // setting thumbnail
    WindowsFormsExtensions.SetIconicThumbnail(form, preview);
}

Как видим, код получился достаточно лаконичный. Как результат мы можем увидеть окно со следующим preview.

Что интересно, наше приложение может само в фоне изменять это состояние. При этом, если мышь находится на значке приложение и preview в данный момент отображается, то смена изображения во всплывающем окне происходит плавно, без каких-либо побочных эффектов. Так, например, я могу создать таймер, в обработчике которого через определенные промежутки времени изменять состояние.

private readonly string[] _stateTexts = new[] { "Deleting..", "Wireless", "Security", "Stop", "Help", "O'kay", "Playing", "Login", "Warning", "Showcase", "Search", "Processing..", "Locked", "Error", "Refreshing.." };
private readonly Bitmap[] _stateImages = new[] { Images._104, Images._110, Images._111, Images._112, Images._113, Images._114, Images._120, Images._125, Images._129, Images._134, Images._17, Images._25, Images._41, Images._50, Images._52 };
private int _currentIndex = 0;

private void timer1_Tick(object sender, EventArgs e)
{
    if (_currentIndex + 1 >= _stateImages.Length)
    {
        _currentIndex = 0;
    }
    else
    {
        _currentIndex++;
    }
    SetState(this, _stateTexts[_currentIndex], _stateImages[_currentIndex]);
}

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