Медиа плеер в Visual Studio 2010 Часть 2

Использование кода

Создание проекта

Сперва нужно создать новый проект.

Плеер нужно разместить в каждом окне с исходным кодом в Visual Stidio, так что нужно выбрать проект типа Editor Margin из меню Extensibility. Я назвал проект «InternetRadioPlayer». Сразу после создания проекта в Solution Explorer можно увидеть несколько файлов, сгенерированные Visual Studio.

Наибольший интерес для нас представляют файлы InternetRadioPlayer.cs и InternetRadioPlayerFactory.cs. InternetRadioPlayerFactory.cs – это фабрика, которая создает наш контрол для каждой страницы с кодом в Visual Studio. Файл содержит следующий код:

using System.ComponentModel.Composition;

using Microsoft.VisualStudio.Text.Editor;

using Microsoft.VisualStudio.Utilities;

namespace InternetRadioPlayer

{

#region InternetRadioPlayer Factory

/// <summary>

/// Export a <see cref=»IWpfTextViewMarginProvider»/>, which returns an instance of the margin for the editor

/// to use.

/// </summary>

[Export(typeof(IWpfTextViewMarginProvider))]

[Name(InternetRadioPlayer.MarginName)]

[Order(After = PredefinedMarginNames.HorizontalScrollBar)] //Ensure that the margin occurs below the horizontal scrollbar

[MarginContainer(PredefinedMarginNames.Bottom)] //Set the container to the bottom of the editor window

[ContentType(«text»)] //Show this margin for all text-based types

[TextViewRole(PredefinedTextViewRoles.Interactive)]

internal sealed class MarginFactory : IWpfTextViewMarginProvider

{

public IWpfTextViewMargin CreateMargin(IWpfTextViewHost textViewHost, IWpfTextViewMargin containerMargin)

{

return new InternetRadioPlayer(textViewHost.TextView);

}

}

#endregion

}

Я не буду менять этот файл, а помещать весь полезный код я буду в InternetRadioPlayer.cs. Но сначана нам нужно создать класс для проигрывателя (плеера) и контрол для проигрывателя, который потом будет помещаться на страницы с кодом.

Класс плеер

Класс плеер будет отвечать за проигрывание музыки (можно было догадаться по названию). С контролами он пока никак не связан.

Сначала я пытался найти какую-нибудь хорошую библиотеку для работы со звуком, но так и не смог найти ничего простого и легковесного. В конце концов я решил использовать простейшее решение – использовать стандартный WPF контрол – MediaElement. Он может проигрывать практически все, что угодно (по крайней мере то, что может проиграть Window Media Player, на сколько я понимаю).

Пришло время создать класс плеер в файле 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 _playlist = new Dictionary();

private static Player _instance;

public event EventHandler Changed;

public Dictionary 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

}

}

Ничего слишком сложного тут нет. Плеер имеет четыре состояния: Unknown (плейлист не загружен), Stopped (проигрывание осановлено), Paused (пауза) and Playing (идет проигрывание музыки).

Есть методы, которые меняют это состояние и внутреннее состояние плеера (MediaElement).

LoadPlayList загружает плейлист из выбранного файла.

Событие Changed сообщает о том, что подписчикам нужно обновить внешний вид плеера.

Реньше я писал, что одной из проблем является то, что нужно сделать плеер общим для каждой из страниц с кодом. В этом случае я использовал статическую переменную _instance, чтобы не позволять никому создавать дополнительные плееры.

InternetRadioPlayerFactory создает экземпляр контрола с плеером (который будут описан чуть дальше) для каждой страницы с кодом. Соответственно, если бы я создавал плеер в каждом из контролов, можно было бы проигрывать сразу несколько песен с разных страниц, что не очень хорошо.

Элемент управления — плеер

Теперь перейдем к созданию контрола – это то, что мы будем видеть на страницах с кодом в Visual Studio.

Жмем Add->New Item и выбираем User Control (WPF) из закладки WPF. Я назвал новый контрол PlayerControl.

<UserControl x:Class=»InternetRadioPlayer.PlayerControl»

xmlns=»http://schemas.microsoft.com/winfx/2006/xaml/presentation&#187;

xmlns:x=»http://schemas.microsoft.com/winfx/2006/xaml&#187;

