Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Media Player in Visual Studio 2010 (VSX)

0.00/5 (No votes)
31 Mar 2010 1  
Player extension for Visual Studio that switches between Internet radio stations

Introduction

I always thought that developing Extensions for Visual Studio is very hard task. And I'm sure it was, but today it's really easy to create your own Extensions and even share them with everyone worldwide.

This was my first experience in developing extensions for Visual Studio and I hope - not the last.

I like listening to Internet radio or music from my PC while working with my code. Sometimes switching between different radio stations (for example when some station goes down) it is difficult because I also have to switch my "context" to find another station.

Then I thought that it would be really cool if I could do this from Visual Studio. It could even increase my productivity! That's how the idea of player extension for Visual Studio came to me.

Prerequisite for Extension Development

For developing Visual Studio 2010 Extensions you will need the following:

Using Extension

Playlists for Player Extension are text files in some strange format (that's because I created it).

Playlist may look like this:

16bit radio >>> http://www.16bit.fm/play/16bit.fm_192.m3u
dance radio >>> http://danceradio.ru/playlist160.m3u
Dinamyte Radio >>> http://80.254.15.12:8000/listen.pls
song 1 >>> D:/0/11.mp3
song 2 >>> D:/0/12.mp3

Where '>>>' is a delimiter. Everything to the left from delimiter is a name of the radio station (song/playlist or anything that can be played by Windows Media Player except video). You can print whatever you like here.

Actual path to the media is placed to the right from the delimiter.

mediaplayerinvisualstudio1.png

First of all you have to create your playlist (in your preferred text editor) then load it with the Player Extension (button 'Load'). Next select media which you want to listen to from the list.

After that you can you standart controls (Play/Pause, Stop, Volume slider).

Developing Media Player Extension for Visual Studio 2010

While developing Player Extension I had to face two problems:

  • How to play media from different sources?
  • How to make player common for all pages (I mean code editors in Visual Studio)?

Ways to solve those problems are discussed in the next chapter

Using the Code

Creating Project

First of all we need create project.

mediaplayerinvisualstudio2.png

Player should be placed to the text editor so we choose Editor Margin from Extensibility menu. I named the project "InternetRadioPlayer." Right after creating the project you'll get a few files that are already written for you.

Most interesting for us now is InternetRadioPlayer.cs and InternetRadioPlayerFactory.cs. InternetRadioPlayerFactory.cs is a factory that creates our controls for each code editor page. We will not change it we will place all useful code to InternetRadioPlayer.cs. But first let's create player class and player control.

Player Class

What do we need to play sound? Yes, the player! Let's create one.

I tried to find a good media library or control in Internet but finally I came out with the simplest solution - using the MediaElement from WPF control library. It can play media from almost any source: single songs, videos, playlists, Internet radio etc.

Now let's create a new class file and name it Player.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Controls;
using System.IO;
using System.Text.RegularExpressions;
 
namespace InternetRadioPlayer
{
    public class Player
    {
        private MediaElement _player;
        public PlayerState State { get; set; }
        private Dictionary<string, string> _playlist = new Dictionary();
        private static Player _instance;
        public event EventHandler Changed;
 
        public Dictionary<string, string> Playlist
        {
            get
            {
                return _playlist;
            }
        }
 
        public double Volume
        {
            get
            {
                return _player.Volume;
            }
            set
            {
                _player.Volume = value;
                OnChanged();
            }
        }
 
        public static Player Instance
        {
            get
            {
                if (_instance == null)
                {
                    _instance = new Player();
                }
                return _instance;
            }
        }
 
        public int SelectedIndex { get; set; }
 
        private Player()
        {
            _player = new MediaElement();
            _player.LoadedBehavior = MediaState.Manual;
            _player.UnloadedBehavior = MediaState.Manual;
        }
 
        public void SetSource(int index, string uri)
        {
            SelectedIndex = index;
            _player.Source = new Uri(uri);
            State = PlayerState.Stopped;
            OnChanged();
        }
 
        public void Play()
        {
            _player.Play();
            State = PlayerState.Playing;
            OnChanged();
        }
 
        public void Stop()
        {
            _player.Stop();
            State = PlayerState.Stopped;
            OnChanged();
        }
 
        public void Pause()
        {
            _player.Pause();
            State = PlayerState.Paused;
            OnChanged();
        }
 
        public void Unload()
        {
            if (State != PlayerState.Stopped)
            {
                _player.Stop();
            }
            State = PlayerState.Unknown;
        }
 
        public void LoadPlaylist(string fileName)
        {
            _playlist.Clear();
            string[] lines = File.ReadAllLines(fileName);
            foreach (var item in lines)
            {
                string[] str = Regex.Split(item, ">>>");
                _playlist.Add(str[0].Trim(), str[1].Trim());
            }
            OnChanged();
        }
 
        private void OnChanged()
        {
            if (Changed != null)
            {
                Changed(this, null);
            }
        }
    }
 
    public enum PlayerState
    {
        Unknown,
        Stopped,
        Paused,
        Playing
    }
}

Nothing is very hard to understand in this code. The player has four states: Unknown (nothing is loaded yet), Stopped, Paused and Playing. A few methods change player state. The LoadPlayList method loads playlist from selected file.

There is also an event Changed which tells subscribers (player user controls) to update the UI.

Another interesting thing here is a static variable instance. Remember I told you that there was a problem while making player common for all text editor pages.

InternetRadioPlayerFactory creates instances of player control for each page. This means that if we had a standalone player encapsulated in each control we would have the situation when different songs are being played from different pages.

Having static instance of Player and a private constructor don't allow user controls to create new instances of Player.

Player Control

Now let's create a user control for our player. Click "Add new item" and choose User Control (WPF) from WPF tab. Name it PlayerControl.

<UserControl x:Class="InternetRadioPlayer.PlayerControl"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             mc:Ignorable="d" Height="30" Width="440">
    <Border BorderThickness="1" CornerRadius="3,3,3,3" Width="436" Height="30">
        <Border.BorderBrush>
            <SolidColorBrush 
                Color="{DynamicResource {x:Static SystemColors.DesktopColorKey}}"/>
        </Border.BorderBrush>
        <Grid Height="30" Width="440" >
            <ComboBox Name="comboBox1" Height="20" Width="134" Margin="179,4,127,6" 
                SelectionChanged="comboBox1_SelectionChanged"
                ToolTip="Choose Playlist" IsEnabled="False" />
        <Button Content="Play" Height="22" HorizontalAlignment="Left"
            Margin="5,3,0,5" Name="btnPlay" VerticalAlignment="Center"
            Width="55" Click="btnPlay_Click" />
        
        
        
        <Button Content="Stop" Height="22" HorizontalAlignment="Left"
            Margin="62,3,0,5" Name="btnStop" VerticalAlignment="Center"
            Width="55" Click="btnStop_Click" />
        
        
        
        <Button Content="Load" Height="22" HorizontalAlignment="Right"
            Margin="0,3,265,5" Name="btnLoad" VerticalAlignment="Center"
            Width="55" Click="btnLoad_Click"/>
        
        
        
        <Slider Height="24" Margin="313,2,0,0"  HorizontalAlignment="Left"
            Name="slider1" VerticalAlignment="Center" Width="121" Maximum="1"
            Value="0.5" ValueChanged="slider1_ValueChanged" />
    </Grid>
    </Border>
</UserControl>

Everything is really easy here.

And code-behind here does the following:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using Microsoft.VisualStudio.Text.Editor;
using Microsoft.Win32;
using System.IO;
using System.Text.RegularExpressions;
 
namespace InternetRadioPlayer
{
    /// <summary>
    /// Interaction logic for PlayerControl.xaml
    /// </summary>
    public partial class PlayerControl : UserControl
    {
        private Player _player;
 
        public PlayerControl(Player player)
        {
            _player = player;
            _player.Changed += new EventHandler(_player_Changed);
            InitializeComponent();
        }
 
        void _player_Changed(object sender, EventArgs e)
        {
            UpdateState();
        }
 
        private void btnPlay_Click(object sender, RoutedEventArgs e)
        {
            try
            {
                if (_player.State == PlayerState.Playing)
                    _player.Pause();
                else
                    _player.Play();
 
                UpdateButtonContent();
            }
            catch (Exception)
            {
                MessageBox.Show("Error");
            }
        }
 
        private void comboBox1_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            try
            {
                if (comboBox1.SelectedValue != null)
                {
                    if (comboBox1.SelectedIndex != _player.SelectedIndex)
                    {
                        _player.SetSource(comboBox1.SelectedIndex,
                           comboBox1.SelectedValue.ToString());
                        _player.Play();
                        
                        UpdateButtonContent();
                    }
                }
            }
            catch (Exception)
            {
                MessageBox.Show("Error loading stream");
            }
        }
 
        private void btnLoad_Click(object sender, RoutedEventArgs e)
        {
            _player.Unload();
            OpenFileDialog dlg = new OpenFileDialog();
            if (dlg.ShowDialog().Value)
            {
                try
                {
                    _player.LoadPlaylist(dlg.FileName);
                    comboBox1.ItemsSource = _player.Playlist;
 
                    comboBox1.DisplayMemberPath = "Key";
                    comboBox1.SelectedValuePath = "Value";
                    comboBox1.SelectedIndex = 0;
 
                    _player.SetSource(comboBox1.SelectedIndex,
                       comboBox1.SelectedValue.ToString());
 
                    UpdateButtonContent();
                    comboBox1.IsEnabled = true;
                }
                catch (Exception)
                {
                    MessageBox.Show("Error while loading playlist");
                }
            }
        }
 
        private void btnStop_Click(object sender, RoutedEventArgs e)
        {
            _player.Stop();
            UpdateButtonContent();
        }
 
        private void slider1_ValueChanged(object sender, RoutedPropertyChangedEventArgs e)
        {
            _player.Volume = slider1.Value;
        }
 
        private void UpdateState()
        {
            slider1.Value = _player.Volume;
 
            comboBox1.ItemsSource = _player.Playlist;
            comboBox1.DisplayMemberPath = "Key";
            comboBox1.SelectedValuePath = "Value";
            comboBox1.Items.Refresh();
 
            if (comboBox1.SelectedIndex != _player.SelectedIndex)
            {
                comboBox1.SelectedIndex = _player.SelectedIndex;
            }
 
            UpdateButtonContent();
        }
 
        private void UpdateButtonContent()
        {
            btnPlay.IsEnabled = false;
            btnStop.IsEnabled = false;
 
            switch (_player.State)
            { 
                case PlayerState.Stopped:
                    btnPlay.IsEnabled = true;
                    btnPlay.Content = "Play";
                    break;
                case PlayerState.Playing:
                    btnPlay.IsEnabled = true;
                    btnStop.IsEnabled = true;
                    btnPlay.Content = "Pause";
                    break;
                case PlayerState.Paused:
                    btnPlay.IsEnabled = true;
                    btnStop.IsEnabled = true;
                    btnPlay.Content = "Play";
                    break;
                case PlayerState.Unknown:
                    break;
            }
        }
    }
}

