API и FreeBasic. (RichEdit:основы)

О контроле можно думать, как о функционально-расширенном средстве редактирования. Он обеспечивает множество полезных особенностей, которых нет в простых средствах редактирования, например, возможность использовать множество видов и размеров шрифта, глубокий уровень отмены/восстановления, операцией поиска по тексту, встроенные OLE-объекты, поддержка редактирования методом перетаскивания (drag-and-drop), и т.д. Так как richedit контрол имеет так много особенностей, он сохранен в отдельной DLL-библиотеке. Это также означает что, чтобы его использовать, вам недостаточно просто вызывать InitCommonControls, как в других Common-контролах. Вы должны вызвать LoadLibrary, чтобы загрузить richedit DLL.

Проблема состоит в том, что в настоящее время есть уже три версии richedit контрола. Версия 1,2, и 3. В таблице ниже показаны имена DLL для каждой версии.

имя DLL версия RichEdit'а Имя класса Richedit'а
Riched32.dll 1.0 RICHEDIT
RichEd20.dll 2.0 RICHEDIT20A
RichEd20.dll 3.0 RICHEDIT20A

Обратите внимание, что richedit версия 2 и 3 использует то же самое имя DLL. Они также используют одно и то же имя класса! Это может вызвать проблему, если Вы захотите использовать определенные особенности richedit'а версии 3.0. До сих пор, я не нашел официального метода различия между версиями 2.0 и 3.0. Однако, есть рабочий пример, который хорошо работает, я покажу Вам позже.

Dim Shared As HINSTANCE hInstance,hRichEdit
Dim As String RichEditDLL = "riched20.dll"
hRichEdit = LoadLibrary( RichEditDLL)
.......
FreeLibrary(hRichEdit)

Когда richedit dll загружена, она регистрирует класс окна RichEdit. Следовательно вам необходимо загрузить DLL прежде, чем Вы создадите контрол. Имена классов richedit контрола также различны. Теперь Вы можете задать вопрос: а какую версию richedit контрола мне следует использовать? Использование самой последней версии не всегда подходит, если Вы не требуете дополнительных особенностей. В таблице ниже, показаны особенности каждой версии richedit контрола.

Особенность Версия 1.0 Версия 2.0 Версия 3.0
Выделение области x x x
Редактирование уникода   x x
Форматирование символа/абзаца x x x
Поиск по тексту Вперед Вперед/назад Вперед/назад
Внедрение OLE x x x
Редактирование перетащить и отпустить(drag-and-drop) x x
x
Отменить/Повторить Одноуровневый Многоуровневый Многоуровневый
Автоматическое распознавание URL   x x
Поддержка клавиш быстрого доступа(hot key)   x x
операция уменьшения окна   x x
Конец строки CRLF только CR только CR(может подражать версии 1.0)
Увеличение     x
Нумерация абзацев     x
Простая таблица     x
Нормальный и заголовочный стили     x
Цветное подчеркивание     x
Скрытый текст     x
связывание шрифта     x

Вышеуказанная таблица ни в коем случае не полная: я только перечислил важные особенности.

Создание richedit контрола

После загрузки richedit.dll, Вы можете вызывать CreateWindowEx, для создания контрола. Вы можете использовать стили средств редактирования и common windows стили в CreateWindowEx кроме ES_LOWERCASE, ES_UPPERCASE и ES_OEMCONVERT.

Dim Shared As HINSTANCE hInstance,hRichEdit
Dim As String RichEditDLL = "riched20.dll", _
RichEditClass = "RichEdit20A"
Dim Shared As HWND hwndRichEdit
hRichEdit = LoadLibrary( RichEditDLL)
If hRichEdit<>0 Then
            hwndRichEdit = CreateWindowEx(0,RichEditClass,0, _ 
            WS_CHILD Or WS_VISIBLE Or ES_MULTILINE Or WS_VSCROLL _
            Or WS_HSCROLL Or ES_NOHIDESEL,CW_USEDEFAULT,CW_USEDEFAULT, _
            CW_USEDEFAULT, CW_USEDEFAULT, _
            hWnd,Cast(HMENU,RichEditID),hInstance,0)    
    FreeLibrary(hRichEdit)
Endif

Установка по умолчанию цвета текста и фона

У вас может возникнуть проблема с установкой цвета текста и фона в средствах редактирования. Но эта проблема была исправлена в richedit контроле. Чтобы установить цвет фона richedit контрола, вам нужно послать ему EM_SETBKGNDCOLOR. Это сообщение имеет следующий синтаксис:

WParam == цвет. Значение 0 в этом параметре означает, что Windows использует значение цвета из lParam как цвет фона. Если это значение отличное от нуля, Windows использует цвет фона системы Windows. Так как мы посылаем это сообщение, чтобы изменить цвет фона, мы должны поместить 0 в wParam.
LParam == определяет структуру COLORREF цвета, который Вы хотите установить, если wParam - 0.

Например, если бы я захотел установить цвет фона в синий, я бы поместил этот код:

SendMessage ( hwndRichEdit, EM_SETBKGNDCOLOR, 0,&hFF0000)

Устанавливая цвет текста, richedit контрол создает новое сообщение, EM_SETCHARFORMAT. Это сообщение управляет форматированием текста в диапазоне от символа в выделении до всего текста. Это сообщение имеет следующий синтаксис:

WParam == опции форматирования:

SCF_ALL Операция затрагивает весь текст в контроле.
SCF_SELECTION Операция затрагивает только выделенный текст
SCF_WORD или SCF_SELECTION Затрагивает слово в выделении. Если ничего не выделенно, то операция затронет то слово на котором находится курсор. Флаг SCF_WORD должен использоваться с SCF_SELECTION.

LParam == указатель на структуру CHARFORMAT ИЛИ CHARFORMAT2, которая определяет форматирование текста, которое нужно применить. CHARFORMAT2 доступен только для richedit 2.0 и выше. Это не подразумевает, что Вы должны использовать CHARFORMAT2 с RichEdit 2.0 и выше. Вы все еще можете использовать CHARFORMAT, если добавленные в CHARFORMAT2 особенности вам не нужны.

Type CHARFORMATA field=4
    cbSize As UINT
    dwMask As DWORD
    dwEffects As DWORD
    yHeight As Long
    yOffset As Long
    crTextColor As COLORREF
    bCharSet As Ubyte
    bPitchAndFamily As Ubyte
    szFaceName As Zstring * 32
End Type

