03 September, 2010

Now Playing with MVVM – Animating cover transitions

This is part 6 of a series of posts in which I attempt to reproduce the Zune Now Playing interface using the MVVM pattern within WPF. An introduction to the series and table of contents can be found in the first post of this series.

The Zune Now Playing interface uses a subtle animation when transitioning from one album cover to another. To help me reproduce this effect I used Blend, which provides an intuitive interface for creating keyframe-based animations using storyboard or visual states. I’ve chosen to use storyboards, which I think will make my MVVM approach easier to implement because if won’t encourage me to rely on the VisualStateManager class, which is closely associated to the world of the view.

There are a number of documented approaches on the Internet for how to handle animations when using the MVVM pattern. Some initially felt like complex implementations which put the purity of separation first, while others conceded to a small splash of code behind in the view. While the code-behind approach certainly appeals to me because it appears to offer an easier solution, I don’t want to go that way, because I’d rather focus on adhering to the guiding principles of the MVVM pattern.

Most of the suggestions only deal with kicking a single animation off, but what I’m after is more complex than that. When an Album is updated, I’ll start a fade out animation. Once the fade out completes, I’ll update the Album in the model and fire the view’s databinding updates via INotifyPropertyChanged. Finally, I’ll start a different animation to fade the album back in. With such interdependency between animations that occur in the view and state that is transitioning in the viewmodel, this quickly becomes a chicken-or-the-egg problem. I don’t want animation constructs in my viewmodel because the goal is to keep the project blendable, and to allow designers to tweak the animation in Blend.

My approach is similar to those that appear in some of the articles I’ve linked to above. I’ve ended up using a combination of an enum for the various states of the image transition process, a couple of data triggers, and a storyboard completed trigger. Here’s what the flow looks like:

MVVMAnimationFlow

Here’s how I’ve modified the CoverViewModel to accommodate the change. An external entity would call UpdateAlbum to start the process.


namespace NowPlayingMVVM.ViewModel {
public enum ImageState {
New,
BeforeChange,
Changing,
AfterChange
}

public class CoverViewModel : ViewModelBase, IAlbumConsumer {
private Album theAlbum;
private ImageState theImageState = ImageState.New;
...
public ImageState ImageState {
get { return theImageState; }
set {
theImageState = value;
NotifyPropertyChanged("ImageState");
if(value == ImageState.Changing) {
NotifyPropertyChanged("ArtworkUrl");
NotifyPropertyChanged("Name");
}
}
}

public void UpdateAlbum(Album anAlbum) {
if(theAlbum == anAlbum) return;
if (theAlbum == null) {
theAlbum = anAlbum;
ImageState = ImageState.Changing;
} else {
ImageState = ImageState.BeforeChange;
theAlbum = anAlbum;
}
}
}
}

And on the View side


<Image x:Name="image" Source="{Binding ArtworkUrl}" Stretch="Fill" Width="280" Height="280" RenderTransformOrigin="0.5,0.5">
<Interactivity:Interaction.Triggers>
<ei:DataTrigger Binding="{Binding ImageState}" Value="BeforeChange">
<!-- When ImageState becomes BeforeChange, fire FadeOut animation -->
<ei:ControlStoryboardAction Storyboard="{StaticResource FadeOut}" />
</ei:DataTrigger>
<ei:StoryboardCompletedTrigger Storyboard="{StaticResource FadeOut}">
<!-- When FadeOut completes, update ImageState to Changing -->
<ei:ChangePropertyAction PropertyName="ImageState" TargetObject="{Binding}" Value="Changing" />
</ei:StoryboardCompletedTrigger>
<ei:DataTrigger Binding="{Binding ImageState}" Value="Changing">
<!-- When ImageState becomes Changing, fire FadeIn animation -->
<ei:ControlStoryboardAction Storyboard="{StaticResource FadeIn}" />
</ei:DataTrigger>
<ei:StoryboardCompletedTrigger Storyboard="{StaticResource FadeIn}">
<!-- When FadeIn completes, update ImageState to AfterChange -->
<ei:ChangePropertyAction PropertyName="ImageState" TargetObject="{Binding}" Value="AfterChange" />
</ei:StoryboardCompletedTrigger>
</Interactivity:Interaction.Triggers>
</Image>

