Tuesday, August 4, 2026

#D.MVVM — Four Years of Silence and Twelve Days of AI

Hello, my friends!

If you have been reading this blog for a while, you know the pattern. Every year or two, there was a new post about my (D.)MVVM framework, and the message was always more or less the same: it is nearly there, I have rewritten the bindings, stay tuned.

The last of those posts was in December 2021. After that: nothing. Not a single word about #D.MVVM for four and a half years.

Today I can tell you why — and what has changed since July 4th.

What happened in 2021

If you did not read it back then, here is the short version of The trap of wanting to make it perfect.

My designated release date for the beta was December 2019. One week before that date, I cancelled it. Not because the framework did not work — it did — but because it was a source code distribution, and after a close look at my own code I could not defend shipping it. It was messy and barely readable.

So I started refactoring. Then I deleted all binding rules and the main binding unit, because I was convinced there had to be a better way.

There was. But by the time I came back to it, I no longer understood my own source code. I tried; I looked for distractions, and it took me about ten attempts before I actually started working on the binding routines again.

That is the honest reason for the silence. Not lack of interest — lack of a way back in.


The years in between

In the meantime, I did what everybody does in that situation: I worked around it.

If you read Escape the Button-Click development Part I and Part II, you have seen exactly that. Move the code out of the form, create a handler unit, call it a controller or a view model if you like, wire up a PropertyChange with an integer and a case statement. Not fancy, but it does the job.

That was never the plan. That was the workaround I used because the real thing was sitting in a repository I did not want to open. For four years I barely touched it.

And then July 4th

On July 4th — and my American friends may enjoy the timing — I decided that my code finally deserved independence from the Form.

If you have read my AI series, you know what happened next. This is the same story as in From Copy & Paste to AI Agents, but this time applied to the one project I had given up on.

And to be clear about the honest part: what happened in those few days would have cost me months. Not because it is difficult — because I would never have found the time. That is what the gap between 2021 and today actually measures. Not a hard problem. A schedule.

So what is in there now?

Binding by name. You put a TEdit called Name on your form and a field called fName in your ViewModel, and they are connected. A button called Save finds the method that saves. Prefixes like canshow, canedit, and a hint suffix control visibility, enabled state, and hint text. There is no assignment code in the view, and there is nothing to configure. The form is plain, clicked together the way you always did it.

The ComboBox problem is solved. That was the concrete thing that blocked me in 2021: one control that needs to bind to three different ViewModel fields — text, index, and the item list. The solution is that a binding rule may ask the real component at runtime what it actually is. A ComboBox with style csDropDown binds all three. Set it to csDropDownList, and the text binding simply disappears, because there is no free text anymore. Nobody configures that. The rule looks and decides.

The visual property. In 2021 I announced a type called TVisualProperty<T>, so that you could write fName.Enabled := false in the ViewModel instead of inventing another boolean field for it. What came out of it is better than what I announced. The visual part does not live in a special generic type — it lives in the base class of every property. So any bound property can control visibility, enabled state, hint, and a combined state, and it reads the live state back from the control.

There is still a type called TVisualProperty, but it is now the special case, not the rule: it is for controls where the value does not interest you at all. A panel, a label, an image. You can show it, hide it, and gray it out from the ViewModel with a single field declaration.

One ViewModel, both frameworks. This is the one that surprises people most. A ViewModel is not similar for VCL and FMX. It is the same file. The demos prove it: the identical .pas is used in three projects — one VCL, one FMX, and one test project without any GUI at all.

Forms that assemble themselves. A view is rarely one form. In my applications, it is a person, an address, a list of phone numbers, and a list of bank accounts — four parts on one mask, in #D.MVVM you do not wire those together. You give the host view a field whose name starts with Frame_, and the sub-view registered under that name appears there. No configuration, no code in the view, nothing to drop onto a form. The framework creates the sub-view, creates its ViewModel, and subscribes the child to the parent so changes flow through — the multi-binding I described here back in 2020.

The part I like most is what happens when you swap one area out. A sub-view that leaves its slot is not destroyed; it is parked. It keeps its state, and when it comes back, it is exactly as you left it. And you do not even have to ask for the swap: add an enumeration to the ViewModel, assign a value, and the slot switches to the corresponding view. Because it is an enumeration and not a string, the compiler checks it for you.

