簡體   English   中英

如何根據內容自動設置static控件的寬度和高度?

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

我正在嘗試創建一個Label class ,稍后我可以重用它。 我所做的是創建一個DrawString控件,然后使用 GDI+ 庫在其上繪制字符串。

幾乎完成了,我只有一個問題,我需要自動設置 static 控件的寬度和高度以適合其上的文本。

/** 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;
}

我想過用GetTextExtentPoint32來計算字符串的高度和寬度,不幸的是,因為字體大小和字體系列,我沒有這樣做。

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);  
}

我應該如何解決它?

更新

這是我的 class 的完整源代碼。

控制標簽.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();
    }
}

控制標簽.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;
};

#endif

Header.h

#pragma once

#pragma comment(linker,"\"/manifestdependency:type='win32' \
name='Microsoft.Windows.Common-Controls' version='6.0.0.0' \
processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"") 

#include <stdexcept>
#include <system_error>

#include <Windows.h>
#include <commctrl.h>
#include <windowsx.h>
#include <dwmapi.h>
#include <tchar.h>
#include <string>
#include <thread>
#include <chrono>

#include <objidl.h>
#include <gdiplus.h>
using namespace std;
using namespace Gdiplus;
#pragma comment (lib,"Gdiplus.lib")
#pragma comment(lib, "dwmapi.lib")
#pragma comment(lib, "comctl32.lib")

然后在我的主window上,我是這樣使用的。

case WM_CREATE:{  
    ControlLabel controlLabel;
    controlLabel.Label(123, hwnd); //create the label.
    controlLabel.SetXPosition(10); //set the x position.
    controlLabel.SetYPosition(10); //set the x position.
    controlLabel.SetText("Hello World, this is 25 font size."); //set the text.
    controlLabel.SetFontSize(20); //set the font size.
    controlLabel.SetFontFamily("Calibri"); //set the font family.
    controlLabel.SetFontColor(15, 86, 209); //set the font color.
    controlLabel.SetBackgroundColor(220, 222, 224, true); //set the background color.
    
    /**
        I'm still planning to add more options like italic, bold, etc.
    **/
}

Polar建議的解決方案有效,但我不知道這是否是正確的方法。 我仍在閱讀給定的文檔。

有兩種方法可以計算指定文本字符串的寬度和高度。 您不能使用GetTextExtentPoint32Graphics::MeasureString來獲取實際大小,具體取決於您繪制文本的方式。

在您的情況下,您正在使用GDI+繪制文本,但您正在使用經典GDI測量文本的寬度和高度,由於縮放,這將為您提供不同的結果。 雖然您仍然可以使用 GetTextExtentPoint32 來測量文本。 但是,您需要處理DPI以使寬度和高度與使用 GDI+ 繪制的方式相同。 GDI+ 是對 GDI 的改進,兩者之間存在差異。 例如,他們如何繪制字符串的縮放比例。

以下代碼顯示如何使用Graphics::DrawString繪制字符串並使用Graphics::MeasureString計算寬度和高度。 繪圖和計算是使用GDI+完成的。

/** Draw the string using GDI+ Graphics::DrawString **/
void DrawString(){
    HDC hdc = GetDC(hwnd);                                              //get dc of your handle.
    Graphics g(hdc);
    
    FontFamily  fontFamily("Arial Black");                              // Set font family.
    Font        font(&fontFamily, 12, FontStyleRegular, UnitPixel);     // Create the font.
    SolidBrush  brush(Color(255, 0, 0, 0));                             // Set font color
    PointF      pointF(0.0f, 0.0f);                                     // Set X and Y position.

    TextRenderingHint hint = g.GetTextRenderingHint();                  // Get the text rendering hint.
    g.SetTextRenderingHint(TextRenderingHintAntiAlias);                 // Make sure the rendering is high quality. 
    g.DrawString(L"Hello World", -1, &font, pointF, &brush);            // Draw the string.
    
    DeleteObject(&font);                                                // always delete the font when done.
    DeleteObject(&brush);                                               // always delete the brush when done.
    ReleaseDC(LabelHandle, hdc);                                        // always release the DC when done.
}

