Click here to Skip to main content
Click here to Skip to main content

RCM v1.4 - Theming library: Customize the Appearance of Forms and Common Controls

By , 27 Feb 2010
 
title.jpg

Billy, Are You Skinning in There?

Sounds kinda nasty.. but rest assured gentle reader, it is a perfectly safe and socially acceptable thing to do (at least in the current context). Skinning is what the uxtheme.dll is likely doing in your Operating System right now, that is, subclassing a control in order to alter its appearance via various methods. Most of the common controls still look much the same as they did back in the days of Win95, just flat grey blocks with very little in the way of aesthetic appeal, and certainly not acceptable in today's OS market where users have come to expect a lot of flash and polish. The theming engine uses a variety of techniques to polish the UI, but its primary method is to blit graphics stored in a DLL over the surface of the control. With Vista, we are also seeing some additional fireworks of pulsing and fading UI elements, but beneath it all, many of the same controls we used more than a decade ago. There are also several popular commercial skinning applications that can further enhance the interface, and a few libraries ported towards developers that can give your GUI the much sought after edge in an application's visual appeal. The problem, however, is that the theme library is difficult to work with, and some of the API is obscure with few examples in the wild; also, if you intend on doing anything outside the expected norm, you are forced to draw your components from scratch. Using methods provided by VS designers or within the root classes like owner-drawn, custom drawn, and even WPF, has the disadvantage that you are limited to the methods and interfaces provided by the authors of that solution, and without the source code to the control or library, you are trapped within the limits of their vision. So.. I am providing a solution, a nexus of classes from which you can skin the form and most of the common controls. Think of this as the beginning of an Open Source theming library, evolve it in whatever direction you wish.

Using this Library

I have designed this library to be as automated as possible, with only three commands needed to start skinning the form and controls:

  1. Instantiate the class and add the form's handle.
  2. Use the Add method to add the control type and its associated bitmap(s).
  3. If you are skinning the Form control, call the Start method.

That's all there is to it.

The bitmaps used to skin the controls are formatted in the same way as most common skinning engines like Win-blinds, with literally thousands of skins to choose from. I have included the bitmaps and PNGs used in the demo to give you an idea of the format, but that can also be determined by each class' state enum; for example, the radio button has eight states that directly correspond to the RADIOBUTTONSTATES enum:

private enum RADIOBUTTONSTATES : int
{
    RBS_UNCHECKEDNORMAL = 1,
    RBS_UNCHECKEDHOT = 2,
    RBS_UNCHECKEDPRESSED = 3,
    RBS_UNCHECKEDDISABLED = 4,
    RBS_CHECKEDNORMAL = 5,
    RBS_CHECKEDHOT = 6,
    RBS_CHECKEDPRESSED = 7,
    RBS_CHECKEDDISABLED = 8,
};

The form's frame has four parts: caption, left side, right side, and bottom. Each part has two states, active and inactive. The form's buttons have three states: normal, hover, and pressed. To determine the state layout of an image, simply check the enum for that control type in its class.

Have DLL, Will Travel

Since this is a library, it is portable and not bound only to C# developers. That's right, it can be used in VB! I know how much you VB guys love your user controls, so I included a VB demo project. For that matter, this should be accessible to all .NET language implementations, perhaps even from VS6, (though I haven't tried it). The classes are also designed so that they can be employed independently. Most of them will require the cStoreDc and cGraphics classes which can be simply embedded into the parent class.

How Bill Stole Christmas, and Other Tales of High Misadventure

Well.. it just wouldn't be one of my articles if I didn't complain about Microsoft, right? Initially, I had intended on writing a library that could skin any control, any form, regardless of its origin (MFC/.NET), but this soon proved to be a very challenging task. The form and button skinning classes (cRCM and cButton) should be able to skin MFC controls, but it was after the button class fiasco that I broke from the original design spec. It seems that in .NET, the checkbox, radio button, and command buttons are all just an owner drawn button control, so using GetClassName to retrieve the control type always yielded the same long .NET namespace. I managed to work through this by first testing if the control handle belonged to a .NET class; if so, a control is created with the handle and the necessary properties extracted from the control.

private BUTTONPARTS buttonStyle(IntPtr handle)
{
    String name = String.Empty;
    Control ctl = Control.FromHandle(handle);
    Type t = ctl.GetType();
    name = t.Name.ToLower();

    if (name.Contains("radio"))
    {
        ctl.Paint += new PaintEventHandler(ctl_Paint);
        return BUTTONPARTS.BP_RADIOBUTTON;
    }
    if (name.Contains("checkbox"))
    {
        ctl.Paint += new PaintEventHandler(ctl_Paint);
        return BUTTONPARTS.BP_CHECKBOX;
    }
    if (name.Contains("button"))
    {
        ctl.Paint += new PaintEventHandler(ctl_Paint);
        return BUTTONPARTS.BP_PUSHBUTTON;
    }
    else
        return BUTTONPARTS.BP_UNKNOWN;
}

This (of course) was just the first serious snafu, but enough to convince me of two things: creating a library that skins controls outside of the .NET empire might be a bit too ambitious, and C# was probably not the best language for something like that anyways. You will find a lot of 'unmanaged' code mixed throughout this library, simply because the Graphics class is just too slow to do real-time complex rendering (try replacing BitBlt with Graphics.DrawImage in cRCM::drawWindow, then resize the form, and the truth of this will become quite obvious). The other problem is that even though there seems to be a multitude of properties associated with every control (many are redundant), it seems that every control lacked some property that is required to custom draw an element, like the scrollbar button size, or a combobox button handle. So, no matter what, you need to use the API to compete with the level of graphics sophistication possible in a language like C++.

