• 0

How to replace buttons with pics?


Question

I wrote a tiny software that saves phone numbers in C and i'm looking for an easy way to replace my:

Add = CreateWindow (
				"button", 
				"Add",
				WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
				350, 10,
				10 * cxChar, 5 * cyChar / 4,
				hwnd, 
				(HMENU)(EDITID),
				((LPCREATESTRUCT) lParam) -> hInstance, 
				NULL);

with pictures. how can i do that?

p.s - any other tips on how to make it look better would be highly appreciated ;)

33.PNG

Thanks

Edited by Shasoosh
Link to comment
Share on other sites

16 answers to this question

Recommended Posts

  • 0

Add the BS_BITMAP or BS_ICON style, load the bitmap (with GDI, WIC, GDI+ or whatever), and then pass the handle of it to the button by sending it a BM_SETIMAGE.

If you want more control of the drawing (say you want to show it on Aero glass), you can custom draw the button by handling the NM_CUSTOMDRAW buttons send to their parent. This is very simple as well.

As for making it look better (saw your edit), you need to load the v6 common controls that support visual styles through a manifest.

If you use VS2008, put this in a file called "comctl6.manifest" (or whatever) and then just add it to the project. It should be automatically embedded by the build process (otherwise go to properties, manifest tool, input, and add it under additional manifests.)

<assembly
  xmlns="urn:schemas-microsoft-com:asm.v1"
	manifestVersion="1.0">
  <dependency>
	<dependentAssembly>
	  <assemblyIdentity
		  type="win32"
		  name="Microsoft.Windows.Common-Controls"
		  version="6.0.0.0"
		  processorArchitecture="*"
		  publicKeyToken="6595b64144ccf1df"
		  language="*"/>
	</dependentAssembly>
  </dependency>
 </assembly>

You also need to set the fonts for the various controls. Windows does not do this automatically. You do this by sending the control a WM_SETFONT message with the font handle. To get the proper fonts, you need to manually load these. Here is a basic example of how you can load all the system fonts:

HFONT m_hCaptionFont;
HFONT m_hMenuFont;
HFONT m_hMessageFont;
HFONT m_hSmCaptionFont;
HFONT m_hStatusFont;
HFONT m_hIconFont;

...


const HFONT IconFont()
{
	return m_hIconFont;
}

etc

NONCLIENTMETRICS NonClientMetrics;
NonClientMetrics.cbSize = sizeof(NONCLIENTMETRICS);
SystemParametersInfo(SPI_GETNONCLIENTMETRICS,
	sizeof(NONCLIENTMETRICS), &NonClientMetrics, 0);

m_hCaptionFont = CreateFontIndirect(&NonClientMetrics.lfCaptionFont);
m_hMenuFont = CreateFontIndirect(&NonClientMetrics.lfMenuFont);
m_hMessageFont = CreateFontIndirect(&NonClientMetrics.lfMessageFont);
m_hSmCaptionFont = CreateFontIndirect(&NonClientMetrics.lfSmCaptionFont);
m_hStatusFont = CreateFontIndirect(&NonClientMetrics.lfStatusFont);

LOGFONT lficon;
SystemParametersInfo(SPI_GETICONTITLELOGFONT, sizeof(LOGFONT), &lficon, 0);
m_hIconFont = CreateFontIndirect(&lficon);

Then you could set the font of a control like this: SendMessage(hwnd, WM_SETFONT, (WPARAM)IconFont(), 0);

That's the basics of it.

Edited by hdood
Link to comment
Share on other sites

  • 0

Thanks for the replay.

i added BS_BITMAP:

Add = CreateWindow (
				"button", 
				"Add",
				WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | BS_BITMAP ,
				350, 10,
				10 * cxChar, 5 * cyChar / 4,
				hwnd, 
				(HMENU)(EDITID),
				((LPCREATESTRUCT) lParam) -> hInstance, 
				NULL);

now how can i load the bitmap?

