I'm currently going through a process of bringing together code and controls I've been using in the last few applications and compiling my own "toolkit". The idea will be a very opinionated way of building WP7 applications rather than a generic control library. Some parts will be controls like the "empty list box" discussed in this post and other parts will be extensions to the Caliburn.Micro MVVM framework for how I like to work.
The first control is an extension of the ListBox control to display certain content when there are no items in the list. It's a simple extension but most of the time we forget to add this functionality especially when we're dealing with lots of lists in our application.
The first thing we'll do is create our class ListBox (not the best name, but no other one like EmptyListBox suites, thankfully we have namespaces for conflicting names) obviously inheriting from the system ListBox, we'll had two dependency properties, EmptyContent and EmptyContentTemplate. This will allow two different ways of defining the content to display, much like Button or ContentControl.
public static readonly DependencyProperty EmptyContentTemplateProperty =
DependencyProperty.Register("EmptyContentTemplate", typeof(DataTemplate), typeof(ListBox), null);
public static readonly DependencyProperty EmptyContentProperty =
DependencyProperty.Register("EmptyContent", typeof(object), typeof(ListBox), null);
public ListBox()
{
DefaultStyleKey = typeof(ListBox);
}
public object EmptyContent
{
get
{
return GetValue(EmptyContentProperty);
}
set
{
SetValue(EmptyContentProperty, value);
}
}
public DataTemplate EmptyContentTemplate
{
get
{
return (DataTemplate)GetValue(EmptyContentTemplateProperty);
}
set
{
SetValue(EmptyContentTemplateProperty, value);
}
}
Our default Style in Generic.xaml will be pretty simple.
<Style TargetType="controls:ListBox">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="Foreground" Value="{StaticResource PhoneForegroundBrush}"/>
<Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Disabled"/>
<Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Auto"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="BorderBrush" Value="Transparent"/>
<Setter Property="Padding" Value="0"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="controls:ListBox">
<Grid>
<ContentPresenter x:Name="EmptyContent" Content="{TemplateBinding EmptyContent}" ContentTemplate="{TemplateBinding EmptyContentTemplate}" RenderTransformOrigin="0.5,0.5" >
<ContentPresenter.RenderTransform>
<CompositeTransform/>
</ContentPresenter.RenderTransform>
</ContentPresenter>
<ScrollViewer x:Name="ScrollViewer" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" Foreground="{TemplateBinding Foreground}" Padding="{TemplateBinding Padding}">
<ItemsPresenter/>
</ScrollViewer>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
We'll then override the OnItemsChanged to add our new behaviour when items are added or removed.
There are a few different ways we can implement the required functionality each with their own pros and cons.
Using Control Template Parts
The first is using Template Parts, this allows controls to tell developers which sub-controls they expect in the control template. The can then interact with them using the FindTemplateChild method. The ListBox control already defines a ScrollViewer as a part, we would then add a ContentPresenter as a second part. In our overriden method we could then set the Visibility of each control appropriately. This approach is the simplest and the code is as follows.
private ContentControl emptyContent;
private ScrollViewer scrollViewer;
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
emptyContent = (ContentControl)GetTemplateChild("EmptyContent");
scrollViewer = (ScrollViewer)GetTemplateChild("ScrollViewer");
}
protected override void OnItemsChanged(NotifyCollectionChangedEventArgs e)
{
base.OnItemsChanged(e);
ApplyTemplate();
var hasItems = Items.Count > 0;
emptyContent.Visibility = hasItems ? Visibility.Collapsed : Visibility.Visible;
scrollViewer.Visibility = hasItems ? Visibility.Visible : Visibility.Collapsed;
}
Using Visual States
The second approach is to use the VisualStateManager to hide and show parts of our control template. I like this approach better as it gives better flexibility to developers extending the control. By defining two states "Empty" and "NonEmpty" we put it back in the developers hands about how each state should look, rather than arbitrarily defining the behavior ourselves. This approach makes the control code itself simpler than the first but we have more xaml in the Control Template due to having to define the new states.
protected override void OnItemsChanged(NotifyCollectionChangedEventArgs e)
{
base.OnItemsChanged(e);
ApplyTemplate();
VisualStateManager.GoToState(this, Items.Count > 0 ? "NonEmpty" : "Empty", true);
}
The xaml for the Visual States is pretty long so I won't post it here but it will be available in the download available soon.
Over all I prefer the second approach due it's freedom and the ability to add animations. The default template has a simple "continuum" animation bringing the content into view.
Download the source.
A few months ago I released download details for the first three months of sales of To Do Today. Just after that release I moved the price down from $2.99 to $0.99 as an experiment in pricing. Here's the results.
Download numbers weren't changed by a noticeable amount, averaging around 300 downloads a month. The big change was the shift in conversion rate, previously 20% of people who downloaded the trial ended up purchasing the application, with the price drop this number jumped to around 35%.
If you've done the maths in your head you'll realise that while the increase in numbers is great to see it's actually a change that means I'm earning less money. I figured this would be the case, however I was hoping that a lower selling price would increase downloads and sales and push To Do Today up the rankings in the Marketplace which would in turn trigger more downloads and sales in a magical spiral of upwardsness. Sadly this hasn't seemed to be occurring.
In the coming month I'll be updating To Do Today to 1.5 to add some customisation features that people have been requesting such as the home screen, and a feature that I've really had to use lately, the ability to postpone tasks. During this release I'll be shifting the price to around $1.99 to see how this impacts downloads and sales.
Also coming out this month will be another app I've been using to keep track of my spending and giving myself a daily allowance. More about this soon.
The email app on Windows Phone 7 has slick delete animation whenever you delete a single email, the whole page shrinks in size and then falls of the bottom of the page, once I'd seen it I knew I wanted to bring a similar animation to To Do Today. Here's how I created it in Blend.
While the UI for Blend is great for laying up my pages on the phone it's a little difficult to work with animations using the default layout. Thankfully Blend has the concept of Workspaces, if you go to the Window / Workspaces menu and select Animation Blend will rearrange the user interface to better deal with things like timelines.
The first thing we do create the storyboard, under the storyboard section select the New button and then give the storyboard a name, I'm calling mine AnimateDelete (original I know).
Once the storyboard is created Blend is in record mode, anything we do to our page is recorded as part of the storyboard.
To animate the entire page we'll be manipulating the element LayoutRoot, so locate that in the Objects tree. The first step is to create a keyframe on the timeline about half a second in, this will be the frame where we've finished shrinking the page and start the drop. Move the timeline marker out to half a second into the timeline and hit the record keyframe button.