private void drawCombo()
{
    ComboBox cb = (ComboBox)Control.FromHandle(_hComboWnd);
    COMBOBOXINFO cbi = new COMBOBOXINFO();
    RECT tr = new RECT(cb.ClientRectangle.Width - 12, 1, 12, 
                       cb.ClientRectangle.Height - 2);
    RECT client = new RECT(cb.ClientRectangle.Width, 0, 0, 
                           cb.ClientRectangle.Height);
    int width = _oComboboxBitmap.Width / 4;
    IntPtr hdc = GetDC(_hComboWnd);
    Rectangle cr = cb.ClientRectangle;
    int offset = 0;

    // get button size
    cbi.cbSize = Marshal.SizeOf(cbi);
    if (SendMessage(_hComboWnd, CB_GETCOMBOBOXINFO, 0, ref cbi) != 0)
        tr = cbi.rcButton;
    else
        tr = new RECT(cb.ClientRectangle.Width - width, 1, width, 
                      cb.ClientRectangle.Height - 2);
     ...

As you can see from the above snippet, the combobox button size is extracted via a call from SendMessage using the CB_GETCOMBOBOXINFO flag. Think of SendMessage as the ultimate Get/Set accessor; there are many properties in the root classes that would otherwise be unavailable (intentionally or otherwise).

To the Heart of It

form-menu.jpg

Forms

Possibly the most problematic and downright difficult controls to draw. There are many messages that can trigger an unexpected need to repaint part or all of the non-client area of a form. There is also a lack of good examples in the wild of skinning a form properly, so it required some serious trial and error to get it right. Now make no mistake, this is not using SetWindowRgn on a borderless form, this is painting over the frame in much the same way that window themes are painted. One problem I have seen in other examples of this, is how to resize the client area to the proper dimension, given the window type and size of the new frame parts. This is done by intercepting the WM_NCCALCSIZE message and changing the RECT sizes in the NCCALCSIZE_PARAMS structure. The structure contains an array of three rectangles; the first contains the current client size; we modify these values, then copy the RECT to the second element, which tells the form the new client dimensions.

case WM_NCCALCSIZE:
    if (m.WParam != IntPtr.Zero)
    {
        NCCALCSIZE_PARAMS ncsize = (NCCALCSIZE_PARAMS)
		Marshal.PtrToStructure(m.LParam, typeof(NCCALCSIZE_PARAMS));
        WINDOWPOS wp = (WINDOWPOS)Marshal.PtrToStructure
				(ncsize.lppos, typeof(WINDOWPOS));
        ncsize.rect0 = calculateFrameSize(wp.x, wp.y, wp.cx, wp.cy);
        ncsize.rect1 = ncsize.rect0;
        Marshal.StructureToPtr(ncsize, m.LParam, false);
        m.Result = (IntPtr)WVR_VALIDRECTS;
    }
    else
    {
        RECT rc = (RECT)m.GetLParam(typeof(RECT));
        rc = calculateFrameSize
		(rc.Left, rc.Top, rc.Right - rc.Left, rc.Bottom - rc.Top);;
        Marshal.StructureToPtr(rc, m.LParam, true);
        m.Result = MESSAGE_PROCESS;
    }
    base.WndProc(ref m);

In order to do this properly, we first have to subtract the default window sizes from the source RECT, which vary depending on the windows style:

private RECT calculateFrameSize(int x, int y, int cx, int cy)
{
    RECT windowRect = new RECT(x, y, x + cx, y + cy);

    // sizeable
    if ((GetWindowLong(ParentWnd, GWL_STYLE) & WS_THICKFRAME) == WS_THICKFRAME)
    {
        windowRect.Left -= GetSystemMetrics(SYSTEM_METRIC.SM_CXFRAME);
        windowRect.Right += GetSystemMetrics(SYSTEM_METRIC.SM_CXFRAME);
        windowRect.Top -= (GetSystemMetrics(SYSTEM_METRIC.SM_CYCAPTION) + 
                           GetSystemMetrics(SYSTEM_METRIC.SM_CYFRAME));
        windowRect.Bottom += GetSystemMetrics(SYSTEM_METRIC.SM_CYFRAME);
    }
    // dialog
    else if ((GetWindowLong(ParentWnd, GWL_STYLE) & WS_DLGFRAME) == WS_DLGFRAME)
    {
        windowRect.Left -= GetSystemMetrics(SYSTEM_METRIC.SM_CXDLGFRAME);
        windowRect.Right += GetSystemMetrics(SYSTEM_METRIC.SM_CXDLGFRAME);
        windowRect.Top -= (GetSystemMetrics(SYSTEM_METRIC.SM_CYSMCAPTION) + 
                           GetSystemMetrics(SYSTEM_METRIC.SM_CYDLGFRAME));
        windowRect.Bottom += GetSystemMetrics(SYSTEM_METRIC.SM_CYFRAME);
        if ((GetWindowLong(ParentWnd, GWL_EXSTYLE) & 
		WS_EX_WINDOWEDGE) == WS_EX_WINDOWEDGE)
            windowRect.Top -= 2;
    }

    // 3d
    if ((GetWindowLong(ParentWnd, GWL_EXSTYLE) & WS_EX_CLIENTEDGE) == WS_EX_CLIENTEDGE)
        InflateRect(ref windowRect, 2, 2);
    // toolwindow
    if ((GetWindowLong(ParentWnd, GWL_EXSTYLE) & WS_EX_TOOLWINDOW) == WS_EX_TOOLWINDOW)
        windowRect.Top += 2;

    // reset client area
    windowRect.Left += (_oLeftFrameBitmap.Width / 2);
    windowRect.Right -= (_oRightFrameBitmap.Width / 2);
    windowRect.Bottom -= (_oBottomFrameBitmap.Height / 2);
    windowRect.Top += (_oCaptionBarBitmap.Height / 2);
    return windowRect;
}

caption-buttons.jpg

Another issue is trying to emulate the caption button behavior, for example, when the mouse is held down, then dragged away from the button and released, getting the button visual state to change correctly, as there is no NC_MOUSELEAVE message. We do this by starting a timer that runs through the window procedure fired after a NC_MOUSEMOVE message:

case WM_NCMOUSEMOVE:
    _eLastButtonHit = hitTest();
    if ((_eLastButtonHit == HIT_CONSTANTS.HTCLOSE) ||
        (_eLastButtonHit == HIT_CONSTANTS.HTMAXBUTTON) ||
        (_eLastButtonHit == HIT_CONSTANTS.HTMINBUTTON))
    {
        startTimer();
        invalidateWindow();
    }
    base.WndProc(ref m);
    break;

case WM_TIMER:
    _buttonTimer += 1;
    HIT_CONSTANTS hitTimer = hitTest();
    if ((hitTimer == HIT_CONSTANTS.HTCLOSE) ||
        (hitTimer == HIT_CONSTANTS.HTMAXBUTTON) ||
        (hitTimer == HIT_CONSTANTS.HTMINBUTTON))
    {
        if (hitTimer != _eLastButtonHit)
        {
            stopTimer();
            invalidateWindow();
        }
        else
        {
            if (_buttonTimer > 500)
                stopTimer();
        }
    }
    else
    {
        if (!leftKeyPressed())
        {
            stopTimer();
            invalidateWindow();
        }
    }
    base.WndProc(ref m);
    break;
private void startTimer()
{
    if (_buttonTimer > 0)
        stopTimer();
    SetTimer(ParentWnd, 66, 100, IntPtr.Zero);
}

private void stopTimer()
{
    if (_buttonTimer > 0)
    {
    KillTimer(ParentWnd, 66);
        _buttonTimer = 0;
    }
}

caption-focused.jpg

When using buttons of different sizes from the default, we have to let the OS know so that the hit testing can trigger internal events, like showing the tooltip when hovering over a caption button. This is done by storing the button sizes, doing relative hit testing, then passing the correct HIT_CONSTANTS flag through the result in WM_NCHITTEST:

case WM_NCHITTEST:
    _eLastWindowHit = (HIT_CONSTANTS)DefWindowProc(m.HWnd, m.Msg, m.WParam, m.LParam);
    _eLastButtonHit = hitTest();
    if ((_eLastButtonHit == HIT_CONSTANTS.HTCLOSE) ||
        (_eLastButtonHit == HIT_CONSTANTS.HTMAXBUTTON) ||
        (_eLastButtonHit == HIT_CONSTANTS.HTMINBUTTON))
    {
        m.Result = (IntPtr)_eLastButtonHit;
    }
    else
    {
        m.Result = (IntPtr)_eLastWindowHit;
        base.WndProc(ref m);
    }
    break;

The classes in the library are all set to public to allow independent access, but the primary interfaces are housed in the cRcm class. Addition of controls to the skinning engine is done through the Add method, with overloads for different control types. The Add method uses the EnumChildWindows API that returns child control handles through a delegate. These are then tested for their type, and added to dictionaries that store the control's skinning class instance with the control's handle used as a key. Controls can be removed either by group or by handle using the Remove methods.

private bool EnumWindow(IntPtr handle, IntPtr pointer)
{
    GCHandle gch = GCHandle.FromIntPtr(pointer);
    List<intptr> list = gch.Target as List<intptr>;

    if (list != null)
    {
        list.Add(handle);
        return true;
    }
    return false;
}

private List<intptr> GetChildWindows(IntPtr parent)
{
    List<intptr> result = new List<intptr>();
    GCHandle listHandle = GCHandle.Alloc(result);
    try
    {
        EnumWindowProc childProc = new EnumWindowProc(EnumWindow);
        EnumChildWindows(parent, childProc, GCHandle.ToIntPtr(listHandle));
    }
    finally
    {
        if (listHandle.IsAllocated)
            listHandle.Free();
    }
    return result;
}

public void Add(ControlType ct, Bitmap thumb, Bitmap track)
{
    if (thumb == null)
        throw new Exception("Required image is either missing or invalid.");
    if (track == null)
        throw new Exception("Required image is either missing or invalid.");

    List<intptr> list = GetChildWindows(_hParentWnd);
    StringBuilder nameBldr = new StringBuilder(100);
    string ctlname = ct.ToString().ToLower();
    if (ctlname == "trackbar")
    {
        if (_oTrackBarSkin == null)
            _oTrackBarSkin = new Dictionary<IntPtr, cTrackBar>();

        for (int i = 0; i < list.Count; i++)
        {
            if (list[i] != IntPtr.Zero)
            {
                Control ctl = Control.FromHandle(list[i]);
                if (ctl != null)
                {
                    Type t = ctl.GetType();
                    string name = t.Name.ToLower();
                    if (name == ctlname)
                    {
                        _oTrackBarSkin.Add(ctl.Handle, 
                          new cTrackBar(ctl.Handle, thumb, track));
                        ctl.Refresh();
                    }
                }
            }
        }
    }
}

button-states.jpg

Buttons

The cButton class differs from the rest of the control classes in one significant way. All other classes are one instance per control, created automatically and stored in dictionaries by simply adding the control type and associated image(s) to the library:

_cRcm.Add(RCM.ControlType.Button, RCM_Harness.Properties.Resources.vista_command);

The button is added the same way as all the other controls, only the one class handles all controls of type Button, CheckBox, and RadioButton. That is because as I mentioned earlier, these are all the same .NET class, and all owner drawn buttons. The button works more like a managed user control, employing the control's Paint event to do the work, whereas the other controls are all rendered through their window procedures. It keeps track of the various objects by storing their handles and type using the SetProp/GetProp API:

public void Add(IntPtr handle)
{
    // store the control type
    SetProp(handle, "style", (IntPtr)buttonStyle(handle));
}

public void Remove(IntPtr handle)
{
    SetProp(handle, "style", (IntPtr)BUTTONPARTS.BP_UNKNOWN);
}

The command button style is rendered on whole by the class, but the CheckBox and RadioButton controls use an ImageList to draw their images; the text, however, is unaltered.

private void drawCheckbox(Graphics g, RECT bounds, int state)
{
    int height = _oCheckboxBitmap.Height;
    
    // horizontal offset
    int offset = (bounds.Bottom - height) / 2;
    RECT picRect = new RECT(0, 0, 16, 16);
    IntPtr hdc = g.GetHdc();
    IntPtr hbrush = CreateSolidBrush(GetPixel(hdc, 0, 0));
    FillRect(hdc, ref picRect, hbrush);
    g.ReleaseHdc();
    DeleteObject(hbrush);
    g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
    _oCheckboxIml.Draw(g, 1, offset, height, height, state);
}

This method also demonstrates the marriage between API and managed code used throughout these classes. Managed code is used in many places where rendering speed is not a crucial concern.

treeview.jpg

ScrollBars

There are, of course, two types of scrollbars, standalone components, and the scrollbars painted inside of compound controls like a treeview or combobox. Both are very different in that an internal scrollbar is not a scrollbar at all, but rather drawn into the DC of the control to simulate a scrollbar window. They are both alike in one respect though, they are nearly impossible to customize. If you are writing in straight C, you can simply over-paint the control with very little flickering, but this is just not possible given the overhead of .NET. They would flicker like a bad bulb. So, if you can't paint over them, you create a window that sits on top of them and draw on that! Using the CreateWindowEx API, you create an owner-drawn tool window, assign it as a child to the control (with a listview/treeview, etc.), or as a child of the parent. This keeps it anchored in place. Tool windows with these style settings are also click through, so mouse events fire as normal, with all painting done on the mask window. This allows us to skin scrollbars on any control with no flicker.

private void createScrollBarMask()
{
    Type t = typeof(cScrollBar);
    Module m = t.Module;
    IntPtr hInstance = Marshal.GetHINSTANCE(m);
    IntPtr hParent = GetParent(_hScrollBarWnd);
    RECT tr = new RECT();
    Point pt = new Point();

    GetWindowRect(_hScrollBarWnd, ref tr);
    pt.X = tr.Left;
    pt.Y = tr.Top;
    ScreenToClient(hParent, ref pt);

    _hMaskWnd = CreateWindowEx(WS_EX_TOPMOST | WS_EX_TOOLWINDOW,
        "STATIC", "",
        SS_OWNERDRAW | WS_CHILD | WS_CLIPSIBLINGS | WS_OVERLAPPED | WS_VISIBLE,
        pt.X, pt.Y,
        (tr.Right - tr.Left), (tr.Bottom - tr.Top),
        hParent,
        IntPtr.Zero, hInstance, IntPtr.Zero);

    // activate
    SetWindowPos(_hMaskWnd, HWND_TOP,
        0, 0,
        0, 0,
        SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOOWNERZORDER);
}

column-filter.jpg

ListView Headers

Here is something I have seen tried many times, but rarely with good results. The trick is to get the header handle from the listview using SendMessage, then blit the columns within a PAINTSTRUCT; this renders the columns without flicker.

private IntPtr headerWnd
{
    get { return (SendMessage(_hListviewWnd, LVM_GETHEADER, 0, 0)); }
}

protected override void WndProc(ref Message m)
{
    PAINTSTRUCT ps = new PAINTSTRUCT();
    switch (m.Msg)
    {
        case WM_PAINT:
            if (!_bPainting)
            {
                _bPainting = true;
                // start painting engine
                BeginPaint(m.HWnd, ref ps);
                drawHeader();
                ValidateRect(m.HWnd, ref ps.rcPaint);
                // done
                EndPaint(m.HWnd, ref ps);
                _bPainting = false;
                m.Result = MSG_HANDLED;
            }
            else
            {
                base.WndProc(ref m);
            }
            break;

        default:
            base.WndProc(ref m);
            break;
    }
}

I have also added the filter button and sort arrows. The sort arrow uses the DrawThemeBackground API to render the arrow:

private bool drawThemeArrow(IntPtr hdc, Rectangle bounds, bool down)
{
    if (IsAppThemed())
    {
        IntPtr hTheme = OpenThemeData(GetParent(_hHeaderWnd), "Header");
        if (hTheme != IntPtr.Zero)
        {
            RECT tr = new RECT(bounds.Left, bounds.Top, bounds.Right, bounds.Bottom);
            // draw part
            DrawThemeBackground(hTheme, hdc, HP_HEADERSORTARROW, down ? 
                                HSAS_SORTEDDOWN : HSAS_SORTEDUP, ref tr, ref tr);
            CloseThemeData(hTheme);
            return true;
        }
    }
    return false;
}

What's Left?

There is simply no time to cover all of the controls in depth, but here is the rest of the line up so far: Command Button, CheckBox, ComboBox, ListBox, ListView, NumericUpDown, ProgressBar, RadioButton, ScrollBar, TabControl, TrackBar, TreeView... You might be asking 'but what about the menu/status/tool bar controls?' Well, I kind of covered those in vtExtender, so not a high priority right now. They are also a bit involved (especially menus - eech), but I'll try and get to them sometime soon. What I am working on now, started the way things usually do, looking at the fading buttons and pulsing progress bars and asking: How do they do that? I think I know, and am writing another class for this library currently. I might also add corners with odd shapes to the form skinner, and do a little tweaking here and there, we'll see.. enjoy.

Change Log

June 21 - Well, I got around to installing XP this weekend, and what a nasty surprise! Checkboxes and radiobuttons not working at all (a minor change to repair), and scrollbar buttons not working via SendMessage (though it works fine in Vista). Worst of all, when passing over the form for the first time, the caption bar groupbox does a nasty flicker. This happens when the WM_SETCURSOR message notifies the window to redraw the area, but only the first time the message is sent. The trick was to modify the invalidateWindow function by adding the RDM_ERASE message to erase the caption area, then call it after the cursor is changed.

case WM_SETCURSOR:
    if (!windowMaximized)
        base.WndProc(ref m);
    else
        m.Result = MESSAGE_PROCESS;
    invalidateWindow();
    break;

The WM_NCLBUTTONDOWN message response was also modified to consume the message if the relative button position was not hit tested, but the system coordinates of the button are a match (say, if your button sizes or offsets differ from the defaults).

  • June 23 - Added a couple more fixes for XP, form flickered when menu was invoked by clicking the icon on the caption bar.
  • June 24 - Added a resizing method for the scrollbar mask in cScrollbar to keep them in sync with parent control.
case WM_SIZE:
case WM_MOVE:
    sizeCheck();
    base.WndProc(ref m);
    break;
private void sizeCheck()
{
    RECT tr = new RECT();
    RECT tw = new RECT();
    GetWindowRect(_hMaskWnd, ref tw);
    GetWindowRect(_hScrollBarWnd, ref tr);
    if (!EqualRect(ref tr, ref tw))
        SetWindowPos(_hMaskWnd, IntPtr.Zero, 
        tr.Left, tr.Top, tr.Right - tr.Left, tr.Bottom - tr.Top, 
        SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOZORDER);
}

A similar routine was added to the cInternalScrollBar class.

The new code has been uploaded in version 1.4. I'll see if I can get a chance to test it on Windows 7 soon.

License

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

About the Author

Steppenwolfe
Network Administrator
Canada Canada
Member
No Biography provided

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
BugScroll bar Bugmemberyuzhu13 Dec '12 - 3:17 
TreeView,RichTextBox and Panel'Scrollbar have bug.
SuggestionComboBox's Scrollbar no skinmemberyuzhu12 Dec '12 - 20:41 
Very usefull control.
and Can you enhance Combobox's scrollbar skin?
thanks.
QuestionUsing forms shown event handler casue delay in drawing framesmemberMember 456089121 Nov '12 - 6:31 
I'm finding if I put a lengthy process in my shown event handler the custom frames don't get drawn until after the shown event completes. Is there any way around this?
AnswerRe: Using forms shown event handler casue delay in drawing framesmemberSteppenwolfe21 Nov '12 - 6:41 
doevents
BugBug :( [modified]memberiprog224 Jul '12 - 2:19 
When form is maximized, I can move and/or resize it as well as if the form wasn't maximized.

modified 24 Jul '12 - 8:42.

GeneralMy vote of 5memberiprog224 Jul '12 - 0:18 
Very helpful! Thanks!
QuestionMessageBox.Show() Problem!memberAntal Dominik30 Apr '12 - 8:52 
Hello there.
I dont know if anyone else had this problem before.
Here is what I did: I used your form RCM_Harness made it parent to my form.
Like MyForm : RCM_Harness
Now when I use MessageBox.Show("asd","asd") my UI stops updating. My program still runing, but I cant see any changing on my UI, no interraction, nothing.
please help me! Otherwise It's an awesome program! Roll eyes | :rolleyes:
AnswerRe: MessageBox.Show() Problem!memberSteppenwolfe30 Apr '12 - 10:42 
just a caveat, probably needs a repaint during focus events. The best solution is to use your own custom message box..
QuestionResources can not be Disposed [modified]memberzwb110130 Dec '11 - 23:00 
GDI object number will Achieve 10000 ,and the app will be killed~
you can use timer test:
 
frmMain fMain = null;
private void btnTest_Click(object sender, EventArgs e)
{
timerTest.Enabled = !timerTest.Enabled;
}
 
private void timerTest_Tick(object sender, EventArgs e)
{
timerTest.Enabled = false;
if (fMain != null)
{
fMain.Close();
}
fMain = new frmMain();
fMain.Show();
timerTest.Enabled = true;
}
 
Thank you
Who knows most speaks least


modified 31 Dec '11 - 8:53.

AnswerRe: Resources can not be DisposedmemberSteppenwolfe31 Dec '11 - 7:23 
So.. you're writing application that will load a 1000 forms? If there is a gdi leak somewhere, you have source code, fix it..
GeneralRe: Resources can not be Disposed [modified]memberzwb110131 Dec '11 - 23:04 
testing and fixing,I Suspect that the problem is to use the API,for example handle,DC haven't be released~
Who knows most speaks least


modified 1 Jan '12 - 8:24.

GeneralRe: Resources can not be DisposedmemberSteppenwolfe1 Jan '12 - 5:47 
It is normal that some objects are not instantly disposed, (though there is a dispose call in library that should be called when form closes), particularly in a graphics project of this size, but windows disposes of them automatically after application is closed. Resources are probably gdi objects, pens, brushes, bitmaps etc.
GeneralSome bugs [modified]memberAngleV30 Apr '11 - 23:04 
First of all let me give you my compliments about your excellent code here.
 
I have discover some bugs.
 
1.When you maximize the form and then restore it sometimes, not always you can see the default style of buttons drawing uppon custom drawing buttons quickly making it flicker a bit when you hover the mouse on the caption bar.
This happens only in case of maximize and restore.
 
2. In Vista the caption bar buttons are bigger than custom and when you place the mouse a little left than minimize button you can see the Minimize tooltip appear. Try to doubleclick and nothing happens just as the mouse thinks that is over the minimize button.
 
3.Minimize the form. Right click on taskbar and choose Maximize.
You can see the Maximize button has wrong picture and when you click it does nothing.
The WindowMaximized variable does not take the true value. It needs to interecept the Maximize action from Wndproc to change the variable value.
 
case WM_SYSCOMMAND:
int command = m.WParam.ToInt32() & 0xfff0;
if (command == 0xF030)
{
WindowMaximized=true;
}
if (command == 0xf120)
{
WindowMaximized=false;
}

modified on Sunday, May 1, 2011 5:13 AM

GeneralMaximize Question [modified]memberAppreciative User26 Apr '11 - 14:11 
I voted 5 -- marvelous project, well explained, so cool!
 
(Whoops!   I needed to edit this message...I was erroneously changing frmMain.VB to Maximized and running frmMian.CS.   Sorry.)
 
Of course I have a question as well.  
 
Have been playing around it with a class and a base form in a little program.  
 
Was just about to move it into a larger program when I realized that if I make a form Maximized WindowState in Designer, the Maximize/Minimize buttons don't work properly.
 
If I have a NORMAL WindowState, they work superbly.
 
Do you have any suggestions on what I can do to solve this?
 
I am using this full version and not your lite version because I would like the theming of controls as well as forms.   That's just so neat.
 
If you can tell me where to look in the code, I'll play with it and try to post a fix.
 
Thanks in advance...and thanks for taking the time to do this!
 
BTW, my DataGridView is now jealous of all it's control "competitors" because they're themes and it's not!  
 
But I can understand that a Datagridview would probably be a bear to theme.   So many possiblities for columns!
 
Appreciative User
 

 
-- Modified Tuesday, April 26, 2011 8:37 PM
AnswerRe: Maximize QuestionmemberAppreciative User27 Apr '11 - 17:20 
Perhaps there is a better solution, but for someone trying to solve the "Maximize Question", here's what I did.
 
The problem appeared to be in MaximizeWindow in cRcM.cs.  
 
If you replace the If statement in MaximizeWindow with the following, I think you'll see the problem disappear.   It seemed to work okay for me...with minimal testing.
 
To test it's working properly, make your form Maximized and then restore down to Normal.   Then reverse...restore form back from Normal to Maximize.   This was where problem occurred before.
 

   private void MaximizeWindow()
            {
                  if (WindowMaximized)
                  {
 
                     ///------------- Changes start here -----------------------
 
                     ///We note lots of 0 values in _tRestoreRect if Form STARTS as maximized.
                     ///Must determine WHAT Normal size and location SHOULD have been.
 
                     //Grabs instance of Form
                     Form f = (Form)Form.FromHandle(ParentWnd);
                 
                     //Are we having a problem?  
                     //Remember, we don't ALWAYS have a problem, only when STARTS as
                     //Maximized form.   Normal forms are A-OK.
                     //So, let's say we do this "fix" when bottom - top = 0...
                     //Height of 0 would be a VERY short form!                       
                     //Problem is that _tRestoreRect has 0 values, in general!
 
                        if (_tRestoreRect.Bottom - _tRestoreRect.Top == 0)
                        {
                              _tRestoreRect.Left = f.RestoreBounds.Left;                                
                              _tRestoreRect.Right = f.RestoreBounds.Right;                             
                              _tRestoreRect.Bottom = f.RestoreBounds.Bottom;                                                  
_tRestoreRect.Top = f.RestoreBounds.Top;
                        }
 
                     ///------------- Changes end here -----------------------
 
                        //windowMessage(POST_MESSAGES.SC_RESTORE);
                       
                        SetWindowPos(ParentWnd, IntPtr.Zero,
                              _tRestoreRect.Left, _tRestoreRect.Top,
                              _tRestoreRect.Right - _tRestoreRect.Left,
                              _tRestoreRect.Bottom - _tRestoreRect.Top,
                              SWP_NOACTIVATE | SWP_NOOWNERZORDER);
                        WindowMaximized = false;
                  }
 
Hope this helps someone!
 
Appreciative User
GeneralMy vote of 5memberAppreciative User26 Apr '11 - 14:04 
It is something I've been looking for for ages! Marvelous and easy to use...and he explains what he does! Very clever. There are still Winform programmers out there -- without going into WPF, this gives Winforms a tremendous facelift! Thank you!
Questiona bug about maximizememberchen.jian.cn@hotmail.com28 Feb '11 - 16:41 
Steps:
1、 Normal the form, not maximize, show Maximize image
2、 Maximize the form, show Restore image
3、 Change the skin, show Maximize image(Oh...NO....NO....NO); even can't restore the form;
warning: After the step 3, the button should show restore image, not the maximize image
 
Who knows why ... how to fix it?
Generala bugmemberniaoked2 Dec '10 - 20:07 
first minisize of form,and then restore it.
more times.
and you can see the form being smaller and smaller...
GeneralRe: a bugmemberSteppenwolfe3 Dec '10 - 5:27 
fix is in rcm library included in personal wave recorder project. look at wndproc wm_nccalcsize and wm_syscommand:
                case WM_NCCALCSIZE:
                    if (m.WParam != IntPtr.Zero)
                    {
                        NCCALCSIZE_PARAMS ncsize = (NCCALCSIZE_PARAMS)Marshal.PtrToStructure(m.LParam, typeof(NCCALCSIZE_PARAMS));
                        WINDOWPOS wp = (WINDOWPOS)Marshal.PtrToStructure(ncsize.lppos, typeof(WINDOWPOS));
                        // store original frame sizes
                        if (!_bStoreSize)
                        {
                            _bStoreSize = true;
                            _iCaptionHeight = ncsize.rect2.Top - ncsize.rect0.Top;
                            _iFrameHeight = ncsize.rect0.Bottom - ncsize.rect2.Bottom;
                            _iFrameWidth = ncsize.rect2.Left - ncsize.rect0.Left;
                        }
                        if (!_bResetSize)
                        {
                            ncsize.rect0 = CalculateFrameSize(wp.x, wp.y, wp.cx, wp.cy);
                            ncsize.rect1 = ncsize.rect0;
                        }
                        Marshal.StructureToPtr(ncsize, m.LParam, false);
                        m.Result = (IntPtr)WVR_VALIDRECTS;
                    }
                    else
                    {
                        RECT rc = (RECT)m.GetLParam(typeof(RECT));
                        rc = CalculateFrameSize(rc.Left, rc.Top, rc.Right - rc.Left, rc.Bottom - rc.Top); ;
                        Marshal.StructureToPtr(rc, m.LParam, true);
                        m.Result = MESSAGE_PROCESS;
                    }
                    base.WndProc(ref m);
                    break;
 
                case WM_SYSCOMMAND:
                    {
                        UInt32 param;
                        if (IntPtr.Size == 4)
                            param = (UInt32)(m.WParam.ToInt32());
                        else
                            param = (UInt32)(m.WParam.ToInt64());
                        Form f = (Form)Form.FromHandle(_hParentWnd);
                        if (f.FormBorderStyle == FormBorderStyle.Sizable || f.FormBorderStyle == FormBorderStyle.SizableToolWindow)
                            _bIsSizeable = true;
                        if ((param & 0xFFF0) == SC_RESTORE)
                        {
                            f.Height = _szStoreSize.Height;
                            f.Width = _szStoreSize.Width;
                            _bFirstHit = false;
                        }
                        else if (f.WindowState == FormWindowState.Normal)
                        {
                            _szStoreSize = new Size(f.Width, f.Height);
                            InvalidateClient();
                            //ForcedRefresh();
                        }
                        base.WndProc(ref m);
                        break;
                    }
 

GeneralRe: a bugmemberalbert.dc5 Oct '11 - 12:35 
Does not fare well in Win7 Aero. Try repeatedly clicking the Show Desktop button and you'll see the form getting thinner each time. Seen in PWR project as well.
Generalissue w/ LeftClick() callmemberalbert.dc25 Aug '10 - 3:00 
what's the purpose of this call?
 
in winxp, this generates a click outside the form border. if a menu happens to be in that position, the menu drops down. also, if Paint canvass happens to be there, a paint dot is drawn.
 
can the click be done on the form (by modifying the coordinates)?
GeneralRe: issue w/ LeftClick() callmemberSteppenwolfe25 Aug '10 - 3:33 
In xp, first click on some areas of titlebar after form is opened causes a partial erase, and unthemed buttons are shown. This is a workaround.
GeneralRe: issue w/ LeftClick() call [modified]memberalbert.dc5 Oct '11 - 12:46 
Happens in Win7 Classic mode also.
EDIT: Checking for Application.RenderWithVisualStyles on Win7 seems to have fixed this.
 
Also, somehow other forms don't benefit from the workaround. Not sure why, but seems it's clicking the wrong coordinates.

modified 5 Oct '11 - 19:13.

GeneralForm Loadmembermicina4 Jul '10 - 3:21 
Is it possible to remove the form change on form load then use the radio buttons to change the form and buttons etc?
 
Many thanks
GeneralRe: Form LoadmemberSteppenwolfe4 Jul '10 - 5:56 
Of course. Just make sure lib is instantiated but rem the call to skin form. It's all in the form example code.
GeneralRight to Left Supportmemberbguyb30 May '10 - 7:09 
Great job!
Maybe you can help me with "right to left" and "right to left layout" for the form ?
 
Cheers,
Biber G.
GeneralRe: Right to Left SupportmemberHSarabi14 May '12 - 1:50 
please could you help us in right to left support ?
GeneralI can't thank you enough!memberLarioso5 May '10 - 3:27 
Really cool to share this project with us.
 
The most tricky part with automatic scrollbars in MdiClient I hope to fix with the ideas you have in cinternalScrollbar.
 
Really, really nice.
 
For people like me sitting with VS 2005 I can share a little something
 
Open the csproj files with notepad.
Edit the lines
Save and then open in VS
 
for productversion change to 8.0.50727
 
and Import project to MSBuildBinPath instead of the other MSBuildToolsPath.
 
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
 
Then it compiles and runs in a sec.
 
Thanks mr Steppenwolfe
GeneralRe: I can't thank you enough!memberSteppenwolfe18 May '10 - 5:14 
glad to hear it helped you out..
QuestionIs there any way to applyfor dialog boxes like Message box, save as dialog and printer dialogmemberMember 271448727 Apr '10 - 21:32 
Is there any way to applyfor dialog boxes like Message box, save as dialog and printer dialog
GeneralForm instantiated by a themed form is unresponsive when themed form gets minimizedmemberalbert.dc30 Mar '10 - 21:42 
With the theme applied, a non-themed form that's instantiated by a themed form becomes unresponsive (e.g. does not respond to mouse/keyboard, buttons unclickable, form is unmovable) when the instantiating themed-form gets minimized.
 
Initially, I thought this to be a general winforms behavior, that's why I posted this on StackOverflow[^]

But then, I tried commenting out the method that applies the theme and the problem never occurred.
 
So is there a specific message in cRCM.WndProc, perhaps, that causes this effect on forms instantiated by a themed form?
GeneralRe: Form instantiated by a themed form is unresponsive when themed form gets minimizedmemberSteppenwolfe31 Mar '10 - 3:32 
Probably a message being consumed incorrectly.. look at activate/ncactivate
QuestionIs it possible to skin buttons differently on a single form?memberalbert.dc2 Mar '10 - 3:53 
As part of the design requirement, we need to have groups of buttons skinned differently. How do I make this possible using the RCM library? Or is this possible at all?
 
Thanks!
AnswerRe: Is it possible to skin buttons differently on a single form?memberSteppenwolfe2 Mar '10 - 4:47 
different forms can use different skin, otherwise no.
GeneralCode CorrectionmemberSteppenwolfe1 Mar '10 - 11:56 
Add two class level int' _iStoreWidth/_iStoreHeight and this code to the proc in cRcm to correct a resizing error:
                case WM_SYSCOMMAND:
                    {
                        UInt32 param;
                        if (IntPtr.Size == 4)
                            param = (UInt32)(m.WParam.ToInt32());
                        else
                            param = (UInt32)(m.WParam.ToInt64());
                        Form f = (Form)Form.FromHandle(_hParentWnd);
                        if ((param & 0xFFF0) == 0xF120)
                        {
                            f.Height = _iStoreHeight;
                            f.Width = _iStoreWidth;
                        }
                        else if (f.WindowState == FormWindowState.Normal)
                        {
                            _iStoreHeight = f.Height;
                            _iStoreWidth = f.Width;
                        }
                        base.WndProc(ref m);
                        break;
                    }
 

GeneralRe: Parameter is invalid thrown in drawTitlememberSteppenwolfe1 Mar '10 - 6:31 
Are any of those params null?
GeneralRe: Parameter is invalid thrown in drawTitle [modified]memberalbert.dc1 Mar '10 - 7:44 
None of them are null.
 
Thanks.
 
Hi,
 
I integrated this w/ our current project its quite cool that this was able to skin our forms w/ simple calls. One thing we're getting though is that every time we relaunch a form, it throws an invalid parameter error in drawTitle, specifically, this line
g.DrawString(title.ToString(), _oTitleFont, ht, captionBounds, sf);
 
I'm really a winforms newbie; I can only understand part of the code, so I have no clue what could be causing this. I tried googling around and some mentioned that disposing the brush will cause the next call to break (it does not make sense to me because not disposing it will make it leak right?).
 
Anyway, I could really use some help, or at least point me to where I can get advise.
 

Thanks a bunch!
modified on Monday, March 1, 2010 1:57 PM

GeneralRe: Parameter is invalid thrown in drawTitlememberSteppenwolfe1 Mar '10 - 10:30 
Relaunch a form; does that mean closing and re-opening it again? Make sure that 1) there is text there 2) that the library instance is properly disposed and re-instantiated each time form is closed and re-opened. I would suggest that you take another look at the example project to see if anything differs. Also, there is a version of this here that only skins the form, (might make for an easier debug).
 
hth
j
GeneralRe: Parameter is invalid thrown in drawTitlememberalbert.dc1 Mar '10 - 15:59 
Yes, closing then opening the child form again causes it to throw the exception. On one form, the error is thrown on the specific line I pasted; on another form, the same error is thrown on the InitializeComponent in the form's Designer.cs file (a line that instantiates a textbox).
 
I'll try and have another look at the example.
 
Thanks.
GeneralRe: Parameter is invalid thrown in drawTitlememberalbert.dc1 Mar '10 - 18:45 
The error went away after I removed the line (in my form) that sets the _cRcm.TitleFont = this.Font. Where's the appropriate place to set this then?
GeneralRe: Parameter is invalid thrown in drawTitlememberalbert.dc1 Mar '10 - 18:49 
I just realized why the error occurs; TitleFont is being disposed with _cRcm...
GeneralMy vote of 1memberSledgeHammer0127 Feb '10 - 16:06 
Umm... No offense, but .NET already has built in skinning support... its called WPF. Its about 1000x more powerful then what you've done here. Thats probably why you didn't get much interest.
GeneralRe: My vote of 1memberSteppenwolfe28 Feb '10 - 2:39 
I'm not sure why you felt the need to leave four flames on this project, but as I expected you have no published projects of your own.
This was started before the release of 3.5 .Net, so WPF was not an option. As for WPF, I am becoming quite fluent in its use, however, I do find it still has many limitations. WPF works behind the scenes just as this project does, employing subclassing and gdi to do the heavy lifting, its real purpose was as a wrapper to make these tasks accessable to all levels of programmers. Though this is just a hobby for me, I work with seasoned graphics programmers, and they tend to consider WPF as a toy, a tool for amateur programmers, and still write their graphics code in unmanaged c++. This was intended as a teaching device, for people who were curious about the inner workings of forms and controls, or as a foundation/example to how some of these tasks might be accomplished. But if you require automation to get these things done, and aren't willing to learn the hows and whys, then I suppose WPF is the only solution available to you..
GeneralRe: My vote of 1memberSledgeHammer0128 Feb '10 - 7:23 
1) You are correct, I have no published code on CodeProject. I am a professional programmer and do not give out my work for free. Also, my code is owned by the companies I work for.
 
