More Simple Blend Layout Techniques and Quirks
Oct
23
Written by:
10/23/2010 8:58 AM
I have a habit of starting to explore some aspect of Blend and wandering off topic when I’m blogging. Usually it’s some detail that’s new to me and only comes up because I am blogging and feel the need to explain each step. So, rather than clutter things up, I’m going to put them in blogs of their own. Nothing earth-shattering and probably not of much use to experienced Blend users. But if I’ve overlooked details in the past, maybe what I learn as I explore them now will be helpful to newbies.
MainPage and the Default Hosting Web Site
Quite likely, when you create a new Silverlight application you create a web site to host it at the same time, this is the dialog you see in Visual Studio:
I’ve never given much thought of the page that hosts MainPage.xaml or its equivalent, or the default dimensions and alignment settings for UserControl, which is the outermost container of the MainPage UI; if you’re talking about the size of MainPage or the hosted Silverlight control, you’re talking about the size of UserControl. Actually, as you’ll see below,the MainPage object is a UserControl.
MainPage.xaml and ….
<UserControl x:Class="BlendQuirks.MainPage"
… MainPage.xaml.cs combine to define the MainPage class.
namespace BlendQuirks
{
public partial class MainPage : UserControl
{
public MainPage()
{
InitializeComponent();
}
}
}
Incidentally, if we adhere to the Model-ViewModel or MVVM patterns, which we always should if we’re serious about Silverlight, this is the only code we should see in MainPage.xaml.cs.
In Blend design mode we make changes to UserControl and contents to define the UI; as we make changes in design Blend makes changes to MainPage.xaml. We could edit the xaml directly, but we almost always don’t want to – in design mode we can see changes to the UI as we make them, and design mode is not subject to typos – including entries into the custom Expression dialog – errors typed there will be caught that would fail silently at runtime if you typed them directly into xaml.
Before I go any further, let me point out two “special” things about UserControl – which I refer to as if that’s its name, which it’s not, but shorter to type than “UserControl object”.
- It has a Background Brush property, but as far as I can tell the property is always ignored, both in Blend and at run-time. The answers I found on setting it begged the question by advising to set the Background Brush of the UIElement filling UserControl (LayoutRoot).
- When you have UserControl selected in Blend Objects and Timelines the color will be the color of the Background Brush property in LayoutRoot.
- If you click on the Hide icon next to LayoutRoot you’ll see that UserControl is black (actually it’s transparent).
Back to ‘sup with the default settings: If you don’t change anything and run the app you’ll see a blank white page filling the browser.
That’s because LayoutRoot gets a white Background Brush by default, and when you create a project with a hosting web site as we did above, the default settings are that everything expands to fill its container, from the HTML form on the hosting page right on down to LayoutRoot at the bottom. The hosting page is shown below:
<body>
<form id="form1" runat="server" style="height:100%">
<div id="silverlightControlHost">
<object data="data:application/x-silverlight-2," type="application/x-silverlight-2" width="100%" height="100%">
<param name="source" value="ClientBin/BlendQuirks2.xap"/>
<param name="onError" value="onSilverlightError" />
<param name="background" value="white" />
<param name="minRuntimeVersion" value="4.0.50826.0" />
<param name="autoUpgrade" value="true" />
<a href="http://go.microsoft.com/fwlink/?LinkID=149156&v=4.0.50826.0" style="text-decoration:none">
<img src="http://go.microsoft.com/fwlink/?LinkId=161376" alt="Get Microsoft Silverlight" style="border-style:none"/>
</a>
</object><iframe id="_sl_historyFrame" style="visibility:hidden;height:0px;width:0px;border:0px"></iframe></div>
</form>
</body>
I’m going to set the background color of the hosting object from white to orange and put some margins around LayoutRoot so you can see for yourself that:
- UserControl is transparent.
- The hosting object expands to fill the div which expands to fill the form which expands to fill the page.
- LayoutRoot expands to fill UserControl, allowing for the margins.
You can also add margins to UserControl, although you probably wouldn’t. Actually you probably wouldn’t add margins to LayoutRoot either. It makes more sense for the hosting container to determine how much of itself, if any, should show around the edges.
One more point about the dimensions. Even though we see that BlendQuirks2 / MainPage / UserControl is expanding to fill its container, in Blend we see the dimensions of UserControl are set to Auto(400) x Auto(300).
If you look at the xaml, you’ll see those dimensions apply to design mode. This can be confusing, because the properties would look exactly the same if LayoutRoot was set to 400 x 300 and UserControl was auto-sizing to accommodate.
<UserControl x:Class="BlendQuirks2.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="400" Background="#FF9B3939">
<Grid x:Name="LayoutRoot" Background="White" d:IsHidden="True" Opacity="0"/>
</UserControl>
If you removed these design mode dimension settings you’d find that UserControl collapses to nothing when viewed in Blend, but would still fill the screen at run-time because the alignment is still set to stretch.
When the dimensions are set to Auto, if you set the alignment to any setting other than stretch, UserControl will collapse to nothing at runtime as well, sizing itself to accommodate its content, LayoutRoot. Since LayoutRoot is empty UserControl is sized to 0 x 0.
Back to filling the screen; if you position an element in the lower-right:
You get something like this:
So that’s the default. Everything expands to fill its container.
If you want to control the run-time size of MainPage so it’s same size as in Blend you could do it in the host page:
<div id="silverlightControlHost" style="height:300px;width:400px;">
But it’s more likely you’ll want to hard-code the dimensions of UserControl:
Notice that even though the alignment of UserControl is set to stretch it no longer expands to fill its container. Nor does it expand to accommodate its contents. I set LayoutRoot width to 450 resulting in:
You’ll also find that non of the other UserControl alignment settings have any affect, at least as far as I can tell.
One last point about dimensions and alignment. Don’t overlook that you can bind them to data in the ViewModel, or to other properties of other controls on the page; the latter probably would be more useful in manipulating the contents of UserControl than UserControl itself, but both open up all kinds of interesting possibilities.
In the course of some project, I was trying to create a simple page with a heading and a ListBox. I wanted the heading text and ListBox to stay aligned within their row and column if the grid is re-sized, let’s look in and see how I’m doing:
With UserControl sized to 300 x 200 and LayoutRoot dimension set to Auto and alignment set to stretch, give LayoutRoot a background color and hover and click the mouse as shown below to create a new row. Don’t worry about the size being precise. We’re going to lay this out so you can resize after the fact and the contents will re-position or re-size as necessary. This is one of the few occasions when I’m ok with clicking on the design surface.
With LayoutRoot selected, browse Assets/Controls for the Label control and drag it onto LayoutRoot.
Resulting in:
Since this is a heading, I’m just going to center it horizontally and vertically. I’ll also set the Content to Themes and adjust the font size:
Make sure LayoutRoot is selected again and drag a ListBox control onto it from Assets. It’s right next to the Label you just dragged. It’s going to show up as a 100 x 100 white box at the upper left corner of LayoutRoot. We’re going to change the row to 1, set the dimensions to Auto, and the alignment to stretch. We’re also going to add margins all around…
…Ok, this is kind of funny. By default, meaning this is the setting Blend gave it when I dropped the ListBox on LayoutRoot, the row is already set to 1, but the margin is set to position the ListBox in the upper left corner of the entire grid:
You gotta think that some routine in Blend wants it in row 1 and another wants it to be in the upper-left corner and this is the compromise. This is why you want to pay attention to position and size settings when you first add controls in Blend instead of just yanking and banking.
Remember I said we were going to lay the UserControl out so the contents adjust to re-sizing? Turns out that only works if you can restrain yourself from re-sizing by dragging grab bars. In the screen shot below, I’m trying to position ListBox so it re-sizes itself to fill its row and column while maintaining a 10 pixel margin all around.
Turns out if I increase the width of UserControl by dragging the right side that ListBox doesn’t resize itself, and if I go back to ListBox properties I find that the margin has changed to accommodate the change in UserControl instead of ListBox resizing to fill the additional space:
However, if I re-size UserControl by entering the value or, as I prefer, hovering the mouse over the width and then dragging it so you can see the width change dynamically, then the margins remain unchanged and the ListBox resizes as I intended it to. Yet another reason to avoid using the mouse to drag or resize elements in the design area.
Summary
Blend is great, but you have to keep your head in the game when you’re working with it. I’d say the most important thing is to use the Properties panel to make adjustment to the UI, not the design surface, at least if you’re still in my league – if I could play the piano, I would, but I can’t, so I don’t. Doesn’t mean you shouldn’t learn.