Имя поля Описание
CbSize Размер структуры. RichEdit контрол использует это поле, чтобы определить версию структуры, является ли это CHARFORMAT или CHARFORMAT2
DwMask Разряды флагов, которые определяют, какие из следующих членов являются правильными.
CFM_BOLD CFE_BOLD член dwEffects правильный
CFM_CHARSET член BCharSet- правильный.
CFM_COLOR член CrTextColor и значение CFE_AUTOCOLOR члена dwEffects - правильные
CFM_FACE член SzFaceName правильный.
CFM_ITALIC Значение CFE_ITALIC члена dwEffects правильное
CFM_OFFSET член YOffset правильный
CFM_PROTECTED Значение CFE_PROTECTED члена dwEffects правильное
CFM_SIZE член YHeight правильный
CFM_STRIKEOUT Значение CFE_STRIKEOUT члена dwEffects правильное.
CFM_UNDERLINE Значение CFE_UNDERLINE члена dwEffects правильное
DwEffects

Символьные эффекты. Может быть комбинация следующих значений

CFE_AUTOCOLOR Использует системный цвет текста
CFE_BOLD Полужирный
CFE_ITALIC Курсивный
CFE_STRIKEOUT Перечеркнутый.
CFE_UNDERLINE Подчеркнутый.
CFE_PROTECTED Символы защищены; попытка изменять их вызовет уведомительное сообщение EN_PROTECTED.
YHeight Высота символов, в twips (1/1440 дюйма или 1/20 точки принтера).
YOffset Смещение Символа, в twips (1/1440 дюйма или 1/20 точки принтера), от основной линии. Если значение этого члена положительное, символ - верхний индекс; если - отрицательное - нижний индекс.
CrTextColor Цвет текста. Этот член игнорируется, если эффект символа CFE_AUTOCOLOR определен.
BCharSet Набор символов
BPitchAndFamily Семейство Шрифта и шаг.
SzFaceName Символьный массив с нулевым символом в конце, определяющий имя шрифта

Из исследования структуры, Вы увидите, что мы можем изменять эффекты текста (полужирный, курсивный, зачеркнутый, подчеркнутый), цвет текста (crTextColor) и шрифт (вид/размер/набор символов). Текст с флагом CFE_RPOTECTED, отмечен как защищенный, это означает, что, когда пользователь попытается изменить его, то родительскому окну будет послано уведомительное сообщение EN_PROTECTED. И Вы можете либо разрешить, либо запретить изменения.

CHARFORMAT2 добавляет большее количество текстовых стилей, таких как weight шрифта, интервала, цвета фона текста, кернинга, и т.д. Если Вы не нуждаетесь в этих дополнительных особенностях, просто используйте CHARFORMAT.

Чтобы установить формат текста, Вы должны указать диапазон текста, к которому хотите применить формат. Richedit контрол присваивает каждому символу свой номер (идентификатор), начинающийся с 0: первый символ имеет идентификатор 0, второй - 1 и так далее. Чтобы указать диапазон текста, вы должны дать richedit контролу, два числа: ИДЕНТИФИКАТОРЫ первого и последнего символа диапазона. Чтобы применить форматирование к тексту с EM_SETCHARFORMAT, у вас есть 3 выбора:

  1. Применить ко всему тексту (SCF_ALL)
  2. Применить к выделенному тексту (SCF_SELECTION)
  3. Применить к целому слову в выделенной области (SCF_WORD or SCF_SELECTION)

Первый и второй выборы прямые, последний требует некоторых объяснений. Если текущее выделение охватывает только один или большее количество символов в слове, но не, целое слово, определяя флаг SCF_WORD+SCF_SELECTION, применяется форматирование текста к целому слову. Даже если ничего не выделено, форматирование применяется к целому слову, над которым находится курсор вставки.

Чтобы использовать EM_SETCHARFORMAT, Вы должны заполнить некоторые члены структуры CHARFORMAT (или CHARFORMAT2). Например, если мы хотим установить цвет текста, мы заполним структуру CHARFORMAT следующим образом:

Dim Cf As CHARFORMAT
...
cf.cbSize = Sizeof(cf)
cf.dwMask = CFM_COLOR
cf.crTextColor = &hFF0000
SendMessage(hwndRichEdit, EM_SETCHARFORMAT, _
  SCF_ALL, @cf)

Этот фрагмент кода устанавливает цвет текста richedit контрола в синий. Обратите внимание, что, если в richedit контроле нет никакого текста, когда мы посылаем сообщение EM_SETCHARFORMAT, текст, введенный в richedit контроле после сообщения будет использовать формат текста, указанный с сообщением EM_SETCHARFORMAT.

Установка текста/сохранение текста

Те, кто уже использовали Edit контролы, наверняка знакомы с WM_GETTEXT/WM_SETTEXT, для установки/получения текста в\из контрола. Этот метод работает и с richedit контролом, но не может работать с большими файлами. Edit контрол ограничивает текст, который может быть введен в него 64КБ, но richedit контрол может принимать текст намного больше. Это было бы не правильное решение, выделить очень большой блок памяти (типа 10 мб) чтобы получить текст с помощью WM_GETTEXT. Richedit контрол предлагает новый подход к этому методу, такой как текстовый поток.

Сделать это просто, вы передаете адрес функции richedit контролу. И richedit контрол вызовет эту функцию, передавая ей адрес буфера, когда он готов. Функция заполнит буфер данными, которые требуется послать контролу или считает данные из буфера и будет ждать следующего запроса, пока операция не закончена. Эта парадигма используется для обоих операций: потоковый ввод (установка текста) и потоковый вывод (получение текста из контрола). Вы увидите, что этот метод более эффективен: буфер обеспечивается richedit контролом непосредственно, так что данные разделены на куски. Операции включают два сообщения: EM_STREAMIN и EM_STREAMOUT

Оба сообщения EM_STREAMIN и EM_STREAMOUT используют одинаковый синтаксис:

WParam == опции.

SF_RTF Данные в формате RTF (rich-text format)
SF_TEXT Данные в формате открытого текста
SFF_PLAINRTF Только ключевые слова, общие ко всем языкам в потоке.
SFF_SELECTION Если цель операции - выделенный текст. Если вводите текст, он заменяет текущее выделение. Если вы выводите текст, то будет выведен только выделенный в настоящее время текст. Если этот флаг не определен, операция работает со всем текстом в контроле.
SF_UNICODE ( Доступна для RichEdit 2.0 и выше) Определяют текст уникода.

LParam == указывают на структуру EDITSTREAM, которая имеет следующее определение:

Type EDITSTREAM field=4
    dwCookie As DWORD
    dwError As DWORD
    pfnCallback As EDITSTREAMCALLBACK
End Type

