In the previous installment of this series on path layout, I explained how to add a rotation offset to an element in the list box while keeping well-fitting highlights. I also promised that I’d explain in greater depth what is going on.
As a side benefit, you will also learn how to change these pesky blue selection highlights of a list box.
The next post in this series will be back to focusing on path layout itself.
Let’s begin with explaining what a ListBox actually does with items coming from a data source:
- For every item of data, a data template is instantiated. The data template (also called “generated item template”) is the stencil that defines how the data item is rendered. If your data item is just a string of text, as in our example above, the data template might be as simple as a single text block. For more complicated data, the template could for example resemble a form or any desired data visualization. Your imagination is the limit.
- The instantiated data template with the right data is then wrapped in a ListBoxItem. The ListBoxItem basically adds selection and hover highlights and things like this to the data template – it is kind of a decorator for the data, depending on list box selection state.
- The list box items are then arranged in the list box, based on the ItemsPanel that the list box uses for laying out its children. By default, a list box contains a stack panel for this purpose, which just arranges things in a stack. You can use any other layout panel available in SL or from a third party instead. In fact, PathListBox, uses a PathLayout panel.
- FInally, the chrome of the list box is rendered, as determined by the style applied to the list box itself. This chrome determines how the scroll bars are rendered etc.
It might be worth to try out the following analogy:
- Think of a list box as a huge shelf in an automated warehouse. The template of the list determines how the shelf itself is painted.
- The layout panel for the list box is the policy of the warehouse that determines how items are placed into the shelves: From right to left, from top to bottom, in a circle etc.
- In the warehouse, every item must be placed on a palette before it can be placed on the shelf – otherwise the robots can’t handle the item. The ListBoxItem is the equivalent of the palette.
- On the palette, our payload is placed. How this payload looks like is determined by the data template and the data we want to place on the palette itself.
Now that I have described the different templates that let you customize different parts of a list box, I can come back to the question of what the best place is to add an additional transformation to an item in a list box, and why.
There are two obvious places where we can place transforms: One is in the data template. Basically, we can move around the payload on the palette. That works fine, as long as we keep the payload within the bounds of the palette. If we don’t, in the warehouse reality our item will probably get damaged in the process.
In the list box reality, the issue is quite similar: Because the selection highlight of a list box is defined by the ListBoxItem (the palette in the analogy above), the highlight won’t fit if we transform the payload beyond the bounds of the palette.
On the other hand, if we transform the palette (the ListBoxItem), the highlight and the content get transformed together – of course, if you move the palette everything on it will move as well.
The following picture illustrates this in three normal list boxes:
The first column is a normal list box with the default SketchFlow templates for list boxes, without any transformation applied.
In the second column, a rotation is applied to the data template (the payload of the palette) but now that payload hangs over the edges of the palette.
In the third example, the rotation is applied to the ListBoxItem, the palette. This causes both content and highlight to rotate together, which is what we want.
In a PathListBox, the issue is exactly the same. The following picture is the selection highlight of the PathListBox with the items aligned to the path normal. As you can see, the highlight fits the item:
If we apply a rotation to the data template (the content of the palette, again the content will hang over the edge of the palette:
As you can see, the content of each item of the list box gets rotated, but not the selection highlight. That, of course, is probably not what we want here.
The last picture shows the rotation applied to the ListBoxItem, properly rotating content and container.
As you can also see in the picture above, I have changed the color of the selection highlight. As I promised you a side benefit from reading this explanation, I’ll explain in a moment how to do this. Just one more hurdle to take.
Transforms in PathListBox
Normal ListBoxes use ListBoxItems as their palettes for the content. PathListBox uses a different type of palette: Our palettes are of type PathListBoxItem. There are two reasons for that: One is that PathListBoxItems let us expose some additional advanced features for laying out stuff along a path that I will explain in a future post. The second reason is that PathListBoxItems actually handle the alignment of elements to the normal of the path. Or in other words, the PathListBoxItem knows where on a path it is positioned, and what the angle of the normal is at that point. It then uses this angle to rotate itself for alignment. The rotation for this is applied to the top-level grid in the ItemsContainerStyle.
If you flip back in this post, you read this:
“You might be tempted to edit the transform on the top level grid. But don’t do that – if you touch the RenderTransform of that Grid, you’ll remove the automatic orientation along the normal that PathListBox added. We therefore need an extra level of transform.”
The reason why you should not apply a transform to the top-level grid of the PathListBoxItem template manually is that this template would override the automatic alignment. Let’s look again how this template looks like by default:
As we can’t touch the transform of this Grid, we need to add another one, so that we gain another place to add the rotation offset. The easiest way to do this is to select all the content of the grid, and use the “Group Into” functionality to wrap these items into an additional container, as described above. This container can then be moved or rotated as desired.
Changing Selection Highlights
Finally, the promised extra benefit: How to edit the selection color in a list box?
As explained above, the key piece of knowledge is that the visuals that make up the highlights are defined in the ListBoxItem template. If you look at the screen shots above, there are three rectangles, in drawing order labeled fillColor, fillColor2 and FocusVisualElement. Basically, you need to edit these elements to change the highlight color.
To edit these effectively, some knowledge of editing visual states is useful. ListBoxItem defines a nice set of states:
Not all of these are relevant to this discussion, but MouseOver, Selected and Focused are. Each of these lives in its own state group. The state groups affect the elements as follows:
- CommonStates affects the rectangle named fillColor. It is set to transparent in Normal and Disabled state, and to slightly opaque (35%) in MouseOver, providing a light blue tint.
- FocusStates affects the rectangle named FocusVisualElement. It is set to invisible for Unfocused, and to Visible when focused.
- SelectionStates affects the rectangle named fillColor2. The rectangle is transparent except in Selected state, when it is 75% opaque.
You can edit the look of the visuals by selecting the desired state (e.g. MouseOver) and then selecting the associated visual (usually, this visual will have a small red dot in its icon in the object tree, to indicate that a value is set for this state).
For example, the little red dot in the fillColor icon when the MouseOver state is selected indicates that the fillColor is set in this state – so this is the object you want to modify for MouseOver.
Now, states in different state groups are not mutually exclusive. For example, an item can be Selected, Focused and have the mouse over it. As only one state can be selected for editing in the state pane at any given time, how can you properly see what you are doing when you need to get a combination state like Selected-MouseOver right?
The answer is state pinning. State pinning lets you force a particular state to be active a design time, so that you can see the impact of its visuals, even while editing a different state. Let’s say you have MouseOver selected for editing. You now want to see the effect of Selected on MouseOver to make sure that MouseOver will be visible. To do so, click the little dot to the left of a state name. An eye icon will appear to indicate that the state is now forced active and its settings will affect the design time.
The following screenshot shows the Selected state pinned to “on” for reference, while MouseOver is being edited.
So much for today. After this sidebar, the next post will have more on path layout itself.