C# Avalonia没有内置的MediaElement,我们用LibVLC实现一个就行了。Avalonia官方实现的,要收费。配置文章里更新了。https://www.cnblogs.com/dalgleish/p/18967204
MediaElement类,已完善功能。之后会有专门一章来测试这个播放器代码,现在可以简单使用播放功能。
using Avalonia; using Avalonia.Controls; using Avalonia.Layout; using Avalonia.Media; using Avalonia.Platform; using Avalonia.Threading; using LibVLCSharp.Avalonia; using LibVLCSharp.Shared; using System; using System.Buffers; using System.Diagnostics; using System.IO; using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; namespace Shares.Avalonia.CustomControls { public class MediaElement : UserControl, IDisposable { private static LibVLC? libVlc; private VideoView? videoView; private MediaPlayer? mediaPlayer; private Media? currentMedia; private EventHandler<MediaParsedChangedEventArgs>? parsedChangedHandler; private Stream? currentStream; private MediaInput? currentMediaInput; private bool isDisposed; private bool isStopping; private bool updatingTimeFromPlayer; private bool updatingMuteFromPlayer; private DispatcherTimer? seekDebounceTimer; private long pendingSeekTime; private bool hasPendingSeek; private bool isScrubbing; private bool stretchPending; private CancellationTokenSource? sourceCts; private bool isVideoDetached; private EventHandler<EventArgs>? pausedHandler; private EventHandler<EventArgs>? stoppedHandler; private EventHandler<EventArgs>? playingHandler; private EventHandler<MediaPlayerVolumeChangedEventArgs>? volumeChangedHandler; private EventHandler<MediaPlayerTimeChangedEventArgs>? timeChangedHandler; private EventHandler<MediaPlayerLengthChangedEventArgs>? lengthChangedHandler; private EventHandler<EventArgs>? mutedHandler; private EventHandler<EventArgs>? unmutedHandler; private EventHandler<MediaPlayerBufferingEventArgs>? bufferingHandler; private EventHandler<EventArgs>? endReachedHandler; public event EventHandler? Paused; public event EventHandler? Stopped; public event EventHandler? Playing; public event EventHandler? VolumeChanged; public event EventHandler? TimeChanged; public event EventHandler? LengthChanged; public event EventHandler? Muted; public event EventHandler? Unmuted; public event EventHandler? Buffering; public event EventHandler? MediaEnded; public static readonly StyledProperty<Uri> SourceProperty = AvaloniaProperty.Register<MediaElement, Uri>(nameof(Source)); public Uri Source { get => GetValue(SourceProperty); set => SetValue(SourceProperty, value); } public static readonly StyledProperty<uint> VideoWidthProperty = AvaloniaProperty.Register<MediaElement, uint>(nameof(VideoWidth)); public uint VideoWidth { get => GetValue(VideoWidthProperty); private set => SetValue(VideoWidthProperty, value); } public static readonly StyledProperty<uint> VideoHeightProperty = AvaloniaProperty.Register<MediaElement, uint>(nameof(VideoHeight)); public uint VideoHeight { get => GetValue(VideoHeightProperty); private set => SetValue(VideoHeightProperty, value); } public static readonly StyledProperty<bool> AutoPlayProperty = AvaloniaProperty.Register<MediaElement, bool>(nameof(AutoPlay), false); public bool AutoPlay { get => GetValue(AutoPlayProperty); set => SetValue(AutoPlayProperty, value); } public static readonly StyledProperty<int> VolumeProperty = AvaloniaProperty.Register<MediaElement, int>(nameof(Volume), 100); public int Volume { get => GetValue(VolumeProperty); set => SetValue(VolumeProperty, value); } public static readonly StyledProperty<bool> IsMutedProperty = AvaloniaProperty.Register<MediaElement, bool>(nameof(IsMuted), false); public bool IsMuted { get => GetValue(IsMutedProperty); set => SetValue(IsMutedProperty, value); } public static readonly StyledProperty<long> TimeProperty = AvaloniaProperty.Register<MediaElement, long>(nameof(Time), 0L); public long Time { get => GetValue(TimeProperty); set => SetValue(TimeProperty, value); } public static readonly StyledProperty<long> LengthProperty = AvaloniaProperty.Register<MediaElement, long>(nameof(Length), 0L); public long Length { get => GetValue(LengthProperty); private set => SetValue(LengthProperty, value); } public static readonly StyledProperty<float> RateProperty = AvaloniaProperty.Register<MediaElement, float>(nameof(Rate), 1.0f); public float Rate { get => GetValue(RateProperty); set => SetValue(RateProperty, value); } public static readonly StyledProperty<int> SpuProperty = AvaloniaProperty.Register<MediaElement, int>(nameof(Spu), -1); public int Spu { get => GetValue(SpuProperty); set => SetValue(SpuProperty, value); } public static readonly StyledProperty<Stretch> StretchProperty = AvaloniaProperty.Register<MediaElement, Stretch>(nameof(Stretch), Stretch.Fill); public Stretch Stretch { get => GetValue(StretchProperty); set => SetValue(StretchProperty, value); } public static readonly StyledProperty<string> AspectRatioProperty = AvaloniaProperty.Register<MediaElement, string>(nameof(AspectRatio)); public string AspectRatio { get => GetValue(AspectRatioProperty); set => SetValue(AspectRatioProperty, value); } public static readonly StyledProperty<double> BufferingProgressProperty = AvaloniaProperty.Register<MediaElement, double>(nameof(BufferingProgress), 0.0); public double BufferingProgress { get => GetValue(BufferingProgressProperty); private set => SetValue(BufferingProgressProperty, value); } static MediaElement() { if (IsDesignerProcess) return; Core.Initialize(null); if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) libVlc = new LibVLC("--directx-use-sysmem", "--network-caching=2000", "--no-plugins-cache"); else libVlc = new LibVLC("--network-caching=2000", "--no-plugins-cache"); } private static bool IsDesignerProcess { get { if (Design.IsDesignMode) return true; var v = Environment.GetEnvironmentVariable("AVALONIA_DESIGNER"); if (string.Equals(v, "1", StringComparison.OrdinalIgnoreCase) || string.Equals(v, "true", StringComparison.OrdinalIgnoreCase)) return true; try { var name = Process.GetCurrentProcess().ProcessName; if (name.Contains("Avalonia", StringComparison.OrdinalIgnoreCase) && name.Contains("Designer", StringComparison.OrdinalIgnoreCase)) return true; } catch { } return false; } } public MediaElement() { if (libVlc is null) { Content = null; return; } seekDebounceTimer = new DispatcherTimer { Interval = TimeSpan.FromMilliseconds(120) }; seekDebounceTimer.Tick += (s, e) => FlushPendingSeek(); mediaPlayer = new MediaPlayer(libVlc); videoView = new VideoView { MediaPlayer = mediaPlayer }; Content = videoView; HookPropertyObservers(); HookPlayerEvents(mediaPlayer); ApplyAllToPlayer(); } protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e) { DetachVideoViewEarly(); base.OnDetachedFromVisualTree(e); } protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e) { base.OnAttachedToVisualTree(e); isVideoDetached = false; ApplyAllToPlayer(); if (AutoPlay) mediaPlayer?.Play(); } private void HookPropertyObservers() { this.GetObservable(SourceProperty).Subscribe(uri => _ = OnSourceChangedAsync(uri)); this.GetObservable(RateProperty).Subscribe(r => { if (isDisposed) return; Try(() => mediaPlayer?.SetRate(r)); }); this.GetObservable(SpuProperty).Subscribe(s => { if (isDisposed) return; Try(() => mediaPlayer?.SetSpu(s)); }); this.GetObservable(VolumeProperty).Subscribe(v => { if (isDisposed || mediaPlayer is null) return; var clamped = Math.Clamp(v, 0, 100); Try(() => { if (mediaPlayer.Volume != clamped) mediaPlayer.Volume = clamped; }); }); this.GetObservable(IsMutedProperty).Subscribe(m => { if (isDisposed || mediaPlayer is null || updatingMuteFromPlayer) return; Try(() => { if (mediaPlayer.Mute != m) mediaPlayer.Mute = m; }); }); this.GetObservable(TimeProperty).Subscribe(t => { if (isDisposed || mediaPlayer is null || updatingTimeFromPlayer) return; pendingSeekTime = Math.Max(0, t); hasPendingSeek = true; isScrubbing = true; seekDebounceTimer?.Stop(); seekDebounceTimer?.Start(); }); this.GetObservable(BoundsProperty).Subscribe(_ => ScheduleApplyStretch()); } private void HookPlayerEvents(MediaPlayer player) { pausedHandler = (s, e) => UI(() => Paused?.Invoke(this, EventArgs.Empty)); stoppedHandler = (s, e) => UI(() => { ApplyAudioToPlayer(); isStopping = false; Stopped?.Invoke(this, EventArgs.Empty); }); playingHandler = (s, e) => UI(() => { isStopping = false; ApplyAudioToPlayer(); Playing?.Invoke(this, EventArgs.Empty); }); volumeChangedHandler = (s, e) => UI(() => { if (isStopping) return; VolumeChanged?.Invoke(this, EventArgs.Empty); }); mutedHandler = (s, e) => UI(() => { updatingMuteFromPlayer = true; SetCurrentValue(IsMutedProperty, true); updatingMuteFromPlayer = false; Muted?.Invoke(this, EventArgs.Empty); }); unmutedHandler = (s, e) => UI(() => { updatingMuteFromPlayer = true; SetCurrentValue(IsMutedProperty, false); updatingMuteFromPlayer = false; Unmuted?.Invoke(this, EventArgs.Empty); }); timeChangedHandler = (s, e) => UI(() => { if (mediaPlayer is null) return; if (isScrubbing) { TimeChanged?.Invoke(this, EventArgs.Empty); return; } updatingTimeFromPlayer = true; SetCurrentValue(TimeProperty, mediaPlayer.Time); updatingTimeFromPlayer = false; TimeChanged?.Invoke(this, EventArgs.Empty); }); lengthChangedHandler = (s, e) => UI(() => { if (mediaPlayer is null) return; Length = mediaPlayer.Length; LengthChanged?.Invoke(this, EventArgs.Empty); }); bufferingHandler = (s, e) => UI(() => { BufferingProgress = e.Cache / 100.0; Buffering?.Invoke(this, EventArgs.Empty); }); endReachedHandler = (s, e) => UI(() => { if (mediaPlayer is null) return; Try(() => { isStopping = true; mediaPlayer.Stop(); mediaPlayer.Time = 0; updatingTimeFromPlayer = true; SetCurrentValue(TimeProperty, 0L); updatingTimeFromPlayer = false; ApplyAudioToPlayer(); }); MediaEnded?.Invoke(this, EventArgs.Empty); }); player.Paused += pausedHandler; player.Stopped += stoppedHandler; player.Playing += playingHandler; player.VolumeChanged += volumeChangedHandler; player.Muted += mutedHandler; player.Unmuted += unmutedHandler; player.TimeChanged += timeChangedHandler; player.LengthChanged += lengthChangedHandler; player.Buffering += bufferingHandler; player.EndReached += endReachedHandler; } private void UnhookPlayerEvents(MediaPlayer player) { if (pausedHandler is not null) player.Paused -= pausedHandler; if (stoppedHandler is not null) player.Stopped -= stoppedHandler; if (playingHandler is not null) player.Playing -= playingHandler; if (volumeChangedHandler is not null) player.VolumeChanged -= volumeChangedHandler; if (mutedHandler is not null) player.Muted -= mutedHandler; if (unmutedHandler is not null) player.Unmuted -= unmutedHandler; if (timeChangedHandler is not null) player.TimeChanged -= timeChangedHandler; if (lengthChangedHandler is not null) player.LengthChanged -= lengthChangedHandler; if (bufferingHandler is not null) player.Buffering -= bufferingHandler; if (endReachedHandler is not null) player.EndReached -= endReachedHandler; } private void FlushPendingSeek() { seekDebounceTimer?.Stop(); if (isDisposed || mediaPlayer is null) { hasPendingSeek = false; isScrubbing = false; return; } if (!hasPendingSeek) { isScrubbing = false; return; } hasPendingSeek = false; Try(() => mediaPlayer.Time = pendingSeekTime); Dispatcher.UIThread.Post(() => { if (!isDisposed) isScrubbing = false; }, DispatcherPriority.Background); } public void Seek(TimeSpan timeSpan) { if (mediaPlayer is null || isDisposed) return; var ms = (long)Math.Max(0, timeSpan.TotalMilliseconds); pendingSeekTime = ms; hasPendingSeek = true; isScrubbing = true; seekDebounceTimer?.Stop(); seekDebounceTimer?.Start(); updatingTimeFromPlayer = true; SetCurrentValue(TimeProperty, ms); updatingTimeFromPlayer = false; } public void SeekToPercentage(double percentage) { if (mediaPlayer is null || isDisposed) return; var len = mediaPlayer.Length; if (percentage > 100 || (len <= 0 && percentage > 1)) { var ms = (long)Math.Max(0, percentage); pendingSeekTime = ms; hasPendingSeek = true; isScrubbing = true; seekDebounceTimer?.Stop(); seekDebounceTimer?.Start(); updatingTimeFromPlayer = true; SetCurrentValue(TimeProperty, ms); updatingTimeFromPlayer = false; return; } var p = percentage; if (p > 1) p /= 100.0; p = Math.Clamp(p, 0, 1); Try(() => mediaPlayer.Position = (float)p); if (len > 0) { var ms = (long)(len * p); updatingTimeFromPlayer = true; SetCurrentValue(TimeProperty, ms); updatingTimeFromPlayer = false; } } public bool IsPlaying => mediaPlayer?.IsPlaying ?? false; public void Play() => mediaPlayer?.Play(); public Task PlayAsync() => Dispatcher.UIThread.InvokeAsync(() => mediaPlayer?.Play()).GetTask(); public bool CanPlay() => !(mediaPlayer?.IsPlaying ?? false); public void Stop() { if (mediaPlayer is null || isDisposed) return; isStopping = true; Try(() => mediaPlayer.Stop()); UI(ApplyAudioToPlayer); } public bool CanStop() => mediaPlayer?.IsPlaying ?? false; public void Pause() => mediaPlayer?.Pause(); public bool CanPause() => mediaPlayer?.IsPlaying ?? false; public void TogglePlayPause() { if (mediaPlayer?.IsPlaying == true) mediaPlayer.Pause(); else mediaPlayer?.Play(); } public void Mute() => IsMuted = true; public bool CanMute() => !IsMuted; public void Unmute() => IsMuted = false; public bool CanUnmute() => IsMuted; public void ToggleMute() => IsMuted = !IsMuted; public void ToggleCloseCaption() { if (Spu != -1) Spu = -1; else Spu = 0; } public bool IsSeekable => mediaPlayer?.IsSeekable ?? false; public void Download() { if (Source?.Scheme != "avares") Process.Start(new ProcessStartInfo { FileName = mediaPlayer?.Media?.Mrl, UseShellExecute = true }); } private async Task OnSourceChangedAsync(Uri uri) { if (isDisposed || libVlc is null || mediaPlayer is null) return; if (uri is null) return; var old = Interlocked.Exchange(ref sourceCts, new CancellationTokenSource()); Try(() => { old?.Cancel(); old?.Dispose(); }); var token = sourceCts!.Token; try { UI(() => { Length = 0; updatingTimeFromPlayer = true; SetCurrentValue(TimeProperty, 0L); updatingTimeFromPlayer = false; pendingSeekTime = 0; hasPendingSeek = false; isScrubbing = false; seekDebounceTimer?.Stop(); }); CleanupCurrentMedia(); token.ThrowIfCancellationRequested(); Media media; if (uri.Scheme == "avares") { using var src = AssetLoader.Open(uri); var ms = new MemoryStream(); src.CopyTo(ms); ms.Position = 0; currentStream = ms; currentMediaInput = new CallbackStreamMediaInput( ms, onOpen: size => Console.WriteLine($"[MediaElement] MediaInput.Open size={size}"), onRead: read => { }, onSeek: offset => Console.WriteLine($"[MediaElement] MediaInput.Seek offset={offset}"), onClose: () => Console.WriteLine("[MediaElement] MediaInput.Close")); media = new Media(libVlc, currentMediaInput); } else { media = new Media(libVlc, uri); } ConfigureMediaOptions(media); if (token.IsCancellationRequested || isDisposed || mediaPlayer is null) { Try(() => media.Dispose()); return; } mediaPlayer.Media = media; currentMedia = media; parsedChangedHandler = (s, e) => { UI(() => { if (isDisposed) return; foreach (var track in media.Tracks) { if (track.TrackType == TrackType.Video) { var videoTrack = track.Data.Video; VideoWidth = videoTrack.Width; VideoHeight = videoTrack.Height; ScheduleApplyStretch(); break; } } }); }; media.ParsedChanged += parsedChangedHandler; try { await Task.Run(() => { if (token.IsCancellationRequested) return; try { media.Parse(MediaParseOptions.ParseNetwork); } catch { try { media.Parse(); } catch { } } }, token); } catch (OperationCanceledException) { return; } catch { } if (token.IsCancellationRequested || isDisposed || mediaPlayer is null) return; ApplyAllToPlayer(); if (AutoPlay && VisualRoot is not null && !isDisposed) mediaPlayer.Play(); } catch (OperationCanceledException) { } catch (Exception ex) { Console.WriteLine("[MediaElement] 警告:媒体播放失败:" + ex.Message, ex); } } private static void ConfigureMediaOptions(Media media) { media.AddOption(":file-caching=0"); media.AddOption(":network-caching=2000"); media.AddOption(":live-caching=0"); media.AddOption(":disc-caching=0"); media.AddOption(":sout-mux-caching=0"); media.AddOption(":no-sub-autodetect-file"); media.AddOption(":no-video-title-show"); } private void CleanupCurrentMedia() { if (currentMedia is not null && parsedChangedHandler is not null) Try(() => currentMedia.ParsedChanged -= parsedChangedHandler); parsedChangedHandler = null; if (mediaPlayer is not null) mediaPlayer.Media = null; Try(() => currentMedia?.Dispose()); currentMedia = null; Try(() => currentMediaInput?.Dispose()); currentMediaInput = null; Try(() => currentStream?.Dispose()); currentStream = null; } private void ApplyAudioToPlayer() { if (mediaPlayer is null || isDisposed) return; Try(() => { mediaPlayer.Volume = Math.Clamp(Volume, 0, 100); mediaPlayer.Mute = IsMuted; }); } private void ApplyAllToPlayer() { if (mediaPlayer is null || isDisposed) return; Try(() => { ApplyAudioToPlayer(); mediaPlayer.SetRate(Rate); mediaPlayer.SetSpu(Spu); if (!string.IsNullOrWhiteSpace(AspectRatio)) mediaPlayer.AspectRatio = AspectRatio; if (Time > 0) mediaPlayer.Time = Math.Max(0, Time); }); } private void ScheduleApplyStretch() { if (stretchPending) return; stretchPending = true; Dispatcher.UIThread.Post(() => { stretchPending = false; if (isDisposed || videoView is null || mediaPlayer is null) return; if (VideoWidth <= 0 || VideoHeight <= 0) { videoView.Width = double.NaN; videoView.Height = double.NaN; videoView.HorizontalAlignment = HorizontalAlignment.Stretch; videoView.VerticalAlignment = VerticalAlignment.Stretch; return; } var availableWidth = Bounds.Width; var availableHeight = Bounds.Height; if (availableWidth <= 0 || availableHeight <= 0) return; var aspectVideo = (double)VideoWidth / VideoHeight; var aspectContainer = availableWidth / availableHeight; switch (Stretch) { case Stretch.None: AspectRatio = $"{VideoWidth}:{VideoHeight}"; videoView.Width = VideoWidth; videoView.Height = VideoHeight; videoView.HorizontalAlignment = HorizontalAlignment.Left; videoView.VerticalAlignment = VerticalAlignment.Top; break; case Stretch.Fill: AspectRatio = $"{availableWidth}:{availableHeight}"; videoView.Width = availableWidth; videoView.Height = availableHeight; videoView.HorizontalAlignment = HorizontalAlignment.Stretch; videoView.VerticalAlignment = VerticalAlignment.Stretch; break; case Stretch.Uniform: AspectRatio = $"{VideoWidth}:{VideoHeight}"; if (aspectContainer > aspectVideo) { videoView.Height = availableHeight; videoView.Width = availableHeight * aspectVideo; } else { videoView.Width = availableWidth; videoView.Height = availableWidth / aspectVideo; } videoView.HorizontalAlignment = HorizontalAlignment.Center; videoView.VerticalAlignment = VerticalAlignment.Center; break; case Stretch.UniformToFill: if (aspectContainer > aspectVideo) { var height = availableWidth / aspectVideo; AspectRatio = $"{availableWidth}:{height}"; videoView.Width = availableWidth; videoView.Height = height; } else { var width = availableHeight * aspectVideo; AspectRatio = $"{width}:{availableHeight}"; videoView.Height = availableHeight; videoView.Width = width; } videoView.HorizontalAlignment = HorizontalAlignment.Center; videoView.VerticalAlignment = VerticalAlignment.Center; break; } Try(() => { if (!string.IsNullOrWhiteSpace(AspectRatio)) mediaPlayer.AspectRatio = AspectRatio; }); }); } private void DetachVideoViewEarly() { if (isVideoDetached) return; isVideoDetached = true; if (videoView is null) return; if (Dispatcher.UIThread.CheckAccess()) { videoView.MediaPlayer = null; return; } Dispatcher.UIThread.Post(() => { if (isDisposed || videoView is null) return; videoView.MediaPlayer = null; }, DispatcherPriority.Send); } private void UI(Action action) { if (isDisposed) return; if (Dispatcher.UIThread.CheckAccess()) { action(); return; } Dispatcher.UIThread.Post(() => { if (!isDisposed) action(); }); } private static void Try(Action action) { try { action(); } catch { } } public void Dispose() { if (isDisposed) return; isDisposed = true; Try(() => { sourceCts?.Cancel(); sourceCts?.Dispose(); }); sourceCts = null; Try(() => seekDebounceTimer?.Stop()); DetachVideoViewEarly(); if (mediaPlayer is not null) Try(() => UnhookPlayerEvents(mediaPlayer)); CleanupCurrentMedia(); Try(() => mediaPlayer?.Stop()); Try(() => mediaPlayer?.Dispose()); mediaPlayer = null; videoView = null; } private sealed class CallbackStreamMediaInput : MediaInput { private readonly Stream stream; private readonly Action<ulong>? onOpen; private readonly Action<int>? onRead; private readonly Action<ulong>? onSeek; private readonly Action? onClose; public CallbackStreamMediaInput(Stream stream, Action<ulong>? onOpen, Action<int>? onRead, Action<ulong>? onSeek, Action? onClose) { this.stream = stream ?? throw new ArgumentNullException(nameof(stream)); this.onOpen = onOpen; this.onRead = onRead; this.onSeek = onSeek; this.onClose = onClose; CanSeek = stream.CanSeek; } public override bool Open(out ulong size) { try { if (stream.CanSeek) stream.Seek(0L, SeekOrigin.Begin); size = stream.CanSeek ? (ulong)stream.Length : ulong.MaxValue; onOpen?.Invoke(size); return true; } catch { size = 0UL; return false; } } public override int Read(IntPtr buf, uint len) { try { if (len == 0) return 0; var max = (int)Math.Min(len, int.MaxValue); var rented = ArrayPool<byte>.Shared.Rent(max); try { var read = stream.Read(rented, 0, max); if (read > 0) Marshal.Copy(rented, 0, buf, read); onRead?.Invoke(read); return read; } finally { ArrayPool<byte>.Shared.Return(rented); } } catch { return -1; } } public override bool Seek(ulong offset) { try { if (!stream.CanSeek) return false; stream.Seek((long)offset, SeekOrigin.Begin); onSeek?.Invoke(offset); return true; } catch { return false; } } public override void Close() { try { if (stream.CanSeek) stream.Seek(0L, SeekOrigin.Begin); } catch { } onClose?.Invoke(); } } } }
AssemblyResources.axaml代码
<Window xmlns="https://github.com/avaloniaui" 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" Height="300" Width="300" x:Class="AvaloniaUI.AssemblyResources" Title="AssemblyResources"> <!--https://github.com/videolan/libvlcsharp/tree/3.x--> <Grid RowDefinitions="*,auto"> <MediaElement Name="Sound" Source="avares://AvaloniaUI/Resources/Sounds/金刚经.mp4"/> <StackPanel Grid.Row="1"> <Button Name="Play" Click="cmdPlay_Click" Content="Play"> <Button.Background> <ImageBrush Source="avares://AvaloniaUI/Resources/Images/Blue hills.jpg" Stretch="Fill"/> </Button.Background> </Button> </StackPanel> </Grid> </Window>
AssemblyResources.axaml.cs代码
using Avalonia; using Avalonia.Controls; using Avalonia.Interactivity; using Avalonia.Markup.Xaml; using Avalonia.Media; using Avalonia.Media.Imaging; using Avalonia.Platform; using Shares.Avalonia; using System; namespace AvaloniaUI; public partial class AssemblyResources : Window { public AssemblyResources() { InitializeComponent(); } private void cmdPlay_Click(object? sender, RoutedEventArgs e) { var uri = new Uri("avares://AvaloniaUI/Resources/Images/Winter.jpg"); using var stream = AssetLoader.Open(uri); Play.Background = new ImageBrush() { Source = new Bitmap(stream), Stretch = Stretch.Fill }; Sound.Play(); } }
运行效果

浙公网安备 33010602011771号