Implement the system scroll bar skin-changing function

Source: Internet
Author: User

HTTP://BLOG.SINA.COM.CN/S/BLOG_4C3538470100GEWS.HTML1 implements the system scroll bar skin-changing function

For the various control skin functions in Windows, it is most difficult to change the skin of the scroll bar, especially the system scroll bar of the control, such as the system scroll bar, such as edit, ListBox, ListView, TreeView, etc., in order to realize its custom skin function, There seems to be no way to achieve it by conventional means.

For general skin customization is usually achieved by customizing WM_PAINT, WM_ERASEBKGND, Wm_ctlcolorxxx, Nm_customdraw. However, the drawing of the system scroll bar, the normal, the Sun method does not work, Microsoft put a road block dead. According to my observation test, the system scroll bar has many messages that have been drawn, including Wm_ncpaint, Wm_ncmousemove, Wm_ncmouseleave, Wm_hscroll, Wm_vscroll, WM_KEYDOWN, wm_ MouseWheel and so on, some of these messages can be customized, but some cannot be customized. For example, Wm_hscroll can not be customized, if we deal with this message, we have to control the scroll bar range, position, paging value, and these values we usually do not have, Microsoft did not tell us, for the different controls it control the scroll bar exactly what strategy, do not know. If we do not customize this wm_hscroll, and the default processing it to perform the drawing of the scroll bar, this is really Jing, people helpless. Of course not completely no way, dead end. In order to overcome obstacles, climb obstacles, everyone has a different strategy, there is climbing climb, safety is not high, but also take the path detour, more laborious.

In the online search, I also carefully study, there are two basic ways to achieve the system scroll bar skin, one way is the hook API, that is, the way to intercept the API, there is a simulation method.

The interception API, in effect, modifies the operating system's API entry. Because the system draw scroll bar is through various drawing functions, such as setscrollinfo (), the basic system through this kind of function to achieve the drawing of the scroll bar, this API interception, that is, we write a setscrollinfo (), to achieve the drawing of the scroll bar in person, In order to achieve the custom drawing of the scroll bar, we want to achieve the skin appearance of various styles. There are two kinds of API interception: one is to modify the system loaded into the memory executable module import address, replace with the address of the pseudo API we wrote, so that the API call can automatically jump to this pseudo-API, there is a direct modification of the API function at the first address of the machine instructions, save the scene, write the Jump command, Enables the system to automatically jump to the pseudo-API we write when it executes to this API. What I want to say is that this interception method is really a little scary, there are security problems, the virus often do this kind of thing. This type of brute-force modification of the operating system, it is easy to cause the system firewall or anti-virus software error, windows in principle does not allow this kind of thing; second, once the program that uses this method crashes due to an exception, the original modification to the operating system is not restored, which may harm the stability of all processes in the system, Causes a panic or run exception. For commercial engineering development, often taboo this point, are inclined to the pursuit of stability, usually ning with the clumsy method, do not play skillfully skill; its three, this method of thread synchronization problem: Because you modify the program instructions at the same time, if the other threads in the process is executed here, the problem will burst out, the single CPU may not have any problems, system memory and CPU buffering can be synchronized, but the multi-core system is hard to say; In addition, this method has a portability problem: in one version of the system, through the interception API is valid, in another version of the system, it is not necessarily effective, after all, Microsoft to achieve the scroll bar drawing, it does not specify which function to implement, Maybe it has another idea in the new system, and API blocking is not going to work.

Below I want to elaborate is the simulation method, this method does not have to intercept the API, all the technical implementation is bound in the system to allow the scope, I meticulous when the law-abiding citizen.

The so-called simulation method is to place a simulation window in the area of the system scroll bar, which is designed to draw the system scroll bar. Of course, we also want to intercept several messages with a ScrollBar control to ensure that the simulation window is drawn correctly. The following is an example of a ListView control's horizontal scroll bar, where vertical scrollbar skinning can be pushed.

First we will create a simulation window on the area of the scrollbar, covering the scroll bar exactly:

HWND Hlistview = ...; Handle to//listview window