DwCookie Определенное приложением значение, которое будет передаваться к функции определенной в члене pfnCallback ниже. Мы обычно передаем функции некоторые важные значения, такие как хэндл файла, для использования в stream-in/out процедуре.
DwError Отображает результат операции stream-in (чтения) или stream-out (записи). Значение 0 - нет ошибон. Значение отличное от нуля может быть результатом функции EditStreamCallback или кода, указывающего, что произошла ошибка.
PfnCallback Указатель на функцию EditStreamCallback, которая является определенной приложением функцией, которая управляет передачей данных. Управление вызывает функцию неоднократно, передавая часть данных с каждым запросом

Editstream функция имеет следующее определение:

Type EDITSTREAMCALLBACK As Function ( _
dwCookie As DWORD,PBuffer As PBYTE,NumBytes As Long, _
PBytesTransferred As Long Ptr) As DWORD

Вы должны создать функцию с вышеупомянутым прототипом в вашей программе. И затем передайте ее адрес с помощью EM_STREAMIN или EM_STREAMOUT через структуру EDITSTREAM.

Для операции stream-in (загрузка текста в richedit контрол):

DwCookie: определенное приложением значение вы передаете с EM_STREAMIN через структуру EDITSTREAM. Мы почти всегда передаем хэндл файла, содержание которого мы хотим установить в контрол.
PBuffer: указывает на буфер, переданный richedit контролом, который получит текст от вашей функции.
NumBytes: максимальное количество байт, которые вы может записать в буфер (pBuffer) в этом запросе. Вы всегда ДОЛЖНЫ придерживаться этого предела, т.е., вы можете посылать меньшее количество данных чем значение в NumBytes, и не должны послать большее количество данных чем это значение. Вы можете считать это значение, как размер буфера pBuffer.
pBytesTransferred: указывает на переменную (dword), указывающую число байтов, которые вы фактически передали в буфер. Это значение обычно идентично значению в NumBytes. Исключение когда данных послано меньше, чем размер буфера, обычно когда достигнут конец файла.

Для операции stream-out (получение текста из richedit контрола):

DwCookie: Такой же, как в операции stream-in. Мы обычно передаем хэндл файла, в который мы хотим записать данные.
PBuffer: указывает на буфер, обеспеченный richedit контролом, который заполнен данными из richedit контрола. Чтобы получить его размер, Вы должны посмотреть значение NumBytes.
NumBytes: размер данных в буфере, указанном в pBuffer.
PBytesTransferred: указывает на переменную (dword), в которую вы должны установить значение, индицирующее число байт, которые Вы фактически считали из буфера.

Функция возвращает 0, если не было ошибок, и richedit контрол продолжит вызывать функцию, если данные все еще есть. Если произошла ошибка в течение процесса, и вы хотите остановить операцию, верните ненулевое значение, и richedit контрол откажется от данных, указанных в pBuffer. Значение ошибки/успеха будет заполнено в поле dwError структуры EDITSTREAM, так что вы можете проверить результат операции после возврата изSendMessage.

ПРИМЕР

Пример ниже это - простой редактор, которым Вы можете открывать файлы , редактировать, и сохранять их. Он использует RichEdit версии 2.0 или выше.

#INCLUDE Once "windows.bi"
#INCLUDE Once "win/richedit.bi"
#INCLUDE Once "win/commdlg.bi"

Declare Function WinMain (Byval hInst As HINSTANCE, _
Byval hPrevInst As HINSTANCE, _
Byval szCmdLine As LPSTR, _
Byval iCmdShow As Integer ) As Integer
Declare Function wndproc(hwnd As HWND, msg As Uinteger,_
wparam As WPARAM, lparam As LPARAM) As Integer

Enum Iczelion
IDM_OPEN=40001
IDM_SAVE
IDM_CLOSE
IDM_SAVEAS
IDM_EXIT
IDM_COPY
IDM_CUT
IDM_PASTE
IDM_DELETE
IDM_SELECTALL
IDM_OPTION
IDM_UNDO
IDM_REDO
IDR_MAINMENU=101
IDD_OPTIONDLG =101
IDD_FINDDLG
IDD_GOTODLG
IDD_REPLACEDLG
IDR_MAINACCEL
IDC_BACKCOLORBOX =1000
IDC_FINDEDIT   =1000
IDC_TEXTCOLORBOX =1001
IDC_MATCHCASE =1001
IDC_REPLACEEDIT =1001
IDC_WHOLEWORD
IDC_DOWN
IDC_UP
IDC_LINENO
IDM_FIND =40014
IDM_FINDNEXT
IDM_REPLACE
IDM_GOTOLINE
IDM_FINDPREV
RichEditID =300
End Enum


Dim Shared As String ClassName,AppName,RichEditDLL,RichEditClass, _
NoRichEdit,ASMFilterString,OpenFileFail,WannaSave, _
ASMSection,ZeroString

ClassName = "IczEditClass"
AppName = "IczEdit version 2.0"
RichEditDLL = "riched20.dll"
RichEditClass = "RichEdit20A"
NoRichEdit = "Cannot find riched20.dll"
ASMFilterString = "Text files (*.txt, *.ini, *.doc ,*.asm)"_
+Chr(0)+"*.txt;*.ini;*.doc;*.asm"+Chr(0)
OpenFileFail = "Cannot open the file"
WannaSave = "The data in the control is modified. Want to save it?"
ASMSection = "ASSEMBLY"
ZeroString = Chr(0)

Dim Shared As Integer BackgroundColor = &hFFFFFF , _
TextColor, _
FileOpened, _
CustomColors(16)


Dim Shared As HINSTANCE hInstance,hRichEdit
Dim Shared As HWND hwndRichEdit
Dim Shared As String*256  _
FileName , _
AlternateFileName

'---------------Старт----------------------
hInstance=GetModuleHandle( NULL)
hRichEdit = LoadLibrary( RichEditDLL)
If hRichEdit<>0 Then
    WinMain(hInstance,0,0, SW_SHOWDEFAULT)
    FreeLibrary(hRichEdit)
Else
    MessageBox(0,NoRichEdit,AppName,MB_OK Or MB_ICONERROR)
Endif
ExitProcess(0)

