Click here to Skip to main content
12,503,146 members (62,527 online)
Click here to Skip to main content
Add your own
alternative version

Stats

15.4K views
4K downloads
28 bookmarked
Posted

TaskbarNotifiers, Resizable Skinned MSN Messenger-Like Popups with Video

, 8 Jan 2012 CPOL
Rate this:
Please Sign up or sign in to vote.
Resizable Skins Made from Web Pages with Video

Introduction

I have always enjoyed playing with non-rectangular skins and this article is just about having fun making skins in different ways. John O'Byrne wrote an article here on CodeProject back in 2003 entitled TaskbarNotifier, a skinnable MSN Messenger-like popup in C# that was a nice article on creating a non-rectangular TaskbarNotifier. I thought I would take his code as a starting point, fix some minor bugs, and add a number of additional features such as creating skins from web pages for really cool animations using jquery, adding a non-rectangular video player, and making non-rectangular skins resizable by loading the non-rectangualr regions from files or resources and scalling the regions using XFORM and Transform. And unlike his original project, I created a DLL that can be loaded and used by other applications which made more sense to me. I am not an artist so some of the edges of the skins are a little rough but I hope the reader understands that this article is about just having fun with regions and not about my artwork!

Features

The MSN messenger like popup includes:

  • Skins created from the webbrowser control AND skins created from bitmaps
  • Made all skins resizable using XFORM and Transform
  • Using a web page as a skin for really cool-looking skins
  • A skinnable 3-State close button for bitmap skins
  • Clickable title text & Clickable content text
  • Custom fonts and colors for the different states of the text (normal/hover)
  • Animation speed parameters
  • Playing video in a non-rectangular window in a skin
  • Fast creation of a Region from bitmap using a tolerance factor
  • Loading a region from a file & Resizing the Region Dynamically
  • Saving a region to a file THE CORRECT WAY!
  • Loading a region from an embedded resource

How the DLL and Demo Work

I included a separte project called "NotifierDemo" that loads the DLL "TaskNotifier.dll" to illustrate how to use this DLL. In addition, NotifierDemo also allows you to create and save regions to files so we can use the saved regions as files or embedded resources. There are two types of skins in this demo. The first are those that are just a skinned WinForm painted with a bitmap, i.e. the "Bmp Skin" buttons. The other type is a skinned WinForm with a WebBrowser Control and an instance of a media player, i.e., the "WebSkin" buttons in the NotifierDemo screen shown below are "Cargo Door," "Stone TV," and "Stargate." The bottom half of the screen below allows you to create a region from a bitmap including a tolerance factor.  The button in the lower left of the screen below called "Create Form from Region File" will load the region frrom the region file you select and RESIZE the region to fit the dimensions you have typed into the Width and Height fields on the right of this button. I only set the code for an animated slide from the lower right-hand corner of the screen but the reader can easily modify the code to slide from any of the corners of the screen. It should be pointed out that some of the resources for these skins can be placed in the DLL as embedded resources or they can loose in any directory. In this demo to make things easier I put some skin resources in directories and others as embeddede resources to illustrate using both approaches.

TaskbarNotifiers/TaskbarNotifiers2.jpg

 

Creating A Region from A Bitmap with Tolerance

 

You can find dozens of examples of creating a region from a bitmap. I used the approach below in C# that includes using a "Tolerance Factor" to help to smooth out the rough curves. Speed is not critical here because we will be using only the regions created in our skins and not dynamically creating the regions from a bitmap. When I first started this article I resized the skins by just first resizing the bitmap image and then creating the region again from the resized bitmap--that also works fine if you prefer that approach. My own preference is to create the region and resize the regions from a file or embedded region resource which seems a bit faster.

public Region getRegion(Bitmap inputBmp, Color transperancyKey, int tolerance)
{
    // Stores all the rectangles for the region
    GraphicsPath path = new GraphicsPath();

    // Scan the image
    for (int x = 0; x < inputBmp.Width; x++)
    {
        for (int y = 0; y < inputBmp.Height; y++)
        {
            if (!colorsMatch(inputBmp.GetPixel(x, y), transperancyKey, tolerance))
                path.AddRectangle(new Rectangle(x, y, 1, 1));
        }
    }

    // Create the Region
    Region outputRegion = new Region(path);

    // Clean up
    path.Dispose();

    return outputRegion;
}