HWND pWnd =:: GetParent (Hlistview);

HWND Hbuddy =:: CreateWindowEx (Ws_ex_noactivate, "Buddy_window", "", ws_clipsiblings| ws_disabled| Ws_child, 0, 0, 0, 0, pWnd, NULL, gmodule, NULL);

Buddy_window is your registered simulation window class. Note that the window must have ws_disabled style, this is a key, this style can ensure that the mouse operation through the simulation window, and directly control to the covered scroll bar, simulation window itself does not accept any mouse keyboard input. In addition to read the scrollbar rectangle and its various elements of the visual, usable, and pressure under the state can be done through the GetScrollBarInfo () this API, but to illustrate a point, this API some bugs, we can download freecl 2.03 version of the source code, which fixed the problem.

After you finish creating the simulation window, you will install a window procedure that you write to the ListView to intercept various messages that cause the ScrollBar property to change:

LRESULT CALLBACK Mylistviewproc (HWND hwnd, UINT MSG, WPARAM WPARAM, LPARAM LPARAM)

{

Case WM_LBUTTONDOWN:

......

Case WM_LBUTTONDBLCLK:

......

Case Wm_ncmousemove:

......

Case Wm_ncmouseleave:

......

}

......

Goldlistviewproc = (Wnnproc):: GetWindowLong (Hlistview, GWL_WNDPROC);

:: SetWindowLong (Hlistview, GWL_WNDPROC, LONG (&mylistviewproc));//Installation window procedure

Message Processing :

First of all we want to intercept the Wm_nclbuttondown and WM_NCLBUTTONDBLCLK two messages, when you press the mouse on the scroll bar, immediately trigger the Wm_nclbuttondown, if you press the mouse twice in a row, there is also a wm_ Nclbuttondblclk. Note When you double-click only one Wm_nclbuttondown message, instead of two. The second mouse button will appear with a wm_nclbuttondblclk. In fact, these two messages we can wait and see, and do the same deal. When the system processes this two messages, it triggers many other messages in its internal processing, including several wm_hscroll, wm_capturechanged, and Wm_ncmouseleave. We are mainly dealing with wm_hscroll, because it is the most valuable. After you release the mouse, the system sends and processes the last Wm_hscroll, which is returned from the Wm_nclbuttondown or WM_NCLBUTTONDBLCLK:

if (msg = = Wm_nclbuttondown | | msg = = WM_NCLBUTTONDBLCLK)

{

Note that the default processing will have n wm_hscroll messages, and this call will not return until your push-drag operation is complete.

Inside this call, I estimate that the system will go into a message loop, because after holding down the left button, Wm_ncmousemove

And Wm_nclbuttonup are no longer triggered. The internal estimate is to capture the wm_ncmousemove message, which repeatedly refreshes the scrolling

box position, if necessary you can install the mouse hook to capture the mouse to move the message, in time to refresh the simulation window in the scroll box

The location. If you just respond to the wm_hscroll message, you may feel that the drag and drop of the scroll box is relatively stagnant and not smooth.

SetWindowsHookEx (...);

Lresutl code =:: CallWindowProc (Goldlistviewproc, Hlistview, MSG, WParam, LParam);

UnhookWindowsHookEx (...);

return code;

}

I suggest you go to Microsoft's website to download the Controlspy 2.0 gadget, which is useful for monitoring the control's messages.

Next we look at the Wm_hscroll message, this message is usually generated when the system processes Wm_nclbuttondown or WM_NCLBUTTONDBLCLK, but sometimes it may be deliberately sent by the program, and scroll bar operation is not related.

if (msg = = Wm_hscroll)

{

:: CallWindowProc (Goldlistviewproc, Hlistview, MSG, WParam, LParam);

1) Reading the data of the scroll bar

2) then draw the scroll bar on the simulation window

//......

return 0;

}

There are also many other messages that can cause changes in the scrollbar properties, such as the user's keyboard action, the Mouse wheel action, which may cause the thumb position of the scrollbar to change, resulting in wm_keydown and WM_MouseWheel, respectively, The system then performs the drawing of the scroll bars in these two messages, so you have to intercept them, but the processing method is the same as Wm_hscroll, which is no longer verbose.

