tags:

views:

87

answers:

1

Edit: original question below, but I revise it now that I have some code to post....

I am created an editbox which only accepts an integer as input. I didn't use a TMaskEDit because I want to derive a bunch of others from it (only accepts float, and both float & integer with min/max properties).

Here's my code. I think the problem is that my property editor SetValue() is never being called, so maybe I haven't registered it correctly?

unit IntegerEditBoxMinMax;
  // An edit box which only accepts integer values between the stated mix/max values

interface

uses
  SysUtils, Classes, Controls, StdCtrls, DesignEditors;

type

  TMinimumProperty = class(TIntegerProperty)
    procedure SetValue(const newValue: string); override;
  end;  

  TMaximumProperty = class(TIntegerProperty)
    procedure SetValue(const newValue: string); override;
  end;  

  TValueProperty = class(TIntegerProperty)
    procedure SetValue(const newValue: string); override;
  end; 


  TIntegerEditBoxMinMax = class(TCustomEdit)
    private
      FMinimum : Integer;
      FMaximum : Integer;
      FValue   : Integer;

    published  { Published declarations - available in the Object Inspector at design-time }
      property Minimum  : Integer read FMinimum write FMinimum;
      property Maximum  : Integer read FMaximum write FMaximum;
      property Value    : Integer read FValue   write FValue;

  end;  // of class TIntegerEditBoxMinMax()

procedure Register;

implementation

Uses TypInfo, Consts, DesignIntf, Dialogs;

  procedure Register;
  begin
    RegisterComponents('Standard', [TIntegerEditBoxMinMax]);
    RegisterPropertyEditor(TypeInfo(Integer), TIntegerEditBoxMinMax, 'Minumim', TMinimumProperty);
    RegisterPropertyEditor(TypeInfo(Integer), TIntegerEditBoxMinMax, 'Maximum', TMaximumProperty);
    RegisterPropertyEditor(TypeInfo(Integer), TIntegerEditBoxMinMax, 'Value',   TValueProperty);
  end;

  procedure TMinimumProperty.SetValue(const newValue: string);
    var L: Longint;
        min, max : Integer;
        propInfo: PPropInfo;
        exceptionString : String;
  begin
MessageDlg('editor !!', mtWarning, [mbOK], 0);   // This is never seen !!
    L := StrToInt(newValue);                { convert string to number }
    with GetTypeData(GetPropType())^ do     { this uses compiler data for type Integer }
      min  := GetOrdProp(GetComponent(0), 'Minimum');
      max  := GetOrdProp(GetComponent(0), 'Maximum');

      if min > max then
      begin
        exceptionString := 'Minimum value (%l) cannot be greater than maximum (%l)';
        raise Exception.CreateResFmt(@exceptionString, [min, max]);
      end;

      if L < min then
      begin
        PropInfo := GetPropInfo();
        SetOrdProp(Nil , propInfo, Int64(min));
        exceptionString := 'Value (%l) less than new minimum (%l); value set to minimum';
        raise Exception.CreateResFmt(@exceptionString, [L, min]);
      end;

    SetOrdValue(L);                                { if in range, go ahead and set value }
  end;  // of TMinimumProperty.SetValue()


  procedure TMaximumProperty.SetValue(const newValue: string);
    var L: Longint;
        min, max : Integer;
        propInfo: PPropInfo;
        exceptionString : String;
  begin
    L := StrToInt(newValue);                { convert string to number }
    with GetTypeData(GetPropType())^ do     { this uses compiler data for type Integer }
      min  := GetOrdProp(Nil, 'Minimum');
      max  := GetOrdProp(Nil, 'Maximum');

      if max < min then
      begin
        exceptionString := 'Maximum value (%l) cannot be less than minimum (%l)';
        raise Exception.CreateResFmt(@exceptionString, [max, min]);
      end;

      if L > max then
      begin
        PropInfo := GetPropInfo();
        SetOrdProp(Nil , propInfo, Int64(max));
        exceptionString := 'Value (%l) more than new maximum (%l); value set to maximum';
        raise Exception.CreateResFmt(@exceptionString, [L, max]);
      end;

    SetOrdValue(L);      { if in range, go ahead and set value }
  end;  // of TMaximumProperty.SetValue()


  procedure TValueProperty.SetValue(const newValue: string);
    var L: Longint;
        min, max: Integer;
  begin
    L := StrToInt(newValue);             { convert string to number }
    // see also http://www.freepascal.org/docs-html/rtl/typinfo/getobjectprop.html
    // for other useful functions
    with GetTypeData(GetPropType())^ do  { this uses compiler data for type Integer }
    begin
      min := GetOrdProp(Nil, 'Minimum');
      max := GetOrdProp(Nil, 'Maximum');
      // for Float, etc, see http://www.blong.com/Conferences/BorConUK98/DelphiRTTI/CB140.htm

      if (L < min) or (L > max) then              { make sure it's in range... }
        raise Exception.CreateResFmt(@SOutOfRange, [min, max]);   { ...otherwise, raise exception }

      SetOrdValue(L);    { if in range, go ahead and set value }
    end;
  end;  // of TMinimumProperty.SetValue()


end.

Original question:

This link gives a pretty good example of a property editor for an integer property.

How can I change this method ...

procedure TIntegerProperty.SetValue(const Value: string);
var
  L: Longint;
begin
  L := StrToInt(Value);                      { convert string to number }
  with GetTypeData(GetPropType)^ do  { this uses compiler data for type Integer }
    if (L < MinValue) or (L > MaxValue) then { make sure it is in range }
      { otherwise, raise exception }
      raise EPropertyError.Create(FmtLoadStr(SOutOfRange, [MinValue, MaxValue]));
  SetOrdValue(L);                       { if in range, go ahead and set value }
end;

... what I want is to male a new property, say TIntegerMinMaxProperty, derived from TIntegerProperty where the component editor also shows minimum and maximum permitted values for the integer.

I was thinking of adding two new Integer properties, called Minimum & Maximum, but it looks like I could maybe use the existing MinValue & MaxValue if I could only publish them, but I don't see how ... neither of these compile

published  { available in the Object Inspector at design-time }
  property Minimum : Longint read  MinValue write  MinValue;
  property Minimum : Longint read FMinValue write FMinValue;

So it looks like I have two options and am stuck at both :-/

1) find a way to publish those MinValue & MaxValue properties (which is probably cleaner) 2) publish 2 new properties, Minimujm & Maximum and figure how to refer to them in TIntegerProperty.SetValu()

