Если встроенного Undo достаточно, то это очень просто:
Memo1.Perform(EM_UNDO, 0, 0);
Для переключения свойства Enabled пункта меню Undo1:
Undo1.Enabled := Memo1.Perform(EM_CANUNDO, 0, 0) <> 0;
Как можно определить, на какой строке в TMemo находится курсор?
Весь фокус в сообщении EM_LINEFROMCHAR. Попробуйте:
procedure TMyForm.BitBtn1Click(Sender: TObject);
var
ILine: Integer;
begin
ILine := Memo1.Perform(EM_LINEFROMCHAR, $FFFF, 0);
{ Внимание: номера строк начинаются с нуля }
MessageDlg('Line Number: ' + IntToStr(ILine), mtInformation, [mbOK], 0);
end;
15. Как поместить BLOB Memo в компонент TMemo?
Попробуйте так:
procedure TForm1.Button1Click(Sender: TObject);
var
S: TBlobStream;
begin
S := TBlobStream.Create(Table1BBBMemo, bmRead);
Memo1.Lines.LoadFromStream(S);
S.Free;
end;
где:
1. Table1BBBMemo — имя поля BLOB Memo (TMemoField).
2. Memo1 — имя компонента TMemo. Естественно, что этим же способом можно обмениваться информацией с BLOB-полями произвольного типа.
16. Как показать содержимое Memo поля в TDBGrid?
Используйте следующий код для обработки события OnDrawDataCell у TDBGrid. (Перед запуском программы создайте объект TMemoField для memo поля в Fields Editor).
procedure TForm1.DBGrid1DrawDataCell(Sender: TObject; const Rect: TRect; Field: TField; State: TGridDrawState);
var
P: array [0..1023] of Char; { MemoField buffer }
BS: TBlobStream;
S: string;
begin
if Field is TMemoField then
with (Sender as TDBGrid).Canvas do
begin
{ Table1Notes is the TMemoField }
BS := TBlobStream.Create(Table1Notes, bmRead);
FillChar(P, SizeOf(P), #0);
BS.Read(P, SizeOf(P));
BS.Free;
S := StrPas(P);
{ remove carriage returns & line feeds }
while Pos(#13, S) > 0 do S[Pos(#13, S)] := ' ';
while Pos(#10, S) > 0 do S[Pos(#10, S)] := ' ';
{ clear the cell }
FillRect(Rect);
{ fill cell with memo data }
TextOut(Rect.Left, Rect.Top, S);
end;
end;
17. Не возникает событие TSpeedButton.OnDblClick.
Я создаю событие на SpeedButton1.OnDblClick, но оно, похоже, вообще никогда не возникает. OnClick работает. Что делать?
На самом деле работает, только в определенных ситуациях. Если вы помещаете на панель несколько кнопок, то по умолчанию они независимы и соответственно не фиксируются в нажатом состоянии. Поскольку одиночное нажатие мыши на кнопку отрабатывается немедленно, двойной щелчок мыши воспринимается как два нажатия и отпускания. Поэтому OnDblClick и не срабатывает.
Если же кнопки связаны в группу (GroupIndex <> 0), то они могут фиксироваться, и соответственно могут воспринимать двойной щелчок мыши.
18. Как разделить обработку OnClick и OnDblClick? Ведь OnClick будет вызываться всегда, и перед DblClick.
Именно так и происходит в Windows — посылаются оба сообщения. Для того чтобы обработать только какое-то одно событие необходимо чуть "задержать" выполнение OnClick. Сделать это можно следующим способом:
procedure TForm1.ListBox1Click(Sender: TObject);
var
Msg: TMsg;
TargetTime: Longint;
begin
{ get the maximum time to wait for a double-click message }
TargetTime := GetTickCount + GetDoubleClickTime;
{ cycle until DblClick received or wait time run out }
while GetTickCount < TargetTime do
if PeekMessage(Msg, ListBox1.Handle, WM_LBUTTONDBLCLK, WM_LBUTTONDBLCLK, WM_NOREMOVE)
then Exit; { Double click }
MessageDlg('Single clicked', mtInformation, [mbOK], 0);
end;
19. Как определить из обработчика события OnClick в Popup.MenuItem, для какого объекта это произошло?
Используйте свойство PopupComponent компонента TPopupMenu для определения, где была нажата правая кнопка.
procedure TForm1.PopupItem1Click(Sender: TObject);
begin
Label1.Caption := PopupMenu1.PopupComponent.Name;
end;
Свойство ActiveControl для формы тоже можно использовать, однако, ActiveControl не обязательно является тем элементом, для которого произошло событие.
20. Как использовать case, чтобы определить, какой объект вызвал процедуру?
Используйте свойство Tag. Установите значение Tag свое у каждого объекта для опознания. (Использование констант, которые описывают объект — идеально подходит).
case (Sender as TComponent).Tag of