When we do not press the left mouse button, just simply move the mouse in the scrollbar, we will find that the scrollbar button and the thumb will automatically highlight, in fact, these changes are the system in Wm_ncmousemove and Wm_ncmouseleave in the drawing done. For example, when the mouse enters the thumb, it is highlighted, when the mouse left it, it turned gray, you have to deal with the Wm_ncmousemove and wm_ncmouseleave the two messages respectively. Note that only the ListView can have wm_ncmouseleave messages after the theme style is applied, the traditional style of the ListView does not have this message, only the wm_ncmousemove message, so processing highlighting is really a bit troublesome. It is because of the differences between the theme style and the traditional style that I recommend that you also handle wm_themechanged messages to identify different theme patterns and achieve different highlighting. Looking at the graph above, the TreeView scroll bar is themed, but the ListView scroll bar is traditional style, but I added a skin-changing function.

In addition, we should intercept the Wm_ncpaint that handles the ListView, remove the scrollbar's area, and not let it draw a scroll bar. Although the scrollbar is obscured by the simulation window, it is still necessary to do so to prevent unexpected occurrences:

if (msg = = Wm_ncpaint)

{

HRGN wrgn = NULL;

RECT sbrect = Getscrollbarrect ();//a function that reads a scrollbar rectangle

HRGN Srgn =:: CreateRectRgn (Sbrect.left, Sbrect.top, Sbrect.right, Sbrect.bottom

if (WParam = = 1)

{

RECT Wrect;

:: GetWindowRect (Hlistview, &wrect);

Wrgn =:: CreateRectRgn (Wrect.left, Wrect.top, Wrect.right, wrect.bottom););

Wrgn =:: Combinergn (Wrng, Wrgn, Srgn, Rgn_diff);

}

Else

{

Wrgn = (HRGN) WParam;

Wrgn =:: Combinergn (Wrng, Wrgn, Srgn, Rgn_diff);

}

::D eleteobject (SRGN);

:: CallWindowProc (Goldlistviewproc, Hlistview, Wm_ncpaint, WPARAM (WRGN), 0);

if (WParam = = 1)

::D eleteobject (WRGN);

return 0;

}

Also, intercept the wm_windowposchanging message, as the position, size, and Z order of the ListView should be adjusted in time to adjust the position, size, and Z Order of the simulation window. Why do you want to do it in wm_windowposchanging instead of wm_windowposchanged or Wm_move or wm_size? Because it is handled in wm_windowposchanging, you can avoid some drawing problems by allowing the simulation window to adjust its position, size, and Z before the ListView. In fact, I used both wm_windowposchanging and wm_windowposchanged two messages in the FREECL. Adjusts position, size, Z order when handling wm_windowposchanging, and forces the simulation window to be redrawn when wm_windowposchanged is processed.

Finally, it is also stated that the system may cause its scrollbar to automatically disappear or appear automatically because the user changes the size of the control, or to adjust the available state of the scrollbar, such as the edit control where you pull the width to support multiple lines of display, which causes the ScrollBar arrow button to become dimmed and the thumb to disappear. These actions are usually done in wm_windowposchanged, so this message also needs to be intercepted:

if (msg = = wm_windowposchanged)

{

LRESULT lreturn =:: CallWindowProc (Goldlistviewproc, Hlistview, wm_windowposchanged,

WParam, LParam);

if (:: Getnextwindow (Hbuddy, gw_hwndnext)! = Hlistview)

{//Adjust the z-order of the simulation window to make sure it fits snugly on the listview

HWND Habove =:: Getnextwindow (Hlistview, Gw_hwndprev);

UINT flag = swp_noactivate| swp_noredraw| swp_nomove| swp_nosize| swp_nosendchanging;
:: SetWindowPos (Hbuddy, habove?habove:hwnd_top, 0, 0, 0, 0, flag);

}

Tests whether the explicit state of the scrollbar changes and adjusts the implicit state of the simulation window.

If the scrollbar is still showing or the scrollbar is showing changes in the display state of some elements or the position of the control, the size may change,

Force redraw of the simulation window

//......

return lreturn;

}

