Solved

Making an owner-drawn button disabled

Posted on 1998-04-13
25
432 Views
Last Modified: 2013-12-03
I'm writing code to draw an owner-drawn button, and I'm having trouble figuring out how to disable it. Or to get it to look disabled, that is: When I set its window style to be disabled, that looks fine, but it still looks just like a usable button.

The button contains text and a bitmap, so to draw each button, I use DrawFrameControl() to draw the button outline, TextOut() to draw the text, and BitBlt() to copy the bitmap from a preliminary hdc to a memory device context. Then I use BitBlt() again to copy the memory device context to the button's DRAWITEMSTRUCT's device context.

This works fine for enabled buttons, but I can't figure out how to do it for disabled buttons, with graphics appropriately embossed and everything. The only thing I've found in online help is a function called DrawState, which seems to be poorly suited to this purpose, or should I be trying to use it for that?
0
Comment
Question by:HoJu
  • 17
  • 5
  • 2
  • +1
25 Comments
 
LVL 22

Expert Comment

by:nietod
ID: 1412716
answer coming.
0
 
LVL 22

Expert Comment

by:nietod
ID: 1412717
I assume what you want is for the button's contents to have an "etched" appearance.  That is, it should be gray with a white highlight on the lower and right edges.  Right?

If so, you can easily display text this way using the GrayString() Windows API procedure.  Drawing a bitmap is a little more complicated.  

I have to look up the details.  I'll continue in a minute.
0
 
LVL 22

Expert Comment

by:nietod
ID: 1412718
I can't find what I was looking for.  But I think this is the basic idea.  

First you need to create a "mask" of the bitmap to be "etched"  The mask is a monchrome bitmap with white (0) pixels corresponding to the background pixels of the original bitmal and black (1) pixels elswhere.  

Some terms to keep things straight.  I'll call the original color image to be "etched" the source image and its DC the source DC.  I'll call the monochrome mask bitmap the mask and its DC the mask DC.  Finally I'll call the bitmap to contain the final product, the etched bitmap and the etched DC.

