Do I need to put my Interposer class in
all the form that will the particular class ?
Say I want to re-implement TPanel, I redeclare
it as TPanel = class(ExtCtrls.TPanel) in the unit.
Do I need to do this in all the unit that uses
TPanel ?
Thanks in advance !
You yourself have acknowledged that this is a hack. It's not meant to scale well. It's meant for one-time cases where it's not worth the trouble to "do it right" by writing a bona fide custom control.
You can try putting your new class declaration in a separate unit. Make sure that unit appears on uses clauses after the VCL unit that declares the "real" version of the class. If that doesn't work, then yes, you need to make new declarations in every unit that uses the hack.
Since you're doing this to turn TLabel into TStaticText, you'd probably be better off simply changing your TLabel controls into real TStaticText controls on your forms. (That was the answer the last time you asked about this.) That way, the program you ship will be the same program you tested. Otherwise, you're testing a program with one kind of control and shipping one with another.
If you want to design a new component, you'll have to give it a unique name. Otherwise the form designer and the serialization code won't be able to tell them apart. Call it TRoderickPanel or something. Then just replace all your TPanel objects with TRoderickPanel objects on every form that uses them. (GExperts adds a right-click option that makes this much easier.)
Depending on the functionality you are trying to add, you could consider using a class helper. If what you need cannot be done that way, I'd just bite the bullet and create a new class like Mason said. Replacing it right through your code is not that hard really, given that form files are also stored as text.
If you also have Visual Studio installed, that actually has a "Replace in files" command. On top of that I'm sure Google will be able to help you out with stand-alone tools. And I haven't used GExperts in a while, but I'm almost certain that had a "Replace component" command.
This is a far better solution for the long term. If you can use a class helper for what you need, that may be quicker, simpler and less work.
I would instead use the GExperts wizard to replace components to get the behavior you are wanting. Doing what you are suggesting would require re-registration of the components so that the DFM loading mechanism created the proper components, and if I'm not mistaken, the component registry doesn't allow duplicates so would generate an exception. What your suggesting works well with classes, but not components used on a form.
You can put your interposer class into a Unit and add that unit on the uses clause of all units where your interposer needs to act. Pay attention to add this unit AFTER the unit ExtCtrls on the uses clause, otherwise, it will not work
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've been using Delphi for a little over a month, but I still do not know the proper way of doing things.
If I have methods which I can use on a lot of projects should I put them on a Unit, Form or DataModule?
I have not used a unit (in itself), New-> Unit. Should I put my utility methods there? If so, is there an example I can look at, a tutorial or whatever.
I've used DataModules in the past, though it gives me the impression that it should only be used when I'm dealing with databases and such.
Another thing, I'm customizing some forms (for instance a form with some TEdits that do specific things).
To use this, first I add this to the uses in the .dpr
CustomizedForm in '\CForm\CustomizedForm.pas' ;
then I add CustomizedForm to the uses in my mainform (where I'll be using it).
Is this the correct way to do it? I'm was just guessing, it seems to work though I'm not particularly confident that it's the proper way.
I put all mine in a unit called utils.pas. It works for me.
If you have different segmentations of utilities that some programs use but not others, you may want to create separate units for them.
Look at: Anatomy of a Delphi Unit
It is part of the excellent Beginner's Guide to Delphi Programming by Zarko Gajic that is very worthwhile to go through.
For the best "visual" introduction, see Nick Hodges' Thirty Camtasia Demos in Thirty Days. It is for Turbo Delphi which no longer is offered, but is still very similar to full Delphi and lets you visually get a feel for how to do things in Delphi.
Where you put things depends on what they do. The unit associated with a form should really only contain code that's directly related to the user interface that the form presents. Putting business logic directly into a form unit is considered bad practice for a number of reasons. If your utility methods aren't part of a specific form, it's best to put them in a Unit.
As for Data Modules, they're containers for holding non-visual controls. As the name implies, they were created for database access, but they can hold all sorts of other things. For example, at work we have a handful of data modules that contain TImageList controls, which hold lists of icons that are used in various places throughout the app. If you have any non-visual controls that you need to share with several different forms, a Data Module is the logical place to put them.
And yeah, it looks like you're doing the customized form right. If you have a second form that the first form needs access to, (to make it show up when you hit a button or menu item, for example,) then the first form's unit will need the second form's unit in its uses clause. (There are ways around this involving class registration techniques, but that's an advanced topic.)
You might want to avoid using the global form variable that Delphi likes to set up in a form unit, though. It makes your program start up more slowly, and using globals is another thing that's considered a bad practice. A form is an object like any other Delphi object, and you can create it with its constructor, call Show or ShowModal to make it appear, and then call Release or Free on it (read up on TForm.Release in the helpfile to know when you need to use it) when you're done with it.
For starters, create a few different units based on categories - StringUtils.pas, MathUtils.pas, DateTimeUtils.pas, etc - put your functions into those units according to category and be sure to include all the prototypes in your interface section so they'll be visible to other units (but don't get too caught up in how to categorize things...) and keep all your util units in a separate, dedicated directory. Then, point your Delphi or Project library paths to that directory and you'll be able to use the functions in all your utility units. Eventually, you may want to make the units into classes with class functions or persistent structures etc. I myself generally create a project group that includes a package called MyProjectUtils.bpl and I put all my utility units and classes into that package. I never actually deploy it as a package, but since they are wrapped up together I can always check that they all compile properly and they are all available immediately for browsing in the IDE, etc.
Let me preface this by saying I'm fairly new to Unit Testing, Mocks, Stubs, Etc...
I've installed Delphi-Mock-Wizard. When I select a unit and "Generate Mock", a new unit is created but it's very basic and not anything what I understand Mocks to be.
unit Unit1;
(** WARNING -
AUTO-GENERATED MOCK! Change this
unit if you want to, but be aware that
any changes you make will be lost if
you regenerate the mock object (for
instance, if the interface changes).
My advice is to create a descendent
class of your auto-generated mock - in
a different unit - and override
things there. That way you get to
keep them.
Also, the auto-generate
code is not yet smart enough to
generate stubs for inherited
interfaces. In that case, change your
mock declaration to inherit from a
mock implementation that implements
the missing interface. This,
unfortunately, is a violation of the
directive above. I'm working on it.
You may also need to manually change
the unit name, above. Another thing
I am working on.
**)
interface
uses
PascalMock,
TestInterfaces;
type
IThingy = interface;
implementation
end.
Looking at the source there seems to be quite a bit commented out. I'm wondering, has anyone gotten this to work?
My IDE is D2010.
Thanks.
The Delphi Mock Wizard is not finished. It's barely even functional. The output you posted in your question looks like the only thing that tool is capable of generating. If you want to use it, you'll have to wait until it matures. Since it was last updated in October 2008, you might have to wait a long, long time.
You'll have to choose a different direction for your mocking needs. Please see an earlier Stack Overflow question for advice:
What is your favorite Delphi mocking library?
I'd like to create a custom "datamodule" in Delphi, a TDataModule like (maybe inherited) class which would have a custom grid based design interface (one component per line, some properties as columns). Is this possible? Where should I start?
I'm currently using Delphi 2007.
Maybe it is possible. An example of a XML Frame Designer can be found on the page New IDE Building Blocks. Well this example is for Delphi 5, but it might be a good starting point.
As Nick Hodges pointer out here:
Drawing on a DataModule in Delphi
it would be difficult to paint on the TDataModule descendant. Not impossible probably, but not worth the trouble for sure.
I would consider designing a completely new TDataModule from scratch, so you would have complete control over it. Sure a lot of work probably, but in the end it would pay off.
You can find TDataModule class in the Classes.pas unit. But this is just the runtime part of the code. Other parts are IDE related. You can find units related to ToolsAPI in "c:\Program Files\Borland\BDS\4.0\source\ToolsAPI\" for BDS 2006 for instance. You can also derive you own data module from TDataModule and register it with RegisterCustomModule (DesignIntf.pas). I don't know much more on this subject, maybe others do. As I said it would not be easy to do something like that. But maybe with this initial information you can find what you want.
It's not possible exactly as you describe it. You can't place visual components like a grid on a data module. You need a form or frame for that.
If I were to do try to something like you're describing, I'd use a list box to hold a list of individual components, a couple buttons and a selection dialog box for adding and deleting components from the list, and the TJvInspector component from the JVCL for modifying properties. That would be a lot easier than trying to make a one-size-fits-all grid to handle the situation.
After years of coding Delphi programs as untestable code in forms and datamodules, including global variables, and the only classes are the forms themselves, containing all the code I need for the form UI itself.
How would I convert the code to a set of classes that do the actual work? would I need to stop using the datasources/datasets and do everything in classes? do I need an ORM?
There's usually zero need for reuse of the code in the forms, so does it make sense to convert the logic to classes?
If I encounter a form (or other class) with too much responsibility, I usualy follow the pattern below:
Define a new class for the logic.
Create a member variable of the new class in the form.
Create the class in the onCreate and free it in the onDestroy of the form.
Move a single piece of logic (for example a variable) to the new class.
Move or create all methods to the new class.
Compile and test.
Continue until all logic is put in the new class.
Try to decouple the logic class from the form class. (You can even work with interfaces if you like).
There are situations where a single class is not enough, so it is no problem to create more classes. And these classes can have other classes to.
With these steps, you can tackle most of these problems.
To start with I can highly recommend reading the book Refactoring by Martin Fowler.
This will give you a real understanding about how best to sensibly approach introducing changes to the existing (non OO) code to improve maintainability.
I would not look at an ORM until you have a clear understanding about what benefits (if any) one would bring to your application.
I have encoured problem like this with one application, I start doing the following:
Define main classes for most general logic in the code.
In each form, move the code that process the business logic inside the events as function / procedures in that form.
Then Move these functions/procedures to those classes as static methods.
Finally make only the needed code inside forms like validation UI, and calls to the classes.
For the global variables try to omit as much as you can, and just pass the values as parameters to the methods.
I used static methods, because it's easier for you to remove the code from events and just call them without requiring to Create/Free object for each operation. The original design was not designed to separate the forms from business logic code.
The final application was not full OO, but it least it was easier to test the methods without requiring interacting with the forms and events like before.
Sometimes you feel if you redesign the application from scratch it will be easier than to made changes to make it real OO design.
Another book I can highly, highly recommend - in my personal opinion even better suited than the "generic" refactoring book by Fowler - is "Working Effectively with Legacy Code" by Michael Feathers. It truly showcases the major bumps you will hit while doing that kind of work. Oh, and: Refactoring legacy code can be quite hard on your psyche. I hope you can handle frustration... I like this quote (don't remember where I got it from): "God was able to create the world in 6 days, just because there wasn't any legacy code". Good luck. ;)
Importing into Modelmaker is my first action when confronted with an existing Delphi project. Modelmaker will assist you in refactoring your code because:
It graphically represents all the classes, methods, variables, etc.
It is very tightly integrated in the Delphi IDE (main menu, popup menu,
separate Modelmaker explorer,
toolbar, keyboard shortcuts). This
integration allows you to quickly
perform the necessary actions without
leaving the IDE
It has a dedicated "refactoring" module allowing you to quickly create, move
and rename classes and variables without
having to worry about changing the
underlying code. Modelmaker will
automagically change names and
references in all units.
The basic functionality of Modelmaker is easy to learn. Modelmaker is like any other good productivity tool - The more you put into it, the more you get out of it.
Modelmaker is not free but easily pays for itself in increased productivity.
I have not found a better tool for refactoring legacy Delphi code. They offer a free trial and some decent tutorial movies.
Give Modelmaker a try and good luck...
After understand what you need to refactory your code, and if you want an OPF/ORM, I suggest Jazz SDK