PlayerControl takes Player instance as a constructor parameter, we could just get it from Player.Instance but that doesn't matter.

Summary

At this moment we came to final solution structure

mediaplayerinvisualstudio3.png

The last thing we have to do is adding some code to our InternetRadioPlayer.cs. In the next code we are going to create PlayerControl controls and add them to the text editor.

using System;
using System.Windows.Controls;
using System.Windows.Media;
using Microsoft.VisualStudio.Text.Editor;
 
namespace InternetRadioPlayer
{
    /// <summary>
    /// A class detailing the margin's visual definition including both size and content.
    /// </summary>
    class InternetRadioPlayer : Canvas, IWpfTextViewMargin
    {
        public const string MarginName = "InternetRadioPlayer";
        private IWpfTextView _textView;
        private bool _isDisposed = false;
 
        private PlayerControl _player = new PlayerControl(Player.Instance);
 
        /// <summary>
        /// Creates a <see cref="InternetRadioPlayer"/> for a
        /// given <see cref="IWpfTextView"/>.
        /// </summary>
        /// <param name="textView">The <see cref="IWpfTextView"/> to attach
        /// the margin to.</param>
        public InternetRadioPlayer(IWpfTextView textView)
        {
            _textView = textView;
 
            this.Height = 30;
            this.ClipToBounds = true;
 
            //Add Control
            this.Children.Add(_player);
        }
 