Function WinMain (Byval hInst As HINSTANCE, _
    Byval hPrevInst As HINSTANCE, _
    Byval szCmdLine As LPSTR, _
    Byval iCmdShow As Integer ) As Integer
    Dim As WNDCLASSEX wc
    Dim As MSG msg
    Dim As HWND hwnd
    wc.cbSize = Sizeof(WNDCLASSEX)
    wc.style = CS_HREDRAW Or CS_VREDRAW
    wc.lpfnWndProc = @wndproc
    wc.hInstance = hInst
    wc.hbrBackground = Cast(HBRUSH,COLOR_WINDOW+1)
    wc.lpszMenuName = Cast(LPSTR,IDR_MAINMENU)
    wc.lpszClassName = Strptr(ClassName)
    wc.hIcon = LoadIcon(NULL,IDI_APPLICATION)
    wc.hIconSm = wc.hIcon
    wc.hCursor  = LoadCursor(NULL,IDC_ARROW)
    If RegisterClassEx(@wc)=0 Then Return 0
    hwnd = CreateWindowEx(0,ClassName,AppName, _
    WS_OVERLAPPEDWINDOW,CW_USEDEFAULT, _
    CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,NULL,NULL, _
    hInst,NULL)
    ShowWindow(hwnd,SW_SHOWNORMAL)
    UpdateWindow(hwnd)

    While GetMessage(@msg,0,0,0)
        TranslateMessage(@msg)
        DispatchMessage(@msg)
    Wend
End Function

Function StreamInProc( hFile As Handle,pBuffer As PVOID, NumBytes As Integer, pBytesRead As Integer Ptr) As BOOl
    Dim As Integer length
    ReadFile(hFile,pBuffer,NumBytes,Cast(LPDWORD,@length),0)
    *pBytesRead = length
    If length = 0 Then
        Return 1
    Endif
End Function

Function StreamOutProc (hFile As Handle,pBuffer As PVOID, NumBytes As Integer, pBytesWritten As Integer Ptr) As bool
    Dim As Integer length
    WriteFile(hFile,pBuffer,NumBytes,Cast(LPDWORD,@length),0)
    *pBytesWritten = length
    If length = 0 Then
        Return 1
    Endif
End Function

Function CheckModifyState(hWnd As HWND) As BOOL
    If SendMessage(hwndRichEdit,EM_GETMODIFY,0,0)<>0 Then
        Select Case MessageBox(hWnd,WannaSave,AppName,MB_YESNOCANCEL)
            Case IDYES
                SendMessage(hWnd,WM_COMMAND,IDM_SAVE,0)
            Case IDCANCEL
                Return FALSE
        End Select
    Endif
    Return TRUE
End Function

Sub SetColor()
    Dim cfm As CHARFORMAT
    SendMessage(hwndRichEdit,EM_SETBKGNDCOLOR,0,BackgroundColor)
    RtlZeroMemory(@cfm,Sizeof(cfm))
    cfm.cbSize = Sizeof(cfm)
    cfm.dwMask = CFM_COLOR
    cfm.crTextColor = TextColor
    SendMessage(hwndRichEdit,EM_SETCHARFORMAT,SCF_ALL,Cast(LPARAM,@cfm))
End Sub



Function OptionProc(hWnd As HWND, uMsg As UINT, wParam As WPARAM, lParam As LPARAM) As Integer
    Dim clr As ChooseColor
    Select Case uMsg
        Case WM_INITDIALOG
        Case WM_COMMAND
            If Hiword(wParam) = BN_CLICKED Then
                Select Case Loword(wParam)
                    Case IDCANCEL
                        SendMessage(hWnd,WM_CLOSE,0,0)
                    Case IDC_BACKCOLORBOX
                        RtlZeroMemory(@clr,Sizeof(clr))
                        clr.lStructSize=SizeOf(clr)
                        clr.hwndOwner = hWnd
                        clr.hInstance = Cast(HWND,hInstance)
                        clr.rgbResult = BackgroundColor
                        clr.lpCustColors = @CustomColors(0)
                        clr.Flags = CC_ANYCOLOR Or CC_RGBINIT
                        If ChooseColor(@clr)<>0 Then
                            BackgroundColor = clr.rgbResult
                            InvalidateRect(GetDlgItem(hWnd,IDC_BACKCOLORBOX),0,TRUE)
                        Endif
                    Case IDC_TEXTCOLORBOX
                        RtlZeroMemory(@clr,Sizeof(clr))
                        clr.lStructSize = Sizeof(clr)
                        clr.hwndOwner = hWnd
                        clr.hInstance = Cast(HWND,hInstance)
                        clr.rgbResult = TextColor
                        clr.lpCustColors = @CustomColors(0)
                        clr.Flags = CC_ANYCOLOR Or CC_RGBINIT
                        If ChooseColor(@clr)<>0 Then
                            TextColor = clr.rgbResult
                            InvalidateRect(GetDlgItem(hWnd,IDC_TEXTCOLORBOX),0,TRUE)
                        Endif
                    Case IDOK
                        '==================================================================================
                        ' Save the modify state of the richedit control because changing the text color changes the
                        ' modify state of the richedit control.
                        '==================================================================================
                        Var GM = SendMessage(hwndRichEdit,EM_GETMODIFY,0,0)
                        SetColor()
                        SendMessage(hwndRichEdit,EM_SETMODIFY,GM,0)
                        EndDialog(hWnd,0)
                End Select
            Endif
        Case WM_CTLCOLORSTATIC
            If GetDlgItem(hWnd,IDC_BACKCOLORBOX) = lParam Then
                Return Cast(Integer,CreateSolidBrush(BackgroundColor))
            Else
                If GetDlgItem(hWnd,IDC_TEXTCOLORBOX)=lParam Then
                    Return Cast(Integer,CreateSolidBrush(TextColor))
                Endif
            Endif
            Return FALSE
        Case WM_CLOSE
            EndDialog(hWnd,0)
        Case Else
            Return FALSE
    End Select
    Return TRUE
End Function

