p&p for smart devices: zero code adaptive UIs
Note: this entry has moved.
As part of the Mobile Client Software Factory I've been working on for the past few months, we introduced the Orientation Aware Control (OAC). I blogged about it and showed it working. In this post I'll delve a little more on the process you go when building UIs for devices that must adapt to different resolutions and orientations.I've already hinted that the solution we implemented is based on the Localization feature in .NET. So let's see how you go about using it.
First step is to add a new UserControl to your project, and changing the base class to inherit from the OrientationAwareControl class provided with the factory. After you do that, you will see something like the following:
New and modified properties:
- Localizable: as you can see from the property description from the above screenshot, this property is fixed to the True value. This is the proof that OAC uses the localization feature, and therefore this property cannot be changed.
- Orientation: this property can have the values Vertical | Horizontal, and corresponds to the Rotate command (both in context menu as command area in the properties window). It basically allows you to design different UI for the portrait and landscape orientations.
- Resolution: this property allows you to select one of the available resolutions. These resolutions can be the standard ones (VGA and QVGA standard or square), and will show up in a dropdown. More on how this list is populated and how to add custom resolutions later.
- Rotate: rotates the current layout, so that you can start designing the rotated layout. By default, the first time the control is rotated, controls bounds will be checked to see if they fit in the rotated control (height and width are switched). If they don't, they will be moved up and left until all controls fall within bounds of the container user control.
- Switch to Default Layout: by design, in VS2005 you can only add new components/controls through the designer if the current control/form is in the default culture. Because the OAC extends the localization infrastructure, the equivalent of the default culture is the default layout, meaning default culture + default resolution and vertical orientation. More on how to set which is the default resolution later on.
- The other three commands help in designing localized versions of the different resolutions. Basically, whenever you start with a new locale (say Spanish), the default layout is applied to the new resolution/orientation/culture, which is probably not optimum depending on which locale you are designing. For example, say you have QVGA-Spanish already designed, and now you want to design VGA-Spanish. The default culture layout will be applied and auto-scaled to give you a starting point. Well, in the localization case, you probably want to have all the labels and other text properties applied too, so that you don't have to translate everything again. Copy Text Properties is there for that purpose. You just switch back to the QVG-Spanish layout, click Copy Text Properties, and back in the VGA-Spanish, you click Paste Properties. All text properties will be set in all controls, saving you quite a bit of time. The same goes for the layout properties. If you already designed the layout for one, say, VGA-Horizontal resolution, and now you're localizing it, in every locale instead of re-locating all controls, you can simply Paste Properties after you run the Copy Layout Properties from the "source" layout (i.e. VGA-Horizontal, Default culture). Layout properties are basically location and size.
As you see, there's NO code whatesoever that you need to write to detect the current screen size and apply the appropriate resource. All is done automatically by the base Orientation Aware Control. In addition, and because we're reusing the localization feature in .NET, layouts are "inherited" across locales. Also, when no specific layout is found for the current resolution (i.e. a new device just came out), the default layout will automatically be applied.
How do resolutions work
I said above that the available resolutions are rendered as a dropdown you can pick from in the properties window. Here's how it looks:
Notice the description at the bottom of the window. This description is dynamic, and reflects the form factors that support the given resolution. So where is this information pulled from?
This is a pretty advanced control, which was very fun to develop. Turns out that there's an IPlatformInformation service exposed to device control designers, that allows you to list and retrieve form factors defined in Tools | Options | Device Tools | Form Factors options page in Visual Studio. This is how the page looks after you install the Windows Mobile 5.0 SDK:
At first, my control simply reused the FormFactor you already have in custom user controls:
That turned out to be a bad idea, as multiple form factors can have the same physical screen resolution, such as Phone VGA and VGA (both are 480x640). Hence, the OAC designer is hiding this property and providing an alternative one, the Resolution property. Basically it's populating the unique set of resolutions, and building the description dynamically to show which form factors defined in the VS options support the current value.
The OAC is fully integrated with the VS options dialog shown above. You can easily use the Save As button above to create a new form factor based on an existing one. After renaming it, you can click the Properties button and change its resolution. Once you're done, the control will automatically pick the new resolution (you may need to close and reopen the designer):
Note that in the VS options dialog you can also specify which is the default form factor, so that if your default target device is going to be, say, VGA, you don't need to change the resolution in every new control you create.
Stay tunned as I continue to explore the journey of building such a cool control, if I'm allowed to say so ;-). I have little doubt that this is the way to go for the .NET CF v3. We'll see...