views:

325

answers:

3

i have a property editor (descendant of TPropertyEditor) that is used to edit a property.

When it comes time to edit my property, how do i know what property of what object i'm editing? If i'm going to edit a property, i have to know what property i'm editing.

i've been pulling my hair out, sifting through the Delphi help, the online help, and the TPropertyEditor and descendant source code, and i can't find the answer.

i expected something like:

TPropertyEditor = class(...)
public
    procedure Initialize(TheObject: TObject; ThePropertyName: string);
end;

As far as i can tell, my property editor is created, and i will be told to "Edit", and i just have to divine what property they wanted me to edit.


From the help:

Editing the property as a whole

You can optionally provide a dialog box in which the user can visually edit a property. The most common use of property editors is for properties that are themselves classes. An example is the Font property, for which the user can open a font dialog box to choose all the attributes of the font at once.

To provide a whole-property editor dialog box, override the property-editor class’s Edit method.

Edit methods use the same Get and Set methods used in writing GetValue and SetValue methods. In fact, an Edit method calls both a Get method and a Set method. Because the editor is type-specific, there is usually no need to convert the property values to strings. The editor generally deals with the value “as retrieved.”

When the user clicks the ‘...’ button next to the property or double-clicks the value column, the Object Inspector calls the property editor’s Edit method.

Within your implementation of the Edit method, follow these steps:

  1. Construct the editor you are using for the property.
  2. Read the current value and assign it to the property using a Get method.
  3. When the user selects a new value, assign that value to the property using a Set method.
  4. Destroy the editor.

Answer

It's tucked away, and not documented, but i found out how. The property i'm editing that i edit:

TheCurrentValue := TMyPropertyThing(Pointer(GetOrdValue));

Now that i have the value, i can edit it. If i want to replace the property with some other object:

SetOrdValue(Longint(TheNewValue));

The full code:

Create a property editor that descends from TClassProperty:

TMyPropertyEditor = class(TClassProperty)
public
   procedure Edit; override;
   function GetAttributes: TPropertyAttributes; override;
end;

First is the housekeeping, telling Delphi's object inspector that my property editor will display a dialog box, this will make a "..." appear next to the property:

function TMyPropertyEditor.GetAttributes: TPropertyAttributes;
begin
   //We show a dialog, make Object Inspector show "..."
   Result := [paDialog];
end;

Next is the actual work. When the user clicks the "..." button, the object inspector calls my Edit method. The trick that i was missing is that i call my GetOrdValue method. Even though my property isn't an ordinal, you still use it, and just cast the resulting thing to an object:

procedure TMyPropertyEditor.Edit;
var
   OldThing: TMyPersistentThing;
   NewThing: TMyPersistentThing;
begin
   //Call the property's getter, and return the "object" i'm editing:
   OldThing:= TMyPersistentThing(Pointer(GetOrdValue));

   //now that i have the thing i'm editing, do stuff to "edit" it
   DoSomeEditing(OldThing);


   //i don't have to, but if i want to replace the property with a new object
   //i can call the setter:
   NewThing := SomeVariant(OldThing);
   SetOrdValue(Longint(NewThing));
end;
+2  A: 

Property editors only care about (and are registered for) the type of the property, not the specific property itself.

Tim Jarvis
Does this sample code help?http://www.delphi3000.com/articles/article_829.asp?SK=
Warren P
Warren, it's OK, but not quite right. The property editor shouldn't modify the property directly (as by assigning `Hint`). The editor should assign its own `Value` property, and then editor's base class will take care of propagating the new value to *all* the properties being edited, instead of just the first object's `Hint` property. A multi-line string property editor could be generally useful; no need to hard-code it to only working on the `Hint` property.
Rob Kennedy
+2  A: 

If I understand your question right, you're wondering how you're supposed to actually find the value you need to be editing, especially if the object in question contains more than one of them. The answer is that the IDE sets that up for you and the property editor comes "preloaded" by the time Edit is called. TPropertyEditor comes with a bunch of GetValue methods that your Edit function can use to retrieve the value. Or if it's not one of those types, (if it's an object descended from TPersistent, for example,) then you can call GetOrdValue and cast the result to a TPersistent.

Also, you might want to check out TJvPersistentPropertyEditor in the JvDsgnEditors unit of the JVCL to use as a base class. It provides some of the functionality for you.

BTW if you really need it, you can use the GetName method, which will give you the name of the property, but you usually shouldn't have to. And be careful if you're inheriting from something other than TPropertyEditor itself, as GetName is virtual and can be overridden.

Mason Wheeler
+3  A: 

A property editor keeps the information about which objects and properties it's editing in the private FPropList variable. The IDE fills that in by calling your editor's SetPropEntry method. You're then generally supposed to call the various methods of TPropertyEditor to find out the properties' values.

It's not really supposed to matter which property you were asked to edit. Your property editor edits properties of a particular type. For in-place editing, your editor provides an implementation of SetValue that translates the string from the Object Inspector into a value of the proper type for the property, and then you call the appropriate Set function, such as SetOrdValue or SetIntfValue. For whole-property editing, the IDE won't call SetValue. Instead, it will call Edit, and you're expected to call GetOrdValue or GetIntfValue (for example) yourself, since your editor already knows what type of property it's designed to edit.

Remember that property editors, in general, can edit multiple properties simultaneously. The name of the property will be the same for all of them, but the type of component they belong to may vary, and thus so may their getters and setters. Call your property editor's GetName method to find out the name. To get the objects, call GetComponent for each index from 0 to PropCount - 1. (Be careful; there's no range checking in those Get functions.) You can also check whether a specific object is in the list by calling HasInstance. The GetPropInfo method will tell you the PPropInfo pointer for the first property, but I don't think that will necessarily be equal to the pointers of all the other properties. Aside from that, you don't get direct access to the PPropInfo data, but again, it really shouldn't matter. If you think you need that for your editor, you're probably doing something wrong; post a new question with more specific information about your underlying task.

Rob Kennedy