I don’t really love this approach because although it technically doesn’t require the View and ViewModel to know about each other, the two are still tightly bound by the use of the enum and the order of events. When looking at the ViewModel, the trigger logic can only really be understood with a good grasp of what is happening in the View. In addition, while the View still renders in Blend, the animations don’t fire, so I’m unable to test and refine them. Because I’m trying to create a common animation for each of my cover views, I’ve had to resort to creating a TestAnimation user control where I can use the tools to develop the animation I want, before copying the resource to my shared ResourceDictionary. Each of the cover views is a different size, so I can’t take advantage of FrameworkElement.Width, and I have to use RenderTransforms instead.


I wonder if my objections to this approach are petty and over-thought. This solution doesn’t feel right, but it will work for the time being. As I learn more about WPF, I’ll refine my understanding of what the pluses and minuses of this approach are.


Download Project

19 August, 2010

Now Playing with MVVM – Fetching Sample Album / Artist / Song Data

This is part 5 of a series of posts in which I attempt to reproduce the Zune Now Playing interface using the MVVM pattern within WPF. An introduction to the series and table of contents can be found in the first post of this series.

To this point in my Now Playing with MVVM series I’ve been relying on static sample data that I’ve hardcoded into my application. It served me well while I was just working with half a dozen album covers, but now that I’m getting closer to implementing an interface that needs to stretch full screen, I’ll need data that is more dynamic and more interesting to look at.

I don’t use them myself, but a few of my friends and family use the Rhapsody service. Some Binging lead me to a page on the Rhapsody site where they offer up a selection of RSS and OPML feeds of top albums, artists, tracks, new releases, and more. This is a great solution for me, not only because of the amount and depth of data, but because by retrieving it over the Internet, I’ll be forced to deal with binding to data that is not immediately available.

The service I’ve chosen to work with is the New Releases feed. This should be a good source of ever-changing data that pulls from a different genres and album types. If you click directly on the feed URL with a browser, it will display a formatted web page by doing a client side XSL transform, so I used UltraEdit to open the raw feed data to examine the RSS formatting.

<rss version="2.0" xmlns:rhap="http://feeds.rhapsody.com/dtds/">
<channel>
<title>New Releases on Rhapsody Online</title>
<link>http://www.rhapsody.com?rws=%2Fnew-releases.rss</link>
<description>New Releases on Rhapsody Online</description>
<category />
<language>en</language>
<ttl>720</ttl>
<pubDate>Thu, 19 Aug 2010 02:34:12 -0700</pubDate>
<image>
<url>http://static.realone.com/rotw/images/logo_rhapsody_113x22.gif</url>
<title>New Releases on Rhapsody Online</title>
<link>http://www.rhapsody.com?rws=%2Fnew-releases.rss</link>
<description>New Releases on Rhapsody Online</description>
</image>
<item>
<title>God Willin' &amp; The Creek Don't Rise - Ray LaMontagne</title>
<link>http://www.rhapsody.com/goto?rcid=alb.39896780&amp;variant=play&amp;rws=%2Fnew-releases.rss</link>
<category>Adult Alternative</category>
<pubDate>Tue, 17 Aug 2010 13:17:19 -0700</pubDate>
<source url="http://www.rhapsody.com?rws=%2Fnew-releases.rss">New Releases on Rhapsody Online</source>
<guid isPermaLink="false">alb.39896780</guid>
<rhap:rcid xmlns:rhap="rhap">alb.39896780</rhap:rcid>
<rhap:artist xmlns:rhap="rhap">Ray LaMontagne</rhap:artist>
<rhap:artist-rcid xmlns:rhap="rhap">art.6479139</rhap:artist-rcid>
<rhap:price xmlns:rhap="rhap">9.99</rhap:price>
<rhap:album xmlns:rhap="rhap">God Willin' &amp; The Creek Don't Rise</rhap:album>
<rhap:album-rcid xmlns:rhap="rhap">alb.39896780</rhap:album-rcid>
<rhap:album-art xmlns:rhap="rhap">http://image.listen.com/img/170x170/5/1/4/1/2081415_170x170.jpg</rhap:album-art>
<rhap:album-release-date xmlns:rhap="rhap">2010-08-17 13:17:19.0</rhap:album-release-date>
<rhap:album-original-release-date xmlns:rhap="rhap">2010-08-17 13:17:19.0</rhap:album-original-release-date>
<rhap:album-is-available xmlns:rhap="rhap">true</rhap:album-is-available>
<rhap:explicit xmlns:rhap="rhap">false</rhap:explicit>
<rhap:album-type xmlns:rhap="rhap">main-release</rhap:album-type>
<rhap:play-href xmlns:rhap="rhap">http://www.rhapsody.com/goto?rcid=alb.39896780&amp;variant=play&amp;rws=%2Fnew-releases.rss</rhap:play-href>
<rhap:data-href xmlns:rhap="rhap">http://www.rhapsody.com/goto?rcid=alb.39896780&amp;variant=data&amp;rws=%2Fnew-releases.rss</rhap:data-href>
<description>
<![CDATA[God Willin' & The Creek Don't Rise - Ray LaMontagne]]>
</description>
</item>
.Net 3.5 introduced the System.ServiceModel.Syndication namespace which aids in the creation and parsing of RSS and ATOM feeds. This library will make the task of hydrating the Album/Artist/Song object model very easy. To get at the list of items in the feed, you load them from an XMLReader that you’ve passed the RSS URI to:
List<Album> Albums = null;
Uri myFeedUri = new Uri("http://feeds.rhapsody.com/new-releases.rss");
using(XmlReader myReader = XmlReader.Create(myFeedUri.AbsoluteUri)) {
SyndicationFeed myFeed = SyndicationFeed.Load(myReader);
if(myFeed != null) {
Albums = new List<Album>(myFeed.Items.Count());
foreach(SyndicationItem myItem in myFeed.Items) {
//work with the next item
}
}
}