Function wndproc(hwnd As HWND, uMsg As Uinteger,_
    wparam As WPARAM, lparam As LPARAM) As Integer
    Dim  ofn As OPENFILENAME
    Dim editstream As EDITSTREAM
    Dim hFile As HANDLE
    Dim hPopup As HMENU
    Dim pt As Point
    Dim chrg As CHARRANGE
    Select Case uMsg
        Case WM_CREATE
            hwndRichEdit = CreateWindowEx(WS_EX_CLIENTEDGE,RichEditClass,0,WS_CHILD Or WS_VISIBLE Or ES_MULTILINE Or WS_VSCROLL Or WS_HSCROLL Or ES_NOHIDESEL, _
            CW_USEDEFAULT,CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,hWnd,Cast(HMENU,RichEditID),hInstance,0)
            ' Set the text limit. The default is 64K
            '=============================================================
            SendMessage(hwndRichEdit,EM_LIMITTEXT,-1,0)
            '=============================================================
            ' Set the default text/background color
            '=============================================================
            SetColor()
            SendMessage(hwndRichEdit,EM_SETMODIFY,FALSE,0)
            SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER,0,0)
        Case WM_INITMENUPOPUP
            Select Case Loword(LPARAM)
                Case 0
                    If FileOpened=TRUE  Then' a file is already opened
                        EnableMenuItem(Cast(HMENU,wParam),IDM_OPEN,MF_GRAYED)
                        EnableMenuItem(Cast(HMENU,wParam),IDM_CLOSE,MF_ENABLED)
                        EnableMenuItem(Cast(HMENU,wParam),IDM_SAVE,MF_ENABLED)
                        EnableMenuItem(Cast(HMENU,wParam),IDM_SAVEAS,MF_ENABLED)
                    Else
                        EnableMenuItem(Cast(HMENU,wParam),IDM_OPEN,MF_ENABLED)
                        EnableMenuItem(Cast(HMENU,wParam),IDM_CLOSE,MF_GRAYED)
                        EnableMenuItem(Cast(HMENU,wParam),IDM_SAVE,MF_GRAYED)
                        EnableMenuItem(Cast(HMENU,wParam),IDM_SAVEAS,MF_GRAYED)
                    Endif
                Case 1  ' edit menu
                    '=============================================================================
                    ' Check whether there is some text in the clipboard. If so, we enable the paste menuitem
                    '=============================================================================
                    If SendMessage(hwndRichEdit,EM_CANPASTE,CF_TEXT,0)=0 Then
                        EnableMenuItem(wParam,IDM_PASTE,MF_GRAYED)
                    Else
                        EnableMenuItem(wParam,IDM_PASTE,MF_ENABLED)
                    Endif
                    '==========================================================
                    ' check whether the undo queue is empty
                    '==========================================================
                    If SendMessage(hwndRichEdit,EM_CANUNDO,0,0) = 0 Then
                        EnableMenuItem(wParam,IDM_UNDO,MF_GRAYED)
                    Else
                        EnableMenuItem(wParam,IDM_UNDO,MF_ENABLED)
                    Endif
                    '=========================================================
                    ' check whether the redo queue is empty
                    '=========================================================
                    If SendMessage(hwndRichEdit,EM_CANREDO,0,0)  = 0 Then
                        EnableMenuItem(wParam,IDM_REDO,MF_GRAYED)
                    Else
                        EnableMenuItem(wParam,IDM_REDO,MF_ENABLED)
                    Endif
                    '=========================================================
                    ' check whether there is a current selection in the richedit control.
                    ' If there is, we enable the cut/copy/delete menuitem
                    '=========================================================
                    SendMessage(hwndRichEdit,EM_EXGETSEL,0,Cast(Lparam,@chrg))
                    If chrg.cpMin = chrg.cpMax Then ' no current selection
                        EnableMenuItem(wParam,IDM_COPY,MF_GRAYED)
                        EnableMenuItem(wParam,IDM_CUT,MF_GRAYED)
                        EnableMenuItem(wParam,IDM_DELETE,MF_GRAYED)
                    Else
                        EnableMenuItem(wParam,IDM_COPY,MF_ENABLED)
                        EnableMenuItem(wParam,IDM_CUT,MF_ENABLED)
                        EnableMenuItem(wParam,IDM_DELETE,MF_ENABLED)
                    Endif
            End Select
        Case WM_COMMAND
            If lParam=0 Then    ' menu commands
                Select Case Loword(WPARAM)
                    Case IDM_OPEN
                        RtlZeroMemory(@ofn,Sizeof(ofn))
                        ofn.lStructSize = Sizeof(ofn)
                        ofn.hwndOwner = hWnd
                        ofn.hInstance = hInstance
                        ofn.lpstrFilter = Strptr(ASMFilterString)
                        ofn.lpstrFile = Strptr(FileName)
                        FileName = ""
                        ofn.nMaxFile = Sizeof(FileName)
                        ofn.Flags = OFN_FILEMUSTEXIST Or OFN_HIDEREADONLY Or OFN_PATHMUSTEXIST
                        If GetOpenFileName(@ofn)<>0 Then
                            hFile = CreateFile(FileName,GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0)
                            If hFile <> INVALID_HANDLE_VALUE Then
                                '================================================================
                                ' stream the text into the richedit control
                                '================================================================
                                editstream.dwCookie = Cast(DWORD,hFile)
                                editstream.pfnCallback= Cast(EDITSTREAMCALLBACK,@StreamInProc)
                                SendMessage(hwndRichEdit,EM_STREAMIN,SF_TEXT,Cast(LPARAM,@editstream))
                                '==========================================================
                                ' Initialize the modify state to false
                                '==========================================================
                                SendMessage(hwndRichEdit,EM_SETMODIFY,FALSE,0)
                                CloseHandle(hFile)
                                FileOpened = TRUE
                            Else
                                MessageBox(hWnd,OpenFileFail,AppName,MB_OK Or MB_ICONERROR)
                            Endif
                        Endif
                    Case IDM_CLOSE
                        If CheckModifyState(hWnd) = TRUE Then
                            SetWindowText(hwndRichEdit,"")
                            FileOpened = FALSE
                        Endif
                    Case IDM_SAVE
                        hFile = CreateFile(FileName,GENERIC_WRITE,FILE_SHARE_READ,NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,0)
                        If hFile<>INVALID_HANDLE_VALUE Then
                            '================================================================
                            ' stream the text to the file
                            '================================================================
                            editstream.dwCookie = Cast(DWORD,hFile)
                            editstream.pfnCallback= Cast(EDITSTREAMCALLBACK,@StreamOutProc)
                            SendMessage(hwndRichEdit,EM_STREAMOUT,SF_TEXT,Cast(LPARAM,@editstream))
                            '==========================================================
                            ' Initialize the modify state to false
                            '==========================================================
                            SendMessage(hwndRichEdit,EM_SETMODIFY,FALSE,0)
                            CloseHandle(hFile)
                        Else
                            MessageBox(hWnd,OpenFileFail,AppName,MB_OK Or MB_ICONERROR)
                        Endif
                    Case IDM_COPY
                        SendMessage(hwndRichEdit,WM_COPY,0,0)
                    Case IDM_CUT
                        SendMessage(hwndRichEdit,WM_CUT,0,0)
                    Case IDM_PASTE
                        SendMessage(hwndRichEdit,WM_PASTE,0,0)
                    Case IDM_DELETE
                        SendMessage(hwndRichEdit,EM_REPLACESEL,TRUE,0)
                    Case IDM_SELECTALL
                        chrg.cpMin = 0
                        chrg.cpMax = -1
                        SendMessage(hwndRichEdit,EM_EXSETSEL,0,Cast(LPARAM,@chrg))
                    Case IDM_UNDO
                        SendMessage(hwndRichEdit,EM_UNDO,0,0)
                    Case IDM_REDO
                        SendMessage(hwndRichEdit,EM_REDO,0,0)
                    Case IDM_OPTION
                        DialogBoxParam(hInstance,Cast(LPCTSTR,IDD_OPTIONDLG),hWnd,@OptionProc,0)
                    Case IDM_SAVEAS
                        RtlZeroMemory(@ofn,Sizeof(ofn))
                        ofn.lStructSize = Sizeof(ofn)
                        ofn.hwndOwner = hWnd
                        ofn.hInstance = hInstance
                        ofn.lpstrFilter = Strptr(ASMFilterString)
                        ofn.lpstrFile = Strptr(AlternateFileName)
                        AlternateFileName = ""
                        ofn.nMaxFile = Sizeof(AlternateFileName)
                        ofn.Flags = OFN_FILEMUSTEXIST Or OFN_HIDEREADONLY Or OFN_PATHMUSTEXIST
                        If GetSaveFileName(@ofn)<>0 Then
                            hFile = CreateFile(AlternateFileName,GENERIC_WRITE,FILE_SHARE_READ,NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,0)
                            If hFile<>INVALID_HANDLE_VALUE Then
                                '================================================================
                                ' stream the text to the file
                                '================================================================
                                editstream.dwCookie = Cast(DWORD,hFile)
                                editstream.pfnCallback= Cast(EDITSTREAMCALLBACK,@StreamOutProc)
                                SendMessage(hwndRichEdit,EM_STREAMOUT,SF_TEXT,Cast(LPARAM,@editstream))
                                '==========================================================
                                ' Initialize the modify state to false
                                '==========================================================
                                SendMessage(hwndRichEdit,EM_SETMODIFY,FALSE,0)
                                CloseHandle(hFile)
                            Endif
                        Endif
                    Case IDM_EXIT
                        SendMessage(hWnd,WM_CLOSE,0,0)
                End Select
            Endif
        Case WM_CLOSE
            If CheckModifyState(hWnd)=TRUE Then
                DestroyWindow(hWnd)
            Endif
        Case WM_SIZE
            MoveWindow(hwndRichEdit,0,0,Loword(LPARAM),Hiword(LPARAM),TRUE)
        Case WM_DESTROY
            PostQuitMessage(NULL)
        Case Else
            Return DefWindowProc(hWnd,uMsg,wParam,lParam)
    End Select
    Return 0