With that keyframe selected set the Scale transform to 0.5 to halve the size of the page. We then create the keyframe to tell the animation when to stop the drop. At around a second into the timeline create a second keyframe and with that selected set the Translate Y transform to 600.
To make the animations feel a little more fluid lets set some easing functions to both keyframes. Select each keyframe and in the properties panel set the easing function to ExponentialInOut with an exponent of 6.

You can preview the animation using the timeline and tweak the values to suit your own taste.
Now lets plug the animation into the page, I have an event handler for the delete button on the application bar, we'll use the method I created in a previous blog post to call the Delete method on the view model when the animation is complete. If we didn't do this then our business logic would complete so quickly we wouldn't see the animation.
private void OnDeleteAll(object sender, EventArgs e)
{
AnimateDelete.Begin(ViewModel.DeleteAll);
}
Hope this helps some people get an idea how easy adding animation to your apps can be.
In the MVVM (Model, View, ViewModel) pattern in xaml based applications (Windows Phone 7, Silverlight and WPF) there are two different ways a view model will notify the view of changes. If you're unaware of the differences you can end up wondering why your changes aren't being reflected in your view.
The most common is implementing the INotifyPropertyChanged interface, the other is through the INotifyCollectionChanged interface. Most developers really won't use second interface but its useful to know about and how it affects your view model.
When you bind to a property on your view model the binding infrastructure will listen for INotifyPropertyChanged events from your view model for that property.
If the type of the property implements INotifyCollectionChanged (such as ObservableCollection) it will also listen for events off that property.
The distinction is important because a common pattern with developers is implement collection properties as automatic properties. This however limits the way you can interact with the collection such that changes will be reflected in the view.
public class CorrectViewModel : ViewModelBase
{
public CorrectViewModel()
{
Items = new ObservableCollection<Item>();??
}
public ObservableCollection<Item> Items
{
get; set;
}
public void Add()
{
var items = GetItems();
Items.Clear(); // Collection changed raised
foreach(var item in items)
{
Items.Add(item); // Collection changed raised
}
}
private IEnumerable<Item> GetItems()
{
return from i in Enumerable.Range(0, 10)
select new Item
{
Name = "Item: " + i
};
}
}
public class IncorrectViewModel : ViewModelBase
{
public IncorrectViewModel()
{
Items = new ObservableCollection<Item>();
}
public ObservableCollection<Item> Items
{
get;
set;
}
public void Add()
{
var items = GetItems();
Items = new ObservableCollection<Item>(); // No notification raised
foreach(var item in items)
{
// Collection changed raised but view unaware of new collection
Items.Add(item);
}
}
private IEnumerable<Item> GetItems()
{
return from i in Enumerable.Range(0, 10)
select new Item
{
Name = "Item: " + i
};
}
}
Notice the difference between the correct view model and the incorrect one is that the correct one alters the already existing collection and the view is notified with INotifyCollectionChanged events. The incorrect view model replaces the collection with an entirely new one, but because the automatic property doesn't fire an INotifyPropertyChanged event the changes will not be reflected in the view.
The incorrect view model can be fixed by either modifying the Add method to not replace the collection but modify it in place, or to modify the Items collection to fire property changed events.
The eventing mechanisms in .NET are great for being notified when certain things occur, but often you don't care about every occurrence just the next one, really what you're wanting is a callback. The most common scenario I've encountered for this during my development on the Windows Phone 7 is wanting to execute code after an animation is complete and by extension when an animation part of a visual state is complete.
What I want to show is two different mechanisms to create one time event handlers, then how to build simple extension methods to make use of these to create callbacks and then also how to use it with animations and the visual state manager.
The first and easiest method is a self removing event handler, rather than simply adding an event handler to the event we first create the event handler as a separate variable initialised as null. We then initialise the handler as a lambda that does what you need to do and then takes advantage of closures to remove itself from the event.
This can seem quite complicated, what makes this work is that we're splitting what would usually be one statement (declaring the event handler and assigning to the event) into three, declaring the reference to the handler, declaring the handler itself and assigning to the event. By splitting the declaration of the variable and the initialisation means we can reference the variable in the handler itself.
public void AttachWithHandler()
{
EventHandler handler = null;
handler = (s, e) =>
{
MessageBox.Show("Completed!");
storyboard.Completed -= handler;
};
storyboard.Completed += handler;
}
The alternate way is to use the new library from Microsoft called Reactive Extensions this allows us to use a more declarative syntax, for one off event handlers the important method is the Take(1). What's cool about this approach is that you can handle multiple events easily over the first approach. We can also add filters and other features of the Reactive Extensions framework to our handler if required.
public void AttachWithRx()
{
Observable.FromEvent((EventHandler<EventArgs> e) => new EventHandler(e),
e => storyboard.Completed += e,
e => storyboard.Completed -= e)
.Take(1)
.Subscribe(e => MessageBox.Show("Completed"));
}
So now we have our approaches lets build some extension methods to make this into a simple extension method, rather than caring about a full event handler all we really want is to be able to define a callback method and have that executed on event completion. To solve our storyboard completion animation problem we'll create an extension for storyboard that takes a callback and then uses the first approach to use it as a one off event handler.
public static void Begin(this Storyboard storyboard, Action callback)
{
EventHandler handler = null;
handler = (s, e) =>
{
callback();
storyboard.Completed -= handler;
};
storyboard.Completed += handler;
storyboard.Begin();
}
Here's how I'm using it in To Do Today.
private void OnDeleteAll(object sender, EventArgs e)
{
AnimateDelete.Begin(ViewModel.DeleteAll);
}