Create a monochrome bitmap the same dimensions as the source bitmap and select it into a memory device context. Set the background color of the source DC to correspond to the color that is the background of the source bitmap.  (gray or white usually).  You may need to set the the foreground and background colors of the mask DC to black and white (I'm don't think that's needed though).  Then bitblt the source image to the mask DC using SRCCOPY.

Now create a color bitmap for the etched image.  Fill the bitmap with the background color (light gray usually).  Set the etched DC's forground color to white (button highlight) and copy the mask to the etched DC using BitBlt, but copy the image shifted down and to the right.  Then change the etched DC's foreground color to dark gray (button shadow) and copy the mask to the etched DC (not shifted this time).
0
 
LVL 22

Expert Comment

by:nietod
ID: 1412719
When you copy the mask to the etched DC you need to use a raster code of 0x00BB074A.  (not SRCCOPY!)

Hope this helps.  Let me know if you need more help.
0
 
LVL 22

Expert Comment

by:nietod
ID: 1412720
Note it probably is possible to do this with out using the mask.  You just need to determine the right raster code and then just copy the source bitmap to the etched DC twice  (shifted once and with difference colors)  However, I like to create the bitmap and save it.  It comes in hand for other operations as well.
0
 

Author Comment

by:HoJu
ID: 1412721
It's getting there ... After drawing the caption and bitmap into hdc2, I'm ready to BitBlt it to hdcMem, which is the memory device context that the buttons finally use. Using BitBlt with a raster code of 0x00BB047A worked for the first part -- setting a white copy of the hdc2 image into hdcMem. But I've tried a lot of things to get BitBlt to copy the image dark gray for the second BitBlt, and I can't get it to work.

Curiously enough, I didn't have to change the foreground and background colors to get the first BitBlt to work -- the raster code seems to be taking care of all of that by itself.

if (lpdis->itemState & ODS_DISABLED)
{
      hdc2 = CreateCompatibleDC(lpdis->hDC);
      SetBkMode(hdc2, TRANSPARENT);
      SetBkColor(hdc2, RGB(255, 255, 255));
      SelectObject(hdc2,hFont);
      SelectObject(hdc2, CreateBitmap(
                  (rect.right-rect.left)-4,
                  (rect.bottom-rect.top)-4, 1, 1, NULL));
      SelectObject(hdc2, CreatePen(PS_SOLID, 1, 0));

      Rectangle(hdc2, -1, -1, (rect.right-rect.left)-3,
                  (rect.bottom-rect.top)-3);
      BitBlt(hdc2, point.x-1,
                  point.y+((cyLabel-bitmapSize.cy)/2)-1,
                  bitmapSize.cx, bitmapSize.cy,
                  hdc, 0, (cyLabel-bitmapSize.cy)/2, SRCCOPY);
      TextOut(hdc2, point.x + bitmapSize.cx + hdbu - 1,
                  point.y+((cyLabel-size.cy)/2) - 1, str,
                  strlen(str));
      BitBlt(hdcMem, 2, 2, rect.right-rect.left,
                  rect.bottom-rect.top, hdc2, 0, 0,
                  0X00BB074A);
      BitBlt(hdcMem, 1, 1, rect.right-rect.left,
                  rect.bottom-rect.top, hdc2, 0, 0,
                  0X00BB074A);
      DeleteDC(hdc2);
                  // code for disabled buttons
}

0
 
LVL 22

Expert Comment

by:nietod
ID: 1412722
sorry I'm not doing as good a job as I should be.  Its been 3 year's since I dealt with this stuff and my sample code is bad!

I think--For the last two BitBlt()s it is copying in the brush pattern where there are black bits (and leaving white bits alone).  So create a button highlight brush (white) (not pen, not foreground color) and select it in for the first BitBlt.  Then create a button shadow brush for the second BitBlt().  If that doesn't help.  I'll sit down and really look into it tonight.
0
 

Author Comment

by:HoJu
ID: 1412723
Hey, no problem, nietod -- I would never have gotten this far by myself!

I put this in front of the second BitBlt():

SelectObject(hdc2, CreateSolidBrush(GetSysColor(COLOR_BTNHIGHLIGHT)));

... which didn't seem to have any effect, even when I switched the first argument to hdcMem, and when I changed the colors just for the hell of it.
0
 
LVL 22

Expert Comment

by:nietod
ID: 1412724
Looking back I didn't say where to select the brush.  Looks like it should be the destination DC.  Is that where you selected it?  
If that doesn't help post your code so I can see it.
0
 
LVL 23

Expert Comment

by:chensu
ID: 1412725
GrayString can be used for not only text but also bitmaps. Use the callback function to output your original bitmap. GrayString will draw it as grayed.
0
 
LVL 22

Expert Comment

by:nietod
ID: 1412726
chensu, I think you are assuming that hoJo wants to do this the easy, reliable, and accepted way.  I think he wants to stuggle through a half decent solution  :-)
0
 

Author Comment

by:HoJu
ID: 1412727
Nietod, that's about right -- though sometimes it feels like making a pretty Windows app is like trying to shovel water out of a lake ...

Besides, when I used GrayString() earlier -- before I posted this question -- its graying seemed really spotty and not necessarily useful for this purpose.

Anyway, here's the code again:

if (lpdis->itemState & ODS_DISABLED)
{
hdc2 = CreateCompatibleDC(lpdis->hDC);
SetBkMode(hdc2, TRANSPARENT);
SetBkColor(hdc2, RGB(255, 255, 255));
SelectObject(hdc2,hFont);
SelectObject(hdc2, CreateBitmap((rect.right-rect.left)-4, (rect.bottom-rect.top)-4, 1, 1, NULL));
SelectObject(hdc2, CreatePen(PS_SOLID, 1, 0));
Rectangle(hdc2, -1, -1, (rect.right-rect.left)-3, (rect.bottom-rect.top)-3);
BitBlt(hdc2, point.x-1, point.y+((cyLabel-bitmapSize.cy)/2)-1, bitmapSize.cx, bitmapSize.cy, hdc, 0, (cyLabel-bitmapSize.cy)/2, SRCCOPY);
TextOut(hdc2, point.x + bitmapSize.cx + hdbu - 1, point.y+((cyLabel-size.cy)/2) - 1, str, strlen(str));
BitBlt(hdcMem, 2, 2, rect.right-rect.left, rect.bottom-rect.top, hdc2, 0, 0, 0X00BB074A);
SelectObject(hdcMem, CreateSolidBrush(GetSysColor(COLOR_BTNHIGHLIGHT)));
BitBlt(hdcMem, 1, 1, rect.right-rect.left, rect.bottom-rect.top, hdc2, 0, 0, 0X00BB074A);
DeleteDC(hdc2);
}

