2021年3月18日星期四

How to automatically set the width and height of static control base on its content?

I'm trying to create a Label class where I can just reuse it later. What I've done is create a static control, then use the GDI+ library to DrawString on it.

It's almost done, I only have one issue where I need to automatically set the width and height of the static control to fit the text on it.

/** Call this function to redraw the content of static control **/  void ControlLabel::UpdateLabel() {      if(LabelHandle != NULL) {          SetWidthAndHeight();          SetWindowPos(LabelHandle, nullptr, xPosition, yPosition, width, height,  SWP_NOZORDER | SWP_NOOWNERZORDER);          InvalidateRect(LabelHandle, NULL, FALSE);          UpdateWindow(LabelHandle);      }  }  /** THis function is the callback of the static control **/  LRESULT CALLBACK ControlLabel::LabelProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData) {      switch(uMsg) {      case WM_ERASEBKGND: {              if(SetBGColor) { //We only want to do this if the SetColor is modified to true, meaning we want to set the color of background.                  RECT rect;                  GetClientRect(hwnd, &rect);                  FillRect((HDC)wParam, &rect, CreateSolidBrush(RGB(BckR, BckG, BckB))); //set titlebar background color.                  return 1; //return 1, meaning we take care of erasing the background.              }              return 0;          }case WM_PAINT: {              PAINTSTRUCT ps;              HDC hdc = BeginPaint(hwnd, &ps);              Graphics g(hdc);                std::wstring widestr;              widestr = std::wstring(vFontFamily.begin(), vFontFamily.end());                FontFamily  theFontFamily(widestr.c_str());              Font        font(&theFontFamily, vFontSize, FontStyleRegular, UnitPixel);              SolidBrush  brush(Color(255, R, G, B));              PointF      pointF(0.0f, 0.0f);                TextRenderingHint hint = g.GetTextRenderingHint(); // Get the text rendering hint.              g.SetTextRenderingHint(TextRenderingHintAntiAlias); // Set the text rendering hint to TextRenderingHintAntiAlias.                 widestr = std::wstring(text.begin(), text.end());  // convert text to std::wstring:              g.DrawString(widestr.c_str(), -1, &font, pointF, &brush);       // get the C string                EndPaint(hwnd, &ps);              return TRUE;          }case WM_NCDESTROY: {              RemoveWindowSubclass(hwnd, LabelProc, uIdSubclass);              return 0;          }      }      return DefSubclassProc(hwnd, uMsg, wParam, lParam);  }    /** Use this function to create a Label. Parent or WindowHandle must be specify, this is where the Label will be draw. Unique Label ID must be specify. **/  HWND ControlLabel::Label(int Label_ID, HWND WindowHandle) {      SetWidthAndHeight();      LabelHandle = CreateWindowEx(0, L"STATIC", NULL, WS_CHILD | WS_VISIBLE | SS_OWNERDRAW, 0, 0, width, height, WindowHandle, NULL, NULL, NULL); //create the static control.      SetWindowSubclass(LabelHandle, &LabelProc, LABEL_ID, 0);      return LabelHandle;  }  

I thought of using GetTextExtentPoint32 to calculate the height and width of the string, unfortunately, I failed to do so because of the font size and font family.

void ControlLabel::SetWidthAndHeight() {      std::wstring stemp = StringConverter(vFontFamily);      LPCWSTR result = stemp.c_str();        HDC hdc = GetDC(LabelHandle);//static control      const wchar_t* buf = L"Hello World, this is 25 font size.";      /*//(font test 1)         HFONT hFont = CreateFont(vFontSize, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, DEFAULT_CHARSET,      OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH, result);      */        //(font test 2)      FontFamily  theFontFamily(result);      Font        font(&theFontFamily, vFontSize, FontStyleRegular, UnitPixel);        SIZE size;      HFONT oldfont = (HFONT)SelectObject(hdc, &font);      GetTextExtentPoint32(hdc, buf, wcslen(buf), &size);      width = size.cx;      height = size.cy;        SelectObject(hdc, oldfont);      DeleteObject(&font);      ReleaseDC(LabelHandle, hdc);    }  

How should I fix it?

UPDATE

Here's the complete source code of my class.

ControlLabel.cpp

#include "ControlLabel.h"    HWND ControlLabel::LabelHandle = NULL;  int ControlLabel::xPosition = 0;  int ControlLabel::yPosition = 0;  int ControlLabel::width = 0;  int ControlLabel::height = 0;  int ControlLabel::LABEL_ID = 0;  int ControlLabel::vFontSize = 12;  int ControlLabel::R = 0;  int ControlLabel::G = 0;  int ControlLabel::B = 0;  int ControlLabel::BckR = 0;  int ControlLabel::BckG = 0;  int ControlLabel::BckB = 0;  bool ControlLabel::SetBGColor = FALSE;  string ControlLabel::text = "Label";  string ControlLabel::vFontFamily = "Segoe UI";    ControlLabel::ControlLabel() {}    /** This function is used to convert string into std::wstring. **/  std::wstring ControlLabel::StringConverter(const std::string& s) {      int len;      int slength = (int)s.length() + 1;      len = MultiByteToWideChar(CP_ACP, 0, s.c_str(), slength, 0, 0);      wchar_t* buf = new wchar_t[len];      MultiByteToWideChar(CP_ACP, 0, s.c_str(), slength, buf, len);      std::wstring r(buf);      delete[] buf;      return r;  }    /** This function is used to automatically set the Width and Height of static control base on the length of the text. **/  void ControlLabel::SetWidthAndHeights() {          std::wstring fontFamilyTemp = StringConverter(vFontFamily);      std::wstring  textTemp = StringConverter(text);      LPCWSTR textLabel = textTemp.c_str();            HDC hdc = GetDC(LabelHandle);//static control      const wchar_t* buf = L"Hello World, this is 25 font size.";        HFONT hFont = CreateFont(            -MulDiv(vFontSize, GetDeviceCaps(hdc, LOGPIXELSX), 90),            0, 0, 0, // normal orientation            FW_NORMAL,   // normal weight--e.g., bold would be FW_BOLD            false, false, false, // not italic, underlined or strike out            DEFAULT_CHARSET, OUT_OUTLINE_PRECIS, // select only outline (not bitmap) fonts            CLIP_DEFAULT_PRECIS, CLEARTYPE_QUALITY, VARIABLE_PITCH | FF_SWISS, fontFamilyTemp.c_str());        SIZE size;      HFONT oldfont = (HFONT)SelectObject(hdc, hFont);      GetTextExtentPoint32(hdc, textLabel, wcslen(textLabel), &size);      width = size.cx;      height = size.cy;        SelectObject(hdc, oldfont);      DeleteObject(hFont);      ReleaseDC(LabelHandle, hdc);        char buffer[100];      sprintf_s(buffer, "WIDTH: %d | HEIGHT: %d\n", width, height);      OutputDebugStringA(buffer);  }    /** This function will be called when new option is set. For example, fontSize is set. **/  void ControlLabel::UpdateLabel() {      if(LabelHandle != NULL) {          SetWidthAndHeights();          SetWindowPos(LabelHandle, nullptr, xPosition, yPosition, width, height, SWP_NOZORDER | SWP_NOOWNERZORDER);          InvalidateRect(LabelHandle, NULL, FALSE);          UpdateWindow(LabelHandle);      }  }    /** This is the callback function of static control. **/  LRESULT CALLBACK ControlLabel::LabelProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData) {      switch(uMsg) {          case WM_ERASEBKGND: {              if(SetBGColor) { //We only want to do this if the SetColor is modified to true, meaning we want to set the color of background.                  RECT rect;                  GetClientRect(hwnd, &rect);                  FillRect((HDC)wParam, &rect, CreateSolidBrush(RGB(BckR, BckG, BckB))); //set titlebar background color.                  return 1; //return 1, meaning we take care of erasing the background.              }              return 0;          }case WM_PAINT: {              PAINTSTRUCT ps;              HDC hdc = BeginPaint(hwnd, &ps);              Graphics g(hdc);                std::wstring fontFamilyTemp = StringConverter(vFontFamily);              std::wstring  textTemp = StringConverter(text);                FontFamily  theFontFamily(fontFamilyTemp.c_str());              Font        font(&theFontFamily, vFontSize, FontStyleRegular, UnitPixel);              SolidBrush  brush(Color(255, R, G, B));              PointF      pointF(0.0f, 0.0f);                TextRenderingHint hint = g.GetTextRenderingHint(); // Get the text rendering hint.              g.SetTextRenderingHint(TextRenderingHintAntiAlias); // Set the text rendering hint to TextRenderingHintAntiAlias.               g.DrawString(textTemp.c_str(), -1, &font, pointF, &brush);                 EndPaint(hwnd, &ps);              return TRUE;          }case WM_NCDESTROY: {              RemoveWindowSubclass(hwnd, LabelProc, uIdSubclass);              return 0;          }      }      return DefSubclassProc(hwnd, uMsg, wParam, lParam);  }    /** Use this function to create a Label. Parent or WindowHandle must be specify, this is where the Label will be draw. Unique Label ID must be specify. **/  HWND ControlLabel::Label(int Label_ID, HWND WindowHandle) {      LABEL_ID = Label_ID;      LabelHandle = CreateWindowEx(0, L"STATIC", NULL, WS_CHILD | WS_VISIBLE | SS_OWNERDRAW, 0, 0, width, height, WindowHandle, NULL, NULL, NULL); //create the static control.      SetWindowSubclass(LabelHandle, &LabelProc, LABEL_ID, 0);      return LabelHandle;  }    /** Use this function to set the X Position of the Label. **/  void ControlLabel::SetXPosition(int xxPosition) {      if(LabelHandle != NULL) {          xPosition = xxPosition; //set xposition          UpdateLabel();      }  }    /** Use this function to set the Y Position of the Label. **/  void ControlLabel::SetYPosition(int yyPosition) {      if(LabelHandle != NULL) {          yPosition = yyPosition; //set xposition          UpdateLabel();      }  }    /** Use this function to set the text of the Label. **/  void ControlLabel::SetText(string ttext) {      if(LabelHandle != NULL) {          text = ttext; //set text          UpdateLabel();      }  }    /** Use this function to set the font family of the Label. **/  void ControlLabel::SetFontFamily(string font_family) {      if(LabelHandle != NULL) {          vFontFamily = font_family; //set font family          UpdateLabel();      }  }    /** Use this function to set the font size of the Label. **/  void ControlLabel::SetFontSize(int size) {      if(LabelHandle != NULL) {          vFontSize = size; //set font size          UpdateLabel();      }  }    /** Use this Function to set the font color of the Label using RGB. **/  void ControlLabel::SetFontColor(int Rr, int Gg, int Bb) {      if(LabelHandle != NULL) {          R = Rr;           G = Gg;           B = Bb;           UpdateLabel();      }  }    /** Use this Function to set the background color of the Label using RGB. Last parameter must be TRUE if you want to set your own background color. **/  void ControlLabel::SetBackgroundColor(int Rr, int Gg, int Bb, bool setColor) {      if(LabelHandle != NULL) {          SetBGColor = setColor;          BckR = Rr;          BckG = Gg;          BckB = Bb;          UpdateLabel();      }  }  

ControlLabel.h

#pragma once    #ifndef CONTROLLABEL_H  #define CONTROLLABEL_H  #include "Header.h"    class ControlLabel {    public:      ControlLabel();      HWND Label(int Label_ID, HWND WindowHandle);      void SetXPosition(int xPosition);      void SetYPosition(int yPosition);      void SetText(string Text);      void SetFontFamily(string FontFamily);      void SetFontSize(int FontSize);      void SetFontColor(int R, int G, int B);      void SetBackgroundColor(int Rr, int Gg, int Bb, bool SetBGColor);    private:      void UpdateLabel();      void SetWidthAndHeights();      static std::wstring StringConverter(const std::string& s);      static LRESULT CALLBACK LabelProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData);      static HWND LabelHandle;      static SolidBrush vFontColor;      static string text, vFontFamily;      static bool SetBGColor;      static int xPosition, yPosition, width, height, LABEL_ID, vFontSize, R, G, B, BckR, BckG, BckB;  };    

没有评论:

发表评论