End Function

Файл ресурсов:

;=============================================
; Файл ресурсов
;=============================================

#DEFINE IDR_MAINMENU                    101
#DEFINE IDD_OPTIONDLG                   101
#DEFINE IDC_BACKCOLORBOX                1000
#DEFINE IDC_TEXTCOLORBOX                1001
#DEFINE IDM_OPEN                        40001
#DEFINE IDM_SAVE                        40002
#DEFINE IDM_CLOSE                       40003
#DEFINE IDM_SAVEAS                      40004
#DEFINE IDM_EXIT                        40005
#DEFINE IDM_COPY                        40006
#DEFINE IDM_CUT                         40007
#DEFINE IDM_PASTE                       40008
#DEFINE IDM_DELETE                      40009
#DEFINE IDM_SELECTALL                   40010
#DEFINE IDM_OPTION                      40011
#DEFINE IDM_UNDO                        40012
#DEFINE IDM_REDO                        40013
#DEFINE IDC_STATIC                      -1

IDR_MAINMENU MENU DISCARDABLE 
BEGIN
    POPUP "&Файл"
    BEGIN
        MENUITEM "&Открыть...",        IDM_OPEN
        MENUITEM "&Закрыть",           IDM_CLOSE
        MENUITEM "&Сохранить",         IDM_SAVE
        MENUITEM "Сохранить &как...",  IDM_SAVEAS
        MENUITEM SEPARATOR
        MENUITEM "В&ыход",             IDM_EXIT
    End
    POPUP "&Правка"
    BEGIN
        MENUITEM "&Отменить",          IDM_UNDO
        MENUITEM "&Повторить",         IDM_REDO
        MENUITEM "&Копировать",        IDM_COPY
        MENUITEM "&Вырезать",          IDM_CUT
        MENUITEM "Вст&авить",          IDM_PASTE
        MENUITEM SEPARATOR
        MENUITEM "&Удалить",           IDM_DELETE
        MENUITEM SEPARATOR
        MENUITEM "В&ыделить все",      IDM_SELECTALL
    End
    MENUITEM "П&араметры",             IDM_OPTION
End

IDD_OPTIONDLG DIALOG DISCARDABLE  0, 0, 183, 54
STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION 
    | WS_SYSMENU | DS_CENTER
CAPTION "Options"
FONT 8, "MS Sans Serif"
BEGIN
    DEFPUSHBUTTON   "OK",IDOK,137,7,39,14
    PUSHBUTTON      "Отмена",IDCANCEL,137,25,39,14
    GROUPBOX        "",IDC_STATIC,5,0,124,49
    LTEXT           "Цвет фона:",IDC_STATIC,20,14,60,8
    LTEXT  "",IDC_BACKCOLORBOX,85,11,28,14,SS_NOTIFY | WS_BORDER
    LTEXT  "Цвет текста:",IDC_STATIC,20,33,35,8
    LTEXT  "",IDC_TEXTCOLORBOX,85,29,28,14,SS_NOTIFY | WS_BORDER
End

АНАЛИЗ

Программа сначала загружает richedit dll, в нашем случае riched20.dll. Если dll не может быть загружена, то выходим из программы.

hRichEdit = LoadLibrary( RichEditDLL)
If hRichEdit<>0 Then
    WinMain(hInstance,0,0, SW_SHOWDEFAULT)
    FreeLibrary(hRichEdit)
Else
    MessageBox(0,NoRichEdit,AppName,MB_OK Or MB_ICONERROR)
Endif
ExitProcess(0)

После того, как dll успешно загружена, мы переходим к созданию нормального окна, которое будет родительским richedit контрола. Внутри обработчика WM_CREATE, мы создаем richedit контрол:

hwndRichEdit = CreateWindowEx(WS_EX_CLIENTEDGE,RichEditClass,0,WS_CHILD Or WS_VISIBLE Or ES_MULTILINE Or WS_VSCROLL Or WS_HSCROLL Or ES_NOHIDESEL, _
            CW_USEDEFAULT,CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,hWnd,Cast(HMENU,RichEditID),hInstance,0)

Обратите внимание, что мы определяем стиль ES_MULTILINE, иначе контрол будет одиночно-выровненный.

SendMessage(hwndRichEdit,EM_LIMITTEXT,-1,0)

После того, как richedit контрол создан, мы должны установить в нем новый текстовый предел. По умолчанию, richedit контрол имеет предел текста 64КБ, такой же как в простых многострочных Edit контролах. Мы должны расширить этот предел, чтобы оперировать с большими файлами. В вышеупомянутой строке, я определяю -1, которая составляет &hFFFFFFFF, очень большое значение.