The SyndicationItem entity makes the standard RSS elements available to the client, and it also provides a way to access vendor-specific element extensions. The Rhapsody feeds make album, artist, track, and other metadata available through a custom namespace as part of each SyndicationItem. SyndicationItem makes these additional elements available in a collection called ElementExtensions. There is a discussion of how to access these items on Stack Overflow. Here is how I grabbed the album art from the new releases feed shown above.


foreach(SyndicationItem myItem in myFeed.Items) {
Album anAlbum = new Album
{
ID = myItem.Id,
Name = myItem.Title.Text,
Category = myItem.Categories[0].Name,
ArtworkUrl = myItem.ElementExtensions.First(
albumext => albumext.OuterNamespace == "rhap" &&
albumext.OuterName == "album-art")
.GetObject<string>()
};
}


I qualified the OuterNamespace as a precaution against some unforeseen change in the feed where another element with the name “album-art” is introduced into a different namespace inside each item.


I’d also like to build the track list for each album while hydrating them from the new releases feed. Rhapsody offers this capability by accessing the following URI: http://www.rhapsody.com/goto?rcid=<rhap:rcid>&variant=rss-feed. This makes fetching the song metadata a snap:


foreach(SyndicationItem myItem in myFeed.Items) {
Album anAlbum = new Album
{
ID = myItem.Id,
Name = myItem.Title.Text,
Category = myItem.Categories[0].Name,
ArtworkUrl = myItem.ElementExtensions.First(
albumext => albumext.OuterNamespace == "rhap" &&
albumext.OuterName == "album-art")
.GetObject<string>()
};
anAlbum.Songs = GetSongsForAlbum(anAlbum);
}

private List<Song> GetSongsForAlbum(Album aParentAlbum) {
List<Song> Songs = null;
string rssURL = string.Format("http://www.rhapsody.com/goto?rcid={0}&variant=rss-feed", aParentAlbum.ID);
Uri myFeedUri = new Uri(rssURL);
using(XmlReader myReader = XmlReader.Create(myFeedUri.AbsoluteUri)) {
SyndicationFeed myFeed = SyndicationFeed.Load(myReader);
if(myFeed != null) {
Songs = new List<Song>(myFeed.Items.Count());
foreach(SyndicationItem myItem in myFeed.Items) {
Song aSong = new Song
{
Album = aParentAlbum,
Artist = aParentAlbum.Artist,
Name = myItem.ElementExtensions.First(
songext => songext.OuterNamespace == "rhap" &&
songext.OuterName == "track")
.GetObject<string>()
};
Songs.Add(aSong);
}
}
}
return Songs;
}