Finally, the Wm_showwindow and Wm_destroy are processed, and when the ListView is hidden, the simulation window disappears, and when it is displayed, the simulation window is displayed along with it. It is important to destroy the simulation window when the Wm_destroy is triggered when the ListView is destroyed.

Generally speaking, the simulation method is quite troublesome, but more safe, high reliability. For developers, is the message of the specific processing, is the conventional means, for the end user, the implementation of this method of scroll bar self-painting, fully meet the requirements, with full freedom, and even can add more specific features of the scroll bar, such as can be added to the above other buttons, it is added animation, Plus advertising is also possible.

2 tips for customizing the background color and background bitmap of the control.

The first thing to say about the control's drawing process is that when an area of the control needs to be redrawn, WM_ERASEBKGND and WM_PAINT messages are triggered. For example, an area of a control is blocked by another program's window, and then the window is removed, and the content that is blocked by the control needs to be redrawn.

The first step: the system sends a WM_ERASEBKGND message to the control to implement the erase work of the background (sometimes not sent, such as the user may call InvalidateRect (), whose parameters specify not to erase the background, so there is no such message);

The second step: the system sends a WM_PAINT message to the control's window procedure, and the control performs a selective action to trigger the next three steps when handling the WM_PAINT message;

Step three: For some standard controls, such as button, Edit, ListBox, ScrollBar, and static controls, it also sends wm_ctlcolorxxx to the parent window (Wm_ctlcolorbutton, Wm_ctlcoloredit , Wm_ctlcolorstatic, Wm_ctlcolorlistbox, Wm_ctlcolorscrollbar, and so on), these messages return a brush handle, and the system takes the brush handle further to paint its own background. It also found that TrackBar also sent wm_ctlcolorstatic messages to the parent window, and the TreeView was in some cases, but I did not see where Microsoft was explaining it; I often find that many people like to return a null_ to the system when handling this message. Brush the empty brush, thought that the system will not put the previous steps to paint a good background cover, in fact, some controls do not cover, some have problems, like trackbar is so, be careful.

Fourth step: for menus and many standard controls, such as button, Edit, ListBox, Static, combobox it may send Wm_measureitem and wm_drawitem messages to the parent window, plus the Universal Control tab, StatusBar , ListView, header may also have wm_drawitem messages, but for most common controls, such as the TreeView, ListView, Rebar, Trackbar, toolbar, and so on, it sends many of its IDs to the parent window nm_ Customdraw's WM_NOTIFY message. For these two kinds of messages, the user is actually required to perform their own drawing work on the background that has been painted well.

Fifth step: When the control Wm_drawitem or WM_NOTIFY message is not handled by the user, the system will do its own default drawing work, the control is drawn out, this step is not overloaded.

Knowing these steps, it's probably clear to us that we know how to customize the background color and background bitmap for the control. In general, we customize the first step, the third step to achieve their own special background, custom fourth to implement the special drawing of the control itself. Even we can take the entire overloaded control the first step of the WM_ERASEBKGND message and the second step of the WM_PAINT message, the control background and the control draw all by themselves, no one said it would not work. Note, however, that the next 3 steps do not occur when you implement the overloaded handling of WM_PAINT messages.

Custom colors are easy to handle WM_ERASEBKGND, Wm_ctlcolorxxx, Wm_drawitem, wm_notify messages based on the type of control, and in general, custom WM_ERASEBKGND and Wm_ Ctlcolorxxx on the background bitmap a lot of control is particularly troublesome, like a ListBox, you paint the background bitmap, the result because the user action scroll bar or mouse wheel or press the arrow keys, background bitmap also occurs scrolling, this has to redraw the bitmap, for the background color does not have this problem, No matter how you scroll, the color is the color, the bitmap is not, you need to reload the various messages of the scrolling operation to achieve the bitmap redraw. Microsoft does not seem to assume that you will modify the background bitmap of the control, it is not prepared for this situation, always do not hesitate to perform scroll operations on the control canvas. The method of actually implementing the background bitmap often forces us to intercept the various actions that cause the window content to scroll, so the function implementation of the bitmap background is always not very normative and not so credible.

