You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Delphi FireMonkey中TEdit派生类创建关联对象失败求助

Fixing TMyEdit Sync Issues with Self-Created MyString Instance

Let's break down why your TMyEdit isn't working correctly when it creates its own MyString, and walk through the fixes step by step.

Key Issues in Your Code

  1. Global vs. Internal MyString Instance Mismatch: When TMyEdit creates its own MyString, it's stored in the private fMyString field of the edit control—not the global Mystring variable. So when you check the global variable later, it's still nil.
  2. Private Value Property: Your TMyString class has a private value property, which means external code (like your button click handlers) can't actually access it—this should cause a compile error.
  3. Incorrect OnChange Override: You redeclared the OnChange property in TMyEdit, which hides the base TEdit's OnChange event and breaks the default text change trigger logic.
  4. One-Way Sync Only: Your current code only syncs TEdit text to MyString, but doesn't handle the reverse (MyString changes updating the TEdit).

Fixed Implementation

Step 1: Update TMyString for External Access and Change Notifications

First, make the value property public and add an OnChange event to support two-way sync:

type
  TMyString = class(TObject)
  private
    fValue: string;
    fOnChange: TNotifyEvent;
    procedure SetValue(const Value: string);
  public
    property Value: string read fValue write SetValue;
    property OnChange: TNotifyEvent read fOnChange write fOnChange;
  end;

procedure TMyString.SetValue(const Value: string);
begin
  if fValue <> Value then
  begin
    fValue := Value;
    // Notify listeners when the value changes
    if Assigned(fOnChange) then
      fOnChange(Self);
  end;
end;

Step 2: Fix TMyEdit to Handle Sync Properly

Remove the custom OnChange property, use the base TEdit's event, and add two-way sync logic:

TMyEdit = class(TEdit)
  constructor Create(AOwner: TComponent); override;
private
  fMyString: TMyString;
  procedure EditChanged(Sender: TObject);
  procedure MyStringChanged(Sender: TObject);
  procedure SetMyString(const Value: TMyString);
public
  property MyProperty: TMyString read fMyString write SetMyString;
  procedure LoadFromMyString;
end;

constructor TMyEdit.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  // Bind the base TEdit's OnChange event to our handler
  inherited OnChange := EditChanged;
end;

procedure TMyEdit.EditChanged(Sender: TObject);
begin
  if not Assigned(fMyString) then
  begin
    // Create internal MyString if none exists
    fMyString := TMyString.Create;
    // Bind its change event to sync back to the edit
    fMyString.OnChange := MyStringChanged;
  end;
  fMyString.Value := Self.Text;
end;

procedure TMyEdit.MyStringChanged(Sender: TObject);
begin
  // Sync MyString value back to the edit control
  if Sender is TMyString then
    Self.Text := TMyString(Sender).Value;
end;

procedure TMyEdit.LoadFromMyString;
begin
  if Assigned(fMyString) then
    Self.Text := fMyString.Value;
end;

procedure TMyEdit.SetMyString(const Value: TMyString);
begin
  if fMyString <> Value then
  begin
    // Unbind old MyString's event
    if Assigned(fMyString) then
      fMyString.OnChange := nil;
    fMyString := Value;
    // Bind new MyString's event for reverse sync
    if Assigned(fMyString) then
      fMyString.OnChange := MyStringChanged;
    LoadFromMyString;
  end;
end;

Step 3: Update Form Logic to Handle Both Scenarios

Adjust your button handlers to check the TMyEdit's internal MyString (not just the global variable) and sync properly:

procedure TForm1.Button1Click(Sender: TObject);
begin
  Myedit1 := TMyEdit.Create(Form1);
  Myedit1.Parent := Form1;
  Myedit1.Position.X := 10;
  Myedit1.Position.Y := 10;
  Myedit1.Width := 200;
  
  // Use global MyString if it exists, else let TMyEdit create its own
  if Assigned(Mystring) then
    Myedit1.MyProperty := Mystring;
  
  MyEdit1.Text := 'any text';
  Myedit1.EditChanged(Self); // Trigger initial sync
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
  // Check TMyEdit's internal MyString first
  if Assigned(Myedit1.MyProperty) then
    Button2.Text := Myedit1.MyProperty.Value
  else
    Button2.Text := 'MyString not created';

  // Optional: Check global MyString separately
  if Assigned(Mystring) then
    ShowMessage('Global MyString value: ' + Mystring.Value)
  else
    ShowMessage('Global MyString not created');
end;

procedure TForm1.Button3Click(Sender: TObject);
begin
  // Clean up existing global MyString if needed
  if Assigned(Mystring) then
    Mystring.Free;
    
  Mystring := TMyString.Create;
  Mystring.Value := '123';
  
  // Assign global MyString to TMyEdit if it exists
  if Assigned(Myedit1) then
    Myedit1.MyProperty := Mystring;
end;

How This Fixes Your Problem

  • Two-Way Sync: Now changes to either the TEdit text or MyString value will sync to the other.
  • Correct Instance Handling: TMyEdit's MyProperty exposes both externally provided and self-created MyString instances, so you can check it directly instead of relying solely on the global variable.
  • Proper Event Binding: We use the base TEdit's OnChange event instead of hiding it, ensuring text changes trigger the sync logic reliably.

内容的提问来源于stack exchange,提问作者costateixeira

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 07:31:15