Delphi firemonkey-持有对另一个对象的引用的组件-创建对象实例 [英] Delphi firemonkey - Component that holds a reference to another object - creating an object instance

查看:36
本文介绍了Delphi firemonkey-持有对另一个对象的引用的组件-创建对象实例的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图拥有一个包含对另一个对象(MyString)的引用的TEditDescendant,以便在编辑TEdit文本时,MyString得到更新,反之亦然.当MyString已经存在时,此方法有效.但是,如果我需要MyEdit才能创建MyString,则无法使用.

I'm trying to have a TEditDescendant that contains a reference to another object (MyString), so that when I edit the TEdit text, MyString gets updated and vice-versa. This works when MyString is already existing. However, if I need MyEdit to be able to create a MyString, it does not work.

下面的代码是一个最小的示例:

The code below is a minimal example:

  • btnCreateMyEdit创建myEdit并为其提供一些文本.
  • btnCreateMyString创建myString的实例btnCheck显示问题:

  • btnCreateMyEdit creates the myEdit and gives it some text.
  • btnCreateMyString creates an instance of myString btnCheck shows the issues:

  • 如果在存在myString之后(即单击btnCreateMyString之后)创建了btnCheck,则没有问题.
  • 如果在myString存在之前(即单击btnCreateMyString之前)创建了btnCheck,则表明未创建MyString.

我正在使用Delphi Tokyo.

I'm using Delphi Tokyo.

unit Unit1;

interface

uses
  System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
  FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, FMX.Controls.Presentation, FMX.Edit, FMX.StdCtrls;


type
  tMyString= class(TObject)
  private
  fvalue:string;
    property value : string read fvalue write fvalue;
  end;


  TMyEdit = class(TEdit)
    constructor Create(AOwner: TComponent); override;
  private
    fMyString: tMyString;
    fOnChange : TNotifyEvent;
    procedure MyOnChange(Sender : TObject);
  public
    procedure setMyString(prop:tMyString);
    property MyProperty: tMyString read fMyString write setMyString;
    procedure load;
    property OnChange : TNotifyEvent read fOnChange write fOnChange;
  end;

  TForm1 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    Button3: TButton;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure Button3Click(Sender: TObject);
  end;

var
  Form1: TForm1;
  Myedit1:TMyEdit;
  Mystring:TMyString;

implementation

{$R *.fmx}

constructor TMyEdit.Create(AOwner: TComponent);
begin
 inherited Create(AOwner);
 Inherited OnChange := MyOnChange;
end;

procedure TMyEdit.MyOnChange(Sender : TObject);
begin
  if (text<>'') and (fMyString=nil) then fMyString:=TMyString.Create;
  fMystring.value:=self.text;
end;

procedure TMyEdit.load;
begin
  if fMyString<>nil then
  text:=fMyString.value;
end;


procedure TMyEdit.setMyString(prop:tMyString);
begin
  fMyString:=prop;
  load;
end;

procedure TForm1.Button1Click(Sender: TObject);

begin
  Myedit1:=TMyEdit.Create(Form1);
  MyEdit1.Parent:=Form1;
  Myedit1.MyProperty:=MyString;
  MyEdit1.Text:='any text';
  Myedit1.MyOnChange(self);
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
  button2.Text:=Myedit1.myproperty.value;
  button2.Text:= Mystring.value;
end;

procedure TForm1.Button3Click(Sender: TObject);
begin
  mystring:=tmystring.Create;
  mystring.value:='123';
end;

end.

推荐答案

当您希望修改链接到对象的外部对象(您已经存储了对其的引用)中的数据时,我认为最好使用我将其命名为属性转发"的方法.

When you wish to modify data from an external object that is linked to your object (you have a reference to it stored) I think it might be best to use approach which I named "Property forwarding".

现在,基本上,您使用这种方法所做的就是在基础对象中定义一个属性,该属性同时使用getter和setter方法来读取和写入数据.但是,您不像这些getter和setter方法最常见的那样从内部字段读取数据或将数据写入某个内部字段,而是通过直接访问该外部对象从某个外部对象读取数据或将数据写入某些外部对象的方式或使用外部对象自己的属性访问其数据.我建议使用后者,因为这样您就可以更轻松地修改外部对象,而无需修改从该外部对象读取数据或将数据写入该外部对象的所有其他对象.

Now basically what you do in such approach is to define a property in your base object that is making use of both getter and setter methods for reading and writing data. But instead of reading data from or writing data to some internal field as these getter and setter methods most commonly doe you actually define then in a way that instead they are reading data from or writing data to some external object either by directly accessing that objects field or use external object own property for accessing its data. I suggest using the latter because this way you make it easier to modify the external object without the need to modify every other object that is reading data from or writing data to this external object.

这里是一个简短且希望注释良好的代码示例,它将向您展示这种方法的工作原理:

Here is short and hopefully well commented code example that will show you how such approach works:

type
  //Our external object for storing some data
  TMyExternalObject = class (TObject)
  private
    //Field for storing some string value
    fStrValue: String;
  public
    //Property for accessing the value of fStrValue field
    property StrValue: String read fStrValue write fStrValue;
  end;

  //Out base object that will use external object for storing additionall data
  TMyBaseObject = class (TObject)
  private
    //Field for storing reference to our external object
    fMyExternalObject: TMyExternalObject;
  protected
    //Getter method that we will use to forward some data from our external object
    function GetMyExternalObjectStr: string;
    //Setter method that we will use to store some data into our external object
    procedure SetMyExternalObjectStr(AValue: String);
  public
    //Modified constructor which accepts additional optional parameter so that we can
    //set the reference to our external object upon creation of our base class
    //If this parameter is omitted when calling constructor the default value of nil will
    //be set to AMyExternalObject
    constructor Create(AMyExternalObject: TMyExternalObject = nil);
    //Property declaration that uses custom made getter and setter methods
    property ExternalObjectStr: String read GetMyExternalObjectStr write SetMyExternalObjectStr;
  end;