/** Measure the string using GDI+ Graphics::MeasureString **/
void MeasureString(){
    HDC hdc = GetDC(hwnd);                                                      // get dc of your handle.
    Graphics graphics(hdc);                                                     // setup graphics.
    FontFamily  theFontFamily(vFontFamily);                                     // setup your font family.
    Font        font(&theFontFamily, vFontSize, FontStyleRegular, UnitPixel);   // create the font the same way how you do it on your paint message.
    PointF      pointF(0.0f, 0.0f);                                             // use PointF instead of RectF since thats how you paint it.

    RectF boundRect;                                                            // setup boundRect to get the width and height.
    graphics.MeasureString(text, -1, &font, pointF, &boundRect);                // Measure the text of the string

    int width = boundRect.Width;                                                // get the width of text from boundRect.
    in height = boundRect.Height;                                               // get the height of text from boundRect.

    DeleteObject(&font);                                                        // delete the font.
    ReleaseDC(LabelHandle, hdc);                                           // Release the DC.
}

另一個示例展示了如何使用經典 GDI繪制字符串並使用GetTextExtenPoint32計算寬度和高度。 繪圖和計算是使用經典的GDI完成的。

/** Draw the string using the classic GDI DrawText **/
void DrawString(){
    int fontSize = 12;                                                  // Font Size.
    HDC hdc = GetDC(hwnd);                                              // Get DC of your handle.
    HFONT hFont = CreateFont(
         -MulDiv(fontSize, GetDeviceCaps(hdc, LOGPIXELSX), 72),         // Calculate the actual cHeight.
         0, 0, 0,                                                       // Normal orientation
         FW_NORMAL,                                                     // Normal weight--e.g., bold would be FW_BOLD, or use integer from 0 to 1000.
         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,
         TEXT("Arial Black"));                                          // Font family.
   
    HFONT oldfont = (HFONT)SelectObject(hdc, hFont);                    // Select the new font.
    RECT      rect = {0, 0, 200, 50};                                   // Rectangle (in logical coordinates) in which the text is to be formatted.
    
    DrawText(hdc, L"Helow World", -1, &rect, DT_CENTER | DT_VCENTER);   // Draw the text Horizontal and Vertical Center.
   
    SelectObject(hdc, oldfont);                                         // don't forget to select the old.
    DeleteObject(hFont);                                                // always delete the font when done.
    ReleaseDC(hwnd, hdc);                                               // always release dc after using.
}

/** Measure the string using the classic GDI GetTextExtentPoint32 **/
void MeasureString(){
    HDC hdc = GetDC(hwnd);
    HFONT hFont = CreateFont(                                           // Create the font.
    int fontSize = 12;                                                  // Font Size.
    HDC hdc = GetDC(hwnd);                                              // Get DC of your handle.
    HFONT hFont = CreateFont(
         -MulDiv(fontSize, GetDeviceCaps(hdc, LOGPIXELSX), 72),         // Calculate the actual cHeight.
         0, 0, 0,                                                       // Normal orientation
         FW_NORMAL,                                                     // Normal weight--e.g., bold would be FW_BOLD, or use integer from 0 to 1000.
         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,
         TEXT("Arial Black"));                                          // Font family.
   
    HFONT oldfont = (HFONT)SelectObject(hdc, hFont);                    // Select the new font.
    SIZE size;                                                          // Setup Size to get the width and height.
    GetTextExtentPoint32(hdc, L"Hello World", -1, &size);               // Draw the text Horizontal and Vertical Center.

    int width = size.cx;                                                // get the width of text from boundRect.
    int height = size.cy;                                               // get the height of text from boundRect.    

    SelectObject(hdc, oldfont);                                         // don't forget to select the old.
    DeleteObject(hFont);                                                // always delete the font when done.
    ReleaseDC(hwnd, hdc);                                               // always release dc after using.
}

由於您已經在使用 GID+來繪制文本,因此建議使用Graphics::MeasureString來計算文本的大小,這樣可以避免獲取正確 DPI 的麻煩。 您必須使文本的大小與您繪制它的方式相同。 然后使用此方法,您將能夠自動設置 static 控件的寬度和高度。

例如在您的UpdateLabel()上;

/** Call this function to redraw the content of static control **/
void ControlLabel::UpdateLabel() {
    if(LabelHandle != NULL) {
        MeasureString(); //Measure string to get the width and height of it and adjust the size of static control depending on the size of the text.
        SetWindowPos(LabelHandle, nullptr, xPosition, yPosition, width, height,  SWP_NOZORDER | SWP_NOOWNERZORDER);
        InvalidateRect(LabelHandle, NULL, FALSE);
        UpdateWindow(LabelHandle);
    }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM