menu
  Home  ==>  papers  ==>  vcl  ==>  devexpress_tdx_to_tcx_migration   

DevExpress tDx to tCx Automatic migration - Felix John COLIBRI.


1 - Devx tDx to tCx migration

In several recent jobs we had to migrate tDxDbGrids to tCxGrids.

This occurs mainly when the customer shifts to post 2009 Delphi versions, where Unicode is required. The tDx component suite has not been ported by DevExpress to Unicode. Instead they created a new grid and component set, the tCxGrid and other tCx components, which are unicode enabled and are maintained with the successive versions of Delphi.

There are several ways to tackle this problem

  • migrate the tDx set to Unicode. We have seen this with one customer. First of all this is a massive job. Moreover, the migrated tDx components will no longer evolve (new components, new Windows evolutions etc)
  • switch to another Grid (TMS comes to the mind). The effort is even greater than migrating tDx to tCx
  • build a custom grid starting from some basic Unicode grid (tDbGrid) and add feature to mimick the tDxDbGrid. This is possible for very simple uses of the tDxDbGrid, but quickly becomes unmanageable when the customer starts to use more sophisticated features of the grid. Basically you are trying to duplicate the many years of developpment of the DevExpress team. This is a hopeless job
Therefore, the more efficient way is to migrate from tDx to tCx, and this is the topic of this article.




2 - tDx to tCx migration strategy

2.1 - The customer software

In this case, the software
  • basically is a sales management tool for specialized goods
  • which manages
    • customers and retailers
    • sales force and representatives
    • samples to customers
    • quotation
    • orders
    • workshop paperwork
    • invoices
  • and uses DevExpress components
    • this the only grid used
    • around 600 units, 300 grids
    • medium grid sophistication ( 170 / 526 properties of the tDxDbGrid used )
To recap,
  • many tDxDbGrids in many forms
  • medium tDxDbGrid sophistication


2.2 - Available Strategies

To transform tDx components to tCx ones, several strategies are available:
  • manually transform each tDxDbGrid into a tCxGrid
  • use the migration wizard
  • use an automatic conversion tool


2.3 - The manual migration

You have two options :
  • build the tCxGrid from scratch (copy the form, remove the tDxDbGrid, drop a tCxGrid and incrementally modify the tCxGrid properties until the tDxDbGrid behaviour has been reached)
  • use a text editor (NotePad) to replace the types, properties values of the tDxDbGrid in the .PAS and the .DFM and check the behaviour by loading the result in Delphi, and compiling it
In any case, you have to learn how to replace the tDx properties and values with their tCx equivalents.



2.4 - The migration wizard

It is also possible to use the migration wizard. The wizard transforms one tDxDbGrid into a tCxGrid at the time.

The wizard has some limitations

  • we have to drop a tCxGrid on the form for each tDxDbGrid
  • the old tDxDbgrid, its events, all its Uses are still present in the form. The transform only adds the fields and properties to tCxGrid that you dropped on the form
  • this explains why the the code still compiles : the old tDx are still there and the new tCx have minimal properties which will always compile
  • the wizard only handles the fields and properties, not the events. So there are still tDxDbColumns in your form
  • the tDxDbGrids are often aligned with alClient. If the dropped tCxGrid used the same alignment, you could not see both. Therefore the wizard quite understandably does not copy the Align property value. But you have to change the value yourself
  • the wizard drops some properties without any log reporting them
  • the wizard only handles tDxDbGrids and layouts. Not any other tDx component
  • if the application uses form inheritance, you have to migrate all the levels of the hierarchy


2.5 - Manual migration inconvenients

For the two first solutions, the main problem is the test.

Each grid is contained in forms which are part of an intricate form network. So to check whether the modifications are correct, you have to navigate to the target form, which might involve several clicks, CheckBox checks, ComboBox selections etc

First of all you have to understand the navigation in the projects. You do not need to become an expert or read the user manual, but a minimum work has to be done to find your way in the form organization

For one trial, this is not very difficult, but doing this several time and for each grid until the transforms are correct can quickly become a pain in the neck.

form_inheritance_and_navigation



And then you stumble on THE problem which is the test with live data :

  • there are many potential difficulties in getting data to fill your grids
    • first of all you should use a test database and not the production database. This test database is not always available
    • then you need a connection. If you work on the customer premises, this is usually not a problem. But working from your office is not always possible :
      • the customer might be unwilling to open a connection for security reasons
      • the connection could be slow
      • the customer does not provide a Database that you could install on your PC
    • finally the database might not contain some rare case which are handled in a special way by the grids


This is the exact situation where mocks should be used : instead of working with the real database, we build mock table for test purposes.



2.6 - Automatic migration Strategy

For all these reasons we decided to use the following strategy :
  • split each tForm containing a tDxDbGrid into separate tForm containing just this tDxDbGrid and its tDataSource and tDataSet
  • for each of those simple forms
    • build a mock table to populate the grid
    • include the form in a separate project which can be compiled
  • build a migration tool which can successfully transform each of the simpler forms
  • finally forget about the intermediate simpler form and table mocks and use this tool on the real project. Test the result.



3 - Splitting the tForm

3.1 - Isolate the tDx containing forms

The first step is to build a list of the forms containing some tDx component. In all migration jobs, there is usually only a fraction of the Forms and Units concerned about tDx. The percentage might vary, but the average we found is around 30 %

This avoids parsing of the form and units not concerned by the migration.

This step involves the creation of a separate \tForm for each \tDxDbGrid.



3.2 - Algorithm

To avoid working with complex forms nested in an unknown navigation network, we simplified the problem :
  • for each tForm containing some tDxDbGrid, we created one or several simpler forms containing the minimum content
    • one tDxDbGrid
    • the associated tDataSet and persistent tFields
  • those forms were flattened : we created one form only for the leaf forms and removed all the inheritance functionality. This allows to open the form without the inheritance hassle ("cannot find the ancestor" etc)
  • each grid was associated with a .DPR


3.3 - Splitting the multi-grid forms

The first step simply analyzes the .PAS and the .DFM and generates a separate Form for each tDxDbGrid :

split_multi_grid_forms




4 - Removing Form Inheritance

The frequent organization is
  • a base form with some panels and PageControls
  • a middle descendent form with a tDxDbgrid, a tDataSource referenced by the grid and an empty tDataSet referenced by the tDataSource
  • a leaf descendent with
    • a filled tDataSet (with SQL statements and persistent fields
    • a descendent of the tDxDbGrid with more properties, with DxDbColumn components and events
    Note that if the leaf node does not modify the tDataSource (no additional OnDataChange, ReadOnly etc), this component is not present in the .DFM of this form


The intermediate Forms usually only contain a fraction of the properties. Therefore we are only interested in the leaf forms, provided that we gather in this form all the intermediate values not present at the leaf level.



The technique is then to

  • start with the last descendent(s)
  • gather the components properties and values at this level
  • analyze the ancestors using the inheritance chain, and
    • add to the leaf form the ancestor's properties and values not yet present
    • add all interesting components not present at the leaf level (tDxDbGrid, tDataSource, tQuery)


At the same time, we remove from the result form the unnecessary objects, like containers (tPanel, tPageControl) or other components (tButton etc)



Here is a sketch of this process:

flatten_the_form_inheritance

and a partial example:

form_inheritance_txt

This is what we call "flatening the forms"

  • remove the containers
  • remove the unnecessary components
  • include any \tDataSource, \DataSet present in the ancestor but not in the last descendent
  • consolidate the \tDxDbGrid, \tDataSource, \DataSet properties


The same technique can be applied to tFrames.




5 - Creating the mock tables

5.1 - The data used by the grids

In our simpler forms, the grid just displays the content of some table, be it a physical table or a view generated by complex SQL requests.

The grid is totally unaware of the origin of the data: it is just fed with rows and column values from some tDataSet.

It is therefore quite easy to replace the original tDataSet with an artificial tDataSet, provided its column types are matched to those expected by the grid column components.



5.2 - Grid Columns

To fill the grid, you must define its columns. Normally the Devx column editor is used.

Here is an example with a simple tDxDbGrid operating on the "Cars" Devx demo dataset:

tdxdbgrid_tdatasource_tdataset

In this simple example

  • tDxDbGrid1 has a DataSource property pointing to DataSource1
  • DataSource1 has a Dataset property pointing to Table1
The grid has 13 columns (3 shown on the figure) and those columns contain the database table column name
    FieldName = 'ID'
    FieldName = 'Trademark'
    FieldName = 'HP'

There is no field type or field size information. And this is enough to present the data in the grid, at design time and runtime.



This is not enough for building a suitable mock table. At the end of the day, the grid only displays strings. However buiding a mock with only string could violate some statements in the tDxDbGrid events, which could use Integer, Float, Boolean or DateTime expressions.



Could we use a general SQL statement to find the FieldType and Size information ?

Certainly, we could just duplicate the Delphi IDE technique by loading Table MetaData.

This could however become quite involved if the actual query is the result of complex JOINS or stored procedure.



5.3 - Using Persistent Fields

In our case we were lucky to have persistent fields for each tDataSet used by the grids. In this case, the tField properties allows us to build the mocks.

We added the persistent fields to our Cars application, and here is the .DFM

tdxdbgrid_tdatasource_tdataset_fiedls



When persistent fields are present, we use this simple algorithm to build our mocks :

  • for each tDxDbGrid we locate the DataSource
  • this tDataSource is associated with a tDataSet
  • the tDataSet contains the tFields, which contain the field name and the field type and size
And this is enough to generate a CREATE TABLE and INSERT INTO.



5.4 - Using Data Generators

What values can we store in our mock tables ?

Using the data type and size, this is quite easy

  • numeric, boolean and date can be easily randomly generated
  • for strings, we can use
    • random string, but this is not very appealing
    • the classical "lorem ipsum" used in publishing, graphic design and many data processing random text generators
    • the first pages some book like "Tom Sawyer", much more entertaining then this latin text
For strings, we can even be more realistic. The form name, table name and field name allow us to target more narrow domains. It is not difficult to generate "City", "State", "Company" etc.

In fact, we long ago extracted values from the standard database demos (MastApp, FishFact, NorthWind, Pet Store...). So we have lists of realistic looking "Cities", "First Names", "Street", "Companies" which could be used.

To allocate a specific data generator to a field, we simply listed the Forms / Tables / Fields and enrich this list with generators

  • for standard types (Date, Currency) the generator can be automatically generated
  • for string fields we manually associate fields with generators.


Here is a example of the automatically allocated generators:

 
                    -field_name-----  field_type---  size  ----allocated_generator---- 
                                                           name            min  max 
                order_update.dfm 
                detail_query 
                id_customer        TStringField     5  integer         100   199 
                id_order           TStringField     7  integer_string  1000  1999 
                order_date         TDateTimeField   -  date            2015  2016 
                order_amount       TCurrencyField   -  currency        500   3000 
                sales_tax          TCurrencyField   -  currency        100   200 
                code               TStringField     3 
                update_clerk       TStringField     5 
                update_date        TStringField     -  date_string     2015  2016 
                update_slip        TStringField     30 string_5        20 
                

We only added the min / max values of the Integer, Date and currency fields.



In this customer / sales table we specified :

  • the min and max of the Integer fields
  • "company"
  • "address_street"
  • "us states", with a maximum of 5
 
                    -field_name-----  field_type---  size  ----allocated_generator---- 
                                                           name            min  max 
                customer_order_address.dfm 
                  master_query 
                    id_customer       TStringField      5  integer         100   199 
                    id_order          TIntegerField     -  integer         1000  1990 
                    customer_name     TStringField     30  company 
                    street            TStringField     30  address_street 
                    state             TStringField      5  us_states       5 
                    city              TStringField     24  city 
                



Once we have allocated generators to one table, we look for fields with the same name, type and size, and allocate the same generators to those fields. Here is the result of another table having "company", "city" etc:

 
                    -field_name-----  field_type---  size  ----allocated_generator---- 
                                                           name            min  max 
                customer_contract.dfm 
                customer_contract 
                id_customer        TStringField     5  integer         100   199 
                company            TStringField    30  company 
                street             TStringField    30  address_street 
                city               TStringField    30  city 
                contract_number    TStringField    15 
                start_date         TDateTimeField   -  date            2015  2016 
                end_date           TDateTimeField   -  date            2015  2016 
                payment_terms      TStringField     3 
                company_category   TStringField     3 
                



5.5 - More complex cases

There are some cases where the definition of the mock table are not so easy :
  • if the tDataSet is placed in tDataModule. We simply analyze this tDataModule to import the tDataSet in our simpler form
  • the persistent fields have not been created. We then can create those fields on a copy of the original Form
  • the tDataSet is assigned by code, not in the .DFM. In this case, we look at all the tDataSets and use one them which have at least all the fields that the grid has
  • the query is created and linked to the grid by code. We can generate the field information at runtime, reading the tFields or the tFielDefs created when the table is opened. However we have to locate the form, and therefore explore, dig and spelunk. Just the opposite of what we tried to achieve.
  • if the grid involves lookups, more datasets might be brought in, with integrity checks between the tables
  • if all fails, we can always assume tStringFields, and correct the type if the grid events if the form does not compile (for instance an event uses AsIntrger or AsDateTime for some field)


5.6 - Using the mocks

After the "split / flatten / mock operation" operations, we can now load each form, visualize the grid filled with sensible data at design times.



Here is the situation after the split / flatten / mock operation :

dataset_mock_3

and the "split / flatten / mock" steps can be described by :

// -- split
ForEach tDxDbGrid in the leaf form
    create a tForm and a .DPR
    copy the tDxDbGrid
    // -- flatten
    copy the ancestor's properties
    // -- mock
    find the tDataSource and the tDataSet
    using the tDxDbGrid's tDxDbColumns and tDataSet's tFields
        create a Table
        fill the Table




6 - Creating the test projects

6.1 - One .DPR per Form and Grid

We include each simple form and its tDxDbGrid in a single .DPR



6.2 - Compiling the projects

Compilation might fail because
  • the tDxDbGrid events code uses other components which were not included in our simpler form, like
    • tables for master detail relationship or lookups
    • calls to other forms, like dialogs
One easy solution is to comment out all the event statements. So now the project will compile, but we will not be able to fully test the runtime behaviour.

An intermediate solution is to include some of the external items and calls in the simpler form.

There obviously is a tradeoff to the number of external items which could be included.

And since the form has an associated .DPR, we can run the project and examine the effects of user interaction and of the grid events.



6.3 - Batch compilation

Wz can use batch compilation using DCC32 scripts and batch execution using CreateProcess.

Failures can saved in a log.




7 - Migrating to tCx

So far we are only able to display and compile each tDxDbGrid in separate projects.

Now is the time to migrate to tCxGrid :

single_form_compilation_2



This migration was performed by adapting our migration tool to the tDxDbGrid. As already presented in several papers, this .PAS / .DFM conversion tool :

  • starts with a list of types to be migrated (Bde types, tDx type etc)

    TdxBlobEdit
    TdxButtonEdit
    TdxCalcDisplay
    TdxCalcEdit
    TdxCalculator
    TdxCheckEdit
    TdxCheckEditStyleController
    TdxCurrencyEdit
    ...

  • the list of properties, members an events of each type are constructed using RTTI :
    • for the new post Delphi 2010 applications, the "new" RTTI
    • the "old" Delphi RTTI for pre 2010 applications
    In our case, the tDx components are analyzed using the "old RTTI". In this case only the Public and Published properties and events are computed, but not the methods

    Here is an extract of the tDxDbGrid

    // p_rtti_extract_pme.exe - D6 version - NO methods (public)
    // TCustomdxDBGrid
    TdxDBGrid
      properties
        Align
        Anchors
        ArrowsColor
        AutoExpandOnSearch
        AutoSearchColor
        AutoSearchTextColor
        BandColor
        BandFont
        BandMaxRowCount
        BandRowCount
        ...
      events
        OnAddGroupColumn
        OnBackgroundDrawEvent
        OnBandButtonClick
        OnBeforeCalcSummary
        OnBeginDragNode
        ...

  • all the customer forms with some of the target types are analyzed:
    • first we find the names of globals, locals, parameters and members with the target types
    • we then find all the types, properties, members and events actually used in the application at hand.

      Here is a small extract

      tdxdbgrid
         Align
           alClient
              alTop
         Anchors
              [akLeft, akTop, akRight, akBottom]
         ApplyBestFit
              ApplyBestFit(dbgMaitre.Columns[i]);
         AutoExpandOnSearch
              False
         BandColor
              13224393
         BandFont.Charset
              ANSI_CHARSET
              DEFAULT_CHARSET
         BandFont.Color
              clBlack
              clBlue
              clNavy
              clRed
              clWindowText

      This step usually drastically reduces the number of items to handle. In our case :

      • only 33 types among the 52 tDx
      • for the tDxDbGrid, for instance, only 180 among the 600 + available

    • we define a replacement strategy for each of the remaining items found in the forms. This can be
      • either a simple name replacement ("DatabaseName" is replaced by "ConnectionName")
      • or a more elaborate computation using code
      Both the .PAS and the .DFM are modified


The results of those transforms are evaluated and corrected if necessary. In our case, the evaluation uses :
  • visual examination
  • behaviour tests
Since we are migration visual components (the grids !), the visual aspect is essential. You must have both the old and the new grid on the screen to check colors, highlight, overall presentation. This is why we build a tool to be able to visualize pre and post migration grids in single forms, with realistic data.

Then some behavior must be checked: does the tCx sort as the tDx, is the filter working in the same way, are the subtotals grouped in the correct order etc.

We already explained, that to check a transformation, compilation is not enough: you must execute the project and actually load the form to catch some .DFM bugs which passed the compilation but will fail when the .DFM resource is loaded. Our single form project is well suited for this test. And in addition logs of the compilation and the execution error are created.



7.1 - decreasing effort

Knowing all the properties and values used by our customer's units, we could have built the conversion code right from the start.

We prefer a more incremental approach, since all conversions are not fully understood.

The first couple of tDxDbGrid migrations are the more difficult. We have to find the replacements of each property and value of those first forms and include them in the migration tool. Mapping tables have to be designed and populated, ccde transformation designed and written etc.

But as time goes by, the new grids only contain incremental new properties and values. So the time spend to transform the additional forms quickly decreases.



In fact we use a follow up file to check what has been migrated and what has not yet been covered :

  • we start with
    • the list of all forms to be migrated: the "form todo list"
    • the list of all properties to be migrated: the "property todo list"
  • repeatedly, we
    • select a form
    • check if the form contains new properties (not contained in the "property todo list". If some are found:
      • transform them
      • remove them from the "properties todo list"
    • remove the form from the "form todo list"
Obviously the "todo lists" can only decrease over time. And as the work progresses, more and more forms have no new features and do not require any additional effort.

This is in strong contrast to the manual migration route where, even if you perfectly know what mapping to apply, you still have to manually make the changes for every single form.



7.2 - Regression test

To be sure that new modifications of the migration tool do not destroy some previous operations, we periodically run the tool on all the forms migrated so far.

This is where the batch compilation and execution with logs come in handy.




8 - Migrating the original project

The simpler form and the mock tables were just intermediate steps. When the migration of tDx to tCx is correct for every tDx component of every simpler form, we apply it on the complete project.



So we use a two phase approach :

  • split / flatten / and mock to build and fine tune the transformation tool
  • apply the transformation tool to the whole application
which can be depicted as :

strategy




9 - Workload

9.1 - The basic migration tool

This tool already existed and has been used for many migration jobs.

How much did this tool costs ?

  • it uses the scanner and parser of Niklaus WIRTH's "Algorithm + Data Structures = programming" book as well as snippets from the Zurich P4 compiler. The current version is based on a Delphi Grammar and a parser generator. Several versions of this recursive descent parser have been published and can be downloade from our sites
  • for the .DFM part, we published an article about the .DFM grammar along with a .DFM parser. The parser used here is an upgraded and up to date version of the published one. The parser generates a .DFM tree (similar to DOM) which is easy to visit and modify
  • the RTTI analyzer was coded over time, with the "Old RTTI" as well as the "New RTTI" versions
  • creation the type -> variable -> property / method / events -> transform tool was constructed for our first migration job (migrate a 2.600 unit project from BDE to Ado)
  • the data generators were also coded a couple of years ago
Should we recode this from scratch (but with our today's knowledge), a rough estimate would be around 4 months



9.2 - The split / flatten / mock

This part, which is at the heart of the current effort, was coded in about 3 weeks.



9.3 - The adaptation to Devx

The adaptation of the migration tool to the Devx migration took around 4 weeks

This heavily depended on the complexity of the grids used by our customer.



9.4 - Other migration techniques

The project family had 30 projects, the around 600 units, 300 tDxDbGris. Assuming that a tDxDbGrid can be transformed into a tCxGrid in 1 hour, this would cost 37 days. But this is a very approximate estimation.



9.5 - Cost of future DevExpress migrations

For the next migrations, we only have to analyze and include the project's new tDx types and properties. The customer only pays for this incremental work.




10 - Remarks

  • the customer software was particularlly well suited for our automatic approach : many rather medium complexity grids.

    In other case, the project revolves around 2 or 3 central grids, using a large portion of the tDxDbGrid functionalities.

    If the conversion tool does not handle them all, the effort to include them is more important and the cost cannot be averaged over several grids.

    Now if the customer has several of those sophisticated grids, then the benefit becomes again obvious. In any case, the gained knowlege can be used for other migrations.

  • our tool is more .DFM oriented than .PAS, in contrast to Database migrations, where data access components are often spread all over in the .PAS. For grids, most of the action lives in the properties. In fact the Grid editor's business is to encapsulate slick grid behavior in properties : the developer selects a couple of properties instead of writing sorting, filtering, pivoting code

  • of course we included in the same migration tool the other tDx components (tDxEdit, tDxDbGridButtonColumn, tDxDbGridLayoutList etc)

  • the individual projects containing the simpler forms are delivered to the customer, should he want to use them for some checks. However, those projects are not the final acceptance of the migration. The user still has to submit the result to his QA team and run acceptance tests

  • did we build a universal DevExpress migration tool ? Certainly not. We already told that we took a minimalist approach, to be able to convert the units at hand. For new customers, we only will have to add other components, properties, methods, events.

    To build such a exhaustive product, we would have to cover all tDx types, properties, methods and events. Code the transforms and test them.



10.1 - Unit Test

In some sense we are performing some kind of tForm unit test. The term "mock table" also relates to this testing universe

There are differences :

  • unit test is centered around code (although some GUI test can be conducted). Our tool concentrates on visual presentation and properties
  • unit test usually works on the statement / procedure level, whereas we have a broader granularity


10.2 - Cost / benefit evaluation

For the weaknesses :
  • the high upfront cost of building the tool. In our case, most of the techniques were available and the migration tool already created for other domains.
  • can slow down the migration of the first forms, since we have to build the tool (or adjust to the customer's tDx habits)
  • still requires the creation of individual tDx to tCx property, method and event conversion rules. But it makes the process easier to test.


And for the strengths, the automatic tool
  • can avoid to loose or miss some properties (wrong .TXT find and replace of the .PAS and .DFM, for instance)
  • can report on the tDx components or properties without any tCx direct equivalent. They can be reported on a per form / grid basis
  • can be run many times, allowing improvements as the tests sends feedback. With manual conversion the individual developer gains insights in the process, but this know how cannot be easily transfered to other developers / companies
  • the creation of separate projects with a single tForm and a single tDxDbGrid allows better and quicker tests
  • is able to perform regression tests, with compilation and execution error logs
  • will speed up the customer feedback with earlier deliveries. It is even possible to perform the migration before any Data Access / Unicode / 64 migration (since tDx and tCx can be installed on Delphi 5 and higher).
  • can be used for early "proof of concept" jobs
  • the next customer only pays for incremental costs (adjustment to his tDx choices)
  • is more efficient for whole multi-grid project migration. This, of course, is difficult to proove


10.3 - Other uses of the .PAS / .DFM transforms

The same "split / flatten / mock" divide and conquer strategy can be applied to several other legacy code transforms :
  • quite similar to grid switch is report switch. You might have to migrate from Quick Report to Fast Report or from Crystal to Report Builder. In this case too, the properties rather than the code are central to the conversion effort.

    There is an additional complexity with the page change rules (sub-totals should not appear at the top of the next page etc)

    Reports also involve not only a generation change but also a Vendor change, and therefore often an architecture change

  • migrate from one Sql Engine / Data Acess component set to another Engine / Data Access set.

    The split phase is not exactly the same. Instead of using the
      :tDxDbGrid -> tDataSource -> tDataSet => simpler_form
    pathes, we use
      tDataset -> simpler_form

    In this case we often have to use simpler forms with a simple main tDataSet, and several of its associates tDataSet (master / detail relations). The focus here is on the data access, which involves

    • more code handling
    • tables and their dependency
    For legacy code, the split and mock approach finally offers the testing tool that has eluded us for so long (afraid of navigation, database knowledge ...)

  • other candidates could be the replacement of compression tools (.ZIP) or Tcp/Ip suites. We would however carefully evaluate the cost / benefit approach before using our tool on those more simple component changes.

  • separate the old "Dataset / computation / display" forms into separate "Model / View / Control" tDataModule, tForm and Unit

    create_mvc_separation_Z

    Splitting the tForm into a tDataModule and a Unit should not be too difficult, but transforming do-it-all events is a little bit more challenging :

    mvc_transformation

  • the same technique can be applied to extract the Model and Controller and replace the View with Web Forms or Web Services communicating with any GUI layer, like PHP and JavaScript

  • a little bit further along the road, introducing Business Objects, and then replacing Queries with some Persistence Layer.

  • finally the extraction could be used by software archeology (wikipedia : "the study of poorly documented or undocumented legacy software implementations, as part of software maintenance") to help the refactoring the code, and especially to introduce some unit tests

    legacy_code_refactoring_and_test

    Or extracting some deeply nested buggy piece of code to to isolate it and fix it using the mocks

    fixing_legacy_code_bugs

    Granted, transforming huge legacy code looks a little bit far fetched, but the tool can be used as an aid for the transformation.




11 - References

DevExpress:
  • DevExpress site which contains freely downloadable documentation in .PDF and Help formats
  • documentation and samples of the previous tDx products is no longer available. We would be happy to migrate their demos, which, a usual, use the most advanced and less intuitive properties that the Vendor found interesting to show.


We published several papers related to this migration

We also offer migration services, training and support




12 - Your comments



As usual:
  • please tell us at fcolibri@felix-colibri.com if you found some errors, mistakes, bugs, broken links or had some problem downloading the file. Resulting corrections will be helpful for other readers
  • we welcome any comment, criticism, enhancement, other sources or reference suggestion. Just send an e-mail to fcolibri@felix-colibri.com.
  • or more simply, enter your (anonymous or with your e-mail if you want an answer) comments below and clic the "send" button
    Name :
    E-mail :
    Comments * :
     

  • and if you liked this article, talk about this site to your fellow developpers, add a link to your links page ou mention our articles in your blog or newsgroup posts when relevant. That's the way we operate: the more traffic and Google references we get, the more articles we will write.



13 - The author

Felix John COLIBRI works at the Pascal Institute. Starting with Pascal in 1979, he then became involved with Object Oriented Programming, Delphi, Sql, Tcp/Ip, Html, UML. Currently, he is mainly active in the area of custom software development (new projects, maintenance, audits, BDE migration, Delphi Xe_n migrations, refactoring), Delphi Consulting and Delph training. His web site features tutorials, technical papers about programming with full downloadable source code, and the description and calendar of forthcoming Delphi, FireBird, Tcp/IP, Web Services, OOP  /  UML, Design Patterns, Unit Testing training sessions.
Created: sep-16. Last updated: dec-2019 - 103 articles, 239 .ZIP sources, 1292 figures
Contact : Felix COLIBRI - Phone: (33)1.42.83.69.36 / 06.87.88.23.91 - email:fcolibri@felix-colibri.com
Copyright © Felix J. Colibri   http://www.felix-colibri.com 2004 - 2019. All rigths reserved
Back:    Home  Papers  Training  Delphi developments  Links  Download
the Pascal Institute

Felix J COLIBRI

+ Home
  + articles_with_sources
    + database
    + web_internet_sockets
    + rest_services
    + oop_components
    + uml_design_patterns
    + debug_and_test
    + graphic
    + controls
    + colibri_utilities
    + colibri_helpers
    + delphi
    + IDE
    + firemonkey
    + compilers
    + vcl
      – devx_tdx_to_tcx
      – devx_tdx_to_tcx_migration
      – delphi_sorted_tlistview
  + delphi_training
  + delphi_developments
  + sweet_home
  – download_zip_sources
  + links
Contacts
Site Map
– search :

RSS feed  
Blog