Link to comment
Share on other sites

  • 0
now how can i load the bitmap?

The most basic way, using GDI to load it from a bitmap resource called IDB_BITMAP1 would be this:

HWND hbut = CreateWindowEx(0, WC_BUTTON, L"Test",
	WS_CHILD|WS_VISIBLE|WS_CLIPSIBLINGS|BS_BITMAP,
	x, y, cx, cy
	hwndparent, (HMENU)IDC_TEST, HINST_THISCOMPONENT, NULL);

HBITMAP hbmp = (HBITMAP)LoadImage(HINST_THISCOMPONENT,
	MAKEINTRESOURCE(IDB_BITMAP1), IMAGE_BITMAP, 0, 0, LR_DEFAULTCOLOR);

SendMessage(hbut, BM_SETIMAGE, IMAGE_BITMAP, (LPARAM)hbmp);

Remember that you need to free the resources (bitmaps, fonts, etc) when you're done using them. For bitmaps, that is with DeleteObject.

Link to comment
Share on other sites

  • 0

Thank i so much!

another question, im using

index = ListView_GetNextItem(hWndListView,-1,LVNI_FOCUSED);

to find something on a row i selected. How can i select a row after i find it?

For example (from the pic above) i'm doing a search for the first name asf, after i find the row i want it to be selected.

Thanks

Link to comment
Share on other sites

  • 0

I don't know if you figured it out on your own, but what you're looking for is LVM_SETITEMSTATE which lets you set the state for an item. I think the equivalent macro is ListView_SetItemState, but I'm not familiar with these. Anyway, here's a basic example of selecting an item:

BOOL SelItem(_In_ HWND hWnd, _In_ int nIndex)
{
	LVITEM lvi;

	if(FALSE == SendMessage(hWnd, LVM_ENSUREVISIBLE, nIndex, FALSE))
	{
		return FALSE;
	}

	lvi.stateMask = LVIS_FOCUSED|LVIS_SELECTED;
	lvi.state = LVIS_FOCUSED|LVIS_SELECTED;

	if(FALSE == SendMessage(hWnd, LVM_SETITEMSTATE, nIndex, (LPARAM)&lvi))
	{
		return FALSE;
	}

	return TRUE;
}

Called like this: success = SelItem(listview, itemnumber);

You might want to consider a different strategy though, and that is to have the actual data stored in a separate list, and then use this to conditionally populate the listview on demand. In other words, you get a filter function so that if the user searches for "sdf," only entries containing "sdf" will be shown in the list. This is less cluttered and much more user-friendly.

It should also be designed so that it starts filtering immediately when the user starts typing, so that when he hits "s," it will start showing only entries with "s," and then when he hits "d," it'll restart and show only entries with "sd," and so on. This makes for a much more interactive experience, and means he might be able to find the result he wants with less typing. It also makes it easy to implement various predefined filters, such as locations or first letter of the name, and so on.

You might also want to consider the storage you're using. What happens if the application or system crashes right as you're writing the list to disk? The best answer is to use a database like the ESE, although I recognize that this might have a level of complexity beyond a simple hobby utility.

Link to comment
Share on other sites

  • 0

No problem.

I see you edited it, though. Did you figure out the background thing? I hope you went with the static, as it's very easy to change its background color by simply handling the WM_CTLCOLORSTATIC message in its parent's window procedure like this:

hbrback = CreateSolidBrush(RGB(100, 100, 100));

...

case WM_CTLCOLORSTATIC:
	if((HWND)lParam == hwndstatic)
	{
		SetBkMode((HDC)wParam, TRANSPARENT);
		return (LRESULT)hbrback;
	}
	break;

Just be sure you don't continously create new brushes everytime you get the message. Create it once, or just reuse the one you use as hbrBackground in your window class.

Link to comment
Share on other sites

  • 0

trying it now.

btw, how can i do the same with SetBkColor?

