HOME, PRODUCTS, ARTICLESCONTACT

C++BUILDER AND DELPHI FEEDBACK

VITAL FIXES FOR FUNDAMENTAL OWNERSHIP AND STREAMING PROBLEMS IN THE DELPHI™ IDE

Here too, in the years which have passed since we expressed our imperative need that Borland fix these issues, proper support for streaming/serialization has become a principal instrument of Microsoft .Net tools. Our current projects are just now testing whether Microsoft implementations meet the necessary standards to develop some of the more complex visual control implementations we have succeeded in, despite these critical flaws in Borland Delphi™ and C++Builder™. As to how "well" Borland tools might support .NET implementations which are essential to our own goals... we can only report that no committment to cooperate in rectifying these faults means we cannot continue to use Borland tools.

An often repeated cycle first appears to observers, with a component Developer A reporting their component will not accept controls placed on it at design time. Because the documentation advertises that this behavior succeeds by inclusion of the csAcceptsControls flag, a Developer B reminds Developer A to include csAcceptsControls. Possibly to some embarrassment of Developer B, Developer A responds that they already have.

The rest of us often lose interest at this point, expecting there is nothing to learn from this query. After all, csAcceptsControls "works" in TWinControl, TCustomPanel, TTabSheet and so forth; and so we ask ourselves how credible the reported dysfunctionality can be. Without a serious effort, we can only come up empty handed. But it is this presumptive thinking itself which is flawed, because, as it turns out, csAcceptsControls does not work at all but for the most simple possible cases of TWinControl, TCustomPanel, TTabSheet and so forth — and so, this often repeated pattern is actually prominent evidence that substantial issues exist in what we think carelessly is a proven system.

In what amounts to the proverbial search for a needle in a hay stack, Developer A is no neophyte. They have already spent weeks exhausting every conceivable combination of csAcceptsControls with every further potentially relevant condition they can think of. They have looked everywhere for examples. They have scoured resources for further references. They find none of either.

The nearest potential VCL example they have collected will take weeks of careful study and test code before they can realize how it does not model their problem. Those eventual weeks of work yet face steep odds they may never understand why. To discover so, they may write days of code that melt into weeks and months which ultimately "can never get there from here." After all of this, their efforts remain at inch one, with miles of problem ahead.

Only hoping that Developer B has succeeded in such a design, and receiving no other useful answer, Developer A asks what else they might be doing wrong.

But Developer B too only took the documentation at face value, and their expertise is no greater or more valid than the little they have merely read. It is Developer A who is closer to finding the truth.

Both have assumed csAcceptsControls *works*, because it has in fact succeeded in only the most simple possible classes — TPanel, TWinControl, TCustomPanel, TTabSheet, etc.

Actually, to call these challenges "steep" is to understate what is more an impassable mountain range.

Actually, Developer A hasn't (necessarily) done anything wrong. But to understand that, we and Developer B have to conquer substantial problems that the very designers of the IDE never solved. If we or Developer B had ever succeeded to the end of that difficult road, we would instead answer the original post with an explanation,

  • how csAcceptsControls is broken;
  • that inclusion of csAcceptsControls is merely a minor detail;
  • and that (to say the least), because of the anomalous design of the visual development environment, there are steep, undocumented, technical slopes ahead.

Actually, to call these challenges "steep" is to understate what is more an impassable mountain range. Months pass. The answers are never found. Intended products are never completed. Each Developer A's issue disappears without a solution — or worse, with a retort that is not a solution at all, but appears (quite superficially) to close the issue.

As the years go by, mountain after mountain of futile effort are wasted untold times in untold places as the csAcceptsControls issue recurs again and again.

WHERE csAcceptsControls OUGHT TO BE FIXED

