如何用C#在WPF UI中实时展示Spotify播放数据?
WPF实时更新Spotify播放数据的UI解决方案
核心思路
WPF UI更新必须在UI线程执行,结合MVVM模式实现数据绑定的自动刷新,同时通过定时轮询获取实时播放状态(Spotify API暂不支持Webhook推送,轮询是最可靠的方式)。
关键技术点
- INotifyPropertyChanged接口:实现ViewModel属性变更通知,让UI自动响应数据更新
- Dispatcher:确保跨线程操作UI时切换到UI线程
- 原生HttpClient:替代第三方包,减少依赖降低报错概率
- DispatcherTimer:定时触发API请求,获取最新播放状态
完整代码示例
1. 播放数据ViewModel(实现属性变更通知)
using System.ComponentModel; using System.Runtime.CompilerServices; public class SpotifyPlayerViewModel : INotifyPropertyChanged { private string _trackName; private string _artistName; private string _albumCoverUrl; private string _trackDuration; public string TrackName { get => _trackName; set { _trackName = value; OnPropertyChanged(); } } public string ArtistName { get => _artistName; set { _artistName = value; OnPropertyChanged(); } } public string AlbumCoverUrl { get => _albumCoverUrl; set { _albumCoverUrl = value; OnPropertyChanged(); } } public string TrackDuration { get => _trackDuration; set { _trackDuration = value; OnPropertyChanged(); } } public event PropertyChangedEventHandler PropertyChanged; protected void OnPropertyChanged([CallerMemberName] string propertyName = null) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } }
2. 主窗口XAML(UI数据绑定)
<Window x:Class="SpotifyPlayerTracker.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:SpotifyPlayerTracker" Title="Spotify播放状态" Height="320" Width="400"> <Window.DataContext> <local:SpotifyPlayerViewModel /> </Window.DataContext> <Grid Margin="20"> <StackPanel HorizontalAlignment="Center"> <Image Source="{Binding AlbumCoverUrl}" Width="180" Height="180" Margin="0 0 0 15" Stretch="UniformToFill" /> <TextBlock Text="{Binding TrackName}" FontSize="18" FontWeight="Bold" TextWrapping="Wrap" HorizontalAlignment="Center"/> <TextBlock Text="{Binding ArtistName}" FontSize="14" Foreground="#666666" HorizontalAlignment="Center" Margin="0 5 0 0"/> <TextBlock Text="{Binding TrackDuration}" FontSize="12" Margin="0 8 0 0" HorizontalAlignment="Center"/> </StackPanel> </Grid> </Window>
3. API调用与定时更新逻辑
using System; using System.Collections.Generic; using System.Net.Http; using System.Net.Http.Headers; using System.Threading.Tasks; using System.Windows; using System.Windows.Threading; public partial class MainWindow : Window { private readonly SpotifyPlayerViewModel _viewModel; private readonly HttpClient _httpClient; private readonly DispatcherTimer _updateTimer; private const string SpotifyApiUrl = "https://api.spotify.com/v1/me/player/currently-playing"; private string _accessToken = "YOUR_VALID_SPOTIFY_ACCESS_TOKEN"; // 替换为实际有效Token public MainWindow() { InitializeComponent(); _viewModel = (SpotifyPlayerViewModel)DataContext; // 初始化HttpClient _httpClient = new HttpClient(); UpdateAuthorizationHeader(); // 定时轮询(每2秒更新一次,可根据需求调整) _updateTimer = new DispatcherTimer { Interval = TimeSpan.FromSeconds(2) }; _updateTimer.Tick += async (s, e) => await RefreshCurrentTrackAsync(); _updateTimer.Start(); } private void UpdateAuthorizationHeader() { _httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", _accessToken); } private async Task RefreshCurrentTrackAsync() { try { var response = await _httpClient.GetAsync(SpotifyApiUrl); if (response.StatusCode == System.Net.HttpStatusCode.Unauthorized) { // 这里添加Token刷新逻辑(Spotify Token有效期1小时) // _accessToken = await RefreshSpotifyTokenAsync(); // UpdateAuthorizationHeader(); return; } if (!response.IsSuccessStatusCode) { _viewModel.TrackName = "无播放内容"; _viewModel.ArtistName = ""; _viewModel.AlbumCoverUrl = ""; _viewModel.TrackDuration = ""; return; } var trackData = await response.Content.ReadFromJsonAsync<SpotifyCurrentTrackResponse>(); if (trackData?.Item == null) { _viewModel.TrackName = "无播放内容"; _viewModel.ArtistName = ""; _viewModel.AlbumCoverUrl = ""; _viewModel.TrackDuration = ""; return; } // 更新ViewModel属性,自动触发UI刷新 _viewModel.TrackName = trackData.Item.Name; _viewModel.ArtistName = string.Join(", ", trackData.Item.Artists.Select(a => a.Name)); _viewModel.AlbumCoverUrl = trackData.Item.Album.Images[0]?.Url ?? ""; _viewModel.TrackDuration = TimeSpan.FromMilliseconds(trackData.Item.DurationMs).ToString(@"mm\:ss"); } catch (Exception ex) { // 可添加日志记录,避免程序崩溃 } } } // 对应Spotify API返回的JSON结构(仅保留所需字段) public class SpotifyCurrentTrackResponse { public TrackItem Item { get; set; } } public class TrackItem { public string Name { get; set; } public List<Artist> Artists { get; set; } public Album Album { get; set; } public long DurationMs { get; set; } } public class Artist { public string Name { get; set; } } public class Album { public List<Image> Images { get; set; } } public class Image { public string Url { get; set; } }
常见问题解决
- 跨线程UI更新报错:如果API请求在非UI线程执行,需用
Dispatcher包裹属性更新:Application.Current.Dispatcher.Invoke(() => { _viewModel.TrackName = trackData.Item.Name; }); - 图片不显示:WPF默认限制部分HTTPS图片加载,可在App.xaml添加配置:
然后修改Image绑定:<Application.Resources> <BitmapImage x:Key="DefaultCover" UriSource="pack://application:,,,/Assets/DefaultCover.png"/> </Application.Resources><Image Source="{Binding AlbumCoverUrl, FallbackValue={StaticResource DefaultCover}}"/> - API请求失败:检查Token有效性,确保请求头格式为
Bearer {Token},注意Token过期后需调用刷新接口重新获取。
内容的提问来源于stack exchange,提问作者Witch Of Greed
相关产品推荐
相关产品推荐