2) I have 16+ years experience in C/C++ MFC / Win32 / GDI. I know those technologies inside and out at the EXPERT level (I suspect even at a higher level then you). I served as lead developer / designer on a custom control UI library that did / does what CodeJock and BCGSoft do (better I think). The point is, I am fully knowledge of unmanaged C++ GDI code. BUT, I have left it and MFC behind to move on to WPF (I actually skipped WinForms because the performance was not there) -- because I would like to stay employed and nobody uses MFC / C++ anymore except for legacy code.
 
3) To say WPF is a toy and for amatuers shows your ignorance. You can do things in WPF that are simply not possible even by the best of C++ / GDI programmers without writing 10's of thousands of lines of code. Coming from 16+ yrs of C++ / GDI, I got sick of writing code like:
 
if (WinVista) { }
else if (WinXP) { }
else if (Win2k3 & NoThemes) { }
else if (Win2k3 & YesThemes) { }
 
which is what you have to do in GDI / Win32 to get all your pixels lined up. You often have to special case all the various modes and include fudge factors.
 
4) "Seasoned Graphics Professions who still write their graphics in unmanaged C++"... unless you are working for a game company, that sounds like a bunch of dinosaurs who refuse to change with the times.
 
I stuck with hardcore C++ / GDI programming up until around 2008 and avoided C# / Winforms like the plague.
 