        private void ThrowIfDisposed()
        {
            if (_isDisposed)
                throw new ObjectDisposedException(MarginName);
        }
 
        #region IWpfTextViewMargin Members
 
        /// <summary>
        /// The <see cref="Sytem.Windows.FrameworkElement"/> that implements
        /// the visual representation
        /// of the margin.
        /// </summary>
        public System.Windows.FrameworkElement VisualElement
        {
            // Since this margin implements Canvas, this is the object which renders
            // the margin.
            get
            {
                ThrowIfDisposed();
                return this;
            }
        }
 
        #endregion
 
        #region ITextViewMargin Members
 
        public double MarginSize
        {
            // Since this is a horizontal margin, its width will be bound to
            // the width of the text view.
            // Therefore, its size is its height.
            get
            {
                ThrowIfDisposed();
                return this.ActualHeight;
            }
        }
 
        public bool Enabled
        {
            // The margin should always be enabled
            get
            {
                ThrowIfDisposed();
                return true;
            }
        }
 
        /// <summary>
        /// Returns an instance of the margin if this is the margin that has
        /// been requested.
        /// </summary>
        /// <param name="marginName">The name of the margin requested</param>
        /// <returns>An instance of InternetRadioPlayer or null</returns>
        public ITextViewMargin GetTextViewMargin(string marginName)
        {
            return (marginName == 
                InternetRadioPlayer.MarginName) ? (IWpfTextViewMargin)this : null;
        }
 
        public void Dispose()
        {
            if (!_isDisposed)
            {
                GC.SuppressFinalize(this);
                _isDisposed = true;
            }
        }
        #endregion
    }
}

Run and Test

To test your extension simply press F5 (or Start Debugging from Debug menu). Visual Studio will launch "Experimental Instance" with your extension already installed. You can also debug your Extension.

Create new project or open any existing one.

mediaplayerinvisualstudio4.png

Sometimes you might also need to change Debug environment. Go to Project Properties and set correct path to your devenv.exe.

mediaplayerinvisualstudio5.png

Install the extension

To install your Extension you will need to build it first. You will get .vsix as output, which is the setup for the Extension.

Next go to Extension Manager (Tools > Extension Manager) and manager your extensions (Enable/Disable or Uninstall).

mediaplayerinvisualstudio6.png

Publish

To publish your Extension simply upload .vsix file to Visual Studio Gallery

You can download this Player Extension from here

Summary

With Visual Studio 2010 developing your own extensions is really easy and fun!

History

31.03.2010 First version

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here