Before we explain how what we shall call "composite parent controls" can accept controls from the present development environment, let us first be clear,

  • The place to fix csAcceptsControls is the development environment (not your control).
  • Moreover, there are further critical issues which must be fixed as well:
    • Once you do get csAcceptsControls to work, developers will be able to select and operate on subcomponents of your component that they should never have access to.
    • Yet you are provided no way of preventing them from doing so, even as VCL designs as well were made more difficult, costly, and less developer friendly for want of a variety of vital protection capabilities.

RAMIFICATIONS OF LACKING PROTECTION CAPABILITIES

What are some of the undesirable consequences that you cannot provide well conceived protection against?

  • Developers will be able to select subcomponents and operate upon them, even if the subcomponents are private or protected, and are designed to be accessible *only* by intended interfaces.
  • *Integral* subcomponents will be subject to intolerable processes such as movement, and even deletion.

    For instance, just as you can delete a TTabSheet from TPageControl, developers will be able to inadvertently (or purposely) *attempt* to delete inseparable subcomponents from your component.

    "Attempt?"

    Yes. An attempt to delete a subcomponent will not succeed, but only for the most ungraceful, coincidental IDE behavior. An attempt immediately raises an access violation in the development environment because the unprotectable (!) references to your *integral* subcomponent suddenly point to nothing.

    Of course, no developer can have a valid reason to attempt to delete an integral subcomponent, and certainly every conscientious developer realizes this. But, because the development environment provides no way to protect your integral subcomponents from a variety of adverse actions, developers *can* make such a mistake. For instance, they could select a control they placed on your subcomponent, and, intending to delete it, they could accidentally press delete twice.

    All users of the development environment therefore absolutely require these capacities to protect from selection and undesirable action, because if developers ever do make *this* particular mistake (for instance),

    • The *unprotectable* access violation forces the developer to shut down the development environment — which is left in a condition which (thankfully) prevents them from saving (which would embed the AV in their .dfm file).
    • When they re-open the environment, having the living %!$%3 scared from them, thankfully (*only* because they happened to be unable to save), there sits their project in the state they hoped to find it.

      This is not a product of comprehensive IDE design. Their valuable project's vitals merely happen to remain intact!

BASIC PROBLEM OF FIXING csAcceptsControls IN OUR CONTROLS

Our basic problem is,

  • The advertised function (regions accepting controls dropped on them at design time) *only* succeeds in *one* most simple case where the subject *region* of the control is comprised *only of a root (base) layer*.

    In fact, Developer A after Developer A has discovered this:

    • If including csAcceptsControls *everywhere it can be included* does not deliver the behavior as promised by the documentation, obviously, other issues are involved.

NEAREST PATTERN FOR SOLVING THE csAcceptsControls ISSUE

The source for TPageControl provides your nearest model for dealing with csAcceptsControls issues. Yet only after very substantial study might you see that TPageControl cannot take you where you need to go, even as substantial program flow is introduced to deal with the coincidental capacity to select and delete pages (TTabSheets):

  • If the code base of TPageControl were not subject to this unprotected danger of the development environment, no purpose of curious workarounds (which respond to deletion) would exist. A much simpler system could simply provide intentional deletion.
  • Worse, these workarounds only happen to deal with a separable part. You still have a problem, because your component cannot tolerate deletion of *integral* members.

These are the matters that Developer A's study may never come to understand, and which can lead to tremendous futile programming — hoping to make the TPageControl model work for *integral* subcomponents which are not separable parts, and which cannot tolerate the unprotected capacity of the development environment to delete them.

TPageControl however is no model at all for a greater reason: It paves no ground by which composite controls can accept controls in regions which are not (and should not be) subject to the circumstances that the root layers of TPageControl and TTabSheet are.

This shall be clear in due course.

HOW csAcceptsControls IS BROKEN

As yet, your effort is aware of only one issue, this being that your *composite* control will not accept other controls placed on it at design time.

If you conquer this first issue (and we will prescribe how to do so immediately), your reward will only be to discover the real quagmire. The fact your component does not accept controls is just the tip of the iceberg. You are the Titanic, confidently steaming into waters which are not RAD, and which will soon send your best work to the bottom of the sea.

You are the Titanic, confidently steaming into waters which are not RAD, and which will soon send your best work to the bottom of the sea.

To introduce the iceberg ahead, let us proceed directly to the solution of your perceived issue. You may try this immediately, and indeed, you will find your *subcomponent* will accept controls by the only means it is possible (under the present IDE design) to accept controls:

  • Beyond including the csAcceptsControls flag, you must give ownership of the uppermost layer of your composite control, and of every interceding layer between that uppermost layer and the root layer of your component, *to the form*.

Hmmm. We have of course simply skipped across the vital learnings from which we develop this conclusion. But to get your composite control to accept further controls from the development environment, we need only pass a pointer to the form to the constructors of your interceding subcomponents.

So, if our component will now accept controls from the development environment, how is csAcceptsControls (still) "broken?"

HOW csAcceptsControls IS STILL BROKEN

Backing up a step, we discover our clue how csAcceptsControls is indeed broken; and this clue indeed suggests that things may very well not be right with our only possible solution:

  • What we were doing of course, was to assign ownership of our *subcomponents* to our root component, because the form knows nothing about when to destroy our root component's children.

Having emphasized this, I expect many of you will certainly smell a rat. We have been forced to give ownership to something we should not. The IDE designers have broken an essential pattern; and, as yet, we can only anticipate what ramifications that broken pattern have.

Note, most importantly, that we have not fixed csAcceptsControls:

  • We have done something inadvisable with ownership to get anomalous csAcceptsControls behavior "to work." (Or so we think.)

How does this actually play out?

HOW OUR FIXED csAcceptsControls PLAYS OUT

Our problem soon reveals obstructions which indeed are as surprising and imposing to our project as the iceberg which suddenly loomed ahead of the Titanic. Only after complying with this *requisite of the streaming system* can we discover how far we are from even *finding* (much less solving) the problems which ensue. Our ship is just now grazing the iceberg.

Our problem soon reveals obstructions which indeed are as surprising and imposing to our project as the iceberg which suddenly loomed ahead of the Titanic. Only after complying with this *requisite of the streaming system* can we discover how far we are from even *finding* (much less solving) the problems which ensue. Our ship is just now grazing the iceberg.

To have the slightest idea of what we are getting into, we have to back up again to the vital learnings we have skipped:

  • Why am I constantly referring to "subcomponents," when the original issue was raised as a problem with "a component?"

Every further riddle hinges on this issue; and the answer to this question is simple.

But while we trudge toward solution, let us test if having to deduce the vital answer will stop your project in its tracks.

This is the only existing clue:

  • The streaming system *always* makes the form the owner of our "component" (root layer).

Hmmm, you muse. What does this have to do with anything?

Think about it for a moment.

If the streaming system has made the form the owner of our "component" (root layer), then if we have included the csAcceptsControls flag in every interceding layer and "our component" is still not accepting controls, this is our clue to find out whether ownership by the form must be an (unfortunate) obligation imposed by the streaming system:

  • After all, a single-layered "component" *does* accept controls, and every layer between the controls we want to accept and the form *is* owned by the form.
    • In other words, Developer A had to have a subcomponent layer, or the original scheme would have worked for their control because, as is the case with TWinControl, TPanel, TCustomPanel, TPageControl and TTabSheet, their (whole) *component* complies with the one simple case which does not break the system.
    • This is also why, knowing the involved issues, Developer B would instead have answered from the very beginning with the shortcomings of csAcceptsControls:

      "If you have included csAcceptsControls, then your component must be a composite. Evidently, you have not given ownership of every interceding layer to the form, and..."

Realizing the magnitude of our conquest, tears of joy flood our desk...

Now that we are up to speed with the vital train of thought, we perform our suggested ownership experiment:

  • Late in a busy day after weeks of taxing trials, we give ownership of every interceding layer to the form.
  • We compile.
  • We plunk "our component" onto a form.
  • Presto. It accepts controls.

