You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用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添加配置:
    <Application.Resources>
        <BitmapImage x:Key="DefaultCover" UriSource="pack://application:,,,/Assets/DefaultCover.png"/>
    </Application.Resources>
    
    然后修改Image绑定:
    <Image Source="{Binding AlbumCoverUrl, FallbackValue={StaticResource DefaultCover}}"/>
    
  • API请求失败:检查Token有效性,确保请求头格式为Bearer {Token},注意Token过期后需调用刷新接口重新获取。

内容的提问来源于stack exchange,提问作者Witch Of Greed

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.19 01:17:42