Entries:
Comments:
Posts:

Loading User Information from Channel 9

Something went wrong getting user information from Channel 9

Latest Achievement:

Loading User Information from MSDN

Something went wrong getting user information from MSDN

Visual Studio Achievements

Latest Achievement:

Loading Visual Studio Achievements

Something went wrong getting the Visual Studio Achievements

Classic JukeBox

CJBLogo Remember the classic arcade style jukeboxes? Today we will be creating our own jukebox using WPF, WMP, a little bit of 3D, some very basic electronics (costing less than $10 USD) and a splash of M-V-VM

Introduction

A couple of weeks ago I took my son to the game arcade to play a little… while playing, I noticed in the corner a old jukebox… I started wondering how difficult it would be to recreate one of these using “real” hardware and WPF? This article explorers how to make real hardware control software applications in a practical manner

M-V-VM

“Once a developer becomes comfortable with WPF and MVVM, it can be difficult to differentiate the two. MVVM is the lingua franca of WPF developers because it is well suited to the WPF platform, and WPF was designed to make it easy to build applications using the MVVM pattern (amongst others). In fact, Microsoft was using MVVM internally to develop WPF applications, such as Microsoft Expression Blend, while the core WPF platform was under construction. Many aspects of WPF, such as the look-less control model and data templates, utilize the strong separation of display from state and behavior promoted by MVVM.”

WPF Apps With The Model-View-ViewModel Design Pattern by Josh Smith

The M-V-VM pattern helps to separate the logic from the UI which is VERY important when you actually want to manipulate the logic from external sources (Like real push buttons). Our MediaViewModel is extremely simple…

ViewModel

The MediaViewModel has a collection of albums (Which gets fetched when Initialize() is called) and also exposes some commands

Setting up Windows Media Player media library

WMPMediaLibrary

Ensure that your Windows Media Player (WMP) media library has some media. I added a few albums to my library by clicking on the Add to library… menu option.

TIP - I also ensured that all my CD's that I do add has full ID3 tags and album art

Fetching WMP media library

To fetch the media library from WMP, we have to use a little bit of COM

“Microsoft COM (Component Object Model) technology in the Microsoft Windows-family of Operating Systems enables software components to communicate. COM is used by developers to create re-usable software components, link components together to build applications, and take advantage of Windows services. The family of COM technologies includes COM+, Distributed COM (DCOM) and ActiveX® Controls.”

Adding a reference to a COM component is just as easy as adding a reference to a .NET assembly. Click the Project menu and select Add Reference, then click the COM tab on the Add Reference dialog.

AddWmpComReference

The WMP library is a flat structure of media items, so the Albums -> Tracks hierarchy must be created manually. Here is our Model

Model

We will be abstracting the fetching of the data by using a IMediaLibraryRepository. By using a interface we have the added advantage of supporting other media sources (like iTunes, etc) in the future

C#:

public interface IMediaLibraryRepository 
{ 
    IList<Album> GetAlbums(); 
}

And here is our WMP-specific implementation

C#:

public class WMPMediaLibraryRepository : IMediaLibraryRepository 
{ 
    public IList<Album> GetAlbums() 
    { 
        List<Album> Albums = new List(); 
        WindowsMediaPlayer wmp = new WindowsMediaPlayer(); 
        IWMPPlaylist playlist = wmp.mediaCollection.getAll(); 
        for (int i = 0; i < playlist.count; i++) 
        { 
            IWMPMedia media = (IWMPMedia)playlist.get_Item(i); 
            
            Track track = new Track(); 
            track.Title = media.getItemInfo("Title"); 
            track.Location = media.getItemInfo("SourceUrl"); 
            track.Number = media.getItemInfo("OriginalIndex"); 
            string albumName = media.getItemInfo("Album"); 
            
            var album = (from a in Albums 
                            where a.Name == albumName 
                            select a).FirstOrDefault(); 
            
            if (album != null) 
            { 
                album.Tracks.Add(track); 
            } 
            else 
            { 
                Album a = new Album(); 
                a.Name = albumName; 
                string dir = System.IO.Path.GetDirectoryName(track.Location); 
                FileInfo file = new FileInfo(System.IO.Path.Combine(dir, "Folder.jpg"));                 
                if (file.Exists) 
                { 
                    a.Cover = file.FullName; 
                } 
                a.Artist = media.getItemInfo("AlbumArtist"); 
                a.Tracks.Add(track); 
                if (albumName != string.Empty) 
                { 
                    Albums.Add(a); 
                } 
            } 
        } 
        return Albums; 
    } 
}

Commands

All the interaction between the View and the ViewModel happens by using ICommand. We will be using the RelayCommand

RelayCommand - An ICommand whose delegates can be attached for Execute and CanExecute

The MediaViewModel exposes the following commands

  • NextAlbum
  • PreviousAlbum
  • SongUp
  • SongDown
  • PlaySong

To create a RelayCommand, we first need the CanExecute and Execute delegates

C#:

private bool SongUpCanExecute(object parameter) 
{ 
    return (TracksCollectionView.CurrentPosition > 0); 
} 

private void SongUpExecute(object parameter) 
{ 
    if (TracksCollectionView != null) 
    { 
        TracksCollectionView.MoveCurrentToPrevious(); 
    } 
}

And then create the command

C#:

private ICommand songUp; 
public ICommand SongUp 
{ 
    get 
    { 
        if (songUp == null) 
        { 
            songUp = new RelayCommand(SongUpExecute, SongUpCanExecute); 
        } 
        return songUp; 
    }
}

The RelayCommand is very well suited to the M-V-VM pattern because it allows me to encapsulate the whole command and its executing logic very cleanly in my ViewModel. The View can then just bind to these commands!