Speeding things up


This does the job just fine, but there’s a problem. At the time of this writing, the new releases feed has 144 items in it, meaning that 145 separate HTTP calls are required in order to load the whole media graph. This can be very slow because the requests are made sequentially on the same thread. Let’s introduce a little parallelism into this process.[NOTE: See update below. This code is not thread safe]


using(XmlReader myReader = XmlReader.Create(myFeedUri.AbsoluteUri)) {
SyndicationFeed myFeed = SyndicationFeed.Load(myReader);
if(myFeed != null) {
Albums = new List<Album>(myFeed.Items.Count());
//foreach(SyndicationItem myItem in myFeed.Items)
Parallel.ForEach(myFeed.Items, myItem => {
Album anAlbum = new Album {
ID = myItem.Id,
Name = myItem.Title.Text,
Category = myItem.Categories[0].Name,
Artist = new Artist {
ID = <snip/>,
Name = <snip/>
},
ArtworkUrl = <snip/>
};
anAlbum.Songs = GetSongsForAlbum(anAlbum);
});
}
}

Ok, much better. By taking advantage of the Parallel tasks library in System.Threading.Tasks.Parallel, I’ve cut the load time in half on my development box. But I think I can do better. What if I load the songs collection for each album on a background thread? [NOTE: See update below. This code is not thread safe]


Albums = new List<Album>(myFeed.Items.Count());
Parallel.ForEach(myFeed.Items, myItem => {
Album anAlbum = new Album
{
ID = myItem.Id,
Name = myItem.Title.Text,
Category = myItem.Categories[0].Name,
Artist = <snip/>
ArtworkUrl = <snip/>
};
BackgroundWorker aWorker = new BackgroundWorker();
aWorker.DoWork += (aSender, e) => {
Album myAlbum = e.Argument as Album;
if(myAlbum != null)
myAlbum.Songs = GetSongsForAlbum(anAlbum);
};
aWorker.RunWorkerAsync(anAlbum);
Albums.Add(anAlbum);
});

Wow, much better. The albums collection is returned almost immediately and the songs fill themselves in on a background thread. This is exactly the sort of experience I expect from a WPF application.


UPDATE: While integrating this code back into the Now Playing application, I found that my Parallel.ForEach loop was not returning. After some research, I determined it was caused by concurrent access to the List<Album>, which is not thread safe. The good news is that the .Net framework 4.0 includes a ConcurrentDictionary class, which lends itself well to my use case.


Download Project

Now Playing with MVVM – Replicating the Zune Now Playing layout

This is part 4 of a series of posts in which I attempt to reproduce the Zune Now Playing interface using the MVVM pattern within WPF. An introduction to the series and table of contents can be found in the first post of this series.

In the previous postings I’ve assembled the infrastructure (however simplistic) that will help me reconstruct the Zune Now Playing interface. The plumbing I have in place so far has a lot of shortcomings, but I’m ready to put them behind me for now and focus on the layout for a bit. So the first order of business is to analyze the Zune software to see how they organize the covers. There is a pattern to the layout, which I’ve outlined below.

ZuneNowPlayingTemplateTrace

As you can see, the interface is made up of a collection of templates that overlap, giving it a random feel. With this information, I can lay out the interface by creating a new view that hosts 49 individual cover views of varying sizes.

ZuneTemplateSkeleton

These cover views are arranged in a parent grid by setting the margins for each one individually.

<Grid x:Name="LayoutRoot">
<Views:LargeCover Margin="0,0,0,0" />
<Views:SmallCover Margin="0,280,0,0" />
<Views:SmallCover Margin="70,280,0,0" />
<Views:SmallCover Margin="140,280,0,0" />
<Views:SmallCover Margin="210,280,0,0" />
<Views:SmallCover Margin="0,350,0,0" />
<Views:SmallCover Margin="70,350,0,0" />
...