Now I avoid C++ / GDI like the plague, because as I said, there is no work available in C++ / GDI and I would like to stay gainfully employed and I got tired of the tedious nature of it.
 
By the way, why do you have a hard-on for C++ / GDI when your project is Winforms?
 
And no, I was not "flaming" the project. I was commenting on it. If you can't take negative comments, you shouldn't publish your code on CodeProject. Were you expecting all rainbows and unicorns? Smile | :) .
GeneralRe: My vote of 1memberSteppenwolfe28 Feb '10 - 8:07 
That you had to comment on it -4 times- speaks volumes. Perhaps assuming a mentoring role, even authoring a couple of tutorials on these emerging technologies, (rather then just slinging barbs), might be a better way to go..
As for Wpf, I think it may evolve to be quite cumbersome, mired down with endless layers of idiot proofing and bloated abstractions in the same way that .Net graphics is failing. Maybe not. Right now, I'll stick to what I know and keep an eye on the rest. I may though, author a couple of user controls in Wpf down the road, something I have been considering. I would ask though that you consider a more subtle style of argument if you expect a response from me in the future..
GeneralSmall problemmemberJohnny J.24 Feb '10 - 19:43 
I noticed a small problem with your test applications, both RCM_HarnessVB.exe and WindowsApplication1.exe:
 
