SharpLayout 0.8.5 released

Okay, I think I’ll declare SharpLayout 0.8.5 to be semi-officially released. It doesn’t have a Web site yet (unless you count this blog), but you can download it here. This ZIP contains just the controls themselves, not the demo projects; I’ll probably get around to releasing those later. Installation: Compile the project, right-click in the toolbox, select “Add/Remove Items”, browse to the DLL, and there you go.

So what the heck is SharpLayout? It’s two (so far) Windows Forms controls that do basic flow-layout types of things. FlowLabel is like a Label with word-wrapping, except that a FlowLabel automatically adjusts its height to exactly fit the text inside it (and readjusts when its width changes). Throw in adjustable margins all around, and this is a good way to put several paragraphs of text into a GUI (using the margins for inter-paragraph spacing), and/or interspersing them with hyperlinks, buttons, graphics, whatever. The general idea is to allow you to write something along the lines of Microsoft’s Inductive UI without embedding IE or waiting for Longhorn.

FlowLabel can also display an imagelist image next to the text (and unlike a normal Label, it actually makes space for the image, instead of overlapping it with the text). Other interesting properties: you can specify antialiased text; and you can turn borders on and off for each of the four edges and set the border color.

FlowLinkLabel descends from FlowLabel, and along with all of FlowLabel’s capabilities, adds (not surprisingly) abilities more or less like a LinkLabel, though unlike LinkLabel, it supports hover effects.

Both FlowLabel and FlowLinkLabel are safe to use in a partial-trust environment, and I regularly test them in the Internet zone to make sure they work (though in the Internet zone, FlowLinkLabel loses the ability to cope with a few pathological edge cases).

There are a few features yet to be written, but for the most part, these controls are basically functional. The main things I still want to write are FlowLinkLabel features: (1) focus rectangles (and focus support in general — get focus when you click on the link, that sort of thing), and (2) support for popping up a context menu by using Alt+F10 or the “context menu” key (currently disabled in FlowLinkLabel in order to support another feature: the context menu only appears if you right-click on the hyperlink, not if you right-click on non-hyperlink text or on an empty part of the control).

A few other features are negotiable, since I don’t expect to need them myself: multiple hyperlinks per FlowLinkLabel; support for putting the hyperlink in the middle of a paragraph (you can currently put the link at the beginning of a paragraph, but you can’t have any non-linked text before the link); right-justified text and/or image on the right; and probably others as well.

Feedback is more than welcome, as are questions about the code. This is a blog, after all.

Security permissions in Windows Forms

Whenever I’m writing a custom WinForms control, unless I have some reason to do otherwise, I always try to write it so it’ll work in a partial-trust environment, like embedding the control inside a Web page (Internet zone), or running a program from a network drive (Intranet zone).

Windows.Forms does not tend to make this easy.

So I’m working on my FlowLinkLabel class, right? There’s some text (blue and underlined, of course (well, by default anyway)), there’s possibly an image. And depending on the length of the text string, there may be a bunch of empty space to the right of the text, or below the image. I don’t want the control to react to clicks in that empty area. Only clicks on blue underlined text (or on the image) should count. Okay.

That means I need to do my own click tracking; I need to override OnMouseDown and OnMouseUp. No problem. I also need to process WM_CANCELMODE. Problem!

If the user presses the left mouse button, and then (without letting up on the mouse button yet) Alt+Tabs away, or presses the Windows key to bring up the Start menu, or some ill-behaved application steals the focus because the user just got an instant message, then my OnMouseUp handler will never fire. But I still need to clear my “_isLeftButtonPressed” flag. Granted, if I can’t clear that flag, it will only cause real problems in certain pathological edge cases. But I don’t like leaving edge cases open, even pathological ones.

As I learned back in my Delphi days, Windows tells me about all these rude happenings by sending me a WM_CANCELMODE message. Lovely, says I. I’ll just override WndProc and — boom! Oops, there’s a security demand on WndProc. If I override WndProc in FlowLinkLabel, then nobody in the Internet zone can instantiate FlowLinkLabel. If they try, they get a SecurityException.

