Сохранение индекса Item из Listbox WinForms
есть у меня ListBox с песнями. Хочу реализовать поиск, но пока он только оставляет названия песен, при выборе песен из поиска, программа выбирает песню из основного listbox, а не отфильтрованного. Скорее это связано с тем, что я не оставляю еще индекс из реального listbox, если это так, то я не знаю решения. Как правильно реализовать?
private void My_Music_Load(object sender, EventArgs e)
{
ActiveControl = SearchTextBox;
AudioList = vk.GetAudio();
foreach (Thread item in ThreadsList)
{
item.Start();
}
foreach (var audio in AudioList)
{
Music_List.Items.Add(audio.Artist + " - " + audio.Title);
}
}
private void Music_List_Click(object sender, EventArgs e)
{
int selectedIndex = Music_List.SelectedIndex;
VkNet.Model.Attachments.Audio audio = AudioList[selectedIndex];
string Subtitle = audio.Subtitle;
Invoke(() => TitleBox.Text = audio.Title + " " + audio.Subtitle);
Invoke(() => ArtistBox.Text = audio.Artist);
if (audio.Album != null)
{
Invoke(() => AlbumImage.Image = System.Drawing.Image.FromFile($"Albums\\{audio.Album.Id}.png"));
}
else
{
AlbumImage.Image = Properties.Resources.music;
}
}
private void Music_List_DoubleClick(object sender, EventArgs e)
{
int selectedIndex = Music_List.SelectedIndex;
if (selectedIndex != -1)
{
Task.Run(() => PlayMusic(selectedIndex));
}
}
private void SearchTextBox_TextChanged(object sender, EventArgs e)
{
Music_List.Items.Clear();
foreach (var audio in AudioList)
{
if (audio.Artist.ToLower().Contains(SearchTextBox.Text.ToLower()) || audio.Title.ToLower().Contains(SearchTextBox.Text.ToLower()))
{
Music_List.Items.Add(audio.Artist + " - " + audio.Title);
}
}
}
private async void PlayMusic(int ID)
{
Invoke(() => Player.Stop());
VkNet.Model.Attachments.Audio audio = AudioList[ID];
CurrentTrackID = ID;
if (RandomPlay)
{
RandomCurrentTrackID = RandomTrackIDList.IndexOf(ID);
}
string Subtitle = audio.Subtitle;
Invoke(() => TitleBox.Text = audio.Title + " " + audio.Subtitle);
Invoke(() => ArtistBox.Text = audio.Artist);
if (audio.Album != null)
{
Invoke(() => AlbumImage.Image = System.Drawing.Image.FromFile($"Albums\\{audio.Album.Id}.png"));
}
else
{
AlbumImage.Image = Properties.Resources.music;
}
await Task.Run(() => LoadMusic(ID));
if (CurrentTrackID == ID)
{
try
{
Invoke(() => Player.Open(new Uri($"{Directory.GetCurrentDirectory()}\\Music\\{audio.Title}_{Subtitle}_{audio.Artist}.mp3")));
Invoke(() => Player.Play());
}
catch { }
}
}
public static async Task LoadMusic(int ID)
{
VkNet.Model.Attachments.Audio audio = AudioList[ID];
string Subtitle = audio.Subtitle;
string Title = audio.Title;
string Artist = audio.Artist;
string FileName;
if (!File.Exists($"Music\\{Title}_{Subtitle}_{Artist}.mp3"))
{
try
{
FileName = $"{Title.Substring(0, 2)}{Artist.Substring(0, 2)}{random.Next(0, 999999)}";
}
catch
{
FileName = $"{random.Next(0, 999999)}";
}
ExecuteCommand($"streamlink {audio.Url} live --output {FileName}.ts");
ExecuteCommand($"ffmpeg -i {FileName}.ts -vn -ar 44100 -ac 2 -b:a 192k {FileName}.mp3");
try
{
File.Delete($"{FileName}.ts");
}
catch { }
try
{
File.Move($"{FileName}.mp3", $"Music\\{Title}_{Subtitle}_{Artist}.mp3");
}
catch { }
}
}
Ответы (2 шт):
Не понятно, в чём проблема, но код можно улучшить:
private void SearchTextBox_TextChanged(object sender, EventArgs e) {
Music_List.Items.Clear();
var st = SearchTextBox.Text.ToLower();
Music_List.Items.AddRange(AudioList
.Where(w => w.Artist.ToLower().Contains(st) || w.Title.ToLower().Contains(st))
.Select(s => s.Artist + " - " + s.Title));
}
Если нужно идентифицировать выбранный трек, то стоит добавить какой-нибудь Id, если он есть:
.Select(s => [s.Artist + " - " + s.Title, s.Id]));
Или пронумеровать элементы AudioList:
var i = 0;
Music_List.Items.AddRange(AudioList.Select(s => new {A = s, N = i++ })
.Where(w => w.A.Artist.ToLower().Contains(st) || w.A.Title.ToLower().Contains(st))
.Select(s => new object[] {s.A.Artist + " - " + s.A.Title, s.N}));
Потом по клику в Music_List получаем N кликнутого и по нему выбираем трек из AudioList:
void Music_List_SelectedIndexChanged(object sender, System.EventArgs e) {
var audio = AudioList[(int)((object [])Music_List.SelectedItem)[1]];
// ^ тут нужо уточнить, как к номеру добраться
}
Так как сторонний класс Audio
не переписать, а нужен источник данных для отображения, создам класс-обёртку.
using VkNet.Model.Attachments;
public class AudioContainer
{
public Audio Audio { get; }
public string DisplayTitle { get; }
public AudioContainer(Audio audio)
{
Audio = audio;
DisplayTitle = $"{audio.Artist} - {audio.Title}";
}
}
Поменяю тип AudioList
, кстати использование суффиксов list
и array
неудобно, давайте сразу назовём массив правильно.
private AudioContainer[] Audios { get; set; }
Теперь метод загрузки, здесь какрй-то пуск потоков, я его уберу так как оно к делу не относится.
private void My_Music_Load(object sender, EventArgs e)
{
var audios = vk.GetAudio();
Audios = audios.Select(x => new AudioContainer(x)).ToArray();
Music_List.DataSource = Audios;
Music_List.DisplayMember = nameof(AudioContainer.DisplayTitle);
}
ListBox
загружен.
Если смущает Select
, можно сделать жто циклом
int count = (int)audios.TotalCount;
Audios = new AudioContainer[count];
for (int i = 0; i < count; i++)
{
Audios[i] = new AudioContainer(audios[i]);
}
Но Select.ToArray
выглядит короче, верно? Поизучайте Linq
.
Теперь при вводе текста в фильтр делаем так
private void SearchTextBox_TextChanged(object sender, EventArgs e)
{
TextBox box = (TextBox)sender;
Music_List.DataSource = Audios
.Where(x => x.DisplayTitle.Contains(box.Text, StringComparison.CurrentCultureIgnoreCase))
.ToArray();
}
Теперь перепишем PlayMusic
, чтобы он принимал Audio
, а не индекс. Кстати, етод асинзронный, асинзронность будем использовать правильно.
private async Task PlayMusic(AudioContainer container)
{
Player.Stop();
var audio = container.Audio;
TitleBox.Text = audio.Title + " " + audio.Subtitle;
ArtistBox.Text = audio.Artist;
if (audio.Album != null)
{
AlbumImage.Image = System.Drawing.Image.FromFile(Path.Combine("Albums", $"{audio.Album.Id}.png"));
}
else
{
AlbumImage.Image = Properties.Resources.music;
}
string path = Path.Combine(Environment.CurrentDirectory, "Music", $"{audio.Title}_{audio.Subtitle}_{audio.Artist}.mp3");
await Task.Run(() => LoadMusic(audio, path));
Player.Open(new Uri(path));
Player.Play();
}
Почистил лишние инвоки.
Теперь вызов. Нет никакого смысла здесь в Task.Run
.
private async void Music_List_DoubleClick(object sender, EventArgs e)
{
var container = Music_List.SelectedItem as AudioContainer;
try
{
if (container != null)
{
await PlayMusic(container.Audio);
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
За компанию, вот вам клик
private void Music_List_Click(object sender, EventArgs e)
{
var container = Music_List.SelectedItem as AudioContainer;
if (container == null)
return;
var audio = container.Audio;
TitleBox.Text = audio.Title + " " + audio.Subtitle;
ArtistBox.Text = audio.Artist;
if (audio.Album != null)
{
AlbumImage.Image = System.Drawing.Image.FromFile(Path.Combine("Albums", "{audio.Album.Id}.png");
}
else
{
AlbumImage.Image = Properties.Resources.music;
}
}
Метод загрузки немного упростим
public static void LoadMusic(Audio audio, string path)
{
if (!File.Exists(path))
{
string tmpFile = Path.GetTempFileName();
ExecuteCommand($"streamlink {audio.Url} live --output {tmpFile}.ts");
ExecuteCommand($"ffmpeg -i {tmpFile}.ts -vn -ar 44100 -ac 2 -b:a 192k {tmpFile}.mp3");
File.Delete($"{tmpFile}.ts");
File.Move($"{tmpFile}.mp3", path);
}
}
Теперь переключение между треками, покажу фрагменты кода.
private AudioContainer CurrentTrack;
Текущий
CurrentTrack = Audios[0];
Сбросить
CurrentTrack = null;
Следующий без зацикливания
int index = Array.IndexOf(Audios, CurrentTrack);
if (index >= Audios.Length - 1 || index < 0)
CurrentTrack = null;
else
CurrentTrack = Audios[index + 1];
Следующий с зацикливанием
if (index < 0)
CurrentTrack = null;
else
CurrentTrack = Audios[(index + 1) % Audios.Length];
Случайный
if (index >= 0 && Audios.Length > 1)
{
while (true)
{
int rnd = Random.Shared.Next(Audios.Length);
if (rnd != index)
{
CurrentTrack = Audios[rnd];
break;
}
}
}
else
CurrentTrack = null;