Finally, if the parent control also contains child controls with background transparency, you should reload the parent control's WM_ERASEBKGND message, otherwise the child controls of those transparent backgrounds might not have the correct background content.

The above steps are my personal understanding, not necessarily to Oh! For reference only.


3 Implementing a transparent background for a control

In many cases, we need the background of the control to be transparent, which requires that the background color of the control's parent window, the background bitmap, such as the label control, the Radio radio control, and the check control, usually require drawing on the background of the parent window. However, the control's canvas is required to be transparent, and this technique does not see any explanation from Microsoft in the GDI documentation, but there are other ways.

One: If your program supports desktop theme services, you can invoke the API of the theme service to implement the background. Let's look at this API first:

HRESULT Drawthemeparentbackground (HWND hwnd, HDC HDC, RECT *PRC);

This function is specifically used to draw the background of the parent window, where the HWND parameter is a child window handle, HDC is also the child window of the canvas handle, the PRC is the child window to draw the area, the function is actually to copy the parent window background to the child window, in this way to achieve transparency.

Second: If the program does not support the desktop theme service, you cannot use the method above, such as a program running on Windows 2000. At this point we can send a WM_PAINT message to the parent window, but the wparam parameter that accompanies this message is a canvas handle:

HDC DC = GetDC (NULL);

HWND cWnd = ...;//child window handle

HWND pWnd = ...;//parent window Handle

RECT CRect;

GetClientRect (CWnd, &crect);

Hbitmap bitmap = CreateCompatibleBitmap (DC, Crect.right, Crect.bottom);

ReleaseDC (DC);

HDC MEMDC = CreateCompatibleDC (NULL);

Hgdiobj Oldbitmap = SelectObject (MEMDC, bitmap);

Functions such as setcliprect () can be called here to limit the drawing range

SendMessage (PWnd, WM_ERASEBKGND, (WPARAM) MEMDC, 0);

SendMessage (PWnd, WM_PAINT, (WPARAM) MEMDC, 0);

The background of the parent window has been saved at this MEMDC

User can call BitBlt (...) The function copies the contents of the MEMDC to an area of the child window, thus achieving the transparency effect;

SelectObject (MEMDC, Oldbitmap);

DeleteDC (MEMDC);

DeleteObject (bitmap);

There is certainly a limit to the above approach, because not all parent windows can accept this special WM_PAINT message feature, but MSDN mentions that most controls have this feature, and you should be careful to read its document content.

Third: If the above methods are not, then the most stupid way, the GDI function to brush the background of the child window, but you need to know the parent window background color, background bitmap and other information. For example, to fill the background of a child window with the color of the parent window, you can call FillRect () and so on:

-------------------the background of the parent window is a color condition

COLORREF Pcolor = ...;//The color of the parent window

HDC CDC = ...;//canvas handle for child window

RECT cRect = ...;//the area to be refreshed by the child window

Hbrush brush = CreateSolidBrush (Pcolor);

FillRect (CDC, &crect, brush);

DeleteObject (brush);

-------------------the background of the parent window is a bitmap

Hbitmap Pbitmap = ...; Background bitmap for parent window

HDC CDC = ...;//canvas handle for child window

RECT cRect = ...;//the area to be refreshed by the child window

Point RP;

Setburshorgex (CDC, x, Y, &RP); if it is a bitmap brush, you also need to adjust the brush origin offset of the canvas to ensure seamless

Hbrush brush = CreatePatternBrush (PBITMAP);

FillRect (CDC, &crect, brush);

DeleteObject (brush);

Setburshorgex (CDC, rp.x, RP.Y, NULL); restore the canvas's brush offset

http://blog.csdn.net/witch_soya/article/details/6912281

Implement the system scroll bar skin-changing function

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.