If i give the same RGB is the background it just goes darker, i want to make it transperent.

SetBkColor(hdc,TRANSPARENT);

didn't work

Link to comment
Share on other sites

  • 0

I'm not sure what you are trying to do. You do not need to set the background color of the text if you set the background mode to transparent, because then the static control won't use a text background at all. In other words, the entire background will be the color of the returned brush (hbrback, which should be the same as the window background).

Link to comment
Share on other sites

  • 0

#define BACKGROUND 0x993333
...
	wndclass.hbrBackground = (HBRUSH)CreateSolidBrush(BACKGROUND);
...
			  case 0  : //Add
					ShowWindow(First_Name, SW_SHOW);
					hdc = GetDC(hwnd);
							TextOut (hdc, 50, 50, "First name:", 10);
							ReleaseDC (hwnd, hdc);
					break;   

.....

				  case  11  : //Save
			ShowWindow(First_Name, SW_HIDE);
			hdc = GetDC(hwnd);
			SetTextColor(hdc,RGB(33,33,99));
			TextOut (hdc, 50, 50, "First name:", 10);
			ReleaseDC (hwnd, hdc);

i want that the text First name will blend with the background color when i press save.

how do i make all the text background transparent?

Link to comment
Share on other sites

  • 0

You can't do what you're doing. If you're going to paint manually, you have to it in response to WM_PAINT and then invalidate the area that you are writing the text to after you perform the action that changes the text. You cannot just write to the DC at random like you are doing (what happens if the part needs to be redrawn? how will the drawing code be called?)

This means you also have to store the string and various state information so the paint code knows what to paint. Generally speaking I'm no sure I see why you would want to do this, since the static control can do just what you want and can be updated just by calling SetWindowText() and showing/hiding it on demand?

Link to comment
Share on other sites

  • 0

ok, where should i declare this?

error C2065: 'hwndstatic' : undeclared identifier

and where should i put the line:

hbrback = CreateSolidBrush(RGB(100, 100, 100));

Edited by Shasoosh
Link to comment
Share on other sites

  • 0

hwndstatic is the handle to the static control, in other words the return value from CreateWindow(L"STATIC",...

hbrback is the handle of the brush you want to use as the background for the static. This should be the same brush as you use for the background of the parent. You could, for instance, make it a global variable (defined outside of any function) called g_hbrBackground, and then create it when you create your window class.

globals:

HBRUSH g_hbrBackground;
HWND g_hwndStatic;

Creation:

g_hbrBackground = CreateSolidBrush(RGB(r, g, b));
wndclass.hbrBackground = g_hbrBackground;

Then in your WM_CREATE handler (or wherever):

g_hwndStatic = CreateWindowEx(0, L"STATIC", L"Test",
	WS_CHILD|WS_VISIBLE|WS_CLIPSIBLINGS,
	x, y, cx, cy, hwndparent,
	(HMENU)IDC_STATIC, HINST_THISCOMPONENT, NULL);

and then in the main window procedure:

case WM_CTLCOLORSTATIC:
	if((HWND)lParam == g_hwndStatic)
	{
		SetBkMode((HDC)wParam, TRANSPARENT);
		return (LRESULT)g_hbrBackground;
	}
	break;

Link to comment
Share on other sites

  • 0

The most important errors are:

You put the global variables inside the window procedure. If you do this, then the are not global. You put global variables at the top of the document outside of any functions. You need to do this, because they have to be accessible from several functions.

You didn't change the parent window and instance handle in one of the CreateWindows from my example.

Link to comment
Share on other sites

  • 0
I wrote a tiny software that saves phone numbers in C and i'm looking for an easy way to replace my:

with pictures. how can i do that?

p.s - any other tips on how to make it look better would be highly appreciated ;)

For professional look (skin and others...), you can ask on Professional Win32 group

Link to comment
Share on other sites

This topic is now closed to further replies.
  • Recently Browsing   0 members

    • No registered users viewing this page.