Итак, доброго времени суток, дамы и господа! Сегодня мы рассмотрим историю о том, как я создавал приложение для прослушивания интернет радио.
Первая версия программы была сделана ужасно и выглядела так:

Сделал её когда опыта программирования и знаний было немногоочень мало. Программа написана на C#, проект типа Windows Forms. О существовании WPF я знал, но толком в нем не разбирался. Программа создана просто – перетаскал на форму PictureBox-ы, поставил кнопку Play/Pause, Label-ы, ListBox, а полосу регулирования громкости скачал с CodeProject, подключил библиотеку Windows Media Player.
Код приложения – это сплошные проверки “if”. Знаю, руки бы мне за такое оторвать! Например, событие происходящее при нажатии на кнопку Play:
Или проверка изменения радиостанции:
Мало того код ужасный, плюс ещё имеется один большой недостаток, а именно, если какая-то радиостанция уйдёт в небытие, то её никак не убрать из программы, разве что подправить код и заново скомпилировать.
Но об этом я тогда даже не задумывался, так как радовался, что сделал, наверное, что-то стоящее и интересное.
Прошло некоторое время. Я учился программировать, экспериментировал. Очередной раз, когда задался вопросом «что бы ещё программировать?» я наткнулся на проект этого убогого радио и решил, что надо переписать программунадо сделать версию получше.
Создал новый проект Windows Forms. Поставил на форму кнопку, пару Label-ов, TabControl, ListBox, тот же самый Slider. Добавил новое окно настроек. Воспроизведение происходит по-прежнему с помощью библиотеки Windows Media Player.
Теперь уже событие клика кнопки Play/Pause выглядит так:
Загрузка радиостанций идёт из xml файла. То есть теоретически каждый желающий сможет добавить неограниченное количество радиостанций. Структура xml файла:
А вот и код который загружает радиостанции в ListBox:
Вот что в итоге получилось:

Этому результату я был очень рад.
Опять пролетело какое-то время. Я научился делать приложения для Windows Phone и, соответственно, уже понимал что-то в WPF.
И вот теперь наступает, на мой взгляд, самая интересная часть этой статьи.
Совсем недавно я встретил программу плеер VK – Meridian. Дизайн этой программы мне очень понравился. И тут я задался вопросом: «Почему бы не сделать программу Radio с таким же дизайном, ну или хотя бы что-то подобное?». К тому же был выложен исходник этой программы, которую я попытался изучить. Но сначала ничего дельного из этого не вышло. Пришлось искать. Поискав в google, наткнулся на статью на хабре. Скачал проект.
Создал новый проект WPF. Для приведения окна в желаемый вид добавил в проект скачанные файлы и привязал стиль к главному окну приложения. Поменял цвет окна, расположение значка, добавил новую кнопку “always on top” и вот что в итоге получилось:

Выглядит неплохо.
Для того, чтобы приложение было удобным и функциональным, решил разделить главное окно на 3 части. В первой части будут типы радиостанций, во второй список радиостанций относящиеся к определенному типу и, наконец, в третьей части будет логотип радиостанции. Стиль кнопки play/pause, volume, settings, listbox и scrollbar взял из исходников того самого Meridian Vk player.
После долгих мучений был получен такой код:
</Window.TaskbarItemInfo>
<Grid x:Name=«MainContentPanel» >
<Grid.ColumnDefinitions>
/>
/>
/>
</Grid.ColumnDefinitions>
<Grid x:Name=«LeftContentPanel»>
<Grid.RowDefinitions>
/>
/>
</Grid.RowDefinitions>
<ListBox ScrollViewer.HorizontalScrollBarVisibility=«Disabled» x:Name=«StationTypeList» Grid.Row=«0» Background=«Transparent» Style="{StaticResource CommonListBoxStyle}" ItemContainerStyle="{StaticResource SimpleListBoxItem}" SelectionChanged=«StationTypeList_SelectionChanged»>
<ListBox.ItemTemplate>
/>
</ListBox.ItemTemplate>
<Border Grid.Row=«1» BorderThickness=«0,1,0,0» BorderBrush="#828790">
<Grid x:Name=«ControlsPanel» Height=«100» >
<Grid.ColumnDefinitions>
/>
/>
/>
</Grid.ColumnDefinitions>
<StackPanel Grid.Column=«0»>
<Button x:Name=«SettingsButton» HorizontalAlignment=«Center» Height=«18» Width=«18» Margin=«12,41,0,0» Style="{StaticResource SettingsButtonStyle}" Click=«SettingsButton_Click»/>
<StackPanel Grid.Column=«1»>
/>
<StackPanel Grid.Column=«2»>
<controls:VolumeControl x:Name=«VolumeController» Volume=«100» Margin=«0,35,0,0» />
<Grid x:Name=«RightContentPannel» Grid.Column=«1» Background=«Transparent»>
<ListBox ScrollViewer.HorizontalScrollBarVisibility=«Disabled» x:Name=«StationsList» Grid.Row=«0» Background=«Transparent» Style="{StaticResource CommonListBoxStyle2}" ItemContainerStyle="{StaticResource SimpleListBoxItem}" SelectionChanged=«StationsList_SelectionChanged»>
<ListBox.ItemTemplate>
/>
</ListBox.ItemTemplate>
<Border Grid.Column=«2» BorderThickness=«1,0,0,0» BorderBrush="#828790" >
<Grid Grid.Row=«0» >
<Grid.RowDefinitions>
/>
/>
</Grid.RowDefinitions>
<Image x:Name=«StationImageA» Margin=«15,5,15,5» Grid.Row=«0» Source=«Images/splash.png» Stretch=«Fill»/>
<TextBlock x:Name=«StationNameA» Grid.Row=«1» Text="" FontSize=«17» Foreground="#007ACC" FontWeight=«ExtraBold» Margin=«15,0,5,0»/>
<TextBlock x:Name=«StationMusicA» Grid.Row=«1» Text="" FontSize=«17» Foreground=«White» Margin=«15,25,0,0» TextWrapping=«Wrap»/>
Результат:

Опять же чего-то не хватает, не так ли? Слишком серо и не интересно. Поэтому решил добавить фоновые картинки для каждой отдельной радиостанции и для общего приложения. Каждый тип радиостанций(список слева) обладает своей фоновой картинкой. Все названия типов радиостанций записаны в отдельный xml файл — stations.xml и для каждого типа свой xml файл уже с радиостанциями.
Это структура файла stations.xml:
Первая версия программы была сделана ужасно и выглядела так:

Сделал её когда опыта программирования и знаний было немного
Код приложения – это сплошные проверки “if”. Знаю, руки бы мне за такое оторвать! Например, событие происходящее при нажатии на кнопку Play:
if (isPlaying == true)
{
try
{
wmp.controls.stop();
MainButton.BackgroundImage = Properties.Resources.play;
isPlaying = false;
label1.Text = wmp.currentPlaylist.Item[0].name;
if (Properties.Settings.Default.Theme == "Orange")
{
PlayState.BackgroundImage = Image.FromFile("Skins/Orange/PlayingSmall_Playing.png");
}
else if (Properties.Settings.Default.Theme == "Aqua")
{
PlayState.BackgroundImage = Image.FromFile("Skins/Aqua/PlayingSmall_Playing.png");
}
else if (Properties.Settings.Default.Theme == "Red")
{
PlayState.BackgroundImage = Image.FromFile("Skins/Red/PlayingSmall_Playing.png");
}
}
catch
{
MessageBox.Show("Возникла непредвиденная ошибка.Отсутсвует интернет подключение.\nПроверьте интернет подключение или перезапустите приложение", "Ошибка");
}
}
else if (isPlaying == false)
{
try
{
wmp.controls.play();
MainButton.BackgroundImage = Properties.Resources.pause;
isPlaying = true;
label1.Text = wmp.currentPlaylist.Item[0].name;
if (Properties.Settings.Default.Theme == "Orange")
{
PlayState.BackgroundImage = Image.FromFile("Skins/Orange/PlayingSmall_Pause.png");
}
else if (Properties.Settings.Default.Theme == "Aqua")
{
PlayState.BackgroundImage = Image.FromFile("Skins/Aqua/PlayingSmall_Pause.png");
}
else if (Properties.Settings.Default.Theme == "Red")
{
PlayState.BackgroundImage = Image.FromFile("Skins/Red/PlayingSmall_Pause.png");
}
}
catch
{
MessageBox.Show("Возникла непредвиденная ошибка.Отсутсвует интернет подключение.\nПроверьте интернет подключение или перезапустите приложение", "Ошибка");
}
}
Или проверка изменения радиостанции:
try
{
if (RadioListView.SelectedItems[0].Text == "EuropaPlus")
{
wmp.URL = "http://cast.radiogroup.com.ua:8000/europaplus";
wmp.controls.play();
Properties.Settings.Default.LastRadio = "http://cast.radiogroup.com.ua:8000/europaplus";
label1.Text = wmp.currentPlaylist.Item[0].name;
Properties.Settings.Default.Save();
}
else if (RadioListView.SelectedItems[0].Text == "Наше Радио")
{
wmp.URL = "http://cast.radiogroup.com.ua:8000/nashe";
wmp.controls.play();
Properties.Settings.Default.LastRadio = "http://cast.radiogroup.com.ua:8000/nashe";
label1.Text = wmp.currentPlaylist.Item[0].name;
Properties.Settings.Default.Save();
}
……
Мало того код ужасный, плюс ещё имеется один большой недостаток, а именно, если какая-то радиостанция уйдёт в небытие, то её никак не убрать из программы, разве что подправить код и заново скомпилировать.
Но об этом я тогда даже не задумывался, так как радовался, что сделал, наверное, что-то стоящее и интересное.
Прошло некоторое время. Я учился программировать, экспериментировал. Очередной раз, когда задался вопросом «что бы ещё программировать?» я наткнулся на проект этого убогого радио и решил, что надо переписать программу
Создал новый проект Windows Forms. Поставил на форму кнопку, пару Label-ов, TabControl, ListBox, тот же самый Slider. Добавил новое окно настроек. Воспроизведение происходит по-прежнему с помощью библиотеки Windows Media Player.
Теперь уже событие клика кнопки Play/Pause выглядит так:
if (!isPlaying)
{
play.BackgroundImage = Properties.Resources.play;
isPlaying = true;
player.controls.pause();
}
else if (isPlaying)
{
play.BackgroundImage = Properties.Resources.pause;
isPlaying = false;
player.controls.play();
}
Загрузка радиостанций идёт из xml файла. То есть теоретически каждый желающий сможет добавить неограниченное количество радиостанций. Структура xml файла:
EuropaPlus
Наше Радио
Premium
EuropaPlus2
RRadio
Kazantip
Kiss FM
Radio Record
Horizon FM
Best FM
Abc Christmas
А вот и код который загружает радиостанции в ListBox:
string fileName = "stations.xml";
public void LoadStations()
{
if (File.Exists(fileName))
{
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load(fileName);
XmlNodeList stationNodes = xmlDoc.SelectNodes("//stations/station");
foreach (XmlNode stationNode in stationNodes)
{
string name = stationNode.InnerText;
//FullList – это listbox
FullList.Items.Add(name);
}
xmlDoc.Save(fileName);
}
else
{
MessageBox.Show("Не найдены радиостанции.Увы мой господин.\nНе судьба!Загрузите их с сайта разработчика или переустановите программу.");
}
}
Вот что в итоге получилось:

Этому результату я был очень рад.
Опять пролетело какое-то время. Я научился делать приложения для Windows Phone и, соответственно, уже понимал что-то в WPF.
И вот теперь наступает, на мой взгляд, самая интересная часть этой статьи.
Совсем недавно я встретил программу плеер VK – Meridian. Дизайн этой программы мне очень понравился. И тут я задался вопросом: «Почему бы не сделать программу Radio с таким же дизайном, ну или хотя бы что-то подобное?». К тому же был выложен исходник этой программы, которую я попытался изучить. Но сначала ничего дельного из этого не вышло. Пришлось искать. Поискав в google, наткнулся на статью на хабре. Скачал проект.
Создал новый проект WPF. Для приведения окна в желаемый вид добавил в проект скачанные файлы и привязал стиль к главному окну приложения. Поменял цвет окна, расположение значка, добавил новую кнопку “always on top” и вот что в итоге получилось:

Выглядит неплохо.
Для того, чтобы приложение было удобным и функциональным, решил разделить главное окно на 3 части. В первой части будут типы радиостанций, во второй список радиостанций относящиеся к определенному типу и, наконец, в третьей части будет логотип радиостанции. Стиль кнопки play/pause, volume, settings, listbox и scrollbar взял из исходников того самого Meridian Vk player.
После долгих мучений был получен такой код:
<Window x:Class="Radio.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="clr-namespace:Radio.Controls"
Title="Radio"
Height="460"
Width="620"
MinHeight="400"
MinWidth="510"
Icon="/icon.ico"
WindowStartupLocation="CenterScreen"
Style="{StaticResource VS2012WindowStyle}"
Loaded="Window_Loaded">
<Window.TaskbarItemInfo>
<TaskbarItemInfo x:Name="TaskbarItem">
<TaskbarItemInfo.ThumbButtonInfos>
<ThumbButtonInfo x:Name="TaskbarPlayButton" ImageSource="/Radio;component/Images/Taskbar/play_thumb.png" Click="TaskbarPlayButton_Click"/>
<ThumbButtonInfo x:Name="TaskbarPauseButton" ImageSource="/Radio;component/Images/Taskbar/pause_thumb.png" Click="TaskbarPauseButton_Click"/>
</TaskbarItemInfo.ThumbButtonInfos>
</Window.TaskbarItemInfo>
<Grid x:Name=«MainContentPanel» >
<Grid.ColumnDefinitions>
/>
/>
/>
</Grid.ColumnDefinitions>
<Grid x:Name=«LeftContentPanel»>
<Grid.RowDefinitions>
/>
/>
</Grid.RowDefinitions>
<ListBox ScrollViewer.HorizontalScrollBarVisibility=«Disabled» x:Name=«StationTypeList» Grid.Row=«0» Background=«Transparent» Style="{StaticResource CommonListBoxStyle}" ItemContainerStyle="{StaticResource SimpleListBoxItem}" SelectionChanged=«StationTypeList_SelectionChanged»>
<ListBox.ItemTemplate>
/>
</ListBox.ItemTemplate>
<Border Grid.Row=«1» BorderThickness=«0,1,0,0» BorderBrush="#828790">
<Grid x:Name=«ControlsPanel» Height=«100» >
<Grid.ColumnDefinitions>
/>
/>
/>
</Grid.ColumnDefinitions>
<StackPanel Grid.Column=«0»>
<Button x:Name=«SettingsButton» HorizontalAlignment=«Center» Height=«18» Width=«18» Margin=«12,41,0,0» Style="{StaticResource SettingsButtonStyle}" Click=«SettingsButton_Click»/>
<StackPanel Grid.Column=«1»>
/>
<StackPanel Grid.Column=«2»>
<controls:VolumeControl x:Name=«VolumeController» Volume=«100» Margin=«0,35,0,0» />
<Grid x:Name=«RightContentPannel» Grid.Column=«1» Background=«Transparent»>
<ListBox ScrollViewer.HorizontalScrollBarVisibility=«Disabled» x:Name=«StationsList» Grid.Row=«0» Background=«Transparent» Style="{StaticResource CommonListBoxStyle2}" ItemContainerStyle="{StaticResource SimpleListBoxItem}" SelectionChanged=«StationsList_SelectionChanged»>
<ListBox.ItemTemplate>
/>
</ListBox.ItemTemplate>
<Border Grid.Column=«2» BorderThickness=«1,0,0,0» BorderBrush="#828790" >
<Grid Grid.Row=«0» >
<Grid.RowDefinitions>
/>
/>
</Grid.RowDefinitions>
<Image x:Name=«StationImageA» Margin=«15,5,15,5» Grid.Row=«0» Source=«Images/splash.png» Stretch=«Fill»/>
<TextBlock x:Name=«StationNameA» Grid.Row=«1» Text="" FontSize=«17» Foreground="#007ACC" FontWeight=«ExtraBold» Margin=«15,0,5,0»/>
<TextBlock x:Name=«StationMusicA» Grid.Row=«1» Text="" FontSize=«17» Foreground=«White» Margin=«15,25,0,0» TextWrapping=«Wrap»/>
Результат:

Опять же чего-то не хватает, не так ли? Слишком серо и не интересно. Поэтому решил добавить фоновые картинки для каждой отдельной радиостанции и для общего приложения. Каждый тип радиостанций(список слева) обладает своей фоновой картинкой. Все названия типов радиостанций записаны в отдельный xml файл — stations.xml и для каждого типа свой xml файл уже с радиостанциями.
Это структура файла stations.xml:
<?xml version="1.0" encoding="utf-8"?>
<stations version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/">
Club
Stations/club.xml
Images/StationTypes/club.png
Pop
Stations/pop.xml
Images/StationTypes/pupmusic.png
Rap/HipHop/RnB
Stations/rap_hiphop.xml
Images/StationTypes/rap.png
Игровые
Stations/gaming.xml
Images/StationTypes/game.png
……
К примеру структура файла club.xml:
<?xml version="1.0" encoding="utf-8"?>
<stations version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/">
161FM
stream.161.fm:8000/256
Images/Stations/club/161fm.png
1Mix Radio – EDM Stream
georgios.1mix.co.uk:8022/;stream.nsv
Images/Stations/club/1mixEDM.png
ElectroPOP
pub5.di.fm/di_electropop
Shtorm.FM клубный
live.shtorm.fm:8000/mp3
Images/Stations/club/shtormclub.png
…..
При желании каждый сможет добавить свои разделы, радиостанции и их картинки.
В результате получилось это:

Для воспроизведения радиостанции была выбрана библиотека Bass.Net. Важной отличительной способностью это библиотеки в том, что она умеет показывать имя текущей песни. Решил ее использовать. Но с самого начала возникли проблемы. При выборе радиостанции окно застревало на несколько секунд. Понятно, что происходит подключение к интернет радиостанции, загрузка и наконец его воспроизведение. Пришлось сделать событие воспроизведения радиостанции через BackgroundWorker. И всё заработало должным образом. Ниже представлены скрины программы.






При обнаружении ошибок пишите в ЛС.