We can't believe it. With our jaw on the floor, we blink incredulously at the product of so incredibly much futile work. Realizing the magnitude of our conquest, tears of joy flood our desk. Deservedly, we close shop and head to the tavern to celebrate, and to renew spirits diminished by far too much expended energy over what should be (and is advertised to be) a very simple issue.

CHAPTER 9 — STILL FIXING csAcceptsControls

The next morning as we open our project, the hair rises on the back of our neck as we recall again what we have done and what we went through to get here. Buckets of tears flow across our desk again as we reflect on our rather incredible success.

We have indeed built the Titanic. And now, halfway across the sea and with too few life boats, we have to fix "just a small crease" with a slight headache.

If we're still using Delphi 7 or earlier, in only a second or two our project opens...

OOOOOOOOOOOOOOPS! ACCESS VIOLATION CITY!

What the @^$$$@#%?????

We have indeed built the Titanic. And now, halfway across the sea and with too few life boats, we have to fix "just a small crease" with a slight headache.

I will explain straight-away what has just happened:

  • *By requisites dictated by the design of the visual development environment*, the constructors of your subcomponents are colliding with instances of the subcomponents which are streamed back in by the form.

Swallow, and believe me: It's now time to think how long you can stand the cold.

If you could answer these questions to now, you are floating in frigid waters, your teeth are clicking out of control, and your ship is already headed to the bottom, even as the work you have in building her is very many times the possible lifetime of her one and only partial sailing.

On the other hand we know that if our tool makers fail even to recognize such critical problems, they simply never will send the life boats until after we have met the fate of their work.

The next phase of your wee little "RAD" problem, *merely* (!!!!!) to accept controls at design time, equates to worse than doing something inside out — fixing the streaming system *from the outside*, while its design further obstructs you from doing so. Here, you will lack a pattern or further help from this article, because our own costs were so great in conquering this issue we cannot disclose how we did it.

But what's the sense, in any case? If Borland cannot build a more comprehensive streaming system and provide necessary related capabilities, we can code or whistle all we want. On the one hand we know that some day the advertised privilege will only mean so much work will be for naught. On the other hand we know that if our tool makers fail even to recognize such critical problems, they simply never will send the life boats until after we have met the fate of their work.

MINIMAL RELATED CAPABILITIES FOR "COMPOSITE PARENT CONTROLS"

If management determines that streaming behavior shall remain based on form ownership (which I say was the wrong concept from the very beginning), here are a few things that successful "composite parent" control designs (obviously) will still need:

  1. csIDECannotSelect
    • If a control including this flag would be selected by any design time process, the IDE instead selects an underlying control by exploring downward by Parent until finding a component owned by the form and not containing the flag. That control is selected in the implicated control's stead.
    • csIDECannotSelect objects are also filtered from the Object Inspector.
  2. csIDECannotDelete
    • Prevents deletion of a selectable subcomponent.
  3. csIDEHonorsPosition
    • Insulates the selected control from usual methods of movement in the IDE, except by exposed properties.
  4. csNotRecreatedByStream
    • This flag eliminates the double creation issue by preventing the streaming system from re-creating the control. (But if you fix this, you may as well base streaming on principles that will work.) Creation and destruction of subcomponents remain the natural responsibility of the root component (even though we're forced to let the form be as much as a pseudo "owner").

Are these issues optional "for the rest of us?"

Only if our tools (and the work we can do with them) are to preclude competing with tools which can do what so many of us have already intended to do — only to be doomed to failure.

© Copyright 1995-2007, by ADVANCE Information Systems, Inc. ALL RIGHTS RESERVED.Copyright 1995-2007, by ADVANCE Information Systems, Inc. ALL RIGHTS RESERVED.

Firefox™.Best viewed in Mozilla Firefox™.