... So the new SelectObject() line is three lines from the bottom, and I've tried it with both hdc2 (the mask DC) and hdcMem (the etched DC) as the first parameter, and neither seems to do anything.
0
Better Security Awareness With Threat Intelligence

See how one of the leading financial services organizations uses Recorded Future as part of a holistic threat intelligence program to promote security awareness and proactively and efficiently identify threats.

 
LVL 22

Expert Comment

by:nietod
ID: 1412728
Right?  I was joking.  Couldn't you hear the sarcasm in my typing?
0
 
LVL 22

Expert Comment

by:nietod
ID: 1412729
Actually.  Gray string grays the image.  It does not etch it.  There is a differenct.  So we probably should make this work.  I'll look over your code.
0
 
LVL 22

Expert Comment

by:nietod
ID: 1412730
One problem  I see already us that the raster operation should be ox00B<8>074  That is an bee-eight.  Not a bee-bee.  

I don't see anything alse.  I that doesn't work post the code that creates the memory DC (hdcMem).
0
 
LVL 10

Expert Comment

by:RONSLOW
ID: 1412731
You can do all this much easier with DrawFrameControl and DrawState.  These WIn32 API calls do all the work of etching, greying etc for you.  Draw state will even draw dimmed etc icons and text, DrawFrameControl lets you draw buttons, checkboxes, scrollbars etc in various states
.

0
 
LVL 22

Expert Comment