Honest note on this one: it works, but it is the one area where I have neither a demo nor proper tests yet. It was verified with a throwaway test program, not with something I can hand you. That is on the list.

Some numbers, since I know you will ask. 78 VCL classes and 71 FMX classes have a binding rule — those are the components that ship with Delphi. Database components are deliberately excluded; the framework ignores them completely. Third-party components can be added in three ways, and a derived class automatically inherits the rule of its ancestor.

There are five services: navigation, action, menu, dialog, and — the newest one — time. That last one gives you a settable, pausable, scalable application time instead of Now, which finally makes date-dependent code testable.

The test suite runs numerous unit tests. On top of that, some self-tests run inside a live application and check the bindings against real controls. All green.

And no, I am not going to claim a coverage percentage. I have not measured it, so I will not print a number.

What the agent could not do

This is the part I find more interesting than the numbers, and if you are thinking about letting an agent loose on your own code, this is the part to read.

An agent diagnosed a double free in the code that closes a view and fixed it. It was not a double free. I noticed because a demo dialog stayed open; the change had to be reverted — and if it had stayed in, it would have introduced a real leak. The fix was confident, well explained, and wrong.

The pattern is always the same. Agents are excellent at working through things and at measuring. They are weak at judging their own diagnosis. Every single time it went wrong, the correction came from the running application — not from the test suite, and certainly not from the agent's own confidence.

So no, this is not a story about AI writing a framework while I was on holiday. It is a story about a very fast junior developer who never gets tired, never gets bored of the boring parts, and needs somebody looking over his shoulder who knows what the application is supposed to do.

What is still missing

Because I promised myself I would not repeat 2019 and announce something that is not there.

Still open: MDI and tab handling, focus control from the ViewModel (that one does not exist at all yet), veto events — the kind where the ViewModel has to say no, do not close — and the presentation layer, which is done for VCL but not for FMX. There is also a list of 22 more control events waiting to be bound.

For the record on two more questions I get regularly: this is built and tested with Delphi 13. Delphi 2007 is out — the code uses generics, inline variables, and modern RTTI. And the ORM connection works through my FDK. Without the FDK you get the framework; you do not get the ORM.

And now?

Here is what "nearly productive" means, in plain words: the framework is now good enough that I can start migrating my own projects onto it. That is the next step, and I fully expect that process to shake out a few more rough edges. It always does.

Once my own applications prove it, it goes on sale — hopefully still this year. It will definitely be a pre-release version. Whether I call it alpha or beta, I have not decided yet. There will be an early bird, and there will be a video.

Ten years after MVVM was the start, and four and a half years after I last dared to write about it.

Stay tuned — and this time I mean it.

PS: The paragraph titled “What is still missing” kind of annoyed me, so I didn't want to publish the blog post. That's why it's only going online almost a month later—because in the meantime, I've finished the FMX Docking, MDI, and Ribbon controls. So that part is done, too.



All the MVVM posts, in reverse order:

2021
The trap of wanting to make it perfect, or #D.MVVM what takes so long?
My road to a useable MVVM Pattern implementation for Delphi!
Outside the MVVM Pattern?

2020
Workflow and multi-binding with #D.MVVM
#D.MVVM — At what point is a framework ready for release?
Live Youtube, Chat, FDK & MVVM…
MVVM is just a concept.

2019
How long does it take to develop a "complete" MVVM framework for Delphi?
MVVM for legacy Apps?
MVVM PropertyChanged is not Component related!
MVVM and mobil app development.
MVVM Survey results and feedback!
Is there a sharp border between MVVM and MVC/MVP?
Delphi and MVVM survey

2018
Pattern, naming and MVVM from a Delphi point of view.
MVVM 2.0 — I did it my way.

2016
MVVM — Oder was ich dafür halte…

2015
MVVM war der Start.

And of course the #D.MVVM videos are still on my YouTube channel — please subscribe, it helps.

Please leave a comment.


Note on the Use of AI: This article was created with the "assistance" of generative AI. The content, technical statements, and conclusions have been reviewed and revised by the author. The author bears full responsibility for the publication.


No comments:

Post a Comment