The interface is still Blendable, which gives me the opportunity to see how the albums line up.


BlendableCoverTemplate



To get a better feel for how the templates are going to come together, I can replace the covers on the MainWindow from previous postings with a couple of these new CoverTemplateViews.


MainWindowTemplated



Sweet. The UI is beginning to take shape, but it also creates a few //TODO items for me.



  • Now that I’m working with larger surface areas, I’ll need some more sample data. I need to be sure that the covers are evenly distributed and changed randomly. I’d also like to introduce song data into the model for future use.
  • The Zune software is resizable, and allows the user to maximize to a single monitor. Since I won’t know the end user’s screen resolution ahead of time, I’ll need to dynamically assemble these CoverTemplateViews inside a host container at runtime.
  • The cover transitions in the Zune software are smooth and buttery. I need to work on the transition animations for my implementation.
  • The Zune software has a nice looking overlay that tints the covers. The overlay is radial, and moves around a bit.

Download Project

13 August, 2010

Now Playing with MVVM – Separating View Models and Services

This is part 3 of a series of posts in which I attempt to reproduce the Zune Now Playing interface using the MVVM pattern within WPF. An introduction to the series and table of contents can be found in the first post of this series.

In the previous post, I quickly threw together an interface that combined a number of album cover views into a parent grid. The datacontext for each view was bound directly to an Album entity that I created in a parent viewmodel. While this was enough to render the interface I was after, it suffered from a few shortcomings:

  • The cover views were directly bound to domain entities pulled out of an ObservableCollection by index, rather than viewmodels with bindable properties. This is bad design, and it bypasses the usefulness of ObservableCollection.
  • I manually created the list of Albums in the MainPageViewModel. This should really be pulled out to a service so that I can fetch Albums from other sources and make them available to any viewmodels in my application that require them
  • The covers were static – they did not change, and there was no accommodation for allowing them to update.

I need to clean this up a bit before I start worrying about the Zune layout. Let’s approach these concerns one at a time.

Binding cover views to viewmodels

To bring this application more in line with the MVVM approach, I want the cover views to use a CoverViewModel as their datacontext. My first step is to create a CoverViewModel class that exposes some of the Album attributes.

public class CoverViewModel : ViewModelBase {
private Album theAlbum;

public string ArtworkUrl { get { return Album.ArtworkUrl; } }
public string Name { get { return Album.Name; } }
public Album Album { get { return theAlbum; } set {
theAlbum = value;
NotifyPropertyChanged("ArtworkUrl");
NotifyPropertyChanged("Name");
} }
}

And now I can use a CoverViewModel in my cover views. At first glance, I figured this would be easy – just add a resource in each of the three cover views, and set the datacontext of the container grid to bind to the resource. You know, something like this:


<UserControl.Resources>
<ViewModel:CoverViewModel x:Key="CVM" />
</UserControl.Resources>
<Grid DataContext="{Binding Source={StaticResource CVM}}" >
<Image Source="{Binding Path=ArtworkUrl}" />
<Label Content="{Binding Path=Name}" />
</Grid>

This would appear to be just what I’m after, but binding declaratively like this introduces another problem - how will I hand each individual instance of the Cover a unique Album? This design is incomplete because it only addresses the pull portion of the binding – it makes no accommodation for setting the Album in the CoverViewModel before it binds to the ArtworkUrl and Name properties. I need a way to package up an Album and a CoverViewModel and hand it off to a cover view when assembling my main window.


If I take a look at where I want to end up, I begin to see that there will need to be some master entity that maintains a list of bound CoverViewModels and available Albums. This entity will essentially do inventory management for the application – ensuring a high degree of ordered randomness in Album binding, and satisfying business rules around differing number of Albums and CoverViewModels. This leads me to my second point of consideration – how do I collect and store Album instances it in such a way that these binding and assembly issues will be resolved?


collection storage VERSUS CONSUMPTION