For more information about custom ICommand implementations, read The Power of ICommand

TurnThePage 3D WPF Book Control

“The Great Library of Alexandria was founded in 300 B.C.E. with the grand objective of collecting the world's knowledge in one place; at its height, the library contained nearly 750,000 scrolls. In the modern world, the British Library contains one of the foremost collections; among its twenty million books and manuscripts are some of the rarest works in existence. It holds the Diamond Sutra, the oldest printed book; Mercator's first atlas of Europe; the Lindisfarne Gospels; Leonardo da Vinci's personal notebook; the Magna Carta; and the Codex Sinaiticus, one of the two earliest Christian Bibles. Such unique items must, of course, be treated with the utmost care. If they are on public display at all, they are well protected behind glass, and direct interaction is limited to a handful of individuals.

Happily, these works are now being digitized for the first time in order to reach a broad audience. Even better, the digitized versions are being turned into a rich interactive experience that adds curatorial content and brings the books to life. In collaboration with a UK-based software developer, the British Library developed a Windows®-based application called Turning the Pages that offers a virtual facsimile in three-dimensional space of a growing number of the library's most precious items”

Turning the Pages with WPF by Tim Sneath

By using the free book control (released by Mitsuru Furuta on CodePlex), we will be transforming our WMP media library into a book browsing experience!

To use the controls, we need to reference the WPMMitsuControls.dll and add the following namespace

xmlns:controls="clr-namespace:WPFMitsuControls;assembly=WPFMitsuControls"

Because Book derive from ItemsControl, we can bind it to any collection using ItemsSource and change the ItemTemplate (Like we would using ListBox, ListView, etc)

<controls:Book ItemsSource="...">
    <controls:Book.ItemTemplate>
        ...
    </controls:Book.ItemTemplate>
</controls:Book>

Accessing the parallel port

parallelPort1

parallelPortReg

We will be using the status register (Base + 1) for feedback from the keypad. To access the keypad we need a hardware IO driver (I will be using inpout32.dll). inpout32.dll is a win32 dll so we need to p/invoke

C#:

public class PortAccess 
{ 
    [DllImport("inpout32.dll", EntryPoint = "Out32")] 
    public static extern void Output(int adress, int value); 
    
    [DllImport("inpout32.dll", EntryPoint = "Inp32")] 
    public static extern int Input(int adress); 
}

For more information on how to access the LPT port using managed code, first read the following CodeProject article:

Electronics 101

Schematic

This is a very basic schematic of how my keypad is wired up...

Cable

I had a keypad laying round which I used but you can also use normal push buttons available at your local electronics shop (Here is a list of push buttons available from RadioShack)

WindowWithKeypadSupport

The last part we need to cover is how to react to a key being pressed on the keypad. I sub-classed the WPF Window and created my own WindowWithKeypadSupport. WindowWithKeypadSupport has a background thread running which monitors the LPT port. If a button is pressed, it raises a PreviewKeypadDown routed event.

C#:

public class WindowWithKeypadSupport : Window 
{ 
    public static readonly RoutedEvent PreviewKeypadDownEvent; 
    
    static WindowWithKeypadSupport() 
    {
        WindowWithKeypadSupport.PreviewKeypadDownEvent = 
            EventManager.RegisterRoutedEvent( "PreviewKeypadDown", 
            RoutingStrategy.Bubble, typeof(KeypadEventHandler), 
            typeof(WindowWithKeypadSupport)); 
    } 
    
    public event RoutedEventHandler PreviewKeypadDown 
    { 
        add 
        { 
            base.AddHandler(WindowWithKeypadSupport.PreviewKeypadDownEvent, value); 
        }         
        remove 
        { 
            base.RemoveHandler(WindowWithKeypadSupport.PreviewKeypadDownEvent, value); 
        } 
    } 
    // The rest of the class is omited for brevity 
} 

To raise the event, we need to invoke back to the UI thread

C#:

Dispatcher.BeginInvoke(DispatcherPriority.Background,
    (SendOrPostCallback)delegate 
    { 
        KeypadEventArgs e = new KeypadEventArgs(
            WindowWithKeypadSupport.PreviewKeypadDownEvent, this); 
        e.Key = CreateKeyFromIOPortValue(keyValue); 
        base.RaiseEvent(e); 
    }, null);

Here is a example of how to instantiate the WindowWithKeyadSupport

<controls:WindowWithKeypadSupport x:Class="ClassicJukebox.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:controls="clr-namespace:ClassicJukebox"
    PreviewKeypadDown="WindowWithKeypadSupport_PreviewKeypadDown" />

TIP - I also react to keyboard buttons being pressed... Use the left, right, up, down and enter keys to control the jukebox

And thats it...

ClassicJukebox

Conclusion

WPF provides a very powerful infrastructure for creating applications that can mix 3D & hardware effortless! The binding architecture and command support allows for great separation of concerns!

And probably the most important advantage of WPF? It makes writing applications fun…

About the author

Rudi Grobler's main area of interest is development in the embedded space (his day-to-day job). In the last 10 years Rudi interfaced to various devices in the embedded space ranging from Graphic LCDs using parallel port, various bill acceptors, RoboHum, smart card readers, Wii remote, data acquisition devices, petrol pumps and much, much more!About 2 years ago, he received a copy of Charles Petzold's WPF book and fell in love…

His ramblings can be found at http://dotnet.org.za/rudi

Follow the Discussion

Remove this comment

Remove this thread

close

Comments Closed

Comments have been closed since this content was published more than 30 days ago, but if you'd like to continue the conversation, please create a new thread in our Forums,
or Contact Us and let us know.