private static bool colorsMatch(Color color1, Color color2, int tolerance)
{
    if (tolerance < 0) tolerance = 0;
    return Math.Abs(color1.R - color2.R) <= tolerance &&
           Math.Abs(color1.G - color2.G) <= tolerance &&
           Math.Abs(color1.B - color2.B) <= tolerance;
}

Saving A Region to A File

Saving a region file correctly is not so simple. What I doubt you will find anywhere is sample code in C# to save a region to a file correctly. Proably because to save a region to a file in C# is a bit tricky since the methods to get the combined region data AND region header all accept a pointer to the region structure. Since the skins in this project all load the regions from either a file or embedded resource we need to be able to save the region created from a bitmap to a file in C# as I do in Bmp2Rgn.cs. There are other ways to save or serialize a region to a file in C# without using unsafe pointers but this approach is just the way I prefer doing it.

 // Create a region called "myRegion" by some means and pass the
// handle to the region, i.e., Hrgn, to "SaveRgn2File" like so:
using (Graphics g = this.CreateGraphics())
    SaveRgn2File(myRegion.GetHrgn(g), sSaveRgnFile);

[SuppressUnmanagedCodeSecurity()]
public unsafe void SaveRgn2File(IntPtr hRgn, string sSaveRgnFile)
{
    Win32.RECT[] regionRects = null;
    IntPtr pBytes = IntPtr.Zero;
    try
    {
        // See how much memory we need to allocate
        int regionDataSize = Win32.GetRegionData(new HandleRef(null, hRgn), 0, IntPtr.Zero);
        if (regionDataSize != 0)
        {
            pBytes = Marshal.AllocCoTaskMem(regionDataSize);
            // Get the pointer, i.e., pBytes, to BOTH the region header AND the region data!
            int ret = Win32.GetRegionData(new HandleRef(null, hRgn), regionDataSize, pBytes);
            if (ret == regionDataSize) // make sure we have RDH_RECTANGLES
            {
                // Cast to the structure
                Win32.RGNDATAHEADER* pRgnDataHeader = (Win32.RGNDATAHEADER*)pBytes;
                if (pRgnDataHeader->iType == 1)  // Make sure we have RDH_RECTANGLES
                {
                    using (FileStream writeStream = new FileStream(sSaveRgnFile, FileMode.Create, FileAccess.ReadWrite))
                    {
                        WriteToStream(writeStream, (void*)pBytes, (uint)ret);
                        writeStream.Close();
                    }
                }
            }
        }
    }
    finally
    {
        if (pBytes != IntPtr.Zero)
        {
            Marshal.FreeCoTaskMem(pBytes);
        }
    }
}

[SuppressUnmanagedCodeSecurity()]
public unsafe static void WriteToStream(FileStream output, void* pvBuffer, uint length)
{
    IntPtr hFile = output.SafeFileHandle.DangerousGetHandle();
    WriteToStream(hFile, pvBuffer, length);
    GC.KeepAlive(output);
}

[SuppressUnmanagedCodeSecurity()]
public unsafe static void WriteToStream(IntPtr hFile, void* pvBuffer, uint length)
{
    if (hFile == NativeConstants.INVALID_HANDLE_VALUE)
        throw new ArgumentException("output", "File is closed");

    void* pvWrite = pvBuffer;

    while (length > 0)
    {
        uint written;
        bool result = SafeNativeMethods.WriteFile(hFile, pvWrite, length, out written, IntPtr.Zero);

        if (!result)
            return;

        pvWrite = (void*)((byte*)pvWrite + written);
        length -= written;
    }
}

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Bill SerGio Jr.
CEO http://www.SerGioApps.com
United States United States
My company finances new Internet startups and we market educational software and mobile apps on National Television using half hour Infomercials.

I started writing code to help run my companies.

We develop software and mobile apps, sell half hour television time, and produce new Infomercials and 90-minute feature movies for television to market software and mobile apps.

We have developed software for Microsoft, MySpace.com, Quicken (Intuit), Mellon Bank, U.S. Army, U.S. Navy, Franklin Templeton, Pepsi, Universal Studios, Ryder Systems, etc.

Bill SerGio
http://www.SerGioApps.com

You may also be interested in...

Pro
Pro

Comments and Discussions

 
GeneralWOW!!! Pin
Zac Greve27-Mar-12 2:24
memberZac Greve27-Mar-12 2:24 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web02 | 2.8.160919.1 | Last Updated 8 Jan 2012
Article Copyright 2012 by Bill SerGio Jr.
Everything else Copyright © CodeProject, 1999-2016
Layout: fixed | fluid