Background 
The following is the relevant code.
  TCustomEdit = class(TWinControl)
  private
    FReadOnly: Boolean;
    procedure SetReadOnly(Value: Boolean);
  protected
    property ReadOnly: Boolean read FReadOnly write SetReadOnly default False;
  end;
  TEdit = class(TCustomEdit)
  published
    property ReadOnly;
  end;
 | 
 
 First Attempts (Did not work) 
The first thing I tried was to simply replace the existing property with a new definition.
    property ReadOnly: Boolean read FReadOnly write SetReadOnly default False;
procedure TMyCustomEdit.SetReadOnly(const Value: Boolean);
begin
  FReadOnly := Value;         // sets the local value
  if FReadOnly then color := FColorWhenReadOnly
               else color := FColorWhenReadWrite;
end;
 | 
procedure TCustomEdit.SetReadOnly(Value: Boolean);
begin
  if FReadOnly <> Value then
  begin
    FReadOnly := Value;
    if HandleAllocated then
      SendMessage(Handle, EM_SETREADONLY, Ord(Value), 0);  // key line - actually changes the component behavior
  end;
end;
 | 
Unfortunately, there is no way to directly access the setter method (since it is private). Instead, I was able to access the parent property using the following.
    property ReadOnly: Boolean read FReadOnly write SetReadOnly default False;
procedure TMyCustomEdit.SetReadOnly(const Value: Boolean);
var
  tempEdit : TEdit;
begin
  tempEdit := self as TEdit;  // cast to parent class
  tempEdit.ReadOnly := Value; // this calls the parent class setter method
  FReadOnly := Value;         // sets the local value, displayed in Object Inspector
  if FReadOnly then color := FColorWhenReadOnly
               else color := FColorWhenReadWrite;
end;
 | 
As I said, my first attempt at the simple solution (above) failed. Next I tried copying the code from TCustomEdit.SetReadOnly to my property .. and it also failed.
    property ReadOnly: Boolean read FReadOnly write SetReadOnly default False;
procedure TMyCustomEdit.SetReadOnly(Value: Boolean);
begin
  if FReadOnly <> Value then
  begin
    FReadOnly := Value;
    if HandleAllocated then
      SendMessage(Handle, EM_SETREADONLY, Ord(Value), 0);
  end;
  if FReadOnly then color := FColorWhenReadOnly
               else color := FColorWhenReadWrite;
end;
 | 
When I set a breakpoint and single stepped the code, it worked .. when I ran it without breakpoints, it failed. The reason was far from obvious.
(To debug this type of problem, you must enable Debug DUCs under Project / Options... / Compiler.)
I eventually determined that when properties are set as the program starts, none of the windows implemented objects exist. As a result, HandleAllocated returns false because FHandle equals zero. However, when setting breakpoints and single stepping the program, I let the mouse hover over the Handle variable .. and THIS caused the program to go ahead and create the edit window.
To be clear, when I
(I love intermittent software .. don't you?)
This is because window'ed components (such as TEdit) are not created until AFTER TForm.FormCreate is called. As a result, the code that actually creates the components must read the available properties and send them to windows. In the case of ReadOnly, I had overridden the original definition and used my own FReadOnly variable. When the component was finally created, the following code used the original variable.
  TCustomEdit = class(TWinControl)
  protected
    procedure CreateParams(var Params: TCreateParams); override;
  end;
procedure TCustomEdit.CreateParams(var Params: TCreateParams);
const
  Passwords: array[Boolean] of DWORD = (0, ES_PASSWORD);
  ReadOnlys: array[Boolean] of DWORD = (0, ES_READONLY);
  CharCases: array[TEditCharCase] of DWORD = (0, ES_UPPERCASE, ES_LOWERCASE);
  HideSelections: array[Boolean] of DWORD = (ES_NOHIDESEL, 0);
  OEMConverts: array[Boolean] of DWORD = (0, ES_OEMCONVERT);
begin
  inherited CreateParams(Params);
  CreateSubClass(Params, 'EDIT');
  with Params do
  begin
    Style := Style or (ES_AUTOHSCROLL or ES_AUTOVSCROLL) or
      BorderStyles[FBorderStyle] or Passwords[FPasswordChar <> #0] or
      ReadOnlys[FReadOnly] or CharCases[FCharCase] or
      HideSelections[FHideSelection] or OEMConverts[FOEMConvert];
    if NewStyleControls and Ctl3D and (FBorderStyle = bsSingle) then
    begin
      Style := Style and not WS_BORDER;
      ExStyle := ExStyle or WS_EX_CLIENTEDGE;
    end;
  end;
end;
 | 
procedure TMyCustomEdit.CreateParams(var Params: TCreateParams);
begin
  inherited CreateParams(Params);
  with Params do begin
    if FReadOnly
      then Style := Style or      ES_READONLY
      else Style := Style and not ES_READONLY;
  end;
end;
 | 
 
 Other Techniques 
var tempEdit : TEdit; begin tempEdit := self as TEdit; // cast to parent class tempEdit.ReadOnly := Value; // this calls the parent class setter method  | 
(TEdit)self.ReadOnly := Value; // this calls the parent class setter method  | 
(self as TEdit).ReadOnly := Value; // this calls the parent class setter method  | 
function TCustomMaskEdit.GetEditText: string;
begin
  Result := inherited Text;  // simply access the property - never thought of this
end;
procedure TCustomMaskEdit.SetText(const Value: string);
begin
  if not IsMasked then
    inherited Text := Value  // simply access the property - never thought of this
  else
 .....
 | 
 
 Final Code 
  TMyCustomEdit = class(TEdit)
  private
    FColorWhenReadOnly : TColor; // the *color* property is the currently displayed color
    FColorWhenReadWrite: TColor; // these allow the color to indicate the readonly state
    procedure SetReadOnly(const Value: Boolean);
  public
    constructor Create(AOwner: TComponent); override;
  published
    property ColorWhenReadOnly : TColor read FColorWhenReadOnly  write SetColorWhenReadOnly  default clBtnFace;
    property ColorWhenReadWrite: TColor read FColorWhenReadWrite write SetColorWhenReadWrite default clWindow;
    property ReadOnly write SetReadOnly;
  end;
constructor TMyCustomEdit.Create(AOwner: TComponent);
begin
  inherited;
  FColorWhenReadOnly  := clBtnFace;
  FColorWhenReadWrite := clWindow ;
end;
procedure TMyCustomEdit.SetReadOnly(const Value: Boolean);
var
  tempEdit : TEdit;
begin
  tempEdit := self as TEdit;  // cast to parent class
  tempEdit.ReadOnly := Value; // this calls the parent class setter method
  if Value then color := FColorWhenReadOnly
           else color := FColorWhenReadWrite;
end;
 | 
 
 Comment 
I hope you find these notes useful.
 
 Advice