And help gratefully appreciated as this is my first sortie into property editors (in case it helps influence the answer to this, my next property will be one that accepts decimal input, with a a point)

+2  A: 

If the MinValue and MaxValue are already properties of your component/control, but are currently public and not published all you need to do is re-declare them in the published section, but only by name:

published
  property MinValue;
  property MaxValue;

That'll change only the visibility of those properties and should make them show up in the object inspector if their types are supported. Have a look at many of the T... versus TCustom... class pairs where the T... class often only redeclares properties as published to make them show up in the Object Inspector.

Edit (In response to comments)

Ok, hang on, if you want to have some control, say a TMyEdit with say a SomeFactor Integer property for which the entry in the Object Inspector would not only show the value of SomeFactor, but also the min and max values allowed for SomeFactor, you'll need a to code property editor that shows its own form.

You can of course add the MinValue and MaxValue to a descendant of TIntegerProperty and use those in the overrides for the GetValue and SetValue methods (that's why they are virtual after all), then register this TMinMaxIntegerProperty for use by the SomeFactor property. However, that won't make the MinValue and MaxValue show up in the Object Inspector as the TIntegerProperty basically only edits/shows the actual value for SomeFactor. To achieve what you want, you would have to code a descendant of a custom property editor, for example one that shows a form to edit and format a string. There are plenty of examples in the components I use, but I can't post their code here of course.

If what you want is simply a TMyEdit with a SomeFactor integer property and want to add a SomeFactorMinValue and a SomeFactorMaxValue to your TMyEdit control, then you can access those in the GetValue/SetValue methods through the Instance reference of the FPropList entry. See SetOrdValue implementation that is used in the TIntegerProperty editor for info. It employs SetOrdProp (from TypInfo) to set the value for on Instance which should be a reference to the TMyEdit control holding the SomeFactor property. With some casting you should then be able to the SomeFactorMinValue and SomeFactorMaxValue properties.

Note: Not sure how the FPropList gets its entries. It gets its count through the constructor though. You may have to do some further investigation here yourself. (For some reason Ctrl-click and code-insight have decided to go on strike in my D2009.)

Marjan Venema
+1 for the help. When I tried that the compiler complained that they are not members of the base class - so where are they coming from??Btw, I now realize the the link quoted about just copied verbatim from the help file, if you choose "find" and "GetTypeData"
Mawg
Mawg
Great update! (Wish I could +1 again) I'll update the question to show the code of what I have so far. Perhaps you could take a look at it? If you keep commenting, I'll keep +1ing until I get it working. Thanks a 1,000,000 for your help so far
Mawg
You will have to flesh out a lot of the calls to the TypInfo functions. Passing `Nil` to the `GetOrdProp` and `SetOrdProp` functions is going to get you Access Violations. I also see a number of other TypInfo functions for which you have not yet coded the parameters. Other than that your code seems to be on the right track.
Marjan Venema
(+1) I will award you the answer as you have done so much work, thanks. I have changed the code quite a bit and am making progress. If I need more help then I will post another question.
Mawg