The interesting part is this: The SecurityException is not thrown by the call to new FlowLinkLabel(). Instead, it’s called when the Just-In-Time compiler tries to JIT the method containing the call to new FlowLinkLabel(). That is, it blows up when it’s partway through trying to JIT-compile InitializeComponent. Which means the exception’s stack trace is marvellously unhelpful; all I see is that an exception occurred in the Form1 constructor, on the line where it tries to call InitializeComponent. I never see anything that tells me which line within InitializeComponent is actually causing the problem, heavens no; so if this is the first time I’m trying to run my app under restricted permissions, I don’t have a clue which third-party component is being ill-behaved. Nice, eh?

Back to FlowLinkLabel. I can’t override WndProc if I want partial trust to work. I was getting started on an elaborate workaround, and then happened to be looking through Reflector and saw that Control.WndProc calls something named OnNotifyMessage. I fire up the help, and read that OnNotifyMessage is specifically meant for partial-trust scenarios! It can’t modify or suppress the message; all it can do is watch the message going by. Which is exactly what I need for WM_CANCELMODE.

Lovely, says I. I’ll just override OnNotifyMessage, and do the appropriate SetStyle() to tell WinForms it’s there. Run it under the Internet zone — it runs! Hooray! Write some more code. Run and — boom!

Undo checkout. Was it my override of OnMouseDown? Try that again, see what happens. No exception. Well, I know it wasn’t my override of OnNotifyMessage, I already tested that. Try again anyway, just to make sure. Hmm. Scratch my head. Finally go through all the motions again, testing much more often this time.

It’s when I add an if statement inside OnNotifyMessage. An if statement that does nothing but read the message struct’s Msg int property.

Open Reflector again. There’s a security demand on the Message structure. It requires unmanaged-code permissions. Which I very much do not have in the Internet zone.

So they give me a method I can override where I can see all these messages flying by. But I can’t see a blooming thing of what’s in those messages. That is frustration in action, folks. It also smells like poor design — OnNotifyMessage exists only for partial-trust scenarios; the docs even go so far as to say you don’t need to bother calling base.OnNotifyMessage(), which heavily implies that Microsoft will never, ever bother to put any of their own code into OnNotifyMessage. And aside from permissions, anything you can do with OnNotifyMessage can be done just as easily by overriding WndProc. OnNotifyMessage exists for one purpose, and yet, it’s useless for that purpose.

Sigh.

I worked around it. The first time FlowLinkLabel.OnNotifyMessage is called, I enter a try..except, where I call another method that tries to read message.Msg (has to be a separate method, since it’s the JIT throwing the exception — if I did it inline, my try..except would never finish getting JITted). If a SecurityException is thrown, I just don’t watch for WM_CANCELMODE anymore, and those pathological edge cases remain open. But any time it turns out that the code can read message.Msg, it will process WM_CANCELMODE quite happily. (Then, of course, I short-circuit my implementation on later calls, so I’m not throwing an exception every time a Windows message is received.) Decent compromise, I guess.

This would’ve been easy in Delphi…

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.

System.ComponentModel

So I’m finally entering the blogging arena. Mostly ’cause I spent most of this afternoon looking for an article I knew I’d read before, but couldn’t find, and that had fallen off my browser history. See, if I had a blog, I would’ve just linked to it when I first found it. (Now I just need to talk the rest of my development team into starting their own blogs too, so they can help sift through all the interesting RSS feeds.)

Anyway, here’s the article: System.ComponentModel. Everything you ever wanted to know about the System.ComponentModel namespace, Component, IComponent, ISite, IContainer, IServiceContainer, GetService, AmbientProperties, etc.

I want a way to share the color settings among all the FlowLinkLabels on a form, in a fairly automatic way, and I’m thinking this article might get me there. We’ll see.