Greetings from the Cloud! August 17th, I have completed the initial migration of this blog to my new host, RackspaceCloud. Thanks to everyone who provided feedback and recommendations for web host providers. I chose the latter. As expected, the port to the new host was not entirely without issue. As such, I had to upgrade to the latest DNN. I tried upgrading the blog module, but the newer version throws even more exceptions than the old.
For now, the comments feature remains disabled. This pretty much solidifies my plan to migrate to different blog software. As of December , this site is now up and running on wordpress and comments are once again enabled. Thanks for bearing with me during this migration! I hope to get back to the interesting stuff a. The Great Migration Begins! If all goes as planned, I will move the site in its entirety to my new host and then resume its normal functioning. Keep your fingers crossed!
I am also planning to simplify my blog UI a bit in the near future, as I am finding DotNetNuke a little too cumbersome to maintain. As such, I am evaluating several alternative blogging applications including the newest version of DotNetNuke, just to be fair. Unless I find a compelling. Microsoft Surface was first publicly unveiled a couple of years ago. Since then, I have periodically been asked how one would go about creating a panel that behaves like the application launcher in Surface.
If you have not seen the app launcher, I would describe the panel as simply a horizontal StackPanel that infinitely cycles its children. I had some extra time this weekend, so I refactored one of my early looping panels into something that might be useful for a wider audience.
This post also includes a simple demo showing how the LoopPanel can be used as the items host of a custom ListBox class. The LoopingListBox class supports dragging its items in a manner similar to the Surface app launcher.
This demo brings the same type of natural user interaction to desktop WPF via mouse that you might find on Surface. If you are actually developing for Surface, be sure to check out the Sidenote for Surface Developers section below for information on why you might prefer my panel over the sample supplied in the Surface documentation. Yes, I could have published an XBAP sample, but I was not willing to give up the glow effect used for the selected character… which is, incidentally, achieved using a DropShadowEffect in the new effects pipeline.
You can also download the entire project with full source code to build the demo yourself. Even in the new pipeline, DropShadowEffect is a rather expensive effect because it requires multiple passes.
The LoopPanel sample is very simple and is therefore not impacted much by the use of DropShadowEffect. If you experience poor performance using DropShadowEffect in a real app scenario, you can usually accomplish a very similar effect by applying a BlurEffect to a second copy of an element residing directly behind the original element.
This allows me to identify the exact properties that will be necessary to support the new layout. In the LoopPanel class, I wanted to support both a horizontal and vertical orientation, just as StackPanel does. This just means a child can be whatever size it desires in the stacking direction, but it is constrained in the nonstacking direction.
To support these layout requirements, it was clear that I would need an Orientation property. Of course, there are some key differences between a LoopPanel and a StackPanel.
For example, a StackPanel arranges its children sequentially, beginning with the first child and stacking subsequent children one right after the other. So I needed a way to define the element that would be considered pivotal during a given layout pass. The pivotal child is the element around which the remaining children are arranged. The pivotal child is placed first. Then its neighboring siblings the subsequent and preceding members of the children collection are placed, working away from the pivotal child in both directions until all remaining visible children are arranged.
This allows for a balanced layout around the pivotal child. This zero-sized rect provides a perf optimization less layout processing and generated render data , especially for elements with elaborate subtrees, which you might have in an ItemsControl with a complex item template. Obviously, any child could be the pivotal child. This meant I would need a property that could be used to identify the index of the pivotal child. I decided to call this the Offset property.
The name might not make a lot of sense at first, but it should shortly. What might make even less sense is that the Offset property is of type double. So why would we need a double value to act as an index into a collection? Actually, only the whole portion of the Offset value is considered when determining the index of the pivotal child.
The fractional portion is then used to further offset that pivotal child. For example, given an Offset value of 6. Then the child at index 7 becomes the pivotal child. What happens when you animate the Offset from 6 to 7, but there are only 5 children?
Well recall that the LoopPanel needs to cycle its children infinitely. To enable this infinite looping of children, the Offset property is completely unbounded. Any double value is valid. So to truly determine the index of the pivotal child, the Offset value is taken modulo the current count of children.
So animating the Offset from 6 to 7 when you only have 5 children is equivalent to animating the Offset from 1 to 2. More specifically, where is the pivotal location for the placement of the pivotal child. We could simply start at the beginning of the available space similar to a StackPanel , but that is rather limiting, especially considering that the motivation for this panel is the app launcher in Surface, which puts the central focus at the center of the panel.
To this end, I decided to allow the pivotal location to be specified using a RelativeOffset property on the LoopPanel. RelativeOffset is a double value ranging from 0 to 1. The default value is 0. Orientation, Offset, and RelativeOffset. To see how these properties are defined and actually used to implement the described layout algorithm, I refer you to the LoopPanel code within the sample project.
As with every panel, the key methods that implement the layout are MeasureOverride and ArrangeOverride. Not a Typical ScrollViewer Paradigm Once the LoopPanel has been implemented, the next logical question becomes how will we support scrolling its children?
Clearly, we have an Offset property that can be used to this effect. The first inclination might be to leverage a ScrollViewer and use its horizontal or vertical offset to update the Offset property on the LoopPanel. There are a couple of problems with this approach. First, a ScrollViewer measures its child to infinity in each of the scrollable directions vertical and horizontal. In a typical ItemsControl scenario, this means that the items host gets to be whatever size it desires.
Instead, it simply wants to layout its children using the space available. In the LoopPanel, however, we want to support an infinite offset range in the orientation direction. As such, we must come up with a new method of looping through scrolling the children in our panel. The most logical choice is to allow a simple dragging gesture to update the Offset property.
It demonstrates how you can build a panel similar to my LoopPanel that will work in conjunction with the SurfaceScrollViewer control. If you are developing exclusively for Surface, you might want to look at that sample. For the record, I am not a fan of the layout logic used within that sample. The purpose of a panel is to provide layout for its children by arranging them within the panel. The sample panel provided in the Surface SDK actually applies a RenderTransform to itself to achieve a big piece of its layout.
I strongly discourage developers from implementing controls that set their own RenderTransform or LayoutTransform properties. These properties do not belong to the control author. Rather, they belong to the person who is using the control. I am also very partial to my own layout algorithm go figure! P , as it supports more options for aligning the children left, right, center, or anywhere in between , it supports both vertical and horizontal looping of children, and it contains render optimizations by arranging nonvisible children using a zero-sized rect.
The approach described in this article will actually work well on both desktop PCs and on Surface. Well, the real reason is that I originally created the panel prior to the introduction of the ISurfaceScrollInfo interface. However, it probably will support these interfaces in the future see Potential Future Enhancements below.
The template below demonstrates how to achieve this: This achieves our aim. Ensuring the Selection is Visible Of course, there is another issue to consider. When an element within a ListBox becomes focused, the framework calls the BringIntoView method on the element. This, in turn, raises a RequestBringIntoView event from the target element. MakeVisible method on the items host. If the items host implements IScrollInfo, it can then take the necessary action to scroll the element into view.
As such, selecting an item that is not within the current view of the LoopPanel does not scroll it into view. This covers the typical ListBox usage scenario.