by:nietod
ID: 1412732
I'm not sure why RONSLOW suggested DrawFrameControl.  It doesn't seem appliucable.  But DrawState() does.  You want to use the DSS_DISABLED option.  The say it "embosses" the image which is the same as "etching" it.  Its only available in Win95 and NT (which is why I didn't use it) but if that's not a limitation for you that should be an easier solution.
0
 

Author Comment

by:HoJu
ID: 1412733
It turns that I'm pretty much just developing for Windows NT, so I can try DrawState. Of course, it isn't working yet -- the DrawFrameControl() call works, but the disabled button contains nothing. Here's the code:

hdc2 = CreateCompatibleDC(lpdis->hDC);
SelectObject(hdc2, CreateCompatibleBitmap(lpdis->hDC, rect.right-rect.left, rect.bottom-rect.top));
SelectObject(hdc2,hFont);
SetBkMode(hdc2, TRANSPARENT);
BitBlt(hdc2, point.x, point.y+((cyLabel-bitmapSize.cy)/2), bitmapSize.cx, bitmapSize.cy, hdc, 0,   (cyLabel-bitmapSize.cy)/2, SRCCOPY);
TextOut(hdc2, point.x + bitmapSize.cx + hdbu, point.y+((cyLabel-size.cy)/2), str, strlen(str));
DrawState(hdcMem, 0, 0, MAKELPARAM(GetCurrentObject(hdc2, OBJ_BITMAP), 0), 0, 0, 0, rect.right=rect.left, rect.bottom-rect.top, DST_BITMAP | DSS_DISABLED);
DeleteDC(hdc2);

0
 
LVL 22

Expert Comment

by:nietod
ID: 1412734
This isn't the EXACT code is it?  If so there are some little problems (not so little) with saving and restoring DC information.  If this is the exact code, let me know.  We need to talk.

Now this code will place the disabled looking bitmap in hdcMem.  I assume you then bitBlt it from there?  Could you show the code that creates hdcMem and the code that bitblts from hdcMem?
0
 
LVL 22

Expert Comment

by:nietod
ID: 1412735
If this is you exact code, it explains why things aren't working.  The bitmap is being deleted before you use it.  Let me know if you understand about cleaning up DC's before I go in to "lecture mode" unneccessarily.
0
 

Author Comment

by:HoJu
ID: 1412736
Hm. I probably  _don't_ completely understand about cleaning up DC's. I'm not sure. At any rate, I did leave out some of the code -- whether this makes me look more or less clueless, I don't know. Here's the more complete code:

bitmapSize.cx = 11;
bitmapSize.cy = 11;
lpdis = (LPDRAWITEMSTRUCT) lParam;
hdcMem = CreateCompatibleDC(lpdis->hDC);
hdc = CreateCompatibleDC(lpdis->hDC);
SetBkColor(hdc, RGB(192, 192, 192));
SetBkMode(hdc, TRANSPARENT);
SelectObject(hdcMem,hFont);
SetBkMode(hdcMem, TRANSPARENT);
rect = lpdis->rcItem;
SelectObject(hdcMem, CreateCompatibleBitmap(lpdis->hDC, rect.right-rect.left, rect.bottom-rect.top));
switch (lpdis->CtlID)
{
case IDC_ADD:
      SelectObject(hdc, hBitmap[0]);
      sprintf(str, "Add");
      break;
case IDC_ADD_ALL:
      SelectObject(hdc, hBitmap[1]);
      sprintf(str, "Add All");
      break;
case IDC_REMOVE:
      SelectObject(hdc, hBitmap[2]);
      sprintf(str, "Remove");
      break;
case IDC_REMOVE_ALL:
      SelectObject(hdc, hBitmap[3]);
      sprintf(str, "Remove All");
      break;
}
GetTextExtentPoint32(hdcMem, str, strlen(str), &size);
point.x = ((rect.right-rect.left) - (bitmapSize.cx+hdbu+size.cx))/2;
if (bitmapSize.cy > size.cy)
{
      point.y = ((rect.bottom-rect.top) - bitmapSize.cy)/2;
      cyLabel = bitmapSize.cy;
}
else
{
      point.y = ((rect.bottom-rect.top) - size.cy)/2;
      cyLabel = size.cy;
}
if (lpdis->itemState & ODS_SELECTED)
{
      DrawFrameControl(hdcMem, &rect, DFC_BUTTON,
      DFCS_BUTTONPUSH | DFCS_PUSHED);
      point.y = point.y+1;
      point.x = point.x+1;
}
else DrawFrameControl(hdcMem, &rect, DFC_BUTTON, DFCS_BUTTONPUSH);
if (lpdis->itemState & ODS_DISABLED)
{
hdc2 = CreateCompatibleDC(lpdis->hDC);
SelectObject(hdc2, CreateCompatibleBitmap(lpdis->hDC, rect.right-rect.left, rect.bottom-rect.top));
SelectObject(hdc2,hFont);
SetBkMode(hdc2, TRANSPARENT);
BitBlt(hdc2, point.x, point.y+((cyLabel-bitmapSize.cy)/2), bitmapSize.cx, bitmapSize.cy, hdc, 0, (cyLabel-bitmapSize.cy)/2, SRCCOPY);
TextOut(hdc2, point.x + bitmapSize.cx + hdbu, point.y+((cyLabel-size.cy)/2), str, strlen(str));
DrawState(hdcMem, 0, 0, MAKELPARAM(GetCurrentObject(hdc2, OBJ_BITMAP), 0), 0, 0, 0, rect.right=rect.left, rect.bottom-rect.top, DST_BITMAP | DSS_DISABLED);
DeleteDC(hdc2);
}
else
{      // code to draw standard, selected buttons
BitBlt(hdcMem, point.x, point.y+((cyLabel-bitmapSize.cy)/2), bitmapSize.cx, bitmapSize.cy, hdc, 0, (cyLabel-bitmapSize.cy)/2, SRCCOPY);
TextOut(hdcMem, point.x + bitmapSize.cx + hdbu, point.y+((cyLabel-size.cy)/2), str, strlen(str));
}
BitBlt(lpdis->hDC, lpdis->rcItem.left, lpdis->rcItem.top, lpdis->rcItem.right - lpdis->rcItem.left, lpdis->rcItem.bottom - lpdis->rcItem.top, hdcMem, 0, 0, SRCCOPY);
DeleteDC(hdcMem);
DeleteDC(hdc);

0
 
LVL 22

Expert Comment

by:nietod
ID: 1412737
Okay.  This may be THE problem.  In any case it certainly is A problem.  A DC is (among other things) a pointer to a structure that contains pointers to a bunch of other objects.  These objects are pens, fonts, brushes, (clipping) regions, bitmaps, and palletes.  (there may be others).  You refer to these objects with handles, which really are just pointers to the objects somewhere in memory.  These objects are allocated from a heap and take memory and other resources, just like objects in C++ or other OO languages..  And these resource can be scarce at times.

Here is the problem.  When you create these objects you must eventually destroy them.  If you create a pen, you must destroy it when you are done with it.  In addition, when you get a DC it comes loaded with one of each of these objects already.  That is it has a pen, brush, font etc.  These objects were created by the OS and it is the OS's job to delete them.  (It probably shares these among applications, so you don't want to delete them!)  For things to work correctly you must insure that three things.