Now that I’ve introduced a CoverViewModel layer between the Album and the view, the view and Album should no longer be aware of each other. Instead, I’ll create a service in my application that is responsible for discovering a list of Albums and making them available to outside entities that wish to use them. The service will be a provider of Albums, and will work with some yet-unknown classes that will be consumers of Albums. I think I’ll break the two into separate interfaces – IAlbumConsumer and IAlbumProvider.

public interface IAlbumProvider {
void Register(IAlbumConsumer anAlbumConsumer);
void RefreshAlbum(IAlbumConsumer anAlbumConsumer);
}
public interface IAlbumConsumer {
void UpdateAlbum(Album anAlbum);
}
IAlbumConsumers will register with IAlbumProviders, who will provide them with Albums when requested. I’ve added a method for IAlbumProviders to push an update to individual IAlbumConsumers, as well as a way for an IAlbumConsumer to call in to to an IAlbumProvider and ask for a refresh.
I’ll move all of that manual Album creation code from the last post out of the MainWindowViewModel and into a separate class. The new StaticAlbumProvider will implement the IAlbumProvider interface, and will maintain a list of available albums, along with a list of registered IAlbumConsumers. A static singleton property gives us a single point of storage for Album data across multiple viewmodel instances. Relevant code follows:

internal class StaticAlbumProvider : IAlbumProvider {
private readonly Dictionary<Album, int> theAlbums = new Dictionary<Album, int>();
private readonly List<IAlbumConsumer> theViewModels = new List<IAlbumConsumer>();
internal static readonly IAlbumProvider Instance = new StaticAlbumProvider();

public StaticAlbumProvider() {
theAlbums.Add(new Album
{
Name = "Waking Up",
Artist = new Artist {Name = "OneRepublic"},
ArtworkUrl = "http://image.listen.com/something.jpg"
}, 0); // counts how many times cover has been used in UI
//add more albums here
}

public void Register(IAlbumConsumer aCoverViewModel) {
theViewModels.Add(aCoverViewModel);
aCoverViewModel.UpdateAlbum(GetNextRandomAlbum());
}

public void RefreshAlbum(IAlbumConsumer anAlbumConsumer) {
anAlbumConsumer.UpdateAlbum(GetNextRandomAlbum());
}

private Album GetNextRandomAlbum() {
//random Album retrieval from theAlbums
return anAlbum;
}
}
Implementing the IAlbumConsumer interface on CoverViewModel is straight forward. Note how a CoverViewModel registers itself upon creation with the StaticAlbumProvider instance. When it does so, the StaticAlbumProvider calls back into the CoverViewModel UpdateAlbum method, handing the viewmodel the album it needs:

public class CoverViewModel : ViewModelBase, IAlbumConsumer {
private Album theAlbum;

public CoverViewModel() {
StaticAlbumProvider.Instance.Register(this);
}

public string ArtworkUrl { get { return theAlbum.ArtworkUrl; } }
public string Name { get { return theAlbum.Name; } }

public void UpdateAlbum(Album anAlbum) {
theAlbum = anAlbum;
NotifyPropertyChanged("ArtworkUrl");
NotifyPropertyChanged("Name");
}
}

bringing together our views and viewmodels

The next step is to figure out a way to hand views different instances of the viewmodels. In a future post I’ll talk about View Model Locators, but for now I’ll take the same shortcut that I took with the StaticAlbumProvider. A simple ViewModelLocator class…

public class ViewModelLocator {
public static readonly ViewModelLocator Instance = new ViewModelLocator();
public IAlbumConsumer CoverViewModel { get { return new CoverViewModel(); } }
}
…allows me to put a keyed resource in a project-wide resource dictionary…

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:svc="clr-namespace:NowPlayingMVVM.Service">
<svc:ViewModelLocator x:Key="Locator" />
</ResourceDictionary>
…and bind to that resource by key name inside my CoverViewModels…

<UserControl.Resources>
<ResourceDictionary Source="../Resources.xaml" />
</UserControl.Resources>
<Grid DataContext="{Binding Source={StaticResource Locator}, Path=CoverViewModel}">
<Image Source="{Binding Path=ArtworkUrl}" />
<Label Content="{Binding Path=Name}" />
</Grid>
…providing context for the binding directives inside the viewmodel. Using a static resource retains the blendability of the cover viewmodels, meaning the album art will render in design view within Visual Studio or Blend.
CoverViewArt

 