When I start up the application, the slider button of the vertical scrollbar is halfway out of the scrollbar bounds.
 
Don't know if that's a problem with your skinning library or something else, but you might want to have a look at that?!?!?
 
/Johnny J.
GeneralRedrawing(Bottom) and Layered Formmemberzerget15 Jan '10 - 7:38 
Hi
First you do a great job with this developThumbs Up | :thumbsup: Cool | :cool: .
Second sorry for my ortography lol D'Oh! | :doh:
 

1.-
i was checking the code and try to use it with an other skin but when i select the Form and then select other windows like a notepad, the windows form lost the focus and the redrawing event redraw bad, i dont know what happen that the botom corner left and bottom corner right not redrawing ok.
 
i try to set an other image neer to the top left corner usin bitblt and when the screen display its ok but when i select an other windows the image disapears and i saw a black rectangle, i gess its the same that happen with the bottom corners, but if i use the stretchblt the corners redraw good, its wear.
 
2.-
i was tryin to layeres the windows form and redraw the skin and it works very well even with some controls with "UpdateLayeredWindow", but i got the problem that when i click the icon in the task bar to minimize the form, the form minimize but the no the "No-client-area" it still on screen and if i set the cursos over the "No-client-area" the screen clean because the "WM_NCACTIVATE" triggered, i trace the event and stop in this part:
WM_MOVE 0X3 WP 0
WM_GETTEXTLENGHT 0X210B8C WP 0
WM_GETTEXT 0X201B8C WP 0X5
WM_SIZE 0X5 WP 0X1
 
