After reading this question, I was wondering if it is possible to modify TCustomEdit to check for the text width in a way that all of its descendants inherit the changes too?
Most changes you make to TCustomEdit will be inherited by descendants. That's generally how inheritance works. It specifically depends on what kind of changes you make, though:
If you edit StdCtrls.pas, then any changes you make will be inherited by any newly compiled code. One obstacle to this technique is getting Delphi accept the changes to your version of StdCtrls.pas without having to recompile other parts of the library that are difficult or impossible to recompile.
If you patch TCustomEdit methods at run time (by the usual technique of overwriting the first few bytes of the method to direct control to a method of your own), then those changes will also be inherited by descendants.
If you patch the TCustomEdit virtual-method table, then some changes will be inherited, but not all. Virtual calls to your patched methods will use your custom version, but non-virtual calls will continue to use the original version. When a descendant uses inherited, the dispatch of that call to the inherited method is not virtual, so the VMT is not involved, and the descendant will end up calling the original TCustomEdit method.
TCustomEdit is in stdctrls Unit. to test out what changes would be inherited, don't modify the one on the VCL path, copy stdctrls to your main project folder and modify it there. Delphi will find your modified unit first and will use it.
This leaves all other projects and VCL patches using/updating the original unit. Your patched unit would not be affected.
If I am right then modifying it even in the new location would cause a rebuild of the relevant VCL units. So when switching to other projects you will need to do a build so it recompiles with the VCL parts using the original unit in effect "switching back".
Related
I have a project with many forms. Most of those forms are inherited from one common base form. As new inherited forms are added, it became necessary to start arranging those forms inside subdirectories.
All of the inherited forms have been successfully placed in their appropriate subdirectories. However, the base ancestor form is still in the project root and needs to be moved into a Common subdirectory. Not so easy, without removing every single inherited form and re-adding them all again. I've faced this situation a few times before, and ended up just biting the bullet and doing it the hard way. But I'm curious if it can be any easier.
At least in Visual Studio, I was able to move things around like this from within the IDE. But Delphi does not support this.
What's the easiest way I can move the ancestor base form into a subdirectory while retaining all its inherited form references?
You didn't specify the Delphi version you are using, but usually you can simply open the unit while working on the project and use Save As to create a copy in the desired folder. After that you can safely remove the files from its original place.
Another option that might work in your Delphi Version is editing the unit file name inside the Project Manager view. Specify a folder relative to the current position will move the unit to the new place. See How to reorganize the folder structure of my units in Delphi?
I'm writing a package for a custom component. This package allows the user/developer to either include numerous pre-written units in their project which are explicitly designed to work with this custom control, or write their own to do the same as one of the existing ones. Think of it like a "plugin" where code is implemented in its own unit, which inherits a particular object and does custom implementation of that common object.
Each "plugin" unit has an initialization clause at the bottom which registers this class with a global list. So long as that unit is "used" anywhere in the project, it gets registered into a global list, which this custom control makes use of to populate a list of all possible "plugin" units.
For example, this custom control has a property:
property PluginIndex: Integer read FPluginIndex write SetPluginIndex;
Internally, this index corresponds with one of the "plugins" which was registered from within the initialization section of its corresponding unit.
These "plugins" are registered via a global object which is instantiated like this upon first use:
function Plugins: TMyPluginList;
implementation
var
_Plugins: TMyPluginList;
function Plugins: TMyPluginList;
begin
if _Plugins= nil then
_Plugins:= TMyPluginList.Create;
Result:= _Plugins;
end;
The difficulty is that, since the control is implemented within a package (and installed into IDE), yet the "plugin" units are "used" from within another project, the package doesn't pick up the presence of these units, and therefore this custom control is also unable to access the global list, since it's in an entirely different context.
When interacting with this control on the form in design-time, if I set this PluginIndex in design-time, it fails because the control hasn't had any "plugins" register themselves in the global list. But, in run-time, assigning that same property works just fine.
How do I do this in a way that my application which uses this custom control (which relies on client-uses of each "plugin" unit) is able to actually detect these units used?
EDIT
The end goal of what I'm doing is to allow this single custom control to be simply dropped on the form, then have specific units "used" in that same form, and finally on this single custom control instance, choose the index of which of those "plugins" to actually use at any given time. I'm trying to encapsulate and automate as much of the process as possible, making it easier for a developer to simply drop this custom-control and not have to write a bunch of other code to make it work.
EDIT
I realize that this may be interpreted as these "plugin" units being a part of the package. That is not the case. Although currently I have no choice but to add these "plugin" units to the package in order to make design-time work right, I don't want it to be a part of the package, and rather up to the developer using this custom control. They can add a combination of pre-made "plugin" units as well as custom-made ones, simply by "using" them in their application.
I am working on a multiple-device application in Embarcadero Delphi XE7.
Being new to delphi I would like to know how (if possible) can I rename a Form's Name.
e.g. TForm3 to something like frmCustomer.
You can change it from the Delphi Object Inspector - official documentation on how to customize the main form of an application.
You got me a bit confused. You want to rename a class name (TForm3) to frmCustomer. A defacto standard is to prefix all classes with a "T", like TfrmCustomer. Never have a class named frmCustomer, use that for the instance instead (here form3 -> frmCustomer). Generally it's a very good idea to be strict about renaming all controls. Makes things a whole lot easier to maintain.
I prefer to use the built in refactoring (shift-ctrl-e) while having the cursor stand in the """thing" you want renamed, e.g. TForm3. That way the name, that be a class, method or attribute, is changed throughout the project.
I can furthermore recommend you install GExperts which, among many good features, have one to name the components while you place them.
I need to change the functionality in a component. What do you do when you get a "you can't override this" message, or you need to change the code in a private method ("method does not exist in base class" message), rendering it impossible to descend the component?
If I face that problem,
I first try to inherit from the component, or its CustomXXX ancestor and see if that fixes the problem. If that doesn't,
I go deeper, i.e. try to intercept the messages that come in. That can be done dynamically. If that turns out to be too deep, because the code that has to be built on that is too extensive, or if I still have to access items I can't access,
I try hacks. One hack is to copy the component and the dependent code to a new unit with a different name, rename the component and modify what needs to be modified.
Sometimes I only need to redo one or two methods to make my new behaviour possible.
Never forget to give the unit a different name and the component a different name too (possibly inheriting from the original component or one of its ancestors, so they remain in the same hierarchy). Do never modify the original sources and then recompile the VCL. That is a maintenance nightmare.
I am no fan of interposer classes, i.e. classes that get the same name but different behaviour than the original classes, inheriting from the original. Their functionality depends on the order of inclusion in the uses clause, and that seems rahter flaky to me. I can't recommend that.
But what I do greatly depends on the problem. I don't think one can (or should) give a blanket advice that covers all situations.
But my main advice: do not modify the original units, always put the new code in a new unit and use a new class name. That way the original and the modified versions can peacefully co-exist, also in the IDE.
There are some (mainly hacky) options when it comes to modifying private methods or behavior therein:
modify the original source, recompile the unit and use the changed dcu as suggested here; never did this but I think this can cause you a good headache when your code uses the new dcu but other VCL code don't
often component behavior is controlled by numerous window messages - look if you can achieve your change by modifying the reaction on some of these messages; you can override the message handling methods (the ones with the message keyword) even if they are declared private and you can also replace the WndProc
you can use hacks like this one which is tinkering with casting
you could use some detour mechanism as described in the answers here
Or you can get another component.
I have a base form with descendents. The base form has a TElRelDataSource on it (from ElPack v4.00). When I open the descendent form I get an error "cannot inherit from (parent form). Contains a component that does not support inheritance". This is with the ancestor forms open (there are two), to avoid the "ancestor for (parent form) not found" error.
Delphi 7 doesn't (AFAIK) support finalising classes, but this seems to be a standard error message. It's in the index for "Tomes of Delphi: Troubleshooting" but I don't have a copy of that and I'm reluctant to spend $US60 to read an entry like "this is why you should avoid VFI" (being pessimistic about the contents of the book). Other than that, no references in Google or Bing.
This is in code that I've recently inherited from someone who left a while ago and I'm the first person to change it since they left. Which means I can't ask how he created the offending form.
So, how do I get around this error message?
You got yourself into an interesting problem. First of all, you say the parent form already includes the offending DataSource, so you've got to ask yourself a question: How did the original developer manage to put the dataset on the parent form and then create child forms, if creating child forms is rejected by the datasource?
The original code didn't have the TElRelDataSource
This implies you're trying to add it your self. Don't add it, find an other solution that doesn't rely on the TElRelDataSource.
The original code did have it!
The original was probably compiled against a different version of TElRelDataSource, one that did allow inheritance. "Inherability" is controlled by the "csInheritable" component style. That is, if the component doesn't include "csInheritable" in ComponentStyle, the Delphi IDE will not allow visual inheritance. You have a number of options:
Use a different version of TElRelDataSource, maybe the original was compiled against a NEWER version of TElRelDataSource. If you are using the latest TElRelDataSource, the author probably decided it's code can't handle visual inheritance so he/she removed the csInheritable flag from ComponentStyle!
Try making your own derived component and set that style yourself. This is easily done, but if TElRelDataSource's author removed the csInheritable flag for a reason, you'll probably get into trouble.
Normally HeartWare's idea would work just fine; Unfortunately it's not that easy with a DATA SOURCE component: I assume you have DB-Aware controls linked to that data source. If you create the DataSource from code, you'll need to re-create the connections to data-aware controls as well, and that's going to take a lot of "if-s" (or clever use of RTTI).
You may try moving the DataSource to a TDataModule. This is probably more trouble then it's worth it.
One option would be to remove the offending component from the form and allocate it dynamically in the FormCreate event. Depending on how many properties the (presumed) non-visual Data Source component has, this could be fairly simple.