Rotating the collection


My last challenge for now is to tackle the problem that the album cover art is static and doesn’t rotate like the Zune Now Playing interface does. The way I constructed my IAlbumConsumer and IAlbumProvider interfaces allows me to force the update from either side of their relationship. It seems to make the most sense to me to drive the updates from the StaticAlbumProvider class, since this is where I’ll eventually keep track of which albums have been bound to the UI, and how many instances of that Album are currently showing.


private void RefreshAnAlbum() {
theRefreshTimer = new Timer {
Interval = (new Random().Next(5) + 1) * 1000,
AutoReset = true
};
theRefreshTimer.Elapsed += delegate {
theViewModels[new Random().Next(theViewModels.Count)].UpdateAlbum(GetNextRandomAlbum());
};
theRefreshTimer.Start();
}

With this change, the covers rotate every few seconds, even on the design surface.
CoverArrayWithArt
Ok, so at this point I feel like I’ve got enough code in place to begin attacking the layout issues of my original goal – duplicating the Zune Now Playing interface. After I have that worked out, I’ll return to this design and look at introducing some existing frameworks to replace my custom viewmodel-related code.


Download Project

05 August, 2010

Now Playing with MVVM - Getting Started: M-V-VM breakdown

This is part 2 of a series of posts in which I attempt to reproduce the Zune Now Playing interface using the MVVM pattern within WPF. An introduction to the series and table of contents can be found in the first post of this series.

When broken down to its most basic elements, the Zune Now Playing interface is a collection of album art images of varying sizes, arranged in a particular order. In order to replicate the UI, my first step will be to try to look at the interface through MVVM goggles. How can I break it down into a domain model, a view template, and a mapping layer between the two?

Model: The domain revolves around Albums, Artists, and Songs. I have already done some domain modeling with this same set of resources, so I’ll borrow existing plumbing from that. I’ll also need a worker to go out and fetch the album data from an external resource.

View:
The Zune Now Playing interface is built from a collection of three different sized user controls. I’ll create small, medium, and large cover views and arrange them in a parent container view that will allow me to lay them out the same way that the Zune software does.

ViewModel:
The three cover views will bind to a common cover viewmodel which will represent the properties associated to an album, and the parent view will bind to a viewmodel that maintains the master list of all available albums. In addition to the bindable properties, the parent viewmodel will also be responsible for randomizing the album collection, reducing the number of duplicate covers, and ensuring that all covers have something to bind to regardless of the number of available albums. The parent viewmodel will also be responsible for shuffling album covers on the UI by kicking off animations that transition from one cover to the next.

Quick Implementation

I’d like to quickly get something up and running to familiarize myself with the WPF binding infrastructure, so for now I’ll just jump in and start coding. I’ll start by creating three user controls – one for each of the three sizes of covers that are found in the Now Playing UI. The album cover views contain mostly the same XAML, the most notable difference being the size of the container. A grid serves as the parent object for Image and Label entities, which are bound to properties on the corresponding DataContext, which for now will be an Album. 

   1:  <Grid x:Name="LayoutRoot" Width="280" Height="280" Background="Red">
   2:      <Image Source="{Binding Path=ArtworkUrl}" Stretch="Fill" Width="280" Height="280"/>
   3:      <Label Content="{Binding Path=Name}" Height="25" Background="#96030303" />
   4:  </Grid>



I’ve given each of the cover views different background colors to provide visual cues when laying them out. I’ve arranged five of these cover views on my MainWindow inside a parent grid, as shown below

CoverArray

Now that the covers are in place, I need a way to provide each of them with an album to bind to. I’ll make these available by creating a viewmodel that the MainWindow can bind to. This MainWindowViewModel will provide an ObservableCollection of Albums. In the interest of time, I’ll manually populate the ObservableCollection in the viewmodel’s constructor for now, rather than fetching the data from an outside source at runtime.


   1:  public class MainWindowViewModel : ViewModelBase {
   2:    public MainWindowViewModel() {
   3:        Albums = new ObservableCollection<Album>();
   4:        Albums.Add(new Album {
   5:            Name = "Waking Up",
   6:            Artist = new Artist { Name = "OneRepublic" },
   7:            ArtworkUrl = "http://image.listen.com/img/170x170/3/3/9/0/1850933_170x170.jpg"
   8:        });
   9:        ....
  10:    }
  11:   
  12:    public ObservableCollection<Album> Albums { get; set; }
  13:  }



