如何根据其内容自动设置静态控件的宽度和高度? [英] How to automatically set the width and height of static control base on its content?

查看:77
本文介绍了如何根据其内容自动设置静态控件的宽度和高度?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试创建一个 Label 类,在以后可以重用它.我要做的是创建一个静态控件,然后使用GDI +库在其上 DrawString .

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

我考虑过使用 GetTextExtentPoint32 来计算字符串的高度和宽度,不幸的是,由于字体大小和字体系列的缘故,我未能做到这一点.

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

我应该如何解决?

更新

这是我班上的完整源代码.

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

#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")

然后在我的主窗口中,我像这样使用它.

Then on my main window, I used it like this.

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 的建议解决方案有效,但是我不知道这是否是正确的方法.我仍在阅读给定的文档.

The suggested solution of Polar works, but I don't know if that's the correct way. I'm still reading the given documentation.

推荐答案

有两种方法可以计算指定文本字符串的宽度和高度.您不能使用 GetTextExtentPoint32 图形:: MeasureString 来获取实际大小,具体取决于您绘制文本的方式.

There are two ways to computes the width and height of the specified string of text. You can use neither GetTextExtentPoint32 and Graphics::MeasureString to get the actual size depending on how you drew your text.

在您的情况下,您正在使用 GDI 会因缩放而给您不同的结果.尽管您仍然可以使用GetTextExtentPoint32来测量文本.但是,您将需要处理DPI ,以使其宽度和高度与使用GDI +绘制时的宽度和高度相同.GDI +是对GDI的改进,两者之间存在差异.例如,缩放比例如何绘制字符串.

In your case, you are drawing the text using GDI+ but you are measuring the width and height of it using the classic GDI which will give you a different result because of scaling. Although you can still use GetTextExtentPoint32 to measure the text. However, you will need to handle the DPI to get the width and height the same as how it is drawn using GDI+. GDI+ is an improvement on GDI and there are differences between the two. For example, the scaling on how they draw a string.

以下代码显示了如何使用 Graphics :: DrawString 绘制字符串以及如何使用 Graphics :: MeasureString 计算宽度和高度.使用 GDI + 完成绘图和计算.

The following code shows how to draw the string using Graphics::DrawString and compute the width and height using Graphics::MeasureString. Drawing and computation are done using 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 完成绘图和计算.

Another example shows how to draw the string using the classic GDI and compute the width and height using GetTextExtenPoint32. Drawing and computation are done using the classic 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的麻烦.您必须使文本的大小与绘制方式相同.然后,使用这种方法,您将能够自动设置静态控件的宽度和高度.

Since you are already using GID+ to draw the text, then it is recommended to use Graphics::MeasureString to compute the size of it, this will avoid the headache of getting the correct DPI. You must get the size of the text the same as how you drew it. Then with this method, you will be able to set the width and height of your static control automatically.

例如在您的 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);
    }
}

这篇关于如何根据其内容自动设置静态控件的宽度和高度?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