implementation

{ TMyBaseObject }

//Modified constructor which can set fMyExternalObject reference to the object that is passed
//as constructor parameter
constructor TMyBaseObject.Create(AMyExternalObject: TMyExternalObject);
begin
  inherited Create;
  //Set the reference of external object to the object that was passed as AMyExternalObject parameter
  //If parameter was omitted the default value of nil which was defined in constructor will be used
  fMyExternalObject := AMyExternalObject;
end;

//Getter method used to forward data from our external object
function TMyBaseObject.GetMyExternalObjectStr: string;
begin
  //Always check to se if fMyExternalObject reference is set to point to existing object otherwise you
  //will cause AccessVialation by trying to read data from nonexistent object
  if fMyExternalObject <> nil then
  begin
    //Simply assign the returned value from our external object property to the result of the method
    result := fMyExternalObject.StrValue;
  end
  else
  begin
    //If fmyExternalObject field does not reference to an existing object you will sill want to return
    //some predefined result. Not doing so could cause code optimizer to remove this entire method from
    //the code before compilation.
    //Delphi should warn you about possibility that function might not have a defined result
    result := 'No external object attached';
  end;
end;

//Setter method used to store some data to external object.
//This method also takes care of creating and linking the external object if one hasn't been linked already
procedure TMyBaseObject.SetMyExternalObjectStr(AValue: String);
begin
  //Check to see if fMyExternalObject already references to an existing external object.
  //If it does not create external object and set fMyExgternalObject to point to it
  if fMyExternalObject = nil then
  begin
    //Create the external object and set fMyExternalObject field to point to it
    fMyExternalObject := TMyExternalObject.Create;
  end;
  //Write our data to external object
  fMyExternalObject.StrValue := AValue;
end;

请注意,此代码示例未进行正确的错误检查(在那里需要几个try..except块.为了使代码更具可读性,我故意省略了它们.

Note this code example does not have proper error checking (would need several try..except blocks there. I purposely omitted them in order to make code more readable.

我的代码也被编写为用于类而不是组件.因此,您必须对其进行修改才能与派生的TEdit组件一起使用.因此,您将必须以不会隐藏TEdit构造函数的默认参数的方式修改构造函数声明.

Also my code is written to work with classes and not components. So you will have to modify it to work with your derived TEdit component. So you will have to modify the constructor declaration in a way that it won't hide default parameters of TEdit's constructor.

注意:虽然我的代码示例将允许您让多个TEdit框读取和修改存储在外部对象中的相同字符串值,但是当外部对象的字符串值出现时,不会导致所有这些TEdit框自动更新其文本被改变了.这样做的原因是我的代码示例没有任何机制来通知其他TEdit框重绘自身,从而显示新的更新文本.

NOTE: While my code example will allow you to have multiple TEdit boxes reading and modifying the same string value that is stored in external object it will not cause all of those TEdit boxes to automatically update their text when the external objects string value is changed. Thre reason for this is that my code example does not have any mechanism for notifying the other TEdit boxes to redraw themselves and thus show the new updated text.

为此,您将必须设计一种特殊的机制,该机制将通知所有TEdit组件,以便它们需要自我更新.这种机制还需要您的外部对象存储对链接到它的所有TEdit组件的引用.如果您决定执行该系统,则应特别注意,因为该系统会引起循环引用,并且可能会阻止自动引用计数在不再需要时正确释放对象.
在这里阅读有关组件通知系统及其工作原理的更多信息可能并不坏.为什么?因为组件通知系统的目的是提供允许您向多个组件通知某些事件的功能.

For this you will have to design a special mechanism which will notify all of the TEdit components so that they need to update themselves. Such mechanism would also require your external object to store the reference to all the TEdit components that are linking to it. If you decide to go and implement such system pay special attention because such system would be causing circular referencing and could prevent Automatic Reference Counting to properly free up the objects when there are no longer needed.
Here it might not be bad to go read some more about component notification system and how it works. Why? Because the purpose of component notification system is to provide such functionality that allows you to notify multiple component of some events.

警告:由于上面的代码示例在必要时创建了这些外部对象,因此您必须确保还有正确的代码来销毁那些创建的外部对象,否则可能会泄漏它们.
现在,如果只有一个TEdit框连接到此类外部对象,则可以在TEdit析构函数中销毁它.但是,如果计划将多个TEdit组件连接到同一外部对象,则必须设计其他机制来跟踪这些外部对象的寿命.

WARNING: Since the above code example is creating these external objects when necessary you will have to make sure there is also proper code for destroying those created external objects otherwise you risk leaking them.
Now if you have just one TEdit box connecting to such external object you can destroy it in TEdit destructor. But if you plan on connection multiple TEdit components to same external object you will have to devise some other mechanism to track the life of these external objects.

希望我的回答对您有所帮助.

I hope my answer will come helpful to you.

无论如何,我建议您阅读有关使用getter和setter方法的更多信息.如果正确使用它们,它们可能会非常强大.

Any way I recommend you read more about using of getter and setter methods. They can be pretty powerful when used correctly.

PS:这种方法不是新颖的.我敢肯定,它曾经被使用过很多次.我也使用"Property Forwarding"这个名称来命名.很有可能它有一些我不知道的不同名称.

PS: This approach is no novelty. I'm pretty sure it was used many times before. Also the name "Property forwarding" is how I named it. It is quite possible that it has some different name that I don't know off.

这篇关于Delphi firemonkey-持有对另一个对象的引用的组件-创建对象实例的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