To make use of the MainWindowViewModel in the parent container, I’ll add a local StaticResource reference to the MainWindowViewModel in the MainWIndow XAML, and bind the DataContext for the parent grid to it.


   1:  <Window x:Class="NowPlayingMVVM.MainWindow"
   2:          xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   3:          xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   4:          Title="Zune Wall" 
   5:          xmlns:my="clr-namespace:NowPlayingMVVM.View"
   6:          xmlns:vm="clr-namespace:NowPlayingMVVM.ViewModel" Width="515" Height="325"
   7:          >
   8:      <Window.Resources>
   9:          <vm:MainWindowViewModel x:Key="MainVM"/>
  10:      </Window.Resources>
  11:      <Grid Height="280" Width="490" DataContext="{Binding Source={StaticResource MainVM}}">
  12:          <my:LargeCover  HorizontalAlignment="Left" VerticalAlignment="Top" />
  13:          ....



By binding the parent grid to an instance of the MainWindowsViewModel, the grid’s child elements can now bind to public properties of the viewmodel. The final step is to bind the DataContext of each cover to one of the Albums in the MainWindowViewModel’s Albums collection.


   1:  <my:LargeCover  Margin="0,0,0,0"     DataContext="{Binding Path=Albums[0]}" />
   2:  <my:MediumCover Margin="280,0,0,0"   DataContext="{Binding Path=Albums[1]}" />
   3:  <my:SmallCover  Margin="280,210,0,0" DataContext="{Binding Path=Albums[2]}" />
   4:  <my:SmallCover  Margin="350,210,0,0" DataContext="{Binding Path=Albums[3]}" />
   5:  <my:SmallCover  Margin="420,210,0,0" DataContext="{Binding Path=Albums[4]}" />



Yes, this is a quick and dirty approach to the problem, but it validates that our model, views, and viewmodel are working together nicely:

CoverArrayBound


What Next?


I’m still new to MVVM, but even without a lot of experience, this approach doesn’t feel right. A couple of objections right off the top of my head:



  • The data is hard-coded and will need to come from a live source in the future. What will that mean for the UI as data is collected in a background thread?
  • The views are all bound to a model entity, not a viewmodel. I need to break that connection – MVVM purists say that views shouldn’t know about the underlying model
  • The album covers are about to a specific index inside the ObservableCollection. I don’t think this will be efficient when I want to update the bound album, as I’ll have to fire a PropertyChanged event on the whole collection if I want my bound element to be notified. But what is the best approach going to be? Should my viewmodel have a property for each album that is bound? That might get out of hand when my view is made up of dozens of album covers.
  • How will I shuffle the bound collection so that the UI isn’t the same every time? I want to be able to write unit tests when I start working on my shuffle algorithm, which I need to consider as I approach this.

Download Project

03 August, 2010

Taking WPF for a ride – Now Playing with MVVM

This is the first in what I plan to be a multi-part series on my experiences with getting started in WPF development using the MVVM platform. In this series of posts I will aim to reproduce the Now Playing screen of the Zune software, pictured below.
Zune - Now Playing
I think the Zune software’s Now Playing screen lends itself well to this type of experiment. It offers a chance to play with:
  • Complex Layout – figuring out how to organize the album covers should prove to be an interesting challenge.
  • MVVM and Data binding – what will the best approach be for binding a collection of albums to my interface?
  • Animations – album cover transitions and the colored overlay
  • Efficiency concerns – how can I manage all those image assets without consuming an inordinate amount of memory?

I plan to blog while I develop the application, so I expect my implementation to go through a number of changes. Because of this approach, some of the code I produce along the way will not be best-in-class, and some of the technical approaches will probably change. The idea is to document the journey, not to take a position on what the best approach may be. I’m learning as I go…

Ok, enough lead in, let’s get to it.

 

Table of contents