WPFとNAudioで音楽プレイヤーを作る~第5回:フォルダ内の楽曲を読み込んでListViewに表示させる~
第5回:フォルダ内の楽曲を読み込んでListViewに表示させる
今回のポイント
TagLibで音楽の情報を読み取る
NotifyPropertyChangedメソッドでViewに変更を通知する
TagLibを導入する
mp3ファイルに書き込まれた情報を読み取るためにTagLibを使用します。
プロジェクト > NuGetパッケージの管理 から TagLibSharp と検索してインストールするだけです。
下側のものをインストールしました。なんか他にもいろいろ種類があったんですがまあいいでしょう。
![](https://assets.st-note.com/img/1688438031372-vnUVw6SdN3.png?width=1200)
フォルダ内の楽曲を読み込む
今回は起動時に特定のフォルダのmp3ファイルを探索してListViewに表示するようにしたいと思います。
まずはCommonフォルダ配下にMusic.csファイルを作成します。
![](https://assets.st-note.com/img/1688438053978-LKh8zMiFAN.png)
Musicクラスには6つのプロパティがあります。
Music.cs
using System;
namespace MusicPlayer.MVVM.Common
{
public class Music
{
/// <summary>
/// 音楽ファイルの絶対パス
/// </summary>
public string Path { get; set; }
/// <summary>
/// タイトル
/// </summary>
public string? Title { get; set; }
/// <summary>
/// アーティスト名
/// </summary>
public string? Artist { get; set; }
/// <summary>
/// アルバム名
/// </summary>
public string? Album { get; set; }
/// <summary>
/// 曲の長さ
/// </summary>
public TimeSpan Time { get; set; }
/// <summary>
/// トラック番号
/// </summary>
public uint Track { get; set; }
}
}
次にModelフォルダの配下にMainWindowModel.csファイルを作成します。
MainWindowModel.cs
using MusicPlayer.MVVM.Common;
using System;
using System.Collections.Generic;
namespace MusicPlayer.MVVM.Model
{
public class MainWindowModel
{
/// <summary>
/// 音楽ファイルの読込処理
/// </summary>
public List<Music> LoadMusicFiles()
{
// MUSIC配下のmp3ファイルのパスを全取得
string[] filePaths = System.IO.Directory.GetFiles(@"C:\ユーザー名\Music\MUSIC", "*.mp3", System.IO.SearchOption.AllDirectories);
// 音楽の情報を読み込んでリストに入れる
List<Music> musicList = new List<Music>();
foreach (string filePath in filePaths)
{
TagLib.File file = TagLib.File.Create(filePath);
Music music = new Music()
{
Path = filePath,
Title = file.Tag.Title,
Album = file.Tag.Album,
Artist = file.Tag.Performers[0],
Track = file.Tag.Track,
Time = new TimeSpan(0, 0, file.Properties.Duration.Minutes, file.Properties.Duration.Seconds),
};
musicList.Add(music);
}
return musicList.OrderBy(m => m.Album)
.ThenBy(m => m.Track)
.ToList();
}
}
}
LoadMusicFilesメソッドではまず指定したフォルダ配下のmp3ファイルを探索します。
System.IO.Directory.GetFilesメソッドの第一引数に探索したいディレクトリ、第二引数には探索するファイルの拡張子を指定します。
今回はマイミュージックフォルダ配下にMUSICフォルダを作成してその中にmp3ファイルを配置しています。
第三引数にSystem.IO.SearchOption.AllDirectoriesを指定するとサブフォルダ内も探索してくれます。
探索が完了するとfilePaths変数にファイルの完全パスが格納されます。
// MUSIC配下のmp3ファイルのパスを全取得
string[] filePaths = System.IO.Directory.GetFiles(@"C:\ユーザー名\Music\MUSIC", "*.mp3", System.IO.SearchOption.AllDirectories);
完全パスが取得できたら先程作成した`Music`クラスのリストを作成します。
TagLib.File file = TagLib.File.Create(filePath);で一曲分の情報を読み込みます。
読み込んだ情報から`Music`クラスの各プロパティを設定しつつオブジェクトを作成します。
// 音楽の情報を読み込んでリストに入れる
List<Music> musicList = new List<Music>();
foreach (string filePath in filePaths)
{
TagLib.File file = TagLib.File.Create(filePath);
Music music = new Music()
{
Path = filePath,
Title = file.Tag.Title,
Album = file.Tag.Album,
Artist = file.Tag.Performers[0],
Track = file.Tag.Track,
Time = new TimeSpan(0, 0, file.Properties.Duration.Minutes, file.Properties.Duration.Seconds),
};
musicList.Add(music);
}
最後にmusicListをLINQで並び替えてから返却しています。
return musicList.OrderBy(m => m.Album)
.ThenBy(m => m.Track)
.ToList();
起動時に音楽ファイルを読み込む
MainWindowModelに作成したLoadMusicFilesメソッドを起動時に呼ぶためにMainWindowViewModelのコンストラクタを修正します。
MainWindowViewModel.cs
using MusicPlayer.MVVM.Common;
using MusicPlayer.MVVM.Model;
using NAudio.Wave;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows.Forms;
using System.Windows.Input;
namespace MusicPlayer.MVVM.ViewModel
{
class MainWindowViewModel : INotifyPropertyChanged
{
// 変数の更新通知用
public event PropertyChangedEventHandler PropertyChanged;
private MainWindowModel _model = new MainWindowModel();
// 音楽の一覧
private List<Music> _musicList;
public List<Music> MusicList
{
get { return this._musicList; }
set
{
this._musicList = value;
NotifyPropertyChanged("MusicList");
}
}
private void NotifyPropertyChanged(String info)
{
if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(info));
}
/// <summary>再生ボタンが押されたときのコマンド</summary>
public ICommand OnPlayButtonClickCommand { get; set; }
/// <summary>
/// コンストラクタ
/// </summary>
public MainWindowViewModel()
{
// フォルダ内の音楽の読込
MusicList = this._model.LoadMusicFiles();
// 再生ボタン用のコマンド
OnPlayButtonClickCommand = new MyRelayCommand(OnPlayButtonClick);
}
private void OnPlayButtonClick()
{
var dialog = new OpenFileDialog();
if (dialog.ShowDialog() == DialogResult.OK)
{
WaveOutEvent outputDevice = new WaveOutEvent();
AudioFileReader afr = new AudioFileReader(dialog.FileName);
outputDevice.Init(afr);
outputDevice.Play();
}
}
}
}
MainWindowModelのインスタンスを用意します。
private MainWindowModel _model = new MainWindowModel();
Musicのリストを扱うプロパティを用意します。
NotifyPropertyChanged("MusicList");を記述することで、MusicListプロパティに値の変化があったときにViewに通知されます。
めちゃくちゃ重要ポイントです。
// 音楽の一覧
private List<Music> _musicList;
public List<Music> MusicList
{
get { return this._musicList; }
set
{
this._musicList = value;
NotifyPropertyChanged("MusicList");
}
}
最後にコンストラクタでMusicListプロパティを初期化します。
/// <summary>
/// コンストラクタ
/// </summary>
public MainWindowViewModel()
{
// フォルダ内の音楽の読込
MusicList = this._model.LoadMusicFiles();
// 再生ボタン用のコマンド
OnPlayButtonClickCommand = new MyRelayCommand(OnPlayButtonClick);
}
音楽を一覧表示する
MusicListの中身をListViewで一覧表示します。
まずは`ListView`の見た目をカスタムしやすくするためにListViewLayoutManagerというプラグインを導入します。
例によってプロジェクト > NuGetパッケージの管理からListViewLayoutManagerと検索してインストールします。
![](https://assets.st-note.com/img/1688438799943-HEwp2FJkwX.png?width=1200)
それではListViewを修正します。
MainWindow.xaml
<Window x:Class="MusicPlayer.MVVM.View.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:MusicPlayer"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800"
xmlns:viewmodels="clr-namespace:MusicPlayer.MVVM.ViewModel"
xmlns:ui="http://schemas.modernwpf.com/2019"
ui:WindowHelper.UseModernWindowStyle="True"
d:DataContext="{d:DesignInstance Type=viewmodels:MainWindowViewModel}"
Background="#262626"
Foreground="White"
MinHeight="600"
MinWidth="950"
xmlns:ctrl="clr-namespace:Itenso.Windows.Controls.ListViewLayout;assembly=Itenso.Windows.Controls.ListViewLayout"
>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="150" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition Height="80"/>
</Grid.RowDefinitions>
<Button
Grid.Column="0" Grid.Row="1"
Width="30" Height="30"
Content="▶"
Command="{Binding OnPlayButtonClickCommand}"
/>
<ui:NavigationView Background="#131313"
Grid.Column="0" Grid.Row="0"
x:Name="NaviView"
IsBackButtonVisible="Collapsed"
IsSettingsVisible="False"
IsTitleBarAutoPaddingEnabled="False"
IsPaneToggleButtonVisible="False"
PaneDisplayMode="Left"
Width="150"
>
<ui:NavigationView.MenuItems>
<ui:NavigationViewItem
Content="Music"
Icon="Audio"
IsSelected="True" />
</ui:NavigationView.MenuItems>
</ui:NavigationView>
<Grid Background="#131313"
Grid.Column="1" Grid.Row="0">
<Grid.RowDefinitions>
<RowDefinition Height="80"/>
<RowDefinition />
</Grid.RowDefinitions>
<TextBlock Grid.Row="0"
Text="Music"
Margin="30,30,30,0"
FontWeight="Bold"
FontSize="24" />
<ListView Grid.Row="1"
Margin="30,0,0,0"
x:Name="MusicList"
SelectionMode="Single"
ctrl:ListViewLayoutManager.Enabled="true"
ItemsSource="{Binding MusicList}"
>
<ListView.View>
<GridView ScrollViewer.VerticalScrollBarVisibility="Auto">
<GridViewColumn Header="Title"
DisplayMemberBinding="{Binding Title}"
ctrl:ProportionalColumn.Width="8"/>
<GridViewColumn Header="Artist"
DisplayMemberBinding="{Binding Artist}"
ctrl:ProportionalColumn.Width="4"/>
<GridViewColumn Header="Album"
DisplayMemberBinding="{Binding Album}"
ctrl:ProportionalColumn.Width="5"/>
<GridViewColumn Header="Track"
DisplayMemberBinding="{Binding Track}"
ctrl:ProportionalColumn.Width="2"/>
<GridViewColumn Header="Time"
DisplayMemberBinding="{Binding Time}"
ctrl:ProportionalColumn.Width="3"/>
</GridView>
</ListView.View>
</ListView>
</Grid>
</Grid>
</Window>
まずWindowタグ内に以下を追加します。
先程インストールしたListViewLayoutManagerが使えるようになります。
xmlns:ctrl="clr-namespace:Itenso.Windows.Controls.ListViewLayout;assembly=Itenso.Windows.Controls.ListViewLayout"
ListViewにはctrl:ListViewLayoutManager.Enabled="true"とItemsSource="{Binding MusicList}"を追加しました。
ListViewLayoutManagerの有効化とViewModelのMusicListプロパティのバインドをしています。
またx:Name="MusicList"とViewModelのNotifyPropertyChanged("MusicList");で設定している名前が一致していることを確認してください。
<ListView Grid.Row="1"
Margin="30,0,0,0"
x:Name="MusicList"
SelectionMode="Single"
ctrl:ListViewLayoutManager.Enabled="true"
ItemsSource="{Binding MusicList}"
>
ListView.Viewでは表示するものの設定をしています。
DisplayMemberBindingでは表示するMusicクラスのプロパティをバインドしています。
ctrl:ProportionalColumn.Width=""ではListViewLayoutManagerを使用して列の幅を割合で指定しています。
GridのようにWidth="8*"とは記述できないのでListViewLayoutManagerを使用しています。
<ListView.View>
<GridView ScrollViewer.VerticalScrollBarVisibility="Auto">
<GridViewColumn Header="Title"
DisplayMemberBinding="{Binding Title}"
ctrl:ProportionalColumn.Width="8"/>
<GridViewColumn Header="Artist"
DisplayMemberBinding="{Binding Artist}"
ctrl:ProportionalColumn.Width="4"/>
<GridViewColumn Header="Album"
DisplayMemberBinding="{Binding Album}"
ctrl:ProportionalColumn.Width="5"/>
<GridViewColumn Header="Track"
DisplayMemberBinding="{Binding Track}"
ctrl:ProportionalColumn.Width="2"/>
<GridViewColumn Header="Time"
DisplayMemberBinding="{Binding Time}"
ctrl:ProportionalColumn.Width="3"/>
</GridView>
</ListView.View>
ここまででこんな感じの見た目になっているでしょうか。
![](https://assets.st-note.com/img/1688439099364-c9VsrcGWgP.png?width=1200)
次回は再生ボタンの修正をしたいと思います。
参考
https://www.urablog.xyz/entry/2018/05/02/070000