65.9K
CodeProject 正在变化。 阅读更多。
Home

CFunctionEdit - n元组输入的自定义控件 v1.2

starIconstarIconstarIcon
emptyStarIcon
starIcon
emptyStarIcon

3.43/5 (7投票s)

2003 年 11 月 9 日

4分钟阅读

viewsIcon

50399

downloadIcon

2714

一个用于 n 元组输入的自定义控件。

Sample screenshot

引言

CFunctionEdit 是一个使用纯 Win32 API 编写的自定义控件,它允许用户输入 n 元组数据(例如数学函数、向量等)。

特点

CFunctionEdit 不限制元组的大小,但目前不支持在运行时更改元组大小。元组大小在创建控件时确定。一旦控件创建完成,您就可以设置和获取文本、限制每个元组的文本大小、更改用于渲染文本的字体、背景颜色,并定义您自己的文本格式化函数来实现彩色和带样式的文本渲染。

  • 版本 1.1 支持文本选择以及剪切、复制、粘贴操作。
  • 版本 1.2 具有弹出菜单和 readonly 样式。

初始化和使用控件

首先,使用应用程序的 HINSTANCE 调用类的 Init() 成员。Init() 函数只是在窗口类尚未注册时注册它。之后,您可以通过提供适当的参数,使用 Create() 成员函数来创建控件的实例。您可以声明您的 CFunctionEdit 变量为静态的,或者使用 new 动态创建。

CFunctionEdit theEdit;
CFunctionEdit *pEdit;
    
void CreateControls()
{
    // Call this only once before creating your controls
    CFunctionEdit::Init(hInstance); 
        
    theEdit.Create(50, 20, 100, 40, hwndParent);
        
    pFunctionEdit = new CFunctionEdit;
    pFunctionEdit->Create(50, 120, 100, 40, hwndParent);
}

在创建控件后,您可以使用 SetText() 更改内容,并使用 GetText() 获取内容。

int CFunctionEdit::SetText(int nIndex, const TCHAR *szText);
int CFunctionEdit::GetText(int nIndex, TCHAR *szText);

这里 nIndex 是元组的索引。索引从 0 开始,一直到元组数量减一。特殊情况是,如果索引等于 -1,则给定的文本将分配给控件中的所有元组。

// Assume we have a CFunctionEdit object
// called theEdit for all the following examples.

theEdit.SetText(-1, TEXT("")); // Clears all the tuples.

theEdit.GetText(0, szText); // Gets the text of the first tuple.

您可以使用 SetTextLimit() 函数来限制每个元组的文本大小。

for(int a = 0; a < nTupleCount; a++)
{
    // At most 3 characters can be entered in each tuple.
    theEdit.SetTextLimit(a, 3);
}

CFunctionEdit 具有以下对控件的输入和渲染有影响的样式标志:

#define FES_NUMERIC      1 // Allows only the input of numbers.
#define FES_UPPERCASE    2 // Converts lowercase characters to uppercase.
#define FES_LOWERCASE    4 // Converts uppercase characters to lowercase.
#define FES_DISABLESPACE 8 // Space character is ignored.
#define FES_CENTERTEXT  16 // The text is centered in the view.
#define FES_READONLY    32 // Space character is ignored.

如果您在 Create() 函数中没有显式提供样式参数,默认情况下将设置 FES_CENTERTEXTFES_DISABLESPACEFES_LOWERCASE 样式。

theEdit.SetStyle(FES_CENTERTEXT | FES_NUMERIC);

可视化特性

可以使用 SetBackgroundColor() 函数更改背景颜色。

theEdit.SetBackgroundColor(RGB(255, 255, 255)); // White background

可以使用 SetFont() 函数更改用于渲染文本的字体。

// Last two parameters are optional
theEdit.SetFont(TEXT("Courier New"), 24, ANSI_CHARSET); 

渲染文本和更改默认文本格式化函数

当用户通过按键更改文本缓冲区时,会调用 FormatText() 函数来格式化文本。该函数负责创建一个由 FORMATTEDTEXTBLOCK 结构组成的列表。它首先添加带有粗体样式的 " ( ",然后为每个元组调用格式化函数,并添加 " ) " 作为最后一个 FORMATTEDTEXTBLOCK。渲染的整个文本是 FORMATTEDTEXTBLOCK 的列表。

typedef struct _tagFormattedTextBlock
{ 
    #ifndef UNICODE 
        std::string strBlock;
    #else 
        std::wstring strBlock;
    #endif
        COLORREF clColor;
        DWORD dwStyle;

} FORMATTEDTEXTBLOCK, *LPFORMATTEDTEXTBLOCK;

