Joe White's Blog Life, .NET, and cats

This would've been easy in Delphi... #.NET #Delphi #sharplayout

I'm writing a FlowLinkLabel WinForms control. It's a LinkLabel-like control that automatically wordwraps, and changes its height as needed when its width changes. There's a plain old non-hyperlinked FlowLabel too. Useful for headings and stuff, or explanatory text, or task panes, or anywhere you want flow layout. I wrote it because I'll be needing it for a project at work, and I wrote it on my own time and will release it open-source, because that way I can use it in my other spare-time projects too. (grin)

Of course, in Delphi this would all be easy. I wouldn't even need to write my own control. I'd just drop a TLabel, set AutoSize to True and WordWrap to True, and boom, it works. The WinForms Label has an AutoSize property (which defaults to False for some reason), but it's horizontal only; it doesn't grok word-wrap, so it's useless for anything resembling flow layout. There's nothing suitable for, say, putting a paragraph of explanatory text at the top of a window, and having it re-wrap and all stay visible as the window gets narrower. (There will be all sorts of flow layout in Longhorn, but that's what, at least two or three years away, and would require all our clients to upgrade to Longhorn when it comes out... so for us, probably at least five or six years away.)

Anyway. So I was hoping to be able to have all the FlowLinkLabels on a form automatically share color settings (link color, hover color, active color, visited color), underline settings, etc. Stylesheet kind of stuff, only for WinForms. That's why I was looking for the article I mentioned yesterday -- I knew it touched on that sort of idea. Creating a service in one part of your app and getting at it from somewhere else.

My general thought was this: Have a non-visual Component (maybe added to the form manually by the application developer, or maybe created automatically the first time you drop a FlowLinkLabel onto a form) that stores the style settings. All the FlowLinkLabels in the form share this same stylesheet component by default (with the ability to override per label if you want some to have different style settings). Everything happens full automatic: you create a new form, you drop a bunch of FlowLinkLabels, and boom, you automatically -- with no more effort than that -- have a centralized place to set the colors for all of them at once. If I was sneaky enough, I thought, it might even be possible to share these stylesheet settings across multiple forms.

Alas, it looks like it's not to be. For starters, given a Component, you can find its Container, but there's no way to get from that Container up to the next Component up the chain (e.g., given an ImageList instance, there's no way to get a reference to its containing UserControl), which would make it hard for the FlowLinkLabels to automatically discover the shared container that lives up in the form/usercontrol, and hard for the stylesheet to automatically notify the labels when its properties get changed. Second, even though Control descends from Component, it's not treated like a Component: the designer adds Components to a Container, but Controls are added to a ControlCollection, and the Container and ControlCollection have no way of referencing each other. So again, no addressability between the controls and the components. Third, the ideal way of storing these settings would be by using IServiceContainer, but this only works if someone creates an IServiceContainer for you -- which would require manual coding on the part of the application developer. Yuck.

Delphi makes this so much easier. TControl descends from TComponent, and it's treated the same way. Iterating through all the components (and controls) owned by a form (or frame) is trivial; you just loop through the Components[] array property. WinForms seems bound and determined to make the same thing hard.

So it looks like there's no good way to make these stylesheets work without forcing the application developer to drop their own stylesheet component onto the form and then manually hook each FlowLinkLabel to it. Which is error-prone (if you add a new label, you have to remember to hook it up), which is exactly what I was hoping to avoid. Feh.