SetColor()

Затем, мы устанавливаем цвет текста и фона. Так как эта операция может быть вызвана и из другой части программы, я поместил код в функцию SetColor.

Sub SetColor()
    Dim cfm As CHARFORMAT
    SendMessage(hwndRichEdit,EM_SETBKGNDCOLOR,0,BackgroundColor)

Установка цвета фона richedit контрола это прямая операция: просто пошлите сообщение EM_SETBKGNDCOLOR richedit контролу. (Если Вы используете многострочные Edit контролы, Вы должны обрабатывать WM_CTLCOLOREDIT). Заданный по умолчанию цвет фона белый.

    RtlZeroMemory(@cfm,Sizeof(cfm))
    cfm.cbSize = Sizeof(cfm)
    cfm.dwMask = CFM_COLOR
    cfm.crTextColor = TextColor

После того, как цвет фона установлен, мы заполняем члены структуры CHARFORMAT, чтобы установить цвет текста. Обратите внимание, что мы заполняем cbSize размером структуры, так что richedit контрол знает, что мы посылаем ему CHARFORMAT, а не CHARFORMAT2. DwMask имеет только один флаг, CFM_COLOR, потому что мы только хотим установить цвет текста, и crTextColor заполнен значением желаемого цвета текста.

    SendMessage(hwndRichEdit,EM_SETCHARFORMAT,SCF_ALL,Cast(LPARAM,@cfm))
End Sub

После установки цвета, Вы должны освободить буфер отмены, потому что действие изменения текста/цвета фона возможно отменить. Мы посылаем сообщение EM_EMPTYUNDOBUFFER, чтобы сделать этого.

SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER,0,0)

После заполнения структуры CHARFORMAT, мы посылаем EM_SETCHARFORMAT richedit контролу, определяя SCF_ALL флаг в wParam, чтобы указать, что мы хотим, чтобы форматирование текста применилось ко всему тексту.

Обратите внимание, что, когда мы создавали richedit контрол, мы не определили его размер и позицию. Дело в том, что мы хотим, чтобы он закрыл всю клиентскую область родительского окна. Мы изменяем его размеры всякий раз, когда изменяется размер родительского окна.

Case WM_SIZE
     MoveWindow(hwndRichEdit,0,0,Loword(LPARAM),Hiword(LPARAM),TRUE)

В вышеупомянутом фрагменте кода, мы используем новые размеры клиентской области в lParam, чтобы изменить размеры richedit контрола с помощью MoveWindow.

Когда пользователь кликает на строке меню Файл/Правка, мы обрабатываем сообщение WM_INITPOPUPMENU так, чтобы мы могли установить состояние некоторых пунктов в подменю перед отображением эго пользователю. Например, если файл уже открыт в richedit контроле, мы хотим отключить пункт открыть в подменю и включить пункт сохранить, сохранить как... и т.д.

В случае со строкой меню Файл, мы используем переменную FileOpened как флаг, чтобы определить, открыт ли уже файл. Если значение в этой переменной TRUE, то мы знаем, что файл уже открыт.

Case WM_INITMENUPOPUP
    Select Case Loword(LPARAM)
        Case 0
           If FileOpened=TRUE  Then' a file is already opened
               EnableMenuItem(Cast(HMENU,wParam),IDM_OPEN,MF_GRAYED)
               EnableMenuItem(Cast(HMENU,wParam),IDM_CLOSE,MF_ENABLED)
               EnableMenuItem(Cast(HMENU,wParam),IDM_SAVE,MF_ENABLED)
               EnableMenuItem(Cast(HMENU,wParam),IDM_SAVEAS,MF_ENABLED)
           Else
               EnableMenuItem(Cast(HMENU,wParam),IDM_OPEN,MF_ENABLED)
               EnableMenuItem(Cast(HMENU,wParam),IDM_CLOSE,MF_GRAYED)
               EnableMenuItem(Cast(HMENU,wParam),IDM_SAVE,MF_GRAYED)
               EnableMenuItem(Cast(HMENU,wParam),IDM_SAVEAS,MF_GRAYED)
           Endif

Как вы можете заметить, если файл уже открыт, мы запрещаем пункты меню открытие и разрешаем пункты меню сохранение, и наоборот если FileOpened - FALSE.

В случае со строкой меню правка, мы сначала должны проверить состояние richedit контрола и буфера обмена.

If SendMessage(hwndRichEdit,EM_CANPASTE,CF_TEXT,0)=0 Then
    EnableMenuItem(wParam,IDM_PASTE,MF_GRAYED)
Else
    EnableMenuItem(wParam,IDM_PASTE,MF_ENABLED)
Endif

Мы сначала проверяем, является ли текст в буфере обмена доступным, посылая сообщение EM_CANPASTE. Если текст доступен, SendMessage возвращает TRUE, и мы разрешаем пункт меню вставка, а если FALSE, то запрещаем.

If SendMessage(hwndRichEdit,EM_CANUNDO,0,0) = 0 Then
    EnableMenuItem(wParam,IDM_UNDO,MF_GRAYED)
Else
    EnableMenuItem(wParam,IDM_UNDO,MF_ENABLED)
Endif

Затем, мы проверяем, является ли буфер отмены пустым, посылая сообщение EM_CANUNDO. Если - не пустой, SendMessage возвращает TRUE, и мы разрешаем пункт меню отмена.

If SendMessage(hwndRichEdit,EM_CANREDO,0,0)  = 0 Then
    EnableMenuItem(wParam,IDM_REDO,MF_GRAYED)
Else
    EnableMenuItem(wParam,IDM_REDO,MF_ENABLED)
Endif

Мы проверяем буфер Повтора, посылая richedit контролу сообщение EM_CANREDO. Если - не пустой, SendMessage возвращает TRUE, и мы разрешаем пункт меню повторить.

SendMessage(hwndRichEdit,EM_EXGETSEL,0,Cast(Lparam,@chrg))
If chrg.cpMin = chrg.cpMax Then ' no current selection
    EnableMenuItem(wParam,IDM_COPY,MF_GRAYED)
    EnableMenuItem(wParam,IDM_CUT,MF_GRAYED)
    EnableMenuItem(wParam,IDM_DELETE,MF_GRAYED)
Else
    EnableMenuItem(wParam,IDM_COPY,MF_ENABLED)
    EnableMenuItem(wParam,IDM_CUT,MF_ENABLED)
    EnableMenuItem(wParam,IDM_DELETE,MF_ENABLED)
Endif

Наконец, мы проверяем, выделенно ли что-нибудь в richedit контроле, посылая сообщение EM_EXGETSEL. Это сообщение использует структуру CHARRANGE, которая определена следующим образом:

Type CHARRANGE field=4
    cpMin As Long
    cpMax As Long
End Type

CpMin содержит индекс позиции символа предшествующего первому символу в диапазоне.
CpMax содержит индекс позиции символа идущего после последнего символа в диапазоне.

После возвращения EM_EXGETSEL, структура CHARRANGE заполнена индексами позиции стартовой и конечной метками выделенного диапазона. Если ничего не выделено, cpMin и cpMax идентичны и мы, запрещаем пункты меню вырезать/копировать/удалить.

Когда пользователь нажимает пункт Открыть, мы отображаем диалоговое окно открытия файла и если пользователь выбирает файл, мы открываем файл и потоком загружаем его содержание richedit контрол.

hFile = CreateFile(FileName,GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0)
If hFile <> INVALID_HANDLE_VALUE Then
   '================================================================
   ' stream the text into the richedit control
   '================================================================
   editstream.dwCookie = Cast(DWORD,hFile)
   editstream.pfnCallback= Cast(EDITSTREAMCALLBACK,@StreamInProc)
   SendMessage(hwndRichEdit,EM_STREAMIN,SF_TEXT,Cast(LPARAM,@editstream))

После того, как файл успешно открыт с CreateFile, мы заполняем структуру EDITSTREAM, подготавливаем к сообщению EM_STREAMIN. Мы выбираем послать хэндл открытого файла через член dwCookie и передать адрес потоковой функции в pfnCallback.

Потоковая процедура сама по себе простая.

Function StreamInProc( hFile As Handle,pBuffer As PVOID, NumBytes As Integer, pBytesRead As Integer Ptr) As BOOl
    Dim As Integer length
    ReadFile(hFile,pBuffer,NumBytes,Cast(LPDWORD,@length),0)
    *pBytesRead = length
    If length = 0 Then
        Return 1
    Endif
End Function

Вы можете заметить, что все параметры потоковой процедуры совершенно соответствуют ReadFile. И возвращаемое значение ReadFile - нас не волнует. А волнует то, что возвратится в буфер pBytesRead. Если кол-во символов равно нулю, возвращаем 1 , в противном случае вернем 0 , что скажет о правильном завершении функции.

SendMessage(hwndRichEdit,EM_SETMODIFY,FALSE,0)
CloseHandle(hFile)
FileOpened = TRUE

После возврата EM_STREAMIN, это означает, что потоковая операция завершена. В действительности, мы должны проверить значение dwError члена структуры EDITSTREAM.

Richedit (и Edit) контролы поддерживают флаг, чтобы указать, изменялось ли их содержание. Мы можем получить значение этого флага, посылая им сообщение EM_GETMODIFY. SendMessage возвращает TRUE, если содержание изменялось. Так как загрузка текста в контрол, это - своего рода модификация. Мы должны установить флаг модификации в FALSE, посылая контролу сообщение EM_SETMODIFY с wParam == FALSE после того, как операция stream-in завершится. Мы немедленно закрываем файл и устанавливаем FileOpened в TRUE чтобы указывать, что файл был открыт.

Когда пользователь кликнет на пункт меню сохранить/сохранить как..., мы используем сообщение EM_STREAMOUT, чтобы вывести содержимое richedit контрола в файл. Как и функция stream-in, функции stream-out - простота сама по себе. Это совершенно соответствует WriteFile.

Текстовые операции такие как вырезать/копировать/вставить/восстановить/отменить, легко осуществимы, посылая richedit контролу сообщение WM_CUT/WM_COPY/WM_PASTE/WM_REDO/WM_UNDO соответственно.

Операции удаление/выделить всe сделаны следующим образом:

Case IDM_DELETE
    SendMessage(hwndRichEdit,EM_REPLACESEL,TRUE,0)
Case IDM_SELECTALL
    chrg.cpMin = 0
    chrg.cpMax = -1
    SendMessage(hwndRichEdit,EM_EXSETSEL,0,Cast(LPARAM,@chrg))

Операция удаление работает с выделением. Я посылаю сообщение EM_REPLACESEL с NULL строкой, чтобы richedit контрол заменил выделенный текст пустой строкой.

Операция выделить всё сделана, посылая сообщение EM_EXSETSEL, установив cpMin == 0 и cpMax ==-1, что равносильно выделению всего текста.

Когда пользователь выбирает строку меню Параметры, мы отображаем диалоговое окно, представляющее текущие цвета фона/текста.

richedit_1.gif 

Когда пользователь кликает на одной из палитры цветов, вызывается диалоговое окно выбора цвета. "Палитра цветов" - фактически статический элемент управления с флагом WS_BORDER и SS_NOTIFY. Статический элемент управления с флагом SS_NOTIFY уведомит его родительское окно с действиями мыши на нем, типа BN_CLICKED (STN_CLICKED). Это - уловка.

Case IDC_BACKCOLORBOX
    RtlZeroMemory(@clr,Sizeof(clr))
    clr.lStructSize=SizeOf(clr)
    clr.hwndOwner = hWnd
    clr.hInstance = Cast(HWND,hInstance)
    clr.rgbResult = BackgroundColor
    clr.lpCustColors = @CustomColors(0)
    clr.Flags = CC_ANYCOLOR Or CC_RGBINIT
    If ChooseColor(@clr)<>0 Then
        BackgroundColor = clr.rgbResult
        InvalidateRect(GetDlgItem(hWnd,IDC_BACKCOLORBOX),0,TRUE)
    Endif

Когда пользователь кликает на одной из палитры цветов, мы заполняем члены структуры CHOOSECOLOR и вызываем диалоговое окно выбора цвета ChooseColor. Если пользователь выбирает цвет, то это значение colorref возвращается в члене rgbResult, и мы сохраняем это значение в переменной BackgroundColor. После этого, мы вынуждаем перекрашивание на палитре цветов, вызывая InvalidateRect на хэндл палитры цветов. Палитра цветов посылает WM_CTLCOLORSTATIC сообщение своему родительскому окну.

If GetDlgItem(hWnd,IDC_BACKCOLORBOX) = lParam Then
    Return Cast(Integer,CreateSolidBrush(BackgroundColor))

Внутри обработчика WM_CTLCOLORSTATIC, мы сравниваем, хэндл статического элемента управления переданного в lParam> с обоими палитрами цветов. Если значение соответствует, мы создаем новую кисть, используя цвет из переменной и немедленно возвращаемся. Статический элемент управления будет использовать недавно созданную кисть, чтобы окрасить ее фон.

Перевод на русский с оригинала: UniSoft, адаптация материалов под FreeBasic: Станислав Будинов

содержание | назад | вперед