typedef std::list<FORMATTEDTEXTBLOCK> FormattedText;

正如您所见,FORMATTEDTEXTBLOCK 用于定义一个具有自身颜色和样式的字符串。dwStyle 成员可以是零,或者是一个以下值(但不能组合):

#define TEXTFORMAT_BOLD 1
#define TEXTFORMAT_ITALIC 2 
#define TEXTFORMAT_UNDERLINE 4

这是默认的格式化函数

LRESULT CFunctionEdit::DefFormatTextProc(int nIndex, 
            const TCHAR *szText, FormattedText &ft) 
{
    FORMATTEDTEXTBLOCK fmttext; 
    
    if(*szText == TEXT('\0')) return 0; 
        
    // Default formatting : black, normal font 
    fmttext.dwStyle = 0;
    fmttext.clColor = RGB(0, 0, 0); 
    fmttext.strBlock = szText; 

    ft.push_back(fmttext);

    return 0;
}

此函数仅将文本以黑色-普通样式添加到 FormattedText 列表中。您可以解析传递给此函数的 szText,并将其拆分为多个 FORMATTEDTEXTBLOCK,每个块具有不同的样式。我在类中提供了一个自定义格式化函数,该函数将数字渲染为蓝色,标点符号渲染为粗体绿色,其他字符渲染为红色。

文本选择

您可以使用 SetSelection() 进行选择。

int CFunctionEdit::SetSelection(TUPLEPOS start, TUPLEPOS end);

typedef struct _tagTuplePos
{ 
    int nTupleIndex;
    int nPosition;

} TUPLEPOS, *LPTUPLEPOS;

TUPLEPOS 结构标识了控件中的一个唯一位置。结构体中的第一个成员 nTupleIndex 是我们要将光标移动到的元组索引。nPosition 是该元组内光标的位置。

TUPLEPOS selstart, selend;

selstart.nTupleIndex = 1;
selstart.nPosition   = 0;

selend.nTupleIndex = 2;
selend.nPosition   = 3;

// selects from 2nd tuple's beginning to 3rd tuple's 3rd position.
theEdit.SetSelection(selstart, selend);

检索选区

可以通过 GetSelection() 检索选中的文本。

theEdit.GetSelection(szBuffer);

定位光标

SetCaretPos() 函数用于定位光标。其原型为:

int CFunctionEdit::SetCaretPos(TUPLEPOS tuplepos);
TUPLEPOS tuplepos;

tuplepos.nTupleIndex = 0;
tuplepos.nPosition   = 0;

// Moves the cursor to the first position of the first tuple;
theEdit.SetCaretPos(tuplepos);

tuplepos.nTupleIndex = 1;
tuplepos.nPosition   = 5;

// Moves the cursor to the 5th position of the second tuple.
theEdit.SetCaretPos(tuplepos);

消息路由

最后,我想对类中的消息路由方式说几句。

typedef struct _tagWindowListEntry 
{ 
    HWND hWnd;
    CFunctionEdit *pEdit;
        
} WINDOWLISTENTRY, *LPWINDOWLISTENTRY;

typedef std::list<WINDOWLISTENTRY> WindowList;

我们有一个列表,其中包含窗口句柄和关联的对象指针。每次使用 Create() 函数创建窗口时,窗口句柄和类实例的指针都会被添加到名为 Windows 的全局变量中,该变量的类型为 WindowList。这样,我们就可以知道哪个句柄属于哪个 CFunctionEdit 对象。当 Windows 向我们的 WindowProc 发送消息时,我们通过 GetEditWindow() 函数找到关联的 CFunctionEdit*。该函数遍历列表以查找给定句柄的对象指针。找到指针后,我们调用该对象的处理程序。

MFC 和 Unicode

虽然我创建该控件时并没有考虑在 MFC 或 Unicode 项目中使用,但我曾在 MFC-Unicode 示例项目中测试过,一切似乎都运行良好。然而,我很难测试控件的所有功能,并找出是否需要为这些类型的项目进行特殊处理。

版本历史

  • 版本 1.0:2003 年 11 月 9 日
    • 首次发布
  • 版本 1.1:2003 年 11 月 10 日
    • 定位光标
    • 选择
    • 剪切、复制、粘贴
  • 版本 1.2:2003 年 11 月 16 日
    • Readonly 样式
    • 弹出菜单

支持与反馈

如有任何评论和建议,请通过 e110870@metu.edu.tr 发送电子邮件给我。

© . All rights reserved.