xmlns:mc=»http://schemas.openxmlformats.org/markup-compatibility/2006&#8243;

xmlns:d=»http://schemas.microsoft.com/expression/blend/2008&#8243;

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>

XAML не содержит ничего интересного, а в cs файле имеем следующее:

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

{

///

/// Interaction logic for PlayerControl.xaml

///

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 получает экземпляр Player в качестве параметра конструктора, можно было просто использовать Player.Instance, но это не важно.

Итог

Сейчас мы уже имеет финальную структуру проекта, она выглядит следующим образом:

Осталось только добавить немного кода в InternetRadioPlayer.cs. Нам нужно создать экземпляры PlayerControl и добавить их в окно с кодом. Это достаточно просто.

using System;

using System.Windows.Controls;

using System.Windows.Media;

using Microsoft.VisualStudio.Text.Editor;

namespace InternetRadioPlayer

{

///

/// A class detailing the margin’s visual definition including both size and content.

///

class InternetRadioPlayer : Canvas, IWpfTextViewMargin

{

public const string MarginName = «InternetRadioPlayer»;

private IWpfTextView _textView;

private bool _isDisposed = false;

private PlayerControl _player = new PlayerControl(Player.Instance);

///

/// Creates a for a given .

///

/// The to attach the margin to.

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

///

/// The that implements the visual representation

/// of the margin.

///

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;

}

}

///

/// Returns an instance of the margin if this is the margin that has been requested.

///

/// The name of the margin requested

/// An instance of InternetRadioPlayer or null

public ITextViewMargin GetTextViewMargin(string marginName)

{

return (marginName == InternetRadioPlayer.MarginName) ? (IWpfTextViewMargin)this : null;

}

public void Dispose()

{

if (!_isDisposed)

{

GC.SuppressFinalize(this);

_isDisposed = true;

}

}

#endregion

}

}

Запускаем и тестируем

Для того, чтобы протестировать новое расширение нужно просто нажать F5 (или выбрать Start Debugging в меню Debug). Visual Studio откроет новое окно с заголовком «Experimental Instance», в которое автоматически устанавливается тестируемое расширение. Кстати, расширение можно отлаживать стандартными способами.

Создадим новый проект или откроем любой существующий, чтобы увидеть работу нашего расширения.


Иногда требуется сделать небольшие настройки проекта. Переходим к свойствам проекта (Project Properties) и на вкладке Debug устанавливаем правильный путь к файлу devenv.exe.


Установка расширений

Чтобы установить расширение в Visual Studio нужно сначала его скомпилировать и собрать (Build). На выходе получается .vsix файл, который, по сути, является инсталлятором расширения. Нужно просто запустить его, чтобы установить расширение.

Меню Extension Manager (Tools > Extension Manager) позволяет управлять всеми установленными расширениями или находить новые в интернете (Enable/Disable или Uninstall).


Публикация

Для того, что опубликовать расширение, нужно просто загрузить .vsix файл на Visual Studio Gallery

Описанное расширение можно скачать по следующей ссылке http://visualstudiogallery.msdn.microsoft.com/en-us/438db96c-05f0-4b85-a877-bf2acba9556f

Заключение

С Visual Studio 2010 разработка собственных расширений стала простым и интересным занятием.

Реклама
Запись опубликована в рубрике Uncategorized с метками , . Добавьте в закладки постоянную ссылку.

3 комментария на «Медиа плеер в Visual Studio 2010 Часть 2»

  1. Уведомление: Материалы от Microsoft для разработчиков. | Dmitry Bulavko:MySpaces

  2. Уведомление: Дайджест технических материалов #3 (Silverlight и RIA) - Alex Krakovetskiy blog - Microsoft User Group Винница

Добавить комментарий

Заполните поля или щелкните по значку, чтобы оставить свой комментарий:

Логотип WordPress.com

Для комментария используется ваша учётная запись WordPress.com. Выход / Изменить )

Фотография Twitter

Для комментария используется ваша учётная запись Twitter. Выход / Изменить )

Фотография Facebook

Для комментария используется ваша учётная запись Facebook. Выход / Изменить )

Google+ photo

Для комментария используется ваша учётная запись Google+. Выход / Изменить )

Connecting to %s