Blogs

dxRAM - Richard Morris' DevExpress blog

September 2009 - Posts

  • What I learned at CodeRage

         

    Last week's virtual CodeRage developer conference was a good place to hang out if you are a Delphi developer.

    I've been doing Delphi conferences since 1995 when I lived in Australia and traveled to BorCon in the US ... which meant I would be on a plane for 14 hours each way, and several timezones away from my paying clients for over a week.

    Despite that, BorCon was the highlight of my professional year.  I was able to network among peers and potential clients.  It was a good chance to see what 3rd party component and tools vendors had done in the past year, and to hear how CodeGear had move the platform forward in the same time.  It was also good to be able to meet up with old friends who were also Delphi developers, and feed their addictions to Tim-Tams.

    Many of us have since moved to other platforms following evolving demand for the kind of software development we do, although as I mentioned in a previous post Delphi aint dead yet and I noticed a lot of long time Delphi guys at CodeRage in the audience as well as doing presentations.

    You can find replays of all the CodeRage sessions at http://conferences.embarcadero.com/coderage/sessions.

    Check out Cary Jensens presentation on 9 Thread Synchronization Options in Delphi Compared which was an excellent summary of the array of options available to Delphi developers.

    Bob Swart did an interesting presentation on building Smart Clients with Delphi and RemObjects

    Ray Konopka always delivers a professional presentation, and  Creating Custom VCL Component Designers was no different.

    You can find mine here

    Tour through DevExpress VCL Suites

    The Technology of the QuantumGrid

    Apparently the most watched was Delphi and Subversion which I'm just downloading now to watch later.  The other day I turned a Windows Home Server into my personal Subversion server for my .NET development using AnkhSVN to integrate Subversion support into Visual studio 2008.  I'd like to add integrated Delphi IDE support as well (I'm currently using Tortoise to integrate Subversion into explorer).

    So which presentations did you attend or download?  Any recommendations?

  • Technology of the QuantumGrid - Source

         

    In my last blog post, I spoke of how the QuantumGrid was able to implement selectable subcomponents to add a bunch of properties, without contributing to a noisy experience in the Object Inspector.

     

    I presented in my CodeRage session a way to do that for their own components, let me show how that works (sample code attached).

     

    I'm going to build an example using a trivial component as the parent container and two trivial persistent classes as child delegates.  Firstly make a Project Group and add two packages, a run-time only one called cxSpecializedComponent and a design-time only one called dclSpecializedComponent.  When building components I usually add a test project (TestSpecializedComponent.dpr) with a simple form that uses the control I am building.

     

    You may recognize the naming convention as the one used by Developer Express in our cross platform components.  This trick will work for Kylix as well as Delphi.  At CodeRage I was using Delphi 2009 (and the screen shots from my last blog post was using Delphi 7) but the same trick will work in Delphi 4+ and CBuilder 4+ all the way up to RAD studio 2010. 

     

    The run-time package

     

    In the run-time package we are going to put our parent component TParent, a base class for all children called TCustomChild, and two descendants from it called TChildOne and TChildTwo.

     

    The TParent must have a property for storing an instance of a TCustomChild, an instance of its class type and its class name.  We will need a class registration mechanism to register all the child classes to and return the type give a class name.

     

    Let's look at the interface for the run-time package;

     

    type
      TCustomChild = class;  // Base class for all Child classes
      TCustomChildClass = class of TCustomChild;  // type of the child Base class

     

      TParent = class(TComponent)
      private
        FChild: TCustomChild;
        FChildClass: TCustomChildClass;
        procedure SetChildClassName(const Value: String);
        function GetChildClassName: String;
        procedure SetChildClass(const Value: TCustomChildClass);
      public
        property ChildClass : TCustomChildClass 
          read FChildClass write SetChildClass;
        procedure CreateChild; virtual;
        procedure DestroyChild; virtual;
      published
        property ChildClassName : String 
          read GetChildClassName write SetChildClassName;
        property Child : TCustomChild 
          read FChild write FChild;
      end

     

      TCustomChild = class(tPersistent)
      private
        FBaseText: String;
      published
        property BaseText : String 
          read FBaseText write FBaseText;
      end

     

      TChildOne = class(TCustomChild)
      private
        FOneText: String;
      published
        property OneText : String 
          read FOneText write FOneText;
      end

     

      TChildTwo = class(TCustomChild)
      private
        FTwoText: String;
      published
        property TwoText : String 
          read FTwoText write FTwoText;
      end

    const
      sChildOne = 'Child One';
      sChildTwo = 'Child Two'; 

     

    function GetRegisteredChilds : TcxRegisteredClasses;

     

    To do our magic we will expose the TParent.Child property (which contains an instance of the child class), but give it a drop down affect that sets the class type by it's name and creates an instance of the child class.  It's all pretty simple to implement, so let's have a look at some code.

     

    First the registration machinery, I am using TcxRegisteredClasses a utility class from a Devex unit called cxClasses, if you don't have the Express Quantum Grid you will have to build one but the exercise is trivial so I won't bore you with the details;

    var
      FRegisteredChilds: TcxRegisteredClasses; 

    function GetRegisteredChilds : TcxRegisteredClasses;
    begin
      if FRegisteredChilds = nil then
        FRegisteredChilds := TcxRegisteredClasses.Create;
      Result := FRegisteredChilds;
    end

    initialization
      GetRegisteredChilds.Register(TChildOne, sChildOne);
      GetRegisteredChilds.Register(TChildTwo, sChildTwo);
    finalization
      GetRegisteredChilds.Unregister(TChildOne);
      GetRegisteredChilds.Unregister(TChildTwo);
    end

     

    Note we are registering our child classes in the singleton class registration object, and later on we can retrieve the type of any class from its name, that will be used mostly by the design-time editors to complete the magic.

     

    The Children don't need any implementation, so let's finish off the run-time unit by implementing TParent;

    procedure TParent.CreateChild;
    begin
      if FChildClass <> nil then
        FChild := FChildClass.Create;
    end

    procedure TParent.DestroyChild;
    begin
      FreeAndNil(FChild);
    end

    function TParent.GetChildClassName: String;
    begin
      if FChild = nil then
        Result := ''
      else
        Result := FChild.ClassName;
    end

    procedure TParent.SetChildClass(const Value: TCustomChildClass);
    begin
      if FChildClass <> Value then
      begin
        DestroyChild;  // remove the preexisting Child Instance
        FChildClass := Value;
        CreateChild;   // create a new Child Instance
      end;
    end

    procedure TParent.SetChildClassName(const Value: String);
    begin
      ChildClass := TCustomChildClass(GetRegisteredChilds.FindByClassName(Value));
    end

     

    The important thing to note in the implementation of the parent is that by setting the ChildClassName property the ChildClass property holding the class type of the Child to be used is created and an instance of the selected class constructed in the Child property (And if one was there previously it was destructed first).

    The design-time package

     

    The design-time package will be "installed" in the palette and will include a Register procedure that registers our component, and 2 property editors that will do our magic. 

     

    All you need in the interface is a register procedure.

    procedure Register;

     

    In the implementation you will need to access CodeGears design-time units, in versions of Delphi from 6 onwards they were not available in source or in run-time packages so you will have to require designide.dcp in your design-time package.  Here is the uses clause for the implementation of the Design-time unit.

    uses
      Types, DesignIntf, DesignEditors, VCLEditors,
      DsgnIntf, TypInfo, Classes,
      SpecializedComponent, // our run-time component
      cxClasses;       // for TcxRegisteredClasses generic class registration class

     

    Now we need to make a Class Property editor descendant that tells the TParent component to construct a new child class by giving it a class name chosen from a drop down.  The prototype for that class will look like this; 

    // Property editor to provide a dropdown selection of registered classes and expose nested properties
    type
      TParentChildProperty = class(TClassProperty)
      protected
        function HasSubProperties: Boolean;
      public
        function GetAttributes: TPropertyAttributes; override;
        function GetValue: String; override;
        procedure GetValues(Proc: TGetStrProc); override;
        procedure SetValue(const Value: String); override;
      end

     

    Now our property is going to have a Jekyll and Hyde personality change depending on whether we have an instance in the Child property.  If we don't have a Child instance then it will be a drop down property editor (a regular Class Editor where you select a class from a given list), if we do it will be a subcomponent editor. 

     

    The HasSubProperties method will be used to gate that personality change.  If we already have a Child class instance then HasSubProperties will return true, but if we have selected multiple components we need to check them all.

    function TParentChildProperty.HasSubProperties: Boolean;
    var
      I: Integer;
    begin // do all components being edited have the ChildClass set
      for I := 0 to PropCount - 1 do
      begin
        Result := TParent(GetComponent( I)).Child <> nil;
        if not Result then Exit;
      end;
      Result := True;
    end;

     

    The GetAttributes method actually does the work of setting up the different persionalities.  Note: paVolatileSubProperties is available in Delphi6 and up only, but the property editor will function just fine in earlier IDEs (where all sub properties where assumed to be volatile).

    function TParentChildProperty.GetAttributes: TPropertyAttributes;
    begin
      Result := inherited GetAttributes;
      if not HasSubProperties then
        Exclude(Result, paSubProperties);
      Result := Result - [paReadOnly] + [paValueList, paSortList, paRevertable, paVolatileSubProperties];
    end;

     

    Now we need to populate the drop down list from our Component registration object.

    procedure TParentChildProperty.GetValues(Proc: TGetStrProc);
    var
      I: Integer;
    begin
      for I := 0 to GetRegisteredChilds.Count - 1 do
        Proc(GetRegisteredChilds.Descriptions[ I ]);
    end;

     

    When a value is selected from the drop down it will be a class name.  We then have to tell the TParent in the Object inspector to go ahead and make one of those and put it in its Child property.  Then we call Modified to tell the object inspector to redraw this editor, and in doing that getAttributes will be called and the new personality established.

    procedure TParentChildProperty.SetValue(const Value: String);
    var
      ChildClass: TCustomChildClass;
      I: Integer;
    begin
      ChildClass := TCustomChildClass(GetRegisteredChilds.FindByClassName(Value));
      if ChildClass = nil then
        ChildClass := TCustomChildClass(GetRegisteredChilds.FindByDescription(Value));
      for I := 0 to PropCount - 1 do
        TParent(GetComponent(I)).ChildClass := ChildClass;
      Modified;
    end;

     

    We flesh out the editor by giving it the text we want to display when it has a valid class.

    function TParentChildProperty.GetValue: string;
    begin
      if HasSubProperties then
        Result := GetRegisteredChilds.GetDescriptionByClass(TCustomChild(GetOrdValue).ClassType)
      else
        Result := '';
    end

     

    Finally, you need to expose the components and property editors to the IDE in the register procedure.  Note we are actually removing the property ChildClassName from the Object Inspector by giving it a nil editor.  We do this because with the TParentChildProperty editor we are serving a dual purpose of selecting the class type and fixing it's sub-properties in the object inspector.

    procedure Register;
    begin
      RegisterComponents('RAM', [TParent]);
      RegisterPropertyEditor(TypeInfo(String), TParent, 'ChildClassName', nil);
      RegisterPropertyEditor(TypeInfo(TCustomChild), TParent,'Child', TParentChildProperty);
    end

    Can I add child events in the same way?

     

    At Developer Express we have built utility classes to enable events to be cascaded in the same way properties are.  This is of course a proprietary technology and can not be redistributed; however for completeness I will show how to use these classes.

     

    If you are building components for developers who already have the Express Quantum Grid 4, for example custom editors that use the Express Editors 4 architecture, you can require the package dclcxLibraryVCLXX where XX is the compiler initial (D,C,K) and the version (ie; D4, C5, K3) and you will be able to descend from our nested event editors.

     

    You will have to use the unit cxPropEditors in the design-time unit.

     

    {$IFDEF DELPHI6}
      Types, DesignIntf, DesignEditors,
      {$IFDEF VCL}
        VCLEditors,
      {$ENDIF}
    {$ELSE}
      DsgnIntf,
    {$ENDIF}
      TypInfo,
      Classes,
      SpecializedComponent,
      cxPropEditors,   // for NestedEvent property editor classes
      cxClasses;       // for TcxRegisteredClasses generic class registration class 

     

    In your TParent you will have to add a dummy editor to use as a placeholder for the location that the events of the child will be hosted.

     

        property ChildEvents: TNotifyEvent read FSubClassEvents write FSubClassEvents;

     

    The design-time editor is quite simple with the Developer Express , simply descend from the TcxNestedEventProperty editor and override the GetInstance method to return a reference to the Child component being edited.

    // Property Editor to expose nested events from persistent sub components
    type
      TParentSpecilizationEventsProperty = class(TcxNestedEventProperty)
      protected
        function GetInstance: TPersistent; override;
      end

     

    function TParentSpecilizationEventsProperty.GetInstance: TPersistent;
    begin
      Result := TParent(GetComponent(0)).Child;
    end

     

    And finally register it for the dummy event made earlier.

     

      RegisterPropertyEditor(TypeInfo(TNotifyEvent), 
                             TParent, 
                             'ChildEvents',
                             TParentSpecilizationEventsProperty); 

     

    Next time you rebuild your design-time package, you will see any events in the Child component classes exposed in the TParent object inspector.

    Can I freely use this technique?

     

    You are welcome to use this technique in your components as long as you do not redistribute any Developer Express units.

     

    To build one set of source for all platforms I suggest building a file similar to the cxGridVer.inc file from ExpressQuantumGrid 4 which will map versions into conditional definitions that will allow you to build uses statements that work correctly for all platforms.  You only need this for the Design-time unit.

     

    unit RegSpecializedComponent;

    {$I cxGridVer.inc}

     

    interface

     

    procedure Register;

     

    implementation

    uses

    {$IFDEF DELPHI6}

      Types, DesignIntf, DesignEditors,

      {$IFDEF VCL}

        VCLEditors,

      {$ENDIF}

    {$ELSE}

      DsgnIntf,

    {$ENDIF}

      TypInfo,

      Classes,

      SpecializedComponent,

      cxClasses;       // for TcxRegisteredClasses generic class registration class

     

    Conclusion

     

    This gives components a very powerful in-place delegation mechanism that will allow you to partition your components into container classes and behavior classes that the containers delegate to, so palette components can have multiple behaviors allowing you to reduce the palette clutter without reducing functionality.

    I've attached some source code, including code to implement events if you have the Quantm Grid.

  • Technology of the QuantumGrid - Object Inspector tricks

         

    I'm presenting a couple of sessions at CodeRage this week.  The first is, called "The technology of the Quantum Grid" which will go live at 1pm PDT, Tuesday Sept 8th.

    This session is about how the QuantumGrid (since v4) does some Object Inspector magic to enable developers to specialize components by allowing you to dynamically add sub-components - it's a very nice way of extending a class in many diverse directions while keeping the property noise to a minimum.

    You can see this happening when you add a column to a grid and select its Properties property.  To see this drop an empty grid on a form, click on ‘Customization’ and add a column to the GridDBTableView and click on it and you will see the Column object in the Object Inspector.


    Notice that the Properties Property is a regular drop down editor, this is the site that an editor class for all cells in this column will be defined.  If you click the drop down button you will see the list of available editor classes that can be used, these can be editors from the Express Editors package, or even your own descendants that you have registered with the Grid architecture.

    Now if you actually select a class, let’s say the CalcEdit class, something special happens to the Properties property.  It changes into a subcomponent property of the class that was selected in the Dropdown.



    In this example we now have the class name CalcEdit in the Properties property and since this is now a subcomponent property we also have all the properties (and events if you look at the events page) specific to just that class.  In effect we have a container class called TcxGridDBColumn that we can specialize with any of a number of utility classes simply my selecting the type from a drop down.

    If you were to drop down and select a different element, say a CurrencyEdit you would then see all the properties and events specific to that class.

    This is a very powerful way to build component specialization by delegation to contained classes, and represents a new way of using the Delphi Object Inspector to improve in place customization of classes, and reduce the clutter of components in the Delphi Palette.  Now you don’t have to have different column classes for each type of Editor, you have just one generic column class that can embed and delegate to custom editors.

    Want to add the same kind of functionality to your own components?

    Come to my CodeRage session at 1pm PDT, on Tuesday the 8th of September (sign up here ) ... and I'll show you how. Cool

  • CodeRage - might be the future of Developer Conferences

         

    CodeGear's CodeRage event next week is a virtual Developer Conference - Really!. 

    Speakers record their presentations in advance using TechSmiths excellent Camtasia (which is the same technology we use to record for tv.Devexpress.com ), the presentations are produced on a schedule from CodeGears web site and Speakers and attendees are able to mingle after the presentation for a Q&A session using Live Meeting.  I have two presentations, one a tour through the suites of the DevExpress VCL Subscription and the other a look into some of the technology of the QuantumGrid. 

    Even if you don't use Delphi, the conference is a chance to see what the state of the art is in Prism (The 1st class .NET language based on Delphi) and hear a different perspective on the same issues that face all developers - and the price (Free) can't be beat.

    The only downside for me is that Speakers have to have their presentation "in the can" way ahead of time, rather than just stepping on stage with an rough agenda of what you plan to show and cutting into some code and seeing where the presentation and audience feedback take you.  I usually prefer those more dynamic presentations (both as attendee and speaker) than ones "on rails" ... but the upside for me is I don't have to take 2 x 14 hour plane trips in coach, and a week and a half of downtime for a 4 day conference ... not to mention the swine flu.  So it's all good Big Smile

    I may have mentioned I lived in Las Vegas for 4 years while CTO of Developer Express (2002 - 2006), and there are many good things to love about Vegas for a small business ... especially a Technology related one.  Cheap direct airfares to everywhere, cheap accomodation, low taxes and every couple of months many of your customers come to town for a conference - and no one does a conference quite like Vegas does whether it be a massive event like CES or a smaller more intimate event like DevConnections. 

    It seems however that the era of the large mega conference with 10,000+ software developers all converging upon one spot on the globe for a week, might be coming to an end - no doubt killed off by the financial times we live in.  Which is going to be tough for destinations like Las Vegas that had become so good at hosting them - I think there will still be some big events like PDC and WWDC, but I suspect more narrowly focused technology conferences will be very tempted to go virtual.

    For those of you who do decide to hang out in my presentations ... see ya next week. 

    And for the rest ... see you virtually at a Developer Conference real soon.

  • DevExpress VCL subscription ready for Delphi 2010 and Windows 7

         

    Go get your bits ... the DevExpress VCL Subscription v46 is live and ready, willing and able to be installed on a fresh new Delphi 2010. 

    The complete "What's new" is over here

    But for those who like to skip straight to the chase ... the highlights are;

    • RAD Studio 2010 support
    • Windows 7 Skin (+3 more)
    • PDF output for the Printing System beta bits in the build
    • **NEW** Layout Control beta bits in the build
    •  ... and a handful of other small but usefull feature requests fulfilled

    I've been playing with the PDF stuff so far (managed to get a demo of the Printing System outputting PDF into my CodeRage presentation for next week) and it's looking pretty solid. 

    I know a bunch of guys are keenly awaiting the next Layout Control too.  If you want to install either of the 2 betas, you have to install 46 fully first to get the shipping versions, and then go back and re-run the install in Modify mode to replace the shipping bits with the beta bits

    NB:  The new Layout demos assume the shipping Printing System and vice versa so those demos might not build happily if you install both betas at the same time ... I did, but then I'm just the kind of risk taker that installs two beta components and I did it on Delphi 2010 on Windows 7 running in a VM on a Mac ... Mwahahahaha.

    As always let support@devexpress.com know if you notice anything strange with the beta bits.

More from DevExpress
Live Chat
Have a pre-sales question?
Need assistance with your evaluation?
We are here to help.
Chat is one of the many ways you can contact members of the DevExpress Team. We are available Monday-Friday between 8:30am and 5:00pm Pacific Time.
If you need additional product information, require pre-sales assistance, or want help with your order, write to us at info@devexpress.com or call us at
+1 (818) 844-3383.