1.  When you return the DC to the OS (DeleteDC(), ReleaseDC(), EndPaint() etc)  It must have the original objects that were supplied when you got it.
2.  Each object that you create you destroy.  (DeleteObject()) or something similar.)
3.  You never delete an object while it is selected into a DC.

The typical way of making this stuff work is
1.  Create an object.
2.  Select it into a DC and save the handle that is returned (this is the handle to the original object.)
3.  Use the DC with the new object.
4.   Select the original object back in using the handle saved from 2.
5.  Delete the object you created.  (This can be done by saving the handle in step 1, or by using the return value of SelectObject() in 4.


0
 
LVL 22

Expert Comment

by:nietod
ID: 1412738
I've made some changes to give you an idea of how to do this.  I probably didn't get everything.  Try to clean it up if you can.  Then if it doesn't work.  Why don't you e-mail me the code.  It is too hard to read here.  I'm at nietod@theshop.net.


      bitmapSize.cx = 11;
      bitmapSize.cy = 11;
      lpdis = (LPDRAWITEMSTRUCT) lParam;
      hdcMem = CreateCompatibleDC(lpdis->hDC);
      hdc = CreateCompatibleDC(lpdis->hDC);
      SetBkColor(hdc, RGB(192, 192, 192));
      SetBkMode(hdc, TRANSPARENT);

      HFONT hFon_hdcMem = SelectObject(hdcMem,hFont);  /////

      SetBkMode(hdcMem, TRANSPARENT);
      rect = lpdis->rcItem;

      HBITMAP hBitMap_hdcMem = SelectObject(hdcMem, CreateCompatibleBitmap(lpdis->hDC, rect.right-rect.left,
      rect.bottom-rect.top));  //////

      switch (lpdis->CtlID)
      {
      case IDC_ADD:
      SelectObject(hdc, hBitmap[0]);
      sprintf(str, "Add");
      break;
      case IDC_ADD_ALL:
      SelectObject(hdc, hBitmap[1]);
      sprintf(str, "Add All");
      break;
      case IDC_REMOVE:
      SelectObject(hdc, hBitmap[2]);
      sprintf(str, "Remove");
      break;
      case IDC_REMOVE_ALL:
      SelectObject(hdc, hBitmap[3]);
      sprintf(str, "Remove All");
      break;
      }
      GetTextExtentPoint32(hdcMem, str, strlen(str), &size);
      point.x = ((rect.right-rect.left) - (bitmapSize.cx+hdbu+size.cx))/2;
      if (bitmapSize.cy > size.cy)
      {
      point.y = ((rect.bottom-rect.top) - bitmapSize.cy)/2;
      cyLabel = bitmapSize.cy;
      }
      else
      {
      point.y = ((rect.bottom-rect.top) - size.cy)/2;
      cyLabel = size.cy;
      }
      if (lpdis->itemState & ODS_SELECTED)
      {
      DrawFrameControl(hdcMem, &rect, DFC_BUTTON,
      DFCS_BUTTONPUSH | DFCS_PUSHED);
      point.y = point.y+1;
      point.x = point.x+1;
      }
      else DrawFrameControl(hdcMem, &rect, DFC_BUTTON, DFCS_BUTTONPUSH);
      if (lpdis->itemState & ODS_DISABLED)
      {
      hdc2 = CreateCompatibleDC(lpdis->hDC);

     HBITMAP hBitMap_hdc2 = SelectObject(hdc2, CreateCompatibleBitmap(lpdis->hDC, rect.right-rect.left,
      rect.bottom-rect.top));

     HFONT hFont_hdc2 =      SelectObject(hdc2,hFont);
      SetBkMode(hdc2, TRANSPARENT);
      BitBlt(hdc2, point.x, point.y+((cyLabel-bitmapSize.cy)/2), bitmapSize.cx, bitmapSize.cy, hdc, 0,
      (cyLabel-bitmapSize.cy)/2, SRCCOPY);
      TextOut(hdc2, point.x + bitmapSize.cx + hdbu, point.y+((cyLabel-size.cy)/2), str, strlen(str));
      DrawState(hdcMem, 0, 0, MAKELPARAM(GetCurrentObject(hdc2, OBJ_BITMAP), 0), 0, 0, 0,
      rect.right=rect.left, rect.bottom-rect.top, DST_BITMAP | DSS_DISABLED);

    DeleteObject(SelectObject(hdc2,hFont_hdc2));
    DeleteObject(SelectObject(hdc2,hBitMap_hdc2));

      DeleteDC(hdc2);
      }
      else
      { // code to draw standard, selected buttons
      BitBlt(hdcMem, point.x, point.y+((cyLabel-bitmapSize.cy)/2), bitmapSize.cx, bitmapSize.cy, hdc, 0,
      (cyLabel-bitmapSize.cy)/2, SRCCOPY);
      TextOut(hdcMem, point.x + bitmapSize.cx + hdbu, point.y+((cyLabel-size.cy)/2), str, strlen(str));
      }
      BitBlt(lpdis->hDC, lpdis->rcItem.left, lpdis->rcItem.top, lpdis->rcItem.right - lpdis->rcItem.left,
      lpdis->rcItem.bottom - lpdis->rcItem.top, hdcMem, 0, 0, SRCCOPY);


    DeleteObject(SelectObject(hdc2,hFont_hdcmem));
    DeleteObject(SelectObject(hdc2,hBitMap_hdcmem));

      DeleteDC(hdcMem);
      DeleteDC(hdc);
0
 
LVL 10

Accepted Solution

by:
RONSLOW earned 50 total points
ID: 1412739
Likewise, email me at Roger_Onslow@compsys.com.au - especially since you've used my suggestion of using DrawState and rejeted my answer.

I have used drawstate (and drawframecontrol) for my own owner drawn buttons and could send you some code if you like.
0
 
LVL 22

Expert Comment

by:nietod
ID: 1412740
RONSLOW seems eager for these points.  E-mail. him.  I suspect he can do at least as well as I can.
0

Featured Post

6 Surprising Benefits of Threat Intelligence

All sorts of threat intelligence is available on the web. Intelligence you can learn from, and use to anticipate and prepare for future attacks.

Join & Write a Comment

This article shows how to make a Windows 7 gadget that extends its U/I with a flyout panel -- a window that pops out next to the gadget.  The example gadget shows several additional techniques:  How to automatically resize a gadget or flyout panel t…
What my article will show is if you ever had to do processing to a listbox without being able to just select all the items in it. My software Visual Studio 2008 crystal report v11 My issue was I wanted to add crystal report to a form and show…
This is Part 3 in a 3-part series on Experts Exchange to discuss error handling in VBA code written for Excel. Part 1 of this series discussed basic error handling code using VBA. http://www.experts-exchange.com/videos/1478/Excel-Error-Handlin…
This demo shows you how to set up the containerized NetScaler CPX with NetScaler Management and Analytics System in a non-routable Mesos/Marathon environment for use with Micro-Services applications.

747 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

14 Experts available now in Live!

Get 1:1 Help Now