Выбрать главу

Choosing Fonts

In general, any program that deals with fonts will want to let the user choose their own font, as well as the colour and style attribute to use when displaying it.

Like the common dialogs for getting open and save file names, there is a common dialog for choosing a font. This is, oddly enough, called ChooseFont() and it works with the CHOOSEFONT structure for you to set the defaults it should start with as well as returning the final result of the users selection.

HFONT g_hfFont = GetStockObject(DEFAULT_GUI_FONT);

COLORREF g_rgbText = RGB(0, 0, 0);

void DoSelectFont(HWND hwnd) {

 CHOOSEFONT cf = {sizeof(CHOOSEFONT)};

 LOGFONT lf;

 GetObject(g_hfFont, sizeof(LOGFONT), &lf);

 cf.Flags = CF_EFFECTS | CF_INITTOLOGFONTSTRUCT | CF_SCREENFONTS;

 cf.hwndOwner = hwnd;

 cf.lpLogFont = &lf;

 cf.rgbColors = g_rgbText;

 if (ChooseFont(&cf)) {

  HFONT hf = CreateFontIndirect(&lf);

  if(hf) {

   g_hfFont = hf;

  } else {

   MessageBox(hwnd, "Font creation failed!", "Error", MB_OK | MB_ICONEXCLAMATION);

  }

  g_rgbText = cf.rgbColors;

 }

}

The hwnd in this call is simply the window you want to use as the parent for the font dialog.

The easiest way to use this dialog is in conjunction with an existing LOGFONT structure, which is most likely from whichever HFONT you are currently using. We set the lpLogFont member of the structure to point to the LOGFONT that we just filled with our current information and also added the CF_INITTOLOGFONTSTRUCT flag so that ChooseFont() knows to use this member. The flag CF_EFFECTS tells ChooseFont() to allow the user to select a colour, as well as Underline and Strikeout attributes.

Oddly enough, the Bold and Italics styles don't count as effects, they are considered part of the font itself and in fact some fonts only come in Bold or Italics. If you want to check or prevent the user from selecting a bold or italic font you can check the lfWeight and lfItalic members of the LOGFONT respectively, after the user has made their selection. You can then prompt the user to make another selection or something change the members before calling CreateFontIndirect().

The colour of a font is not associated with an HFONT, and therefor must be stored seperately, the rgbColors member of the CHOOSEFONT struct is used both to pass in the initial colour and retreive the new colour afterward.

CF_SCREENFONTS indicates that we want fonts designed to work on the screen, as opposed to fonts that are designed for printers. Some support both, some only one or the other. Depending on what you're going to be using the font for, this and many other flags can be found in MSDN to limit exactly which fonts you want the user to be able to select.

Choosing Colours

In order to allow the user to change just the colour of the font, or to let them pick a new colour for anything at all, there is the ChooseColor() common dialog. This is the code used to allow the user to select the background colour in the example program.

COLORREF g_rgbBackground = RGB(255, 255, 255);

COLORREF g_rgbCustom[16] = {0};

void DoSelectColour(HWND hwnd) {

 CHOOSECOLOR cc = {sizeof(CHOOSECOLOR)};

 cc.Flags = CC_RGBINIT | CC_FULLOPEN | CC_ANYCOLOR;

 cc.hwndOwner = hwnd;

 cc.rgbResult = g_rgbBackground;

 cc.lpCustColors = g_rgbCustom;

 if(ChooseColor(&cc)) {

  g_rgbBackground = cc.rgbResult;

 }

}

This is fairly straightforward, again we're using the hwnd parameter as the parent to the dialog. The CC_RGBINIT parameter says to start off with the colour we pass in through the rgbResult member, which is also where we get the colour the user selected when the dialog closes.

The g_rgbCustom array of 16 COLORREF s is required to store any values the user decides to put into the custom colour table on the dialog. You could potentially store these values somewhere like the registry, otherwise they will simply be lost when your program is closed. This parameter is not optional.

Control Fonts

Something else you might want to do at some point is change the font on the controls on your dialog or window. This is usually the case when using CreateWindow() to create controls as we've done in previous examples. Controls like windows use System by default, so we used WM_SETFONT to set a new font handle (from GetStockObject() ) for the control to use. You can use this method with fonts you create from CreateFont() as well. Simply pass the font handle as wParam and set lParam to TRUE to make the control redraw.

I've done this in previous examples, but it makes sense to mention it here because it's relevant and very short:

SendDlgItemMessage(hwnd, IDC_OF_YOUR_CONTROL, WM_SETFONT, (WPARAM)hfFont, TRUE);

Where hfFont is of course the HFONT you want to use, and IDC_OF_YOUR_CONTROL is the ID of whichever control you want to change the font of.

Appendices

Appendix A: References

Books

If you expect anyone online to treat you with respect while you are learning, you NEED to get a good book to learn from. We're here to provide direction and explain things that need explaining, not to be your librarian or teach you step by step.

Programming Windows

by Charles Petzold. The book to get on Win32 API. If you want to write programs using just the API (which is what this tutorial covers), you need this book.

Programming Windows with MFC

by Jeff Prosise. If you want to venture into MFC (after becoming fully accustomed to using the Win32 API), this is the book for you. If you don't like MFC but intend on getting a job doing windows developement, get this anyway, it's better to know than not.