and if i use the minimize button or menu/minimize do the next:
WM_MOVE 0X3 WP 0
WM_GETTEXTLENGHT 0X210B8C WP 0
WM_GETTEXT 0X201B8C WP 0X5
WM_SIZE 0X5 WP 0X1
WM_NCACTIVATE 0X86 0
WM_ACTIVATE 0X6 200000
WM_ACTIVATEAPP 0X1C 0
 
i still looking what happend, because and other problem when i layered the windows and redrawing the controls is that the Left Docking in controls doesn´t work well when i minimize the form when i click the icon in the taskbar to minimize the Form.
GeneralRe: Redrawing(Bottom) and Layered FormmemberSteppenwolfe15 Jan '10 - 8:52 
If you are using UpdateLayeredWindow, expect problems, because it is a slow and poorly implemented style. As for other thing, does it have same issue with original skin (corners not drawing), maybe your images are not formatted properly. If you are modifying code, always keep a clean original copy, and compare effects.
 
hth
j
GeneralRe: Redrawing(Bottom) and Layered Formmemberzerget15 Jan '10 - 11:31 
about the problem with the bottom border corners in the original code do the same, the effect in the vienna corner doesn´t look alot but if you see very well you will see a little movements.
 
some minutes ago i do some test and i fixed, i change this:
 
NEW
int offset = Focused ? 0 Blush | :O ;
 

OLD
int offset = Focused ? 0 :1;
 
and when the Form lost focus keep the skin without problems, and about the layered is a little bit fast when use PNG images Alpha32 bits and procces the image with "LockBits", but i still having the problem of the MIN if i add the WM_Size validating WParma == (IntPtr)1 fix the problem but that action freez a little the screen and i dont like how it looks.
GeneralRe: Redrawing(Bottom) and Layered FormmemberSteppenwolfe15 Jan '10 - 13:44 
Each frame part image is actually two images, the first half is the focused image, the second unfocused. offset is the multiplier for the offset into the frame image, 0 means it draws the first half of the frame image, 1 the second half. If frame is not painting properly when unfocused, it means you have not formatted your images correctly. Also, images must be same shape as a standard windows form. Look carefully at the example images included in the demo. If you want a transparent frame, use method I described to another user here, capture screen dc and blit that onto frame before frame images.

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

Permalink | Advertise | Privacy | Mobile
Web03 | 2.6.130523.1 | Last Updated 27 Feb 2010
Article Copyright 2009 by Steppenwolfe
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid