Is "Replacing non-visual components with code" a proven optimization technique in Delphi 7. Mainly with respect to Database Access.
The Web site you cite talks about replacing a dialog-box component with code that would display the dialog box without the use of any component. The alternative is to write a couple of lines of code to set up and display a dialog box whenever you need one, and to skip the component altogether. It's not really an optimization in speed or size, though. It's not a speed optimization since your code would do exactly what a component would have done anyway, and it's not a size optimization because the space any one component occupies in a program is negligible.
Database components aren't so easily replaceable as dialog-box components. Nearly everything in Delphi is designed to use descendants of the standard database components. If you don't use the components, then you won't be using any of Delphi's database capabilities at all. You can use the database libraries' native APIs if you wish, but I think that would be foolish if your goal is really optimization and you haven't identified the components as the source of your program's non-optimal behavior. Consider how much time and effort it would take you to rewrite your program without the database components.
I don't see how a form-based dataset/query/table/etc., would be faster or slower than one created in code. However, I like to put them in code as it's easier to maintain. I've seen screens with SQL embedded in a component, and then it's overridden in the code. Then I have to stop and investigate to determine which SQL is actually in effect. Sometimes the SQL in the form is good, sometimes it's used for a while and then trumped by the code, sometimes it's never active and the SQL is trumped in the formcreate. So I have to determine whether this is by design, or just sloppy leftovers. Also, it's easy to miss SQL changes in code reviews if they're in the .DFM and not the .PAS. i.e. I don't always look at the .DFM because I'm not interested in whether a label caption changed or a button moved.
So while it's nice for prototyping, when it comes to production code, you're better off having all of your database logic (SQL, table and field definitions) in the .pas file.
Update: I have finally given CnPack a try. Among the dozens of goodies, is a brilliant tool called "convert selected components to code". Form Design Wizard | More... | Convert Selected Components To Code. It does it all for you.
This is not a matter of being a component or not a component. If it comes to database access then BDE is extremely slow so changing it for sth else is a good move.
By the way - optimization is not about 'proven techniques' - it's about identifying a problem and solving it. If the problem happens to be slow db access then this is what you have to change.
Generally no. There is no additional overhead in using a non-visual component. It is created very quickly and works at runtime exactly at the same speed as one "created in code".
Related
We have a large legacy database driven VCL application which consists of over 300 different forms. We would like to provide a practice (dummy) database along with our software which is installed alongside their production database - with the ability to switch in-between them (for training / practice, etc.).
The problem is that when users are using the practice mode, we have to make something very clear to stand out to them so they know they're working in the practice mode. The most ideal solution would be to put a red border around the inside edge of every single form. But there are so many forms that I don't want to modify each and every one of them.
How can I add such a frame (inside the Windows border, not outside) along the edge of every form across the application from one global place?
I cannot use VCL styles, otherwise I would implement it from there. I'm looking for something on a global level which can possibly iterate every form and draw this border. But that also means catching every form's resize messages and re-drawing this border.
The easiest way I can see is to create a base form which has this functionality, and inherit every form across the system from this base form. But this still means making sure every form is based off of this. We've had other issues in the past modifying existing forms' base forms (another subject), so I'd like to avoid that solution, if there's another easier solution.
There are ways to hook into the form streaming system, for example by overriding TComponent.ReadState for your form to add an event handler to the TReader handling your streaming, but they require you to actually change code in the form class in question. There doesn't appear to be any way to globally modify TReader behavior throughout the program. So without an actual base form class for all the forms in your app, there's no easy way to do this.
Even trying to patch TComponent.ReadState in-memory would be very difficult to do right, because it's a virtual method and there's no override for TCustomForm, so any change you made could affect all components and not just forms.
Honestly, the best way to handle this is probably just to bite the bullet and run a global search over your codebase for class(TForm and change them all to be a subclass of some custom form class that exhibits the behavior you're looking for. There are probably theoretically other ways to accomplish it, but they would require some very hairy runtime tricks that could have unpredictable side-effects.
You need to insert a base class in there. TMyBorderForm inherits from TForm, and it has the border behavior. Then make all of your forms inherit from TMyBorderForm, instead of directly from TForm.
There is a variation on Chris' answer, assuming you have your DFMs stored as text.
Define a form that your forms all inherit from.
You load all your PAS files in a text editor and replace the Class(TForm) with class(TMyBaseForm)
IIRC you also have to edit all your DFM files - there's a string 'object' to change into 'inherited'. Try this first with a test app, because I'm writing this based on what I did in the past, and I'm not sure this answer is 100% complete.
You add the border functionality (on/off) to TMyBaseForm
Strictly speaking, this requires you to 'go through all the forms', but 'Open selected files' and 'Replace in all open files' is not really a big deal ;-)
The only drawback is if you do not yet have your DFMs stored as text, you'll have to change that first.
[Inheriting from your own 'base form' is often a good thing for large projects. We use that e.g. for third party components that do not have the default properties we want - if the developers forget to change the default property, the run-time code in the base form will update it].
I would persuade a friend me that using Using Database Components (DB Aware Controls) in Delphi is by far the best option, when developing database applications.
This debated started with him many years ago: still today he is persuaded that using simple controls like TEdit, TStringGrid etc., with a set of getter and setter methods to populate them, is the best solution both in terms of flexibility and maintainability of the whole project.
To me this sound counter-intuitive at least.
I think that using DB Aware Controls, like TDBEdit, TDBGrid etc. is the right thing to do when developing database applications.
So: please help me convince him with sound advises about using DB Aware Controls!
Thank you in advance to all of you that will post at least his own advice, whatever will be in favor of one or the other solution.
--
fabio vitale
DB-Aware vs non DB-Aware is kind of a obsolete discussion. DB-Aware controls have automatic databinding, which means they autofills themself with data, do change detection and write to the members of the datasource bounded; in non-dbaware controls, it's up to you to do those tasks. This can lead to messy code too - or overengineered frameworks just to do databinding. Both scenarios are bad.
The kind of datasource and the quality of the individual control will determine the performance. I've seen a lot of code to databind TStringGrid simply just to avoid TDBGrid because of purism (when TDbGrid would do nicely) - just a overly absurd loss of time.
It became an obsolete discussion when TClientDataset became the de-facto dataset for disconnected applications: you can pull data, disconnect, work on data, connect again and apply changes. Since CDS+DbAware controls will do 99% of the interface, you use the non-data-aware controls for the next 1% (for some complex interfaces).
I still don't have XE2 to see if LiveBindings do the OO databinding work nicely - if so, now all controls can db-aware if needed.
EDIT: in da-soft's answer, he(she?) argues that DbAware controls implements MVC pattern and LachlanG disagrees that, even it does, doesn't that code itself is MVC. I'll give my $0,02 cents here ;-)
I think that the use of a 1:1 relation in DataModule and Entity (as in ERD) - or DataModule and a Form - you can get an application where responsibilities are separated:
form dfm -> Layout and design-time databinding (have only Datasources)
form *.pas -> controls interface and asks Data Module for data (acts like a controller)
Data Module -> methods to answer forms requests for data retrieval
and data updates
I normally have an separated data module for database connection which is passed through forms/entities' properties.
DB-aware controls pros:
The work to load data to a control and save data to a dataset is performed by VCL. You have less to code - really RAD.
The DB-aware controls + TDataSource + TDataSet implements MVC pattern. That helps to separate GUI and data access code.
The code validating, reformating, doing other post processing of the data will be called in well defined moments using event handlers. That helps to centralize such code and avoid confusions with the places where it should be placed. And avoid confusions with the moments when to call it.
Cons:
This is not a universal approach. You cannot bind a DB-aware control to a non-TDataSet data source. This issue was solved with introducing Live Data Binding in Delphi XE2.
Event driven programming may be confusing for some developers. And requires knowledge of TDataSource, TDataSet, TField events and architecture, and lead to controversy with Pros (3). Bad event driven code may be nightmare.
Both DB-aware and non-DB-aware components have advantages and disadvantages, depending on what kind of application you are developing.
For small-to-middle scale applications, using DB-aware components is encouraged, unless there are specific requirements or conditions under which those applications are operating. For example, a simple data entry application would be easier to develop and maintain with DB-aware components. Even middle-to-large scale applications, if properly designed and implemented, will have good performance with DB-aware components.
However, when developing modular applications, especially multi-tier systems, DB-aware components can have negative impact on application performance and maintainability. This is particularly noticeable with applications that access data over the Internet. Main reason for this is because DB-aware components require a constant connection to the database, which can significantly increase network traffic.
Using non-DB-aware components can be more complex since it requires better understanding of the underlying mechanisms, but in multi-user and distributed multi-tier environments it scales far better than anything else. Additionally, you can (and should) separate the data-access layer from the presentation (UI) layer, which will later allow you to make changes to one subsystem (module, layer) without having to to change anything else.
Today, data can be anywhere and most of the times it is not stored on local computers or networks. Using DB-aware components to access that data can have significant negative impact on application and database performance. There are some data-access layers which improve this (UniDAC, AnyDac, ...), so using DB-aware components with them may get you better performance. Still, non-DB-aware components can outperform anything.
It is up to the developer to decide which mechanism to use, because, as I said, it all depends on what kind of application you are making and in what environment will it work.
This may be a carryover from experience with Visual Basic. As I recall, even Microsoft told developers not to use DB-aware controls in production code.
Delphi never had that problem, but as others have said, it depends upon the kind of project you are building and whether you run into performance issues.
Anecdote: a DB-aware control once messed up our InterBase database: to indicate a 'empty' or null date, the date edit control TcxDBDateEdit (ExpressQuantumGrid) uses a negative date value, which represents literally "01/00/0000".
So when users cleared the date edit field in the input form, and posted this value to the database, the records became unreadable (SELECT statements failed).
I'm new to delphi and I'm looking up on ways to learn more about delphi underlying technology plus make something useful here in my job in the process, also if anyone has any tip or see anyway i can improve my idea please fell free to speak your mind...
i want to do make some kind of Scaffolding for dfms, the ideia is the following: i want to generate based on a firebird database table - with well defined domains - the bulk of the Form .
I think the idea is too simple or i'm not seeing the big picture that makes it difficult, i'm trying not to reinvent the well, i've looked up on google but without good results, so if anyone could giveme a direction here i would be most grateful.
UPDATE:
#Larry Lustig thanks, i didn't think about delphi frameworks - i'm going to look them up.
i know something about form objects/handling and database metadata, but i'm unfamiliar with serilizing delphi objects to the HD. Any tips on Serialization and delphi frameworks (opensource so i could take a look :) ) would be welcome!
Sounds like an interesting idea.
Instead of writing a DFM form manually on disk I would use the following approach:
Get the structure of your table by examining the meta data.
Create a TForm and add a control to it for each column you want to represent.
Use Delphi's built in serialization to save the form to disk.
I haven't done this myself, but there are a number of run-time design frameworks that work using this idea.
This would only make sense if you need to build a lot of forms at design time. You can't use the DFM's in your executable's. If you want to build the forms runtime, I suggest you go with Larry Lustig's answer.
We have taken this one step further... we don't build forms. We only write the classes, add some attributes and create the forms at runtime. Users can change this preset form layout during runtime and save their own layout. Data binding between the controls and the database is done with the excellent tiOPF framework. Maybe something you can consider to use as well.
I know it's important to keep user interface code separated from domain code--the application is easier to understand, maintain, change, and (sometimes) isolate bugs. But here's my mental block ...
Delphi comes with components with methods that do what I want, e.g., a RichText Memo component lets me work with rich text. Other components, like TMS's string grid not only do what I want, but I paid extra for the functionality. These features put the R in RAD.
It seems illogical to write my own classes to do things somebody else has already done for me. It's reinventing the wheel [ever tried working directly with rich text? :-) ] But if I use the functionality built into components like these, then I will end up with lots of intermingled UI and domain code--I'll have a form with most of my code built into its event handlers.
How do you deal with this issue? ... Or, if I want to continue using the code others have already written for me, how would you suggest I deal with the issue?
First off, remember that "dealing with rich text" isn't what your app is supposed to do. Your app is supposed to enable the user to perform a certain task, and the code to take care of that task is your domain logic. If part of the task that your program needs to accomplish involves working with rich text, then you can delegate that part of it to a rich text component. The complexity comes, as you mentioned, at the interface between these libraries and domain logic.
The way Delphi domain logic and Delphi UI controls interact with each other is mostly through event handlers. But that doesn't mean that you need to put your domain logic in the event handlers themselves. Instead, try writing units that contain the code for the domain-specific tasks you need to accomplish, and have the event handlers call those units.
In other words, don't write classes to reinvent the wheel and do things others have already done for you. You're right, that would be illogical. But write the application-specific parts that the controls and libraries you use haven't provided as their own separate classes in their own separate units, and connect them through event handlers and public methods or properties on your forms, and you'll end up with decent separation of concerns.
You can create a rather clean separation by using datamodules with clientDataSets to contain your business logic and data objects. You could even take it step further and break the business logic away from the data. Obviously the form contains the UI. Almost every component you use can be data bound which makes it easy to bind them to your clientDataSets.
Just remember Delphi has a way of doing things that don't always fit the best practices that Java or .Net have. Your goal should be to do something that feels right and works for Delphi.
First off, this applies to Delphi 5 Enterprise, as this is what we use at work. There's no view to upgrading any time soon, as this version "does what we need", apparently.
After setting the dgRowSelect and dgMultiSelect options on a TDBGrid, the behavior does not confirm to a standard Windows UI.
I don't think we've ever needed this option before, else I would have noticed how poor the default implementation is on Delphi's TDBGrid. I want Ctrl-Click for single rows (which works OK; not great, but OK) but also Shift-Click for a range selection (which doesn't work).
I suspect I could trap the WM_LBUTTONDOWN message and process it manually in a subclass, but are there any pitfalls that await me down that path?
I'm hoping someone has already had to go through these motions, as I can't imagine people being happy with the poor default effort offered.
The Infopower library, available from Woll2Woll [http://www.woll2woll.com], contains an extended datagrid which includes properties (msoAutoUnselect,msoShiftSelect) that will provide the behavior you want.
These properties were introduced very early in Infopower's history, so even the cheapest version you can find should be adequate. Infopower costs less than three hundred dollars in any case.
I am not affiliated with Woll2Woll in any way; I just use their product.
-Al.