Overlays and DecorationsWhat's underneath the Overlays |
Overlays and Decorations¶
Overlays grew out of the desire to put a background image on a ListView. (See Technical Blog - Overlays for the details).
ObjectListView provides a normal set of operations to manage overlays:
public void AddOverlay(IOverlay overlay);
public bool HasOverlay(IOverlay overlay);
public void RemoveOverlay(IOverlay overlay);
The IOverlay interface is very simple:
public interface IOverlay {
void Draw(ObjectListView olv, Graphics g, Rectangle r);
}
Within the Draw() method, your implementation can draw whatever it likes.
In the Overlays.cs file, you can find several examples of overlays that can serve as your model for implementation. The two most common overlays are image overlays and text overlays.
- ImageOverlay draws an image over the ObjectListView
- TextOverlay is a highly configurable overlay that can draw bordered, colored and backgrounded text. The text can be rotated.
These two overlays are so common that ObjectListView comes with one of each predefined. These predefined overlays are exposed to the IDE, so that they can be configured directly from there. So, for the majority of cases, this is the only understanding of overlays that you will need.
Disabling overlays¶
Though they seem simple, overlays are actually quite tricky underneath. If they are causing problems (for example, if you are seeing GlassPanelForms turn up where you are not expecting them), you can completely disable them by setting UseOverlays to false.
Implementation¶
Overlays are implemented using the Layered Window API. This API allows a toplevel windows to be translucent and partially transparent. Internally, the ObjectListView creates a transparent window, positions it above the control, and then paints the overlays onto that window.
So, when displaying overlays, the ObjectListView creates a new top-level window, and puts that above the control. In this way, it looks like the overlays are drawn onto the list, but in reality, they are completely separate.
And the two shall become one¶
The tricky bits of the overlay implementation are making the overlay window acts as if it is actually part of the ObjectListView.
Obviously, when the ObjectListView moves, the overlay window must move too. If we could make the overlay window a child window of the ObjectListView, then Windows would make that happen for us. But layered windows have to be top level windows. So we have to manually move the overlay window when the ObjectListView moves.
Similarly, when the ObjectListView is resized, we have to manually resize the overlay window to match the new size. If we do this resizing in realtime as the user is resizing the window, it slows down the resizing considerably. Apparently, resizing a large Layered window is an expensive operation. So, we simply hide the overlays while the window is resized, and then show them again once the resizing is complete.
The most difficult bit of making the overlay behaves as if it were part of the ObjectListView is when the ObjectListView is hidden. Simply being hidden (i.e. Visible = false) isn’t a problem, but hidden by placing other controls over the top of the ObjectListView is a major problem.
Remember the overlay window is a separate top level window. Changing the ordering of child windows within another top level window doesn’t affect the overlay window - it thinks it is still directly over the ObjectListView. Which it is, in a way, it’s just that other controls are now over the top of the ObjectListView. The overlay window will still be visible, but the ObjectListView will not be, so the overlays will be drawn on top of whatever controls are over the ObjectListView.
If you think putting controls over the top of other control sounds like a very unlikely scenario, this is exactly how tab controls work. They play with the z-ordering of their TabPanels, moving the current TabPanel in front of the other tabs. As currently implemented, ObjectListView understand the standard Window Forms TabControl, and will correctly hide their overlays when a parent TabControl switches tabs.
But if you are using a non-standard TabControl-like control, you will have to do this hiding of overlay windows yourself. It is enough to call HideOverlays() at the right time. ObjectListView will show their overlays when they are needed.
So, if you find in your app, that a parent control is hiding your ObjectListView (as it should), but you can still see the overlays (when you shouldn’t), you have to call HideOverlays() when the ObjectListView is hidden.