Udostępnij za pośrednictwem


Tworzenie niestandardowej kontrolki przy użyciu procedur obsługi

Przeglądaj przykład. Przeglądanie przykładu

Standardowym wymaganiem dla aplikacji jest możliwość odtwarzania filmów wideo. W tym artykule opisano sposób tworzenia wieloplatformowego Video interfejsu użytkownika aplikacji platformy .NET (.NET MAUI), który używa programu obsługi do mapowania międzyplatformowego interfejsu API sterowania do natywnych widoków w systemach Android, iOS i Mac Catalyst, które odtwarzają filmy wideo. Ta kontrolka może odtwarzać wideo z trzech źródeł:

  • Adres URL reprezentujący zdalny film wideo.
  • Zasób, który jest plikiem osadzonym w aplikacji.
  • Plik z biblioteki wideo urządzenia.

Kontrolki wideo wymagają kontrolek transportu, które są przyciskami do odtwarzania i wstrzymania filmu wideo oraz paska pozycjonowania, który pokazuje postęp w filmie wideo i umożliwia użytkownikowi szybkie przejście do innej lokalizacji. Kontrolka Video może używać kontrolek transportu i paska pozycjonowania dostarczonego przez platformę lub można dostarczyć niestandardowe kontrolki transportu i pasek pozycjonowania. Na poniższych zrzutach ekranu przedstawiono kontrolkę w systemie iOS z niestandardowymi kontrolkami transportu i bez ich użycia:

Zrzut ekranu przedstawiający odtwarzanie wideo w systemie iOS.Zrzut ekranu przedstawiający odtwarzanie wideo przy użyciu niestandardowych kontrolek transportu w systemie iOS.

Bardziej zaawansowana kontrolka wideo będzie miała dodatkowe funkcje, takie jak sterowanie głośnością, mechanizm przerywania odtwarzania wideo po odebraniu wywołania oraz sposób utrzymania ekranu aktywnego podczas odtwarzania.

Architektura kontrolki Video jest pokazana na poniższym diagramie:

Architektura programu obsługi wideo.

Klasa Video udostępnia międzyplatformowy interfejs API dla kontrolki. Mapowanie międzyplatformowego interfejsu API na interfejsy API widoku natywnego jest wykonywane przez klasę VideoHandler na każdej platformie, która mapuje Video klasę na klasę MauiVideoPlayer . W systemach iOS i Mac Catalyst MauiVideoPlayer klasa używa AVPlayer typu w celu zapewnienia odtwarzania wideo. W systemie Android MauiVideoPlayer klasa używa VideoView typu w celu zapewnienia odtwarzania wideo. W systemie Windows MauiVideoPlayer klasa używa MediaPlayerElement typu w celu zapewnienia odtwarzania wideo.

Ważne

Program .NET MAUI rozdziela procedury obsługi z kontrolek międzyplatformowych za pośrednictwem interfejsów. Dzięki temu platformy eksperymentalne, takie jak Comet i Fabulous, zapewniają własne wieloplatformowe kontrolki, które implementują interfejsy, a jednocześnie korzystają z programów obsługi platformy .NET MAUI. Tworzenie interfejsu dla kontrolki międzyplatformowej jest konieczne tylko wtedy, gdy konieczne jest oddzielenie programu obsługi od kontroli międzyplatformowej w podobnym celu lub na potrzeby testowania.

Proces tworzenia wieloplatformowej kontrolki niestandardowej .NET MAUI, której implementacje platformy są dostarczane przez programy obsługi, jest następująca:

  1. Utwórz klasę dla kontrolki międzyplatformowej, która zapewnia publiczny interfejs API kontrolki. Aby uzyskać więcej informacji, zobacz Tworzenie kontrolki międzyplatformowej.
  2. Utwórz wszelkie wymagane dodatkowe typy międzyplatformowe.
  3. Utwórz klasę partial obsługi. Aby uzyskać więcej informacji, zobacz Tworzenie procedury obsługi.
  4. W klasie obsługi utwórz PropertyMapper słownik, który definiuje akcje do wykonania po wystąpieniu zmian właściwości międzyplatformowych. Aby uzyskać więcej informacji, zobacz Tworzenie mapowania właściwości.
  5. Opcjonalnie w klasie obsługi utwórz CommandMapper słownik, który definiuje akcje do wykonania, gdy kontrolka międzyplatformowa wysyła instrukcje do natywnych widoków implementujących kontrolkę międzyplatformową. Aby uzyskać więcej informacji, zobacz Tworzenie mapowania poleceń.
  6. Utwórz partial klasy procedury obsługi dla każdej platformy, która tworzy widoki natywne, które implementują kontrolkę międzyplatformową. Aby uzyskać więcej informacji, zobacz Tworzenie kontrolek platformy.
  7. Zarejestruj procedurę obsługi przy użyciu ConfigureMauiHandlers metod i AddHandler w klasie aplikacji MauiProgram . Aby uzyskać więcej informacji, zobacz Rejestrowanie programu obsługi.

Następnie można użyć wieloplatformowej kontroli. Aby uzyskać więcej informacji, zobacz Korzystanie z kontroli międzyplatformowej.

Tworzenie wieloplatformowej kontrolki

Aby utworzyć kontrolkę międzyplatformową, należy utworzyć klasę pochodzącą z Viewklasy :

using System.ComponentModel;

namespace VideoDemos.Controls
{
    public class Video : View, IVideoController
    {
        public static readonly BindableProperty AreTransportControlsEnabledProperty =
            BindableProperty.Create(nameof(AreTransportControlsEnabled), typeof(bool), typeof(Video), true);

        public static readonly BindableProperty SourceProperty =
            BindableProperty.Create(nameof(Source), typeof(VideoSource), typeof(Video), null);

        public static readonly BindableProperty AutoPlayProperty =
            BindableProperty.Create(nameof(AutoPlay), typeof(bool), typeof(Video), true);

        public static readonly BindableProperty IsLoopingProperty =
            BindableProperty.Create(nameof(IsLooping), typeof(bool), typeof(Video), false);            

        public bool AreTransportControlsEnabled
        {
            get { return (bool)GetValue(AreTransportControlsEnabledProperty); }
            set { SetValue(AreTransportControlsEnabledProperty, value); }
        }

        [TypeConverter(typeof(VideoSourceConverter))]
        public VideoSource Source
        {
            get { return (VideoSource)GetValue(SourceProperty); }
            set { SetValue(SourceProperty, value); }
        }

        public bool AutoPlay
        {
            get { return (bool)GetValue(AutoPlayProperty); }
            set { SetValue(AutoPlayProperty, value); }
        }

        public bool IsLooping
        {
            get { return (bool)GetValue(IsLoopingProperty); }
            set { SetValue(IsLoopingProperty, value); }
        }        
        ...
    }
}

Kontrolka powinna udostępnić publiczny interfejs API, do którego będzie uzyskiwany dostęp jego program obsługi i kontrolować użytkowników. Kontrolki międzyplatformowe powinny pochodzić z Viewelementu , który reprezentuje element wizualny używany do umieszczania układów i widoków na ekranie.

Tworzenie programu obsługi

Po utworzeniu wieloplatformowej kontrolki należy utworzyć klasę partial dla programu obsługi:

#if IOS || MACCATALYST
using PlatformView = VideoDemos.Platforms.MaciOS.MauiVideoPlayer;
#elif ANDROID
using PlatformView = VideoDemos.Platforms.Android.MauiVideoPlayer;
#elif WINDOWS
using PlatformView = VideoDemos.Platforms.Windows.MauiVideoPlayer;
#elif (NETSTANDARD || !PLATFORM) || (NET6_0_OR_GREATER && !IOS && !ANDROID)
using PlatformView = System.Object;
#endif
using VideoDemos.Controls;
using Microsoft.Maui.Handlers;

namespace VideoDemos.Handlers
{
    public partial class VideoHandler
    {
    }
}

Klasa obsługi jest klasą częściową, której implementacja zostanie ukończona na każdej platformie z dodatkową klasą częściową.

Instrukcje warunkowe using definiują PlatformView typ na każdej platformie. W systemach Android, iOS, Mac Catalyst i Windows widoki natywne są udostępniane przez klasę niestandardową MauiVideoPlayer . Ostateczna instrukcja warunkowa using definiuje PlatformView , że jest równa System.Object. Jest to konieczne, PlatformView aby można było użyć typu w programie obsługi do użycia na wszystkich platformach. Alternatywą byłoby zdefiniowanie PlatformView właściwości raz na platformę przy użyciu kompilacji warunkowej.

Tworzenie mapowania właściwości

Każda procedura obsługi zwykle udostępnia maper właściwości, który definiuje akcje, które należy wykonać, gdy zmiana właściwości ma miejsce w kontrolce międzyplatformowej. Typ PropertyMapper to Dictionary element, który mapuje właściwości kontrolki międzyplatformowej na skojarzone akcje.

PropertyMapper program jest zdefiniowany w klasie .NET MAUI ViewHandler<TVirtualView,TPlatformView> i wymaga podania dwóch argumentów ogólnych:

  • Klasa dla wieloplatformowej kontrolki, która pochodzi z klasy View.
  • Klasa programu obsługi.

Poniższy przykład kodu przedstawia klasę VideoHandler rozszerzoną o definicję PropertyMapper :

public partial class VideoHandler
{
    public static IPropertyMapper<Video, VideoHandler> PropertyMapper = new PropertyMapper<Video, VideoHandler>(ViewHandler.ViewMapper)
    {
        [nameof(Video.AreTransportControlsEnabled)] = MapAreTransportControlsEnabled,
        [nameof(Video.Source)] = MapSource,
        [nameof(Video.IsLooping)] = MapIsLooping,
        [nameof(Video.Position)] = MapPosition
    };

    public VideoHandler() : base(PropertyMapper)
    {
    }
}

Jest PropertyMapper to, Dictionary którego klucz jest wartością string i której wartość jest ogólną Actionwartością . Obiekt string reprezentuje nazwę właściwości kontrolki międzyplatformowej i Action reprezentuje metodę static , która wymaga obsługi i kontroli międzyplatformowej jako argumentów. Na przykład podpis MapSource metody to public static void MapSource(VideoHandler handler, Video video).

Każda procedura obsługi platformy musi zapewniać implementacje akcji, które manipulują interfejsami API widoku natywnego. Gwarantuje to, że po ustawieniu właściwości na kontrolce międzyplatformowej podstawowy widok macierzysty zostanie zaktualizowany zgodnie z potrzebami. Zaletą tego podejścia jest to, że umożliwia łatwe dostosowywanie kontrolek międzyplatformowych, ponieważ maper właściwości można modyfikować przez użytkowników kontroli międzyplatformowej bez podklasy.

Tworzenie mapowania poleceń

Każda procedura obsługi może również udostępnić maper poleceń, który definiuje akcje do wykonania, gdy kontrolka międzyplatformowa wysyła polecenia do widoków natywnych. Mapy poleceń są podobne do maperów właściwości, ale umożliwiają przekazywanie dodatkowych danych. W tym kontekście polecenie jest instrukcją i opcjonalnie jej danymi, które są wysyłane do widoku natywnego. Typ CommandMapper to Dictionary element, który mapuje międzyplatformowe elementy sterujące na skojarzone z nimi akcje.

CommandMapper program jest zdefiniowany w klasie .NET MAUI ViewHandler<TVirtualView,TPlatformView> i wymaga podania dwóch argumentów ogólnych:

  • Klasa dla wieloplatformowej kontrolki, która pochodzi z klasy View.
  • Klasa programu obsługi.

Poniższy przykład kodu przedstawia klasę VideoHandler rozszerzoną o definicję CommandMapper :

public partial class VideoHandler
{
    public static IPropertyMapper<Video, VideoHandler> PropertyMapper = new PropertyMapper<Video, VideoHandler>(ViewHandler.ViewMapper)
    {
        [nameof(Video.AreTransportControlsEnabled)] = MapAreTransportControlsEnabled,
        [nameof(Video.Source)] = MapSource,
        [nameof(Video.IsLooping)] = MapIsLooping,
        [nameof(Video.Position)] = MapPosition
    };

    public static CommandMapper<Video, VideoHandler> CommandMapper = new(ViewCommandMapper)
    {
        [nameof(Video.UpdateStatus)] = MapUpdateStatus,
        [nameof(Video.PlayRequested)] = MapPlayRequested,
        [nameof(Video.PauseRequested)] = MapPauseRequested,
        [nameof(Video.StopRequested)] = MapStopRequested
    };

    public VideoHandler() : base(PropertyMapper, CommandMapper)
    {
    }
}

Jest CommandMapper to, Dictionary którego klucz jest wartością string i której wartość jest ogólną Actionwartością . Reprezentuje string nazwę polecenia kontrolki międzyplatformowej i Action reprezentuje metodę static , która wymaga obsługi, kontroli międzyplatformowej i opcjonalnych danych jako argumentów. Na przykład podpis MapPlayRequested metody to public static void MapPlayRequested(VideoHandler handler, Video video, object? args).

Każda procedura obsługi platformy musi zapewniać implementacje akcji, które manipulują interfejsami API widoku natywnego. Gwarantuje to, że po wysłaniu polecenia z kontrolki międzyplatformowej podstawowy widok macierzysty będzie manipulowany zgodnie z potrzebami. Zaletą tego podejścia jest to, że eliminuje potrzebę zasubskrybowania i anulowania subskrypcji zdarzeń kontroli międzyplatformowych. Ponadto umożliwia łatwe dostosowywanie, ponieważ maper poleceń może być modyfikowany przez użytkowników kontrolek międzyplatformowych bez podklasy.

Tworzenie kontrolek platformy

Po utworzeniu maperów dla programu obsługi należy podać implementacje programu obsługi na wszystkich platformach. Można to osiągnąć, dodając implementacje procedury obsługi częściowej klasy w folderach podrzędnych folderu Platformy . Możesz też skonfigurować projekt tak, aby obsługiwał wielowersyjność nazw plików lub wielowersyjność opartą na folderach lub oba te elementy.

Przykładowa aplikacja jest skonfigurowana do obsługi wielowersyjnego określania nazwy pliku, tak aby wszystkie klasy obsługi znajdowały się w jednym folderze:

Zrzut ekranu przedstawiający pliki w folderze Programy obsługi projektu.

Klasa zawierająca VideoHandler mapery nosi nazwę VideoHandler.cs. Implementacje platformy znajdują się w plikach VideoHandler.Android.cs, VideoHandler.MaciOS.cs i VideoHandler.Windows.cs . Ta wielowersyjność oparta na nazwie pliku jest konfigurowana przez dodanie następującego kodu XML do pliku projektu jako elementów podrzędnych węzła <Project> :

<!-- Android -->
<ItemGroup Condition="$(TargetFramework.StartsWith('net8.0-android')) != true">
  <Compile Remove="**\*.Android.cs" />
  <None Include="**\*.Android.cs" Exclude="$(DefaultItemExcludes);$(DefaultExcludesInProjectFolder)" />
</ItemGroup>

<!-- iOS and Mac Catalyst -->
<ItemGroup Condition="$(TargetFramework.StartsWith('net8.0-ios')) != true AND $(TargetFramework.StartsWith('net8.0-maccatalyst')) != true">
  <Compile Remove="**\*.MaciOS.cs" />
  <None Include="**\*.MaciOS.cs" Exclude="$(DefaultItemExcludes);$(DefaultExcludesInProjectFolder)" />
</ItemGroup>

<!-- Windows -->
<ItemGroup Condition="$(TargetFramework.Contains('-windows')) != true ">
  <Compile Remove="**\*.Windows.cs" />
  <None Include="**\*.Windows.cs" Exclude="$(DefaultItemExcludes);$(DefaultExcludesInProjectFolder)" />
</ItemGroup>

Aby uzyskać więcej informacji na temat konfigurowania wielu elementów docelowych, zobacz Konfigurowanie wielowersyjność.

Każda klasa obsługi platformy powinna być klasą częściową i pochodzić z ViewHandler<TVirtualView,TPlatformView> klasy, która wymaga dwóch argumentów typu:

  • Klasa dla wieloplatformowej kontrolki, która pochodzi z klasy View.
  • Typ widoku natywnego, który implementuje kontrolę międzyplatformową na platformie. Powinno to być identyczne z typem PlatformView właściwości w procedurze obsługi.

Ważne

Klasa ViewHandler<TVirtualView,TPlatformView> udostępnia VirtualView właściwości i PlatformView . Właściwość służy do uzyskiwania VirtualView dostępu do kontroli międzyplatformowej z poziomu programu obsługi. Właściwość służy do uzyskiwania PlatformView dostępu do widoku natywnego na każdej platformie, która implementuje kontrolę międzyplatformową.

Każda implementacja programu obsługi platformy powinna zastąpić następujące metody:

  • CreatePlatformView, który powinien utworzyć i zwrócić widok macierzysty, który implementuje kontrolkę międzyplatformową.
  • ConnectHandler, który powinien wykonać dowolną konfigurację widoku natywnego, taką jak inicjowanie widoku natywnego i wykonywanie subskrypcji zdarzeń.
  • DisconnectHandler, które powinno wykonać dowolne czyszczenie widoku natywnego, takie jak anulowanie subskrypcji zdarzeń i usuwanie obiektów.

Ważne

Metoda DisconnectHandler nie jest celowo wywoływana przez program .NET MAUI. Zamiast tego należy wywołać ją samodzielnie z odpowiedniej lokalizacji w cyklu życia aplikacji. Aby uzyskać więcej informacji, zobacz Oczyszczanie widoku natywnego.

Ważne

Metoda DisconnectHandler jest domyślnie wywoływana automatycznie przez program .NET MAUI, chociaż to zachowanie można zmienić. Aby uzyskać więcej informacji, zobacz Odłączanie programu obsługi sterowania.

Każda procedura obsługi platformy powinna również implementować akcje zdefiniowane w słownikach mapowania.

Ponadto każdy program obsługi platformy powinien również dostarczać kod, zgodnie z wymaganiami, aby zaimplementować funkcjonalność kontroli międzyplatformowej na platformie. Alternatywnie może to być udostępniane przez dodatkowy typ, który jest podejściem przyjętym tutaj.

Android

Wideo jest odtwarzane w systemie Android za pomocą elementu VideoView. Jednak w tym miejscu element został hermetyzowany w typieMauiVideoPlayer, VideoView aby widok natywny był oddzielony od procedury obsługi. Poniższy przykład przedstawia klasę VideoHandler częściową dla systemu Android z trzema przesłonięciami:

#nullable enable
using Microsoft.Maui.Handlers;
using VideoDemos.Controls;
using VideoDemos.Platforms.Android;

namespace VideoDemos.Handlers
{
    public partial class VideoHandler : ViewHandler<Video, MauiVideoPlayer>
    {
        protected override MauiVideoPlayer CreatePlatformView() => new MauiVideoPlayer(Context, VirtualView);

        protected override void ConnectHandler(MauiVideoPlayer platformView)
        {
            base.ConnectHandler(platformView);

            // Perform any control setup here
        }

        protected override void DisconnectHandler(MauiVideoPlayer platformView)
        {
            platformView.Dispose();
            base.DisconnectHandler(platformView);
        }
        ...
    }
}

VideoHandler pochodzi z ViewHandler<TVirtualView,TPlatformView> klasy, z argumentem ogólnym Video określającym typ sterowania międzyplatformowego i MauiVideoPlayer argumentem określającym typ hermetyzujący VideoView widok macierzysty.

Zastąpienie CreatePlatformView tworzy i zwraca MauiVideoPlayer obiekt. Przesłonięcia ConnectHandler to lokalizacja do wykonania dowolnej wymaganej konfiguracji widoku natywnego. Przesłonięcia DisconnectHandler to lokalizacja do wykonania dowolnego czyszczenia widoku natywnego, dlatego wywołuje metodę Dispose w wystąpieniu MauiVideoPlayer .

Procedura obsługi platformy musi również zaimplementować akcje zdefiniowane w słowniku mapera właściwości:

public partial class VideoHandler : ViewHandler<Video, MauiVideoPlayer>
{
    ...
    public static void MapAreTransportControlsEnabled(VideoHandler handler, Video video)
    {
        handler.PlatformView?.UpdateTransportControlsEnabled();
    }

    public static void MapSource(VideoHandler handler, Video video)
    {
        handler.PlatformView?.UpdateSource();
    }

    public static void MapIsLooping(VideoHandler handler, Video video)
    {
        handler.PlatformView?.UpdateIsLooping();
    }    

    public static void MapPosition(VideoHandler handler, Video video)
    {
        handler.PlatformView?.UpdatePosition();
    }
    ...
}

Każda akcja jest wykonywana w odpowiedzi na zmianę właściwości w kontrolce międzyplatformowej i jest static metodą, która wymaga obsługi i wystąpień kontrolek międzyplatformowych jako argumentów. W każdym przypadku akcja wywołuje metodę zdefiniowaną w typie MauiVideoPlayer .

Procedura obsługi platformy musi również zaimplementować akcje zdefiniowane w słowniku mapera poleceń:

public partial class VideoHandler : ViewHandler<Video, MauiVideoPlayer>
{
    ...
    public static void MapUpdateStatus(VideoHandler handler, Video video, object? args)
    {
        handler.PlatformView?.UpdateStatus();
    }

    public static void MapPlayRequested(VideoHandler handler, Video video, object? args)
    {
        if (args is not VideoPositionEventArgs)
            return;

        TimeSpan position = ((VideoPositionEventArgs)args).Position;
        handler.PlatformView?.PlayRequested(position);
    }

    public static void MapPauseRequested(VideoHandler handler, Video video, object? args)
    {
        if (args is not VideoPositionEventArgs)
            return;

        TimeSpan position = ((VideoPositionEventArgs)args).Position;
        handler.PlatformView?.PauseRequested(position);
    }

    public static void MapStopRequested(VideoHandler handler, Video video, object? args)
    {
        if (args is not VideoPositionEventArgs)
            return;

        TimeSpan position = ((VideoPositionEventArgs)args).Position;
        handler.PlatformView?.StopRequested(position);
    }
    ...
}

Każda akcja jest wykonywana w odpowiedzi na polecenie wysyłane z kontrolki międzyplatformowej i jest static metodą, która wymaga obsługi i wystąpień kontroli międzyplatformowej oraz opcjonalnych danych jako argumentów. W każdym przypadku akcja wywołuje metodę zdefiniowaną w MauiVideoPlayer klasie po wyodrębnieniu opcjonalnych danych.

W systemie Android klasa hermetyzuje klasę VideoView , MauiVideoPlayer aby widok natywny był oddzielony od programu obsługi:

using Android.Content;
using Android.Views;
using Android.Widget;
using AndroidX.CoordinatorLayout.Widget;
using VideoDemos.Controls;
using Color = Android.Graphics.Color;
using Uri = Android.Net.Uri;

namespace VideoDemos.Platforms.Android
{
    public class MauiVideoPlayer : CoordinatorLayout
    {
        VideoView _videoView;
        MediaController _mediaController;
        bool _isPrepared;
        Context _context;
        Video _video;

        public MauiVideoPlayer(Context context, Video video) : base(context)
        {
            _context = context;
            _video = video;

            SetBackgroundColor(Color.Black);

            // Create a RelativeLayout for sizing the video
            RelativeLayout relativeLayout = new RelativeLayout(_context)
            {
                LayoutParameters = new CoordinatorLayout.LayoutParams(LayoutParams.MatchParent, LayoutParams.MatchParent)
                {
                    Gravity = (int)GravityFlags.Center
                }
            };

            // Create a VideoView and position it in the RelativeLayout
            _videoView = new VideoView(context)
            {
                LayoutParameters = new RelativeLayout.LayoutParams(LayoutParams.MatchParent, LayoutParams.MatchParent)
            };

            // Add to the layouts
            relativeLayout.AddView(_videoView);
            AddView(relativeLayout);

            // Handle events
            _videoView.Prepared += OnVideoViewPrepared;
        }
        ...
    }
}

MauiVideoPlayer pochodzi z CoordinatorLayoutklasy , ponieważ główny widok macierzysty w aplikacji MAUI platformy .NET w systemie Android to CoordinatorLayout. MauiVideoPlayer Klasa może pochodzić z innych natywnych typów systemu Android, ale w niektórych scenariuszach trudno jest kontrolować pozycjonowanie widoku natywnego.

Element VideoView można dodać bezpośrednio do CoordinatorLayoutelementu i umieszczony w układzie zgodnie z potrzebami. Jednak w tym miejscu do elementu zostanie dodany system AndroidRelativeLayout, a element VideoView zostanie dodany do elementu RelativeLayout.CoordinatorLayout Parametry układu są ustawiane zarówno na obiekcie RelativeLayout , jak i VideoView tak, aby VideoView był wyśrodkowany na stronie, i rozszerza się, aby wypełnić dostępne miejsce przy zachowaniu współczynnika proporcji.

Konstruktor subskrybuje VideoView.Prepared również zdarzenie. To zdarzenie jest zgłaszane, gdy wideo jest gotowe do odtwarzania i jest anulowane w Dispose zastąpieniu:

public class MauiVideoPlayer : CoordinatorLayout
{
    VideoView _videoView;
    Video _video;
    ...

    protected override void Dispose(bool disposing)
    {
        if (disposing)
        {
            _videoView.Prepared -= OnVideoViewPrepared;
            _videoView.Dispose();
            _videoView = null;
            _video = null;
        }

        base.Dispose(disposing);
    }
    ...
}

Oprócz anulowania subskrypcji ze Prepared zdarzenia Dispose przesłonięcia przesłonięcia wykonuje również czyszczenie widoku natywnego.

Uwaga

Przesłonięcia Dispose są wywoływane przez przesłonięcia programu obsługi DisconnectHandler .

Kontrolki transportu platformy obejmują przyciski odtwarzania, wstrzymywania i zatrzymywania wideo oraz są udostępniane przez typ systemu Android MediaController . Jeśli właściwość jest ustawiona Video.AreTransportControlsEnabled na truewartość , element MediaController jest ustawiony jako odtwarzacz multimedialny obiektu VideoView. Dzieje się tak, ponieważ gdy AreTransportControlsEnabled właściwość jest ustawiona, maper właściwości programu obsługi gwarantuje, że MapAreTransportControlsEnabled metoda jest wywoływana, co z kolei wywołuje metodę UpdateTransportControlsEnabled w MauiVideoPlayerpliku :

public class MauiVideoPlayer : CoordinatorLayout
{
    VideoView _videoView;
    MediaController _mediaController;
    Video _video;
    ...

    public void UpdateTransportControlsEnabled()
    {
        if (_video.AreTransportControlsEnabled)
        {
            _mediaController = new MediaController(_context);
            _mediaController.SetMediaPlayer(_videoView);
            _videoView.SetMediaController(_mediaController);
        }
        else
        {
            _videoView.SetMediaController(null);
            if (_mediaController != null)
            {
                _mediaController.SetMediaPlayer(null);
                _mediaController = null;
            }
        }
    }
    ...
}

Kontrolki transportu zanikają, jeśli nie są używane, ale można je przywrócić, naciskając film wideo.

Jeśli właściwość jest ustawiona Video.AreTransportControlsEnabled na falsewartość , MediaController element zostanie usunięty jako odtwarzacz multimedialny obiektu VideoView. W tym scenariuszu możesz programowo sterować odtwarzaniem wideo lub dostarczać własne kontrolki transportu. Aby uzyskać więcej informacji, zobacz Tworzenie niestandardowych kontrolek transportu.

Katalizator systemów iOS i Mac

Wideo jest odtwarzane w systemach iOS i Mac Catalyst za pomocą elementu AVPlayer i .AVPlayerViewController Jednak w tym miejscu te typy są hermetyzowane w typie MauiVideoPlayer , aby widoki natywne były oddzielone od programu obsługi. W poniższym przykładzie przedstawiono klasę VideoHandler częściową dla systemu iOS z trzema przesłonięciami:

using Microsoft.Maui.Handlers;
using VideoDemos.Controls;
using VideoDemos.Platforms.MaciOS;

namespace VideoDemos.Handlers
{
    public partial class VideoHandler : ViewHandler<Video, MauiVideoPlayer>
    {
        protected override MauiVideoPlayer CreatePlatformView() => new MauiVideoPlayer(VirtualView);

        protected override void ConnectHandler(MauiVideoPlayer platformView)
        {
            base.ConnectHandler(platformView);

            // Perform any control setup here
        }

        protected override void DisconnectHandler(MauiVideoPlayer platformView)
        {
            platformView.Dispose();
            base.DisconnectHandler(platformView);
        }
        ...
    }
}

VideoHandler pochodzi z ViewHandler<TVirtualView,TPlatformView> klasy, z argumentem ogólnym Video określającym typ kontrolki międzyplatformowej i MauiVideoPlayer argument określający typ hermetyzujący AVPlayer widoki i AVPlayerViewController natywne.

Zastąpienie CreatePlatformView tworzy i zwraca MauiVideoPlayer obiekt. Przesłonięcia ConnectHandler to lokalizacja do wykonania dowolnej wymaganej konfiguracji widoku natywnego. Przesłonięcia DisconnectHandler to lokalizacja do wykonania dowolnego czyszczenia widoku natywnego, dlatego wywołuje metodę Dispose w wystąpieniu MauiVideoPlayer .

Procedura obsługi platformy musi również zaimplementować akcje zdefiniowane w słowniku mapera właściwości:

public partial class VideoHandler : ViewHandler<Video, MauiVideoPlayer>
{
    ...
    public static void MapAreTransportControlsEnabled(VideoHandler handler, Video video)
    {
        handler?.PlatformView.UpdateTransportControlsEnabled();
    }

    public static void MapSource(VideoHandler handler, Video video)
    {
        handler?.PlatformView.UpdateSource();
    }

    public static void MapIsLooping(VideoHandler handler, Video video)
    {
        handler.PlatformView?.UpdateIsLooping();
    }    

    public static void MapPosition(VideoHandler handler, Video video)
    {
        handler?.PlatformView.UpdatePosition();
    }
    ...
}

Każda akcja jest wykonywana w odpowiedzi na zmianę właściwości w kontrolce międzyplatformowej i jest static metodą, która wymaga obsługi i wystąpień kontrolek międzyplatformowych jako argumentów. W każdym przypadku akcja wywołuje metodę zdefiniowaną w typie MauiVideoPlayer .

Procedura obsługi platformy musi również zaimplementować akcje zdefiniowane w słowniku mapera poleceń:

public partial class VideoHandler : ViewHandler<Video, MauiVideoPlayer>
{
    ...
    public static void MapUpdateStatus(VideoHandler handler, Video video, object? args)
    {
        handler.PlatformView?.UpdateStatus();
    }

    public static void MapPlayRequested(VideoHandler handler, Video video, object? args)
    {
        if (args is not VideoPositionEventArgs)
            return;

        TimeSpan position = ((VideoPositionEventArgs)args).Position;
        handler.PlatformView?.PlayRequested(position);
    }

    public static void MapPauseRequested(VideoHandler handler, Video video, object? args)
    {
        if (args is not VideoPositionEventArgs)
            return;

        TimeSpan position = ((VideoPositionEventArgs)args).Position;
        handler.PlatformView?.PauseRequested(position);
    }

    public static void MapStopRequested(VideoHandler handler, Video video, object? args)
    {
        if (args is not VideoPositionEventArgs)
            return;

        TimeSpan position = ((VideoPositionEventArgs)args).Position;
        handler.PlatformView?.StopRequested(position);
    }
    ...
}

Każda akcja jest wykonywana w odpowiedzi na polecenie wysyłane z kontrolki międzyplatformowej i jest static metodą, która wymaga obsługi i wystąpień kontroli międzyplatformowej oraz opcjonalnych danych jako argumentów. W każdym przypadku akcja wywołuje metodę zdefiniowaną w MauiVideoPlayer klasie po wyodrębnieniu opcjonalnych danych.

W systemach iOS i Mac Catalyst MauiVideoPlayer klasa hermetyzuje AVPlayer typy i AVPlayerViewController , aby widoki natywne były oddzielone od programu obsługi:

using AVFoundation;
using AVKit;
using CoreMedia;
using Foundation;
using System.Diagnostics;
using UIKit;
using VideoDemos.Controls;

namespace VideoDemos.Platforms.MaciOS
{
    public class MauiVideoPlayer : UIView
    {
        AVPlayer _player;
        AVPlayerViewController _playerViewController;
        Video _video;
        ...

        public MauiVideoPlayer(Video video)
        {
            _video = video;

            _playerViewController = new AVPlayerViewController();
            _player = new AVPlayer();
            _playerViewController.Player = _player;
            _playerViewController.View.Frame = this.Bounds;

#if IOS16_0_OR_GREATER || MACCATALYST16_1_OR_GREATER
            // On iOS 16 and Mac Catalyst 16, for Shell-based apps, the AVPlayerViewController has to be added to the parent ViewController, otherwise the transport controls won't be displayed.
            var viewController = WindowStateManager.Default.GetCurrentUIViewController();

            // If there's no view controller, assume it's not Shell and continue because the transport controls will still be displayed.
            if (viewController?.View is not null)
            {
                // Zero out the safe area insets of the AVPlayerViewController
                UIEdgeInsets insets = viewController.View.SafeAreaInsets;
                _playerViewController.AdditionalSafeAreaInsets = new UIEdgeInsets(insets.Top * -1, insets.Left, insets.Bottom * -1, insets.Right);

                // Add the View from the AVPlayerViewController to the parent ViewController
                viewController.View.AddSubview(_playerViewController.View);
            }
#endif
            // Use the View from the AVPlayerViewController as the native control
            AddSubview(_playerViewController.View);
        }
        ...
    }
}

MauiVideoPlayer pochodzi z UIViewklasy bazowej w systemach iOS i Mac Catalyst dla obiektów, które wyświetlają zawartość i obsługują interakcję użytkownika z tą zawartością. Konstruktor tworzy AVPlayer obiekt, który zarządza odtwarzaniem i chronometrażem pliku multimedialnego i ustawia go jako Player wartość właściwości .AVPlayerViewController Wyświetla AVPlayerViewController zawartość z elementu AVPlayer i przedstawia kontrolki transportu i inne funkcje. Następnie ustawiono rozmiar i lokalizację kontrolki, co gwarantuje, że film wideo jest wyśrodkowany na stronie i rozwija się w celu wypełnienia dostępnego miejsca przy zachowaniu współczynnika proporcji. W systemach iOS 16 i Mac Catalyst 16 AVPlayerViewController element musi zostać dodany do elementu nadrzędnego ViewController dla aplikacji opartych na powłoce. W przeciwnym razie kontrolki transportu nie są wyświetlane. Widok macierzysty, który jest widokiem z AVPlayerViewControllerobiektu , jest następnie dodawany do strony.

Metoda Dispose jest odpowiedzialna za wykonywanie oczyszczania widoku natywnego:

public class MauiVideoPlayer : UIView
{
    AVPlayer _player;
    AVPlayerViewController _playerViewController;
    Video _video;
    ...

    protected override void Dispose(bool disposing)
    {
        if (disposing)
        {
            if (_player != null)
            {
                DestroyPlayedToEndObserver();
                _player.ReplaceCurrentItemWithPlayerItem(null);
                _player.Dispose();
            }
            if (_playerViewController != null)
                _playerViewController.Dispose();

            _video = null;
        }

        base.Dispose(disposing);
    }
    ...
}

W niektórych scenariuszach filmy wideo nadal odtwarzane po przejściu do strony odtwarzania wideo. Aby zatrzymać wideo, ReplaceCurrentItemWithPlayerItem parametr jest ustawiony na null wartość w Dispose zastąpieniu, a inne czyszczenie widoku natywnego jest wykonywane.

Uwaga

Przesłonięcia Dispose są wywoływane przez przesłonięcia programu obsługi DisconnectHandler .

Kontrolki transportu platformy obejmują przyciski odtwarzania, wstrzymywania i zatrzymywania wideo oraz są dostarczane przez AVPlayerViewController typ. Jeśli właściwość jest ustawiona Video.AreTransportControlsEnabled na true, AVPlayerViewController kontrolki odtwarzania będą wyświetlane. Dzieje się tak, ponieważ gdy AreTransportControlsEnabled właściwość jest ustawiona, maper właściwości programu obsługi gwarantuje, że MapAreTransportControlsEnabled metoda jest wywoływana, co z kolei wywołuje metodę UpdateTransportControlsEnabled w MauiVideoPlayerpliku :

public class MauiVideoPlayer : UIView
{
    AVPlayerViewController _playerViewController;
    Video _video;
    ...

    public void UpdateTransportControlsEnabled()
    {
        _playerViewController.ShowsPlaybackControls = _video.AreTransportControlsEnabled;
    }
    ...
}

Kontrolki transportu zanikają, jeśli nie są używane, ale można je przywrócić, naciskając film wideo.

Jeśli właściwość jest ustawiona Video.AreTransportControlsEnabled na false, AVPlayerViewController kontrolki odtwarzania nie są wyświetlane. W tym scenariuszu możesz programowo sterować odtwarzaniem wideo lub dostarczać własne kontrolki transportu. Aby uzyskać więcej informacji, zobacz Tworzenie niestandardowych kontrolek transportu.

Windows

Wideo jest odtwarzane w systemie Windows za pomocą polecenia MediaPlayerElement. Jednak w tym miejscu element został hermetyzowany w typieMauiVideoPlayer, MediaPlayerElement aby widok natywny był oddzielony od procedury obsługi. W poniższym przykładzie przedstawiono klasę VideoHandler częściową fo Windows z trzema przesłonięciami:

#nullable enable
using Microsoft.Maui.Handlers;
using VideoDemos.Controls;
using VideoDemos.Platforms.Windows;

namespace VideoDemos.Handlers
{
    public partial class VideoHandler : ViewHandler<Video, MauiVideoPlayer>
    {
        protected override MauiVideoPlayer CreatePlatformView() => new MauiVideoPlayer(VirtualView);

        protected override void ConnectHandler(MauiVideoPlayer platformView)
        {
            base.ConnectHandler(platformView);

            // Perform any control setup here
        }

        protected override void DisconnectHandler(MauiVideoPlayer platformView)
        {
            platformView.Dispose();
            base.DisconnectHandler(platformView);
        }
        ...
    }
}

VideoHandler pochodzi z ViewHandler<TVirtualView,TPlatformView> klasy, z argumentem ogólnym Video określającym typ sterowania międzyplatformowego i MauiVideoPlayer argumentem określającym typ hermetyzujący MediaPlayerElement widok macierzysty.

Zastąpienie CreatePlatformView tworzy i zwraca MauiVideoPlayer obiekt. Przesłonięcia ConnectHandler to lokalizacja do wykonania dowolnej wymaganej konfiguracji widoku natywnego. Przesłonięcia DisconnectHandler to lokalizacja do wykonania dowolnego czyszczenia widoku natywnego, dlatego wywołuje metodę Dispose w wystąpieniu MauiVideoPlayer .

Procedura obsługi platformy musi również zaimplementować akcje zdefiniowane w słowniku mapera właściwości:

public partial class VideoHandler : ViewHandler<Video, MauiVideoPlayer>
{
    ...
    public static void MapAreTransportControlsEnabled(VideoHandler handler, Video video)
    {
        handler.PlatformView?.UpdateTransportControlsEnabled();
    }

    public static void MapSource(VideoHandler handler, Video video)
    {
        handler.PlatformView?.UpdateSource();
    }

    public static void MapIsLooping(VideoHandler handler, Video video)
    {
        handler.PlatformView?.UpdateIsLooping();
    }

    public static void MapPosition(VideoHandler handler, Video video)
    {
        handler.PlatformView?.UpdatePosition();
    }
    ...
}

Każda akcja jest wykonywana w odpowiedzi na zmianę właściwości w kontrolce międzyplatformowej i jest static metodą, która wymaga obsługi i wystąpień kontrolek międzyplatformowych jako argumentów. W każdym przypadku akcja wywołuje metodę zdefiniowaną w typie MauiVideoPlayer .

Procedura obsługi platformy musi również zaimplementować akcje zdefiniowane w słowniku mapera poleceń:

public partial class VideoHandler : ViewHandler<Video, MauiVideoPlayer>
{
    ...
    public static void MapUpdateStatus(VideoHandler handler, Video video, object? args)
    {
        handler.PlatformView?.UpdateStatus();
    }

    public static void MapPlayRequested(VideoHandler handler, Video video, object? args)
    {
        if (args is not VideoPositionEventArgs)
            return;

        TimeSpan position = ((VideoPositionEventArgs)args).Position;
        handler.PlatformView?.PlayRequested(position);
    }

    public static void MapPauseRequested(VideoHandler handler, Video video, object? args)
    {
        if (args is not VideoPositionEventArgs)
            return;

        TimeSpan position = ((VideoPositionEventArgs)args).Position;
        handler.PlatformView?.PauseRequested(position);
    }

    public static void MapStopRequested(VideoHandler handler, Video video, object? args)
    {
        if (args is not VideoPositionEventArgs)
            return;

        TimeSpan position = ((VideoPositionEventArgs)args).Position;
        handler.PlatformView?.StopRequested(position);
    }
    ...
}

Każda akcja jest wykonywana w odpowiedzi na polecenie wysyłane z kontrolki międzyplatformowej i jest static metodą, która wymaga obsługi i wystąpień kontroli międzyplatformowej oraz opcjonalnych danych jako argumentów. W każdym przypadku akcja wywołuje metodę zdefiniowaną w MauiVideoPlayer klasie po wyodrębnieniu opcjonalnych danych.

W systemie Windows klasa hermetyzuje klasę MediaPlayerElement , MauiVideoPlayer aby widok natywny był oddzielony od programu obsługi:

using Microsoft.UI.Xaml.Controls;
using VideoDemos.Controls;
using Windows.Media.Core;
using Windows.Media.Playback;
using Windows.Storage;
using Grid = Microsoft.UI.Xaml.Controls.Grid;

namespace VideoDemos.Platforms.Windows
{
    public class MauiVideoPlayer : Grid, IDisposable
    {
        MediaPlayerElement _mediaPlayerElement;
        Video _video;
        ...

        public MauiVideoPlayer(Video video)
        {
            _video = video;
            _mediaPlayerElement = new MediaPlayerElement();
            this.Children.Add(_mediaPlayerElement);
        }
        ...
    }
}

MauiVideoPlayer element pochodzi z Gridklasy , a MediaPlayerElement element jest dodawany jako element podrzędny elementu Grid. Umożliwia to automatyczne ustawianie rozmiaru MediaPlayerElement , aby wypełnić wszystkie dostępne miejsce.

Metoda Dispose jest odpowiedzialna za wykonywanie oczyszczania widoku natywnego:

public class MauiVideoPlayer : Grid, IDisposable
{
    MediaPlayerElement _mediaPlayerElement;
    Video _video;
    bool _isMediaPlayerAttached;
    ...

    public void Dispose()
    {
        if (_isMediaPlayerAttached)
        {
            _mediaPlayerElement.MediaPlayer.MediaOpened -= OnMediaPlayerMediaOpened;
            _mediaPlayerElement.MediaPlayer.Dispose();
        }
        _mediaPlayerElement = null;
    }
    ...
}

Oprócz anulowania subskrypcji ze MediaOpened zdarzenia Dispose przesłonięcia przesłonięcia wykonuje również czyszczenie widoku natywnego.

Uwaga

Przesłonięcia Dispose są wywoływane przez przesłonięcia programu obsługi DisconnectHandler .

Kontrolki transportu platformy obejmują przyciski odtwarzania, wstrzymywania i zatrzymywania wideo oraz są dostarczane przez MediaPlayerElement typ. Jeśli właściwość jest ustawiona Video.AreTransportControlsEnabled na true, MediaPlayerElement kontrolki odtwarzania będą wyświetlane. Dzieje się tak, ponieważ gdy AreTransportControlsEnabled właściwość jest ustawiona, maper właściwości programu obsługi gwarantuje, że MapAreTransportControlsEnabled metoda jest wywoływana, co z kolei wywołuje metodę UpdateTransportControlsEnabled w MauiVideoPlayerpliku :

public class MauiVideoPlayer : Grid, IDisposable
{
    MediaPlayerElement _mediaPlayerElement;
    Video _video;
    bool _isMediaPlayerAttached;
    ...

    public void UpdateTransportControlsEnabled()
    {
        _mediaPlayerElement.AreTransportControlsEnabled = _video.AreTransportControlsEnabled;
    }
    ...

}

Jeśli właściwość jest ustawiona Video.AreTransportControlsEnabled na false, MediaPlayerElement kontrolki odtwarzania nie są wyświetlane. W tym scenariuszu możesz programowo sterować odtwarzaniem wideo lub dostarczać własne kontrolki transportu. Aby uzyskać więcej informacji, zobacz Tworzenie niestandardowych kontrolek transportu.

Konwertowanie kontrolki międzyplatformowej na kontrolkę platformy

Dowolna wieloplatformowa kontrolka .NET MAUI, która pochodzi z Elementklasy , może zostać przekonwertowana na podstawową kontrolkę platformy za ToPlatform pomocą metody rozszerzenia:

  • W systemie Android ToPlatform konwertuje kontrolkę .NET MAUI na obiekt systemu Android View .
  • W systemach iOS i Mac Catalyst ToPlatform konwertuje kontrolkę UIView MAUI platformy .NET na obiekt.
  • W systemie Windows ToPlatform konwertuje kontrolkę FrameworkElement .NET MAUI na obiekt.

Uwaga

Metoda ToPlatform znajduje się w Microsoft.Maui.Platform przestrzeni nazw.

Na wszystkich platformach ToPlatform metoda wymaga argumentu MauiContext .

Metoda ToPlatform może przekonwertować kontrolkę międzyplatformową na podstawową kontrolkę platformy na podstawie kodu platformy, na przykład w częściowej klasie obsługi dla platformy:

using Microsoft.Maui.Handlers;
using Microsoft.Maui.Platform;
using VideoDemos.Controls;
using VideoDemos.Platforms.Android;

namespace VideoDemos.Handlers
{
    public partial class VideoHandler : ViewHandler<Video, MauiVideoPlayer>
    {
        ...
        public static void MapSource(VideoHandler handler, Video video)
        {
            handler.PlatformView?.UpdateSource();

            // Convert cross-platform control to its underlying platform control
            MauiVideoPlayer mvp = (MauiVideoPlayer)video.ToPlatform(handler.MauiContext);
            ...
        }
        ...
    }
}

W tym przykładzie VideoHandler w klasie częściowej dla systemu Android MapSource metoda konwertuje Video wystąpienie na MauiVideoPlayer obiekt.

Metoda ToPlatform może również przekonwertować kontrolkę międzyplatformową na podstawową kontrolę platformy z kodu międzyplatformowego:

using Microsoft.Maui.Platform;

namespace VideoDemos.Views;

public partial class MyPage : ContentPage
{
    ...
    protected override void OnHandlerChanged()
    {
        // Convert cross-platform control to its underlying platform control
#if ANDROID
        Android.Views.View nativeView = video.ToPlatform(video.Handler.MauiContext);
#elif IOS || MACCATALYST
        UIKit.UIView nativeView = video.ToPlatform(video.Handler.MauiContext);
#elif WINDOWS
        Microsoft.UI.Xaml.FrameworkElement nativeView = video.ToPlatform(video.Handler.MauiContext);
#endif
        ...
    }
    ...
}

W tym przykładzie kontrolka międzyplatformowa Video o nazwie video jest konwertowana na podstawowy widok macierzysty na każdej platformie w zastąpieniu OnHandlerChanged() . To zastąpienie jest wywoływane, gdy widok natywny, który implementuje kontrolkę międzyplatformową, jest dostępny i inicjowany. Obiekt zwracany przez metodę ToPlatform może być rzutowany na dokładny typ natywny, czyli MauiVideoPlayer.

Odtwarzanie wideo

Klasa Video definiuje Source właściwość , która służy do określania źródła pliku wideo i AutoPlay właściwości. AutoPlay wartość domyślna to true, co oznacza, że po ustawieniu wideo powinno rozpocząć odtwarzanie automatycznie Source . Aby uzyskać definicję tych właściwości, zobacz Tworzenie kontrolki międzyplatformowej.

Właściwość Source jest typu VideoSource, który jest klasą abstrakcyjną składającą się z trzech metod statycznych, które tworzą wystąpienie trzech klas, które pochodzą z VideoSourceklasy :

using System.ComponentModel;

namespace VideoDemos.Controls
{
    [TypeConverter(typeof(VideoSourceConverter))]
    public abstract class VideoSource : Element
    {
        public static VideoSource FromUri(string uri)
        {
            return new UriVideoSource { Uri = uri };
        }

        public static VideoSource FromFile(string file)
        {
            return new FileVideoSource { File = file };
        }

        public static VideoSource FromResource(string path)
        {
            return new ResourceVideoSource { Path = path };
        }
    }
}

Klasa VideoSource zawiera TypeConverter atrybut, który odwołuje się VideoSourceConverterdo elementu :

using System.ComponentModel;

namespace VideoDemos.Controls
{
    public class VideoSourceConverter : TypeConverter, IExtendedTypeConverter
    {
        object IExtendedTypeConverter.ConvertFromInvariantString(string value, IServiceProvider serviceProvider)
        {
            if (!string.IsNullOrWhiteSpace(value))
            {
                Uri uri;
                return Uri.TryCreate(value, UriKind.Absolute, out uri) && uri.Scheme != "file" ?
                    VideoSource.FromUri(value) : VideoSource.FromResource(value);
            }
            throw new InvalidOperationException("Cannot convert null or whitespace to VideoSource.");
        }
    }
}

Konwerter typów jest wywoływany, gdy Source właściwość jest ustawiona na ciąg w języku XAML. Metoda ConvertFromInvariantString próbuje przekonwertować ciąg na Uri obiekt. Jeśli to się powiedzie, a schemat nie filejest , metoda zwraca UriVideoSourcewartość . W przeciwnym razie zwraca wartość ResourceVideoSource.

Odtwarzanie wideo w sieci Web

Klasa UriVideoSource służy do określania zdalnego wideo za pomocą identyfikatora URI. Definiuje Uri właściwość typu string:

namespace VideoDemos.Controls
{
    public class UriVideoSource : VideoSource
    {
        public static readonly BindableProperty UriProperty =
            BindableProperty.Create(nameof(Uri), typeof(string), typeof(UriVideoSource));

        public string Uri
        {
            get { return (string)GetValue(UriProperty); }
            set { SetValue(UriProperty, value); }
        }
    }
}

Source Gdy właściwość jest ustawiona na UriVideoSourcewartość , maper właściwości programu obsługi gwarantuje, że MapSource metoda jest wywoływana:

public static void MapSource(VideoHandler handler, Video video)
{
    handler?.PlatformView.UpdateSource();
}

Metoda MapSource in powoduje wywołanie UpdateSource metody we właściwości programu obsługi PlatformView . Właściwość PlatformView typu MauiVideoPlayerreprezentuje widok macierzysty, który zapewnia implementację odtwarzacza wideo na każdej platformie.

Android

Wideo jest odtwarzane w systemie Android za pomocą elementu VideoView. Poniższy przykład kodu pokazuje, jak UpdateSource metoda przetwarza Source właściwość, gdy jest UriVideoSourcetypu :

using Android.Content;
using Android.Views;
using Android.Widget;
using AndroidX.CoordinatorLayout.Widget;
using VideoDemos.Controls;
using Color = Android.Graphics.Color;
using Uri = Android.Net.Uri;

namespace VideoDemos.Platforms.Android
{
    public class MauiVideoPlayer : CoordinatorLayout
    {
        VideoView _videoView;
        bool _isPrepared;
        Video _video;
        ...

        public void UpdateSource()
        {
            _isPrepared = false;
            bool hasSetSource = false;

            if (_video.Source is UriVideoSource)
            {
                string uri = (_video.Source as UriVideoSource).Uri;
                if (!string.IsNullOrWhiteSpace(uri))
                {
                    _videoView.SetVideoURI(Uri.Parse(uri));
                    hasSetSource = true;
                }
            }
            ...

            if (hasSetSource && _video.AutoPlay)
            {
                _videoView.Start();
            }
        }
        ...
    }
}

Podczas przetwarzania obiektów typu UriVideoSourcemetoda służy SetVideoUri VideoView do określania wideo do odtwarzania, z obiektem systemu Android Uri utworzonym na podstawie identyfikatora URI ciągu.

Właściwość AutoPlay nie ma odpowiednika w metodzie VideoView, więc Start metoda jest wywoływana, jeśli ustawiono nowy film wideo.

Katalizator systemów iOS i Mac

Aby odtworzyć film wideo w systemach iOS i Mac Catalyst, obiekt typu AVAsset jest tworzony w celu hermetyzacji wideo i służy do tworzenia AVPlayerItemobiektu , który następnie jest przekazywany do AVPlayer obiektu. Poniższy przykład kodu pokazuje, jak UpdateSource metoda przetwarza Source właściwość, gdy jest UriVideoSourcetypu :

using AVFoundation;
using AVKit;
using CoreMedia;
using Foundation;
using System.Diagnostics;
using UIKit;
using VideoDemos.Controls;

namespace VideoDemos.Platforms.MaciOS
{
    public class MauiVideoPlayer : UIView
    {
        AVPlayer _player;
        AVPlayerItem _playerItem;
        Video _video;
        ...

        public void UpdateSource()
        {
            AVAsset asset = null;

            if (_video.Source is UriVideoSource)
            {
                string uri = (_video.Source as UriVideoSource).Uri;
                if (!string.IsNullOrWhiteSpace(uri))
                    asset = AVAsset.FromUrl(new NSUrl(uri));
            }
            ...

            if (asset != null)
                _playerItem = new AVPlayerItem(asset);
            else
                _playerItem = null;

            _player.ReplaceCurrentItemWithPlayerItem(_playerItem);
            if (_playerItem != null && _video.AutoPlay)
            {
                _player.Play();
            }
        }
        ...
    }
}

Podczas przetwarzania obiektów typu UriVideoSourcemetoda statyczna AVAsset.FromUrl służy do określania wideo, który ma być odtwarzany, z obiektem systemu iOS NSUrl utworzonym na podstawie identyfikatora URI ciągu.

Właściwość AutoPlay nie ma odpowiednika w klasach wideo systemu iOS, więc właściwość jest badana na końcu UpdateSource metody w celu wywołania Play metody w AVPlayer obiekcie.

W niektórych przypadkach w systemie iOS filmy wideo będą odtwarzane po przejściu do strony odtwarzania wideo. Aby zatrzymać wideo, ReplaceCurrentItemWithPlayerItem właściwość jest ustawiona na null wartość w zastąpieniu Dispose :

protected override void Dispose(bool disposing)
{
    if (disposing)
    {
        if (_player != null)
        {
            _player.ReplaceCurrentItemWithPlayerItem(null);
            ...
        }
        ...
    }
    base.Dispose(disposing);
}

Windows

Wideo jest odtwarzane w systemie Windows za pomocą elementu MediaPlayerElement. Poniższy przykład kodu pokazuje, jak UpdateSource metoda przetwarza Source właściwość, gdy jest UriVideoSourcetypu :

using Microsoft.UI.Xaml.Controls;
using VideoDemos.Controls;
using Windows.Media.Core;
using Windows.Media.Playback;
using Windows.Storage;
using Grid = Microsoft.UI.Xaml.Controls.Grid;

namespace VideoDemos.Platforms.Windows
{
    public class MauiVideoPlayer : Grid, IDisposable
    {
        MediaPlayerElement _mediaPlayerElement;
        Video _video;
        bool _isMediaPlayerAttached;
        ...

        public async void UpdateSource()
        {
            bool hasSetSource = false;

            if (_video.Source is UriVideoSource)
            {
                string uri = (_video.Source as UriVideoSource).Uri;
                if (!string.IsNullOrWhiteSpace(uri))
                {
                    _mediaPlayerElement.Source = MediaSource.CreateFromUri(new Uri(uri));
                    hasSetSource = true;
                }
            }
            ...

            if (hasSetSource && !_isMediaPlayerAttached)
            {
                _isMediaPlayerAttached = true;
                _mediaPlayerElement.MediaPlayer.MediaOpened += OnMediaPlayerMediaOpened;
            }

            if (hasSetSource && _video.AutoPlay)
            {
                _mediaPlayerElement.AutoPlay = true;
            }
        }
        ...
    }
}

Podczas przetwarzania obiektów typu UriVideoSourceMediaPlayerElement.Source właściwość jest ustawiana na MediaSource obiekt, który inicjuje Uri identyfikator URI wideo do odtwarzania. Po ustawieniu MediaPlayerElement.Source OnMediaPlayerMediaOpened metody obsługi zdarzeń jest zarejestrowana względem MediaPlayerElement.MediaPlayer.MediaOpened zdarzenia. Ta procedura obsługi zdarzeń służy do ustawiania Duration właściwości kontrolki Video .

Na końcu UpdateSource metody Video.AutoPlay właściwość jest badana i jeśli jest ona prawdziwa MediaPlayerElement.AutoPlay , właściwość ma wartość , aby true rozpocząć odtwarzanie wideo.

Odtwarzanie zasobu wideo

Klasa służy do uzyskiwania ResourceVideoSource dostępu do plików wideo osadzonych w aplikacji. Definiuje Path właściwość typu string:

namespace VideoDemos.Controls
{
    public class ResourceVideoSource : VideoSource
    {
        public static readonly BindableProperty PathProperty =
            BindableProperty.Create(nameof(Path), typeof(string), typeof(ResourceVideoSource));

        public string Path
        {
            get { return (string)GetValue(PathProperty); }
            set { SetValue(PathProperty, value); }
        }
    }
}

Source Gdy właściwość jest ustawiona na ResourceVideoSourcewartość , maper właściwości programu obsługi gwarantuje, że MapSource metoda jest wywoływana:

public static void MapSource(VideoHandler handler, Video video)
{
    handler?.PlatformView.UpdateSource();
}

Metoda MapSource in powoduje wywołanie UpdateSource metody we właściwości programu obsługi PlatformView . Właściwość PlatformView typu MauiVideoPlayerreprezentuje widok macierzysty, który zapewnia implementację odtwarzacza wideo na każdej platformie.

Android

Poniższy przykład kodu pokazuje, jak UpdateSource metoda przetwarza Source właściwość, gdy jest ResourceVideoSourcetypu :

using Android.Content;
using Android.Views;
using Android.Widget;
using AndroidX.CoordinatorLayout.Widget;
using VideoDemos.Controls;
using Color = Android.Graphics.Color;
using Uri = Android.Net.Uri;

namespace VideoDemos.Platforms.Android
{
    public class MauiVideoPlayer : CoordinatorLayout
    {
        VideoView _videoView;
        bool _isPrepared;
        Context _context;
        Video _video;
        ...

        public void UpdateSource()
        {
            _isPrepared = false;
            bool hasSetSource = false;
            ...

            else if (_video.Source is ResourceVideoSource)
            {
                string package = Context.PackageName;
                string path = (_video.Source as ResourceVideoSource).Path;
                if (!string.IsNullOrWhiteSpace(path))
                {
                    string assetFilePath = "content://" + package + "/" + path;
                    _videoView.SetVideoPath(assetFilePath);
                    hasSetSource = true;
                }
            }
            ...
        }
        ...
    }
}

Podczas przetwarzania obiektów typu ResourceVideoSourcemetoda SetVideoPath VideoView służy do określania wideo do odtwarzania, z argumentem ciągu łączącym nazwę pakietu aplikacji z nazwą pliku wideo.

Plik wideo zasobu jest przechowywany w folderze zasobów pakietu i wymaga od dostawcy zawartości dostępu do niego. Dostawca zawartości jest dostarczany przez klasę VideoProvider AssetFileDescriptor , która tworzy obiekt, który zapewnia dostęp do pliku wideo:

using Android.Content;
using Android.Content.Res;
using Android.Database;
using Debug = System.Diagnostics.Debug;
using Uri = Android.Net.Uri;

namespace VideoDemos.Platforms.Android
{
    [ContentProvider(new string[] { "com.companyname.videodemos" })]
    public class VideoProvider : ContentProvider
    {
        public override AssetFileDescriptor OpenAssetFile(Uri uri, string mode)
        {
            var assets = Context.Assets;
            string fileName = uri.LastPathSegment;
            if (fileName == null)
                throw new FileNotFoundException();

            AssetFileDescriptor afd = null;
            try
            {
                afd = assets.OpenFd(fileName);
            }
            catch (IOException ex)
            {
                Debug.WriteLine(ex);
            }
            return afd;
        }

        public override bool OnCreate()
        {
            return false;
        }
        ...
    }
}

Katalizator systemów iOS i Mac

Poniższy przykład kodu pokazuje, jak UpdateSource metoda przetwarza Source właściwość, gdy jest ResourceVideoSourcetypu :

using AVFoundation;
using AVKit;
using CoreMedia;
using Foundation;
using System.Diagnostics;
using UIKit;
using VideoDemos.Controls;

namespace VideoDemos.Platforms.MaciOS
{
    public class MauiVideoPlayer : UIView
    {
        Video _video;
        ...

        public void UpdateSource()
        {
            AVAsset asset = null;
            ...

            else if (_video.Source is ResourceVideoSource)
            {
                string path = (_video.Source as ResourceVideoSource).Path;
                if (!string.IsNullOrWhiteSpace(path))
                {
                    string directory = Path.GetDirectoryName(path);
                    string filename = Path.GetFileNameWithoutExtension(path);
                    string extension = Path.GetExtension(path).Substring(1);
                    NSUrl url = NSBundle.MainBundle.GetUrlForResource(filename, extension, directory);
                    asset = AVAsset.FromUrl(url);
                }
            }
            ...
        }
        ...
    }
}

Podczas przetwarzania obiektów typu ResourceVideoSourcemetoda GetUrlForResource NSBundle jest używana do pobierania pliku z pakietu aplikacji. Pełna ścieżka musi być podzielona na nazwę pliku, rozszerzenie i katalog.

W niektórych przypadkach w systemie iOS filmy wideo będą odtwarzane po przejściu do strony odtwarzania wideo. Aby zatrzymać wideo, ReplaceCurrentItemWithPlayerItem właściwość jest ustawiona na null wartość w zastąpieniu Dispose :

protected override void Dispose(bool disposing)
{
    if (disposing)
    {
        if (_player != null)
        {
            _player.ReplaceCurrentItemWithPlayerItem(null);
            ...
        }
        ...
    }
    base.Dispose(disposing);
}

Windows

Poniższy przykład kodu pokazuje, jak UpdateSource metoda przetwarza Source właściwość, gdy jest ResourceVideoSourcetypu :

using Microsoft.UI.Xaml.Controls;
using VideoDemos.Controls;
using Windows.Media.Core;
using Windows.Media.Playback;
using Windows.Storage;
using Grid = Microsoft.UI.Xaml.Controls.Grid;

namespace VideoDemos.Platforms.Windows
{
    public class MauiVideoPlayer : Grid, IDisposable
    {
        MediaPlayerElement _mediaPlayerElement;
        Video _video;
        ...

        public async void UpdateSource()
        {
            bool hasSetSource = false;

            ...
            else if (_video.Source is ResourceVideoSource)
            {
                string path = "ms-appx:///" + (_video.Source as ResourceVideoSource).Path;
                if (!string.IsNullOrWhiteSpace(path))
                {
                    _mediaPlayerElement.Source = MediaSource.CreateFromUri(new Uri(path));
                    hasSetSource = true;
                }
            }
            ...
        }
        ...
    }
}

Podczas przetwarzania obiektów typu ResourceVideoSourceMediaPlayerElement.Source właściwość jest ustawiana na MediaSource obiekt, który inicjuje Uri obiekt ze ścieżką zasobu wideo poprzedzonego prefiksem ms-appx:///.

Odtwarzanie pliku wideo z biblioteki urządzenia

Klasa FileVideoSource służy do uzyskiwania dostępu do filmów wideo w bibliotece wideo urządzenia. Definiuje File właściwość typu string:

namespace VideoDemos.Controls
{
    public class FileVideoSource : VideoSource
    {
        public static readonly BindableProperty FileProperty =
            BindableProperty.Create(nameof(File), typeof(string), typeof(FileVideoSource));

        public string File
        {
            get { return (string)GetValue(FileProperty); }
            set { SetValue(FileProperty, value); }
        }
    }
}

Source Gdy właściwość jest ustawiona na FileVideoSourcewartość , maper właściwości programu obsługi gwarantuje, że MapSource metoda jest wywoływana:

public static void MapSource(VideoHandler handler, Video video)
{
    handler?.PlatformView.UpdateSource();
}

Metoda MapSource in powoduje wywołanie UpdateSource metody we właściwości programu obsługi PlatformView . Właściwość PlatformView typu MauiVideoPlayerreprezentuje widok macierzysty, który zapewnia implementację odtwarzacza wideo na każdej platformie.

Android

Poniższy przykład kodu pokazuje, jak UpdateSource metoda przetwarza Source właściwość, gdy jest FileVideoSourcetypu :

using Android.Content;
using Android.Views;
using Android.Widget;
using AndroidX.CoordinatorLayout.Widget;
using VideoDemos.Controls;
using Color = Android.Graphics.Color;
using Uri = Android.Net.Uri;

namespace VideoDemos.Platforms.Android
{
    public class MauiVideoPlayer : CoordinatorLayout
    {
        VideoView _videoView;
        bool _isPrepared;
        Video _video;
        ...

        public void UpdateSource()
        {
            _isPrepared = false;
            bool hasSetSource = false;
            ...

            else if (_video.Source is FileVideoSource)
            {
                string filename = (_video.Source as FileVideoSource).File;
                if (!string.IsNullOrWhiteSpace(filename))
                {
                    _videoView.SetVideoPath(filename);
                    hasSetSource = true;
                }
            }
            ...
        }
        ...
    }
}

Podczas przetwarzania obiektów typu FileVideoSourcemetoda SetVideoPath VideoView jest używana do określania pliku wideo, który ma być odtwarzany.

Katalizator systemów iOS i Mac

Poniższy przykład kodu pokazuje, jak UpdateSource metoda przetwarza Source właściwość, gdy jest FileVideoSourcetypu :

using AVFoundation;
using AVKit;
using CoreMedia;
using Foundation;
using System.Diagnostics;
using UIKit;
using VideoDemos.Controls;

namespace VideoDemos.Platforms.MaciOS
{
    public class MauiVideoPlayer : UIView
    {
        Video _video;
        ...

        public void UpdateSource()
        {
            AVAsset asset = null;
            ...

            else if (_video.Source is FileVideoSource)
            {
                string uri = (_video.Source as FileVideoSource).File;
                if (!string.IsNullOrWhiteSpace(uri))
                    asset = AVAsset.FromUrl(NSUrl.CreateFileUrl(new [] { uri }));
            }
            ...
        }
        ...
    }
}

Podczas przetwarzania obiektów typu FileVideoSourcemetoda statyczna AVAsset.FromUrl służy do określania pliku wideo do odtwarzania przy NSUrl.CreateFileUrl użyciu metody tworzenia obiektu systemu iOS NSUrl na podstawie identyfikatora URI ciągu.

Windows

Poniższy przykład kodu pokazuje, jak UpdateSource metoda przetwarza Source właściwość, gdy jest FileVideoSourcetypu :

using Microsoft.UI.Xaml.Controls;
using VideoDemos.Controls;
using Windows.Media.Core;
using Windows.Media.Playback;
using Windows.Storage;
using Grid = Microsoft.UI.Xaml.Controls.Grid;

namespace VideoDemos.Platforms.Windows
{
    public class MauiVideoPlayer : Grid, IDisposable
    {
        MediaPlayerElement _mediaPlayerElement;
        Video _video;
        ...

        public async void UpdateSource()
        {
            bool hasSetSource = false;

            ...
            else if (_video.Source is FileVideoSource)
            {
                string filename = (_video.Source as FileVideoSource).File;
                if (!string.IsNullOrWhiteSpace(filename))
                {
                    StorageFile storageFile = await StorageFile.GetFileFromPathAsync(filename);
                    _mediaPlayerElement.Source = MediaSource.CreateFromStorageFile(storageFile);
                    hasSetSource = true;
                }
            }
            ...
        }
        ...
    }
}

Podczas przetwarzania obiektów typu FileVideoSourcenazwa pliku wideo jest konwertowana na StorageFile obiekt. MediaSource.CreateFromStorageFile Następnie metoda zwraca MediaSource obiekt, który jest ustawiony jako wartość MediaPlayerElement.Source właściwości.

Pętla wideo

Klasa Video definiuje IsLooping właściwość, która umożliwia kontrolce automatyczne ustawienie pozycji wideo na początek po osiągnięciu końca. Wartość domyślna to false, co oznacza, że filmy wideo nie są automatycznie zapętlane.

Po ustawieniu IsLooping właściwości maper właściwości programu obsługi gwarantuje, że MapIsLooping metoda jest wywoływana:

public static void MapIsLooping(VideoHandler handler, Video video)
{
    handler.PlatformView?.UpdateIsLooping();
}  

Metoda MapIsLooping z kolei wywołuje metodę UpdateIsLooping we właściwości programu obsługi PlatformView . Właściwość PlatformView typu MauiVideoPlayerreprezentuje widok macierzysty, który zapewnia implementację odtwarzacza wideo na każdej platformie.

Android

Poniższy przykład kodu pokazuje, jak UpdateIsLooping metoda w systemie Android włącza pętlę wideo:

using Android.Content;
using Android.Media;
using Android.Views;
using Android.Widget;
using AndroidX.CoordinatorLayout.Widget;
using VideoDemos.Controls;
using Color = Android.Graphics.Color;
using Uri = Android.Net.Uri;

namespace VideoDemos.Platforms.Android
{
    public class MauiVideoPlayer : CoordinatorLayout, MediaPlayer.IOnPreparedListener
    {
        VideoView _videoView;
        Video _video;
        ...

        public void UpdateIsLooping()
        {
            if (_video.IsLooping)
            {
                _videoView.SetOnPreparedListener(this);
            }
            else
            {
                _videoView.SetOnPreparedListener(null);
            }
        }

        public void OnPrepared(MediaPlayer mp)
        {
            mp.Looping = _video.IsLooping;
        }
        ...
    }
}

Aby włączyć pętlę wideo, MauiVideoPlayer klasa implementuje MediaPlayer.IOnPreparedListener interfejs. Ten interfejs definiuje OnPrepared wywołanie zwrotne wywoływane, gdy źródło multimediów jest gotowe do odtwarzania. Gdy Video.IsLooping właściwość to true, UpdateIsLooping metoda ustawia MauiVideoPlayer jako obiekt, który zapewnia OnPrepared wywołanie zwrotne. Wywołanie zwrotne ustawia MediaPlayer.IsLooping właściwość na wartość Video.IsLooping właściwości .

Katalizator systemów iOS i Mac

Poniższy przykład kodu pokazuje, jak UpdateIsLooping metoda w systemach iOS i Mac Catalyst umożliwia zapętlanie wideo:

using System.Diagnostics;
using AVFoundation;
using AVKit;
using CoreMedia;
using Foundation;
using UIKit;
using VideoDemos.Controls;

namespace VideoDemos.Platforms.MaciOS
{
    public class MauiVideoPlayer : UIView
    {
        AVPlayer _player;
        AVPlayerViewController _playerViewController;
        Video _video;
        NSObject? _playedToEndObserver;
        ...

        public void UpdateIsLooping()
        {
            DestroyPlayedToEndObserver();
            if (_video.IsLooping)
            {
                _player.ActionAtItemEnd = AVPlayerActionAtItemEnd.None;
                _playedToEndObserver = NSNotificationCenter.DefaultCenter.AddObserver(AVPlayerItem.DidPlayToEndTimeNotification, PlayedToEnd);
            }
            else
                _player.ActionAtItemEnd = AVPlayerActionAtItemEnd.Pause;
        }

        void PlayedToEnd(NSNotification notification)
        {
            if (_video == null || notification.Object != _playerViewController.Player?.CurrentItem)
                return;

            _playerViewController.Player?.Seek(CMTime.Zero);
        }
        ...
    }
}

W systemach iOS i Mac Catalyst powiadomienie jest używane do wykonywania wywołania zwrotnego, gdy wideo zostało odtworzona na końcu. Video.IsLooping Gdy właściwość to true, UpdateIsLooping metoda dodaje obserwatora dla AVPlayerItem.DidPlayToEndTimeNotification powiadomienia i wykonuje PlayedToEnd metodę po odebraniu powiadomienia. Z kolei ta metoda wznawia odtwarzanie od początku filmu wideo. Video.IsLooping Jeśli właściwość to false, wideo zostanie wstrzymane na końcu odtwarzania.

Ponieważ MauiVideoPlayer dodaje obserwatora dla powiadomienia, musi również usunąć obserwatora podczas oczyszczania widoku natywnego. Jest to realizowane w przesłonięć Dispose :

public class MauiVideoPlayer : UIView
{
    AVPlayer _player;
    AVPlayerViewController _playerViewController;
    Video _video;
    NSObject? _playedToEndObserver;
    ...

    protected override void Dispose(bool disposing)
    {
        if (disposing)
        {
            if (_player != null)
            {
                DestroyPlayedToEndObserver();
                ...
            }
            ...
        }

        base.Dispose(disposing);
    }

    void DestroyPlayedToEndObserver()
    {
        if (_playedToEndObserver != null)
        {
            NSNotificationCenter.DefaultCenter.RemoveObserver(_playedToEndObserver);
            DisposeObserver(ref _playedToEndObserver);
        }
    }

    void DisposeObserver(ref NSObject? disposable)
    {
        disposable?.Dispose();
        disposable = null;
    }
    ...
}

Przesłonięcia Dispose wywołuje metodę DestroyPlayedToEndObserver , która usuwa obserwatora AVPlayerItem.DidPlayToEndTimeNotification dla powiadomienia, a także wywołuje Dispose metodę w NSObjectobiekcie .

Windows

Poniższy przykład kodu pokazuje, jak UpdateIsLooping metoda w systemie Windows włącza pętlę wideo:

public void UpdateIsLooping()
{
    if (_isMediaPlayerAttached)
        _mediaPlayerElement.MediaPlayer.IsLoopingEnabled = _video.IsLooping;
}

Aby włączyć pętlę wideo, UpdateIsLooping metoda ustawia MediaPlayerElement.MediaPlayer.IsLoopingEnabled właściwość na wartość Video.IsLooping właściwości.

Tworzenie niestandardowych kontrolek transportu

Kontrolki transportu odtwarzacza wideo obejmują przyciski odtwarzania, wstrzymywania i zatrzymywania wideo. Te przyciski są często identyfikowane ze znanymi ikonami, a nie tekstem, a przyciski odtwarzania i wstrzymywania są często łączone w jeden przycisk.

Domyślnie kontrolka Video wyświetla kontrolki transportu obsługiwane przez każdą platformę. Jednak po ustawieniu AreTransportControlsEnabled właściwości na false, te kontrolki są pomijane. Następnie można kontrolować odtwarzanie wideo programowo lub dostarczać własne kontrolki transportu.

Zaimplementowanie własnych kontrolek transportu wymaga Video , aby klasa mogła powiadomić jego natywne widoki o odtwarzaniu, wstrzymaniu lub zatrzymaniu wideo oraz poznać bieżący stan odtwarzania wideo. Klasa Video definiuje metody o nazwie Play, Pausei Stop , które zgłaszają odpowiednie zdarzenie, i wysyłają polecenie do polecenia VideoHandler:

namespace VideoDemos.Controls
{
    public class Video : View, IVideoController
    {
        ...
        public event EventHandler<VideoPositionEventArgs> PlayRequested;
        public event EventHandler<VideoPositionEventArgs> PauseRequested;
        public event EventHandler<VideoPositionEventArgs> StopRequested;

        public void Play()
        {
            VideoPositionEventArgs args = new VideoPositionEventArgs(Position);
            PlayRequested?.Invoke(this, args);
            Handler?.Invoke(nameof(Video.PlayRequested), args);
        }

        public void Pause()
        {
            VideoPositionEventArgs args = new VideoPositionEventArgs(Position);
            PauseRequested?.Invoke(this, args);
            Handler?.Invoke(nameof(Video.PauseRequested), args);
        }

        public void Stop()
        {
            VideoPositionEventArgs args = new VideoPositionEventArgs(Position);
            StopRequested?.Invoke(this, args);
            Handler?.Invoke(nameof(Video.StopRequested), args);
        }
    }
}

Klasa VideoPositionEventArgs definiuje Position właściwość, którą można ustawić za pomocą konstruktora. Ta właściwość reprezentuje położenie, w którym odtwarzanie wideo zostało uruchomione, wstrzymane lub zatrzymane.

Ostatni wiersz w metodach Play, Pausei Stop wysyła polecenie i skojarzone dane do VideoHandler. Element CommandMapper for VideoHandler mapuje nazwy poleceń na akcje, które są wykonywane po odebraniu polecenia. Na przykład po VideoHandler odebraniu PlayRequested polecenia wykonuje swoją MapPlayRequested metodę. Zaletą tego podejścia jest to, że eliminuje potrzebę zasubskrybowania i anulowania subskrypcji zdarzeń kontroli międzyplatformowych. Ponadto umożliwia łatwe dostosowywanie, ponieważ maper poleceń może być modyfikowany przez użytkowników kontrolek międzyplatformowych bez podklasy. Aby uzyskać więcej informacji na temat CommandMapperprogramu , zobacz Create the command mapper (Tworzenie mapowania poleceń).

Implementacja MauiVideoPlayer w systemach Android, iOS i Mac Catalyst zawiera PlayRequestedmetody , PauseRequestediStopRequested, które są wykonywane w odpowiedzi na kontrolkę Video wysyłającą PlayRequestedpolecenia , PauseRequestedi .StopRequested Każda metoda wywołuje metodę w widoku natywnym, aby odtworzyć, wstrzymać lub zatrzymać wideo. Na przykład poniższy kod przedstawia metody , PauseRequestedi w StopRequested systemach PlayRequestediOS i Mac Catalyst:

using AVFoundation;
using AVKit;
using CoreMedia;
using Foundation;
using System.Diagnostics;
using UIKit;
using VideoDemos.Controls;

namespace VideoDemos.Platforms.MaciOS
{
    public class MauiVideoPlayer : UIView
    {
        AVPlayer _player;
        ...

        public void PlayRequested(TimeSpan position)
        {
            _player.Play();
            Debug.WriteLine($"Video playback from {position.Hours:X2}:{position.Minutes:X2}:{position.Seconds:X2}.");
        }

        public void PauseRequested(TimeSpan position)
        {
            _player.Pause();
            Debug.WriteLine($"Video paused at {position.Hours:X2}:{position.Minutes:X2}:{position.Seconds:X2}.");
        }

        public void StopRequested(TimeSpan position)
        {
            _player.Pause();
            _player.Seek(new CMTime(0, 1));
            Debug.WriteLine($"Video stopped at {position.Hours:X2}:{position.Minutes:X2}:{position.Seconds:X2}.");
        }
    }
}

Każda z trzech metod rejestruje położenie, w którym wideo zostało odtwarzane, wstrzymane lub zatrzymane, przy użyciu danych wysyłanych za pomocą polecenia .

Ten mechanizm zapewnia, że gdy Playmetoda , Pauselub Stop jest wywoływana na kontrolce Video , jej natywny widok jest poinstruowany o odtwarzaniu, wstrzymaniu lub zatrzymaniu wideo i zarejestrowaniu położenia, w którym wideo zostało odtwarzane, wstrzymane lub zatrzymane. Wszystko to dzieje się przy użyciu podejścia oddzielonego, bez konieczności subskrybowania zdarzeń międzyplatformowych bez konieczności subskrybowania natywnych widoków.

Stan wideo

Implementowanie funkcji odtwarzania, wstrzymywania i zatrzymywania nie jest wystarczające do obsługi niestandardowych kontrolek transportu. Często funkcje odtwarzania i wstrzymywania należy zaimplementować za pomocą tego samego przycisku, który zmienia jego wygląd, aby wskazać, czy wideo jest aktualnie odtwarzane, czy wstrzymane. Ponadto przycisk nie powinien być nawet włączony, jeśli wideo nie zostało jeszcze załadowane.

Te wymagania oznaczają, że odtwarzacz wideo musi udostępnić bieżący stan wskazujący, czy jest odtwarzany, czy wstrzymany, czy nie jest jeszcze gotowy do odtwarzania wideo. Ten stan może być reprezentowany przez wyliczenie:

public enum VideoStatus
{
    NotReady,
    Playing,
    Paused
}

Klasa Video definiuje właściwość powiązaną tylko do odczytu o nazwie Status typu VideoStatus. Ta właściwość jest zdefiniowana jako tylko do odczytu, ponieważ powinna być ustawiana tylko z programu obsługi formantu:

namespace VideoDemos.Controls
{
    public class Video : View, IVideoController
    {
        ...
        private static readonly BindablePropertyKey StatusPropertyKey =
            BindableProperty.CreateReadOnly(nameof(Status), typeof(VideoStatus), typeof(Video), VideoStatus.NotReady);

        public static readonly BindableProperty StatusProperty = StatusPropertyKey.BindableProperty;

        public VideoStatus Status
        {
            get { return (VideoStatus)GetValue(StatusProperty); }
        }

        VideoStatus IVideoController.Status
        {
            get { return Status; }
            set { SetValue(StatusPropertyKey, value); }
        }
        ...
    }
}

Zazwyczaj właściwość powiązana tylko do odczytu ma prywatną set metodę dostępu do Status właściwości, aby umożliwić jej ustawienie z poziomu klasy. Jednak w przypadku pochodnych View obsługiwanych przez programy obsługi właściwość musi być ustawiona spoza klasy, ale tylko przez program obsługi kontrolki.

Z tego powodu inna właściwość jest definiowana z nazwą IVideoController.Status. Jest to jawna implementacja interfejsu IVideoController i jest możliwa przez interfejs implementowany przez klasę Video :

public interface IVideoController
{
    VideoStatus Status { get; set; }
    TimeSpan Duration { get; set; }
}

Ten interfejs umożliwia klasy zewnętrznej Video ustawienie Status właściwości przez odwołanie się do interfejsu IVideoController . Właściwość można ustawić z innych klas i programu obsługi, ale jest mało prawdopodobne, aby została ustawiona przypadkowo. Co najważniejsze, Status nie można ustawić właściwości za pomocą powiązania danych.

Aby ułatwić implementacje programu obsługi w utrzymaniu Status aktualizacji właściwości, Video klasa definiuje UpdateStatus zdarzenie i polecenie:

using System.ComponentModel;

namespace VideoDemos.Controls
{
    public class Video : View, IVideoController
    {
        ...
        public event EventHandler UpdateStatus;

        IDispatcherTimer _timer;

        public Video()
        {
            _timer = Dispatcher.CreateTimer();
            _timer.Interval = TimeSpan.FromMilliseconds(100);
            _timer.Tick += OnTimerTick;
            _timer.Start();
        }

        ~Video() => _timer.Tick -= OnTimerTick;

        void OnTimerTick(object sender, EventArgs e)
        {
            UpdateStatus?.Invoke(this, EventArgs.Empty);
            Handler?.Invoke(nameof(Video.UpdateStatus));
        }
        ...
    }
}

Procedura OnTimerTick obsługi zdarzeń jest wykonywana co dziesiątą sekundy, co wywołuje UpdateStatus zdarzenie i wywołuje UpdateStatus polecenie.

UpdateStatus Gdy polecenie jest wysyłane z kontrolki Video do jego programu obsługi, maper poleceń programu obsługi gwarantuje, że MapUpdateStatus metoda jest wywoływana:

public static void MapUpdateStatus(VideoHandler handler, Video video, object? args)
{
    handler.PlatformView?.UpdateStatus();
}

Metoda MapUpdateStatus in powoduje wywołanie UpdateStatus metody we właściwości programu obsługi PlatformView . Właściwość PlatformView , która jest typu MauiVideoPlayer, hermetyzuje widoki natywne, które zapewniają implementację odtwarzacza wideo na każdej platformie.

Android

Poniższy przykład kodu przedstawia metodę UpdateStatus w systemie Android ustawia Status właściwość :

using Android.Content;
using Android.Views;
using Android.Widget;
using AndroidX.CoordinatorLayout.Widget;
using VideoDemos.Controls;
using Color = Android.Graphics.Color;
using Uri = Android.Net.Uri;

namespace VideoDemos.Platforms.Android
{
    public class MauiVideoPlayer : CoordinatorLayout
    {
        VideoView _videoView;
        bool _isPrepared;
        Video _video;
        ...

        public MauiVideoPlayer(Context context, Video video) : base(context)
        {
            _video = video;
            ...
            _videoView.Prepared += OnVideoViewPrepared;
        }

        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                _videoView.Prepared -= OnVideoViewPrepared;
                ...
            }

            base.Dispose(disposing);
        }

        void OnVideoViewPrepared(object sender, EventArgs args)
        {
            _isPrepared = true;
            ((IVideoController)_video).Duration = TimeSpan.FromMilliseconds(_videoView.Duration);
        }

        public void UpdateStatus()
        {
            VideoStatus status = VideoStatus.NotReady;

            if (_isPrepared)
                status = _videoView.IsPlaying ? VideoStatus.Playing : VideoStatus.Paused;

            ((IVideoController)_video).Status = status;
            ...
        }
        ...
    }
}

Właściwość VideoView.IsPlaying jest wartością logiczną wskazującą, czy wideo jest odtwarzane, czy wstrzymane. Aby określić, czy VideoView nie można odtworzyć ani wstrzymać filmu wideo, Prepared jego zdarzenie musi być obsługiwane. To zdarzenie jest zgłaszane, gdy źródło multimediów jest gotowe do odtwarzania. Zdarzenie jest subskrybowane w konstruktorze MauiVideoPlayer i anulowane w zastąpieniu Dispose . Następnie UpdateStatus metoda używa isPrepared pola i VideoView.IsPlaying właściwości, aby ustawić Status właściwość obiektu Video przez rzutowanie jej na IVideoControllerwartość .

Katalizator systemów iOS i Mac

Poniższy przykład kodu przedstawia metodę UpdateStatus w systemach iOS i Mac Catalyst ustawia Status właściwość :

using AVFoundation;
using AVKit;
using CoreMedia;
using Foundation;
using System.Diagnostics;
using UIKit;
using VideoDemos.Controls;

namespace VideoDemos.Platforms.MaciOS
{
    public class MauiVideoPlayer : UIView
    {
        AVPlayer _player;
        Video _video;
        ...

        public void UpdateStatus()
        {
            VideoStatus videoStatus = VideoStatus.NotReady;

            switch (_player.Status)
            {
                case AVPlayerStatus.ReadyToPlay:
                    switch (_player.TimeControlStatus)
                    {
                        case AVPlayerTimeControlStatus.Playing:
                            videoStatus = VideoStatus.Playing;
                            break;

                        case AVPlayerTimeControlStatus.Paused:
                            videoStatus = VideoStatus.Paused;
                            break;
                    }
                    break;
            }
            ((IVideoController)_video).Status = videoStatus;
            ...
        }
        ...
    }
}

Aby ustawić właściwość , należy uzyskać dostęp do dwóch właściwości AVPlayerStatus właściwości typu AVPlayerStatus i TimeControlStatus właściwości typu AVPlayerTimeControlStatus.Status Właściwość Status można następnie ustawić na Video obiekcie, rzutując ją na IVideoControllerwartość .

Windows

Poniższy przykład kodu przedstawia metodę UpdateStatus w systemie Windows ustawia Status właściwość :

using Microsoft.UI.Xaml.Controls;
using VideoDemos.Controls;
using Windows.Media.Core;
using Windows.Media.Playback;
using Windows.Storage;
using Grid = Microsoft.UI.Xaml.Controls.Grid;

namespace VideoDemos.Platforms.Windows
{
    public class MauiVideoPlayer : Grid, IDisposable
    {
        MediaPlayerElement _mediaPlayerElement;
        Video _video;
        bool _isMediaPlayerAttached;
        ...

        public void UpdateStatus()
        {
            if (_isMediaPlayerAttached)
            {
                VideoStatus status = VideoStatus.NotReady;

                switch (_mediaPlayerElement.MediaPlayer.CurrentState)
                {
                    case MediaPlayerState.Playing:
                        status = VideoStatus.Playing;
                        break;
                    case MediaPlayerState.Paused:
                    case MediaPlayerState.Stopped:
                        status = VideoStatus.Paused;
                        break;
                }

                ((IVideoController)_video).Status = status;
                _video.Position = _mediaPlayerElement.MediaPlayer.Position;
            }
        }
        ...
    }
}

Metoda UpdateStatus używa wartości MediaPlayerElement.MediaPlayer.CurrentState właściwości, aby określić wartość Status właściwości. Właściwość Status można następnie ustawić na Video obiekcie, rzutując ją na IVideoControllerwartość .

Pasek pozycjonowania

Mechanizmy kontroli transportu implementowane przez każdą platformę obejmują pasek pozycjonowania. Ten pasek przypomina suwak lub pasek przewijania i pokazuje bieżącą lokalizację filmu wideo w ramach łącznego czasu trwania. Użytkownicy mogą manipulować paskiem pozycjonowania, aby przejść do przodu lub do tyłu do nowej pozycji w filmie wideo.

Zaimplementowanie własnego paska pozycjonowania wymaga Video , aby klasa znała czas trwania filmu wideo i jego bieżące położenie w tym czasie.

Czas trwania

Jednym z elementów informacji, które kontrolka Video musi obsługiwać niestandardowy pasek pozycjonowania, jest czas trwania filmu wideo. Klasa Video definiuje właściwość powiązaną tylko do odczytu o nazwie Duration, typu TimeSpan. Ta właściwość jest zdefiniowana jako tylko do odczytu, ponieważ powinna być ustawiana tylko z programu obsługi formantu:

namespace VideoDemos.Controls
{
    public class Video : View, IVideoController
    {
        ...
        private static readonly BindablePropertyKey DurationPropertyKey =
            BindableProperty.CreateReadOnly(nameof(Duration), typeof(TimeSpan), typeof(Video), new TimeSpan(),
                propertyChanged: (bindable, oldValue, newValue) => ((Video)bindable).SetTimeToEnd());

        public static readonly BindableProperty DurationProperty = DurationPropertyKey.BindableProperty;

        public TimeSpan Duration
        {
            get { return (TimeSpan)GetValue(DurationProperty); }
        }

        TimeSpan IVideoController.Duration
        {
            get { return Duration; }
            set { SetValue(DurationPropertyKey, value); }
        }
        ...
    }
}

Zazwyczaj właściwość powiązana tylko do odczytu ma prywatną set metodę dostępu do Duration właściwości, aby umożliwić jej ustawienie z poziomu klasy. Jednak w przypadku pochodnych View obsługiwanych przez programy obsługi właściwość musi być ustawiona spoza klasy, ale tylko przez program obsługi kontrolki.

Uwaga

Procedura obsługi zdarzeń zmiany właściwości dla właściwości możliwej Duration do powiązania wywołuje metodę o nazwie SetTimeToEnd, która została opisana w artykule Obliczanie czasu do końca.

Z tego powodu inna właściwość jest definiowana z nazwą IVideoController.Duration. Jest to jawna implementacja interfejsu IVideoController i jest możliwa przez interfejs implementowany przez klasę Video :

public interface IVideoController
{
    VideoStatus Status { get; set; }
    TimeSpan Duration { get; set; }
}

Ten interfejs umożliwia klasy zewnętrznej Video ustawienie Duration właściwości przez odwołanie się do interfejsu IVideoController . Właściwość można ustawić z innych klas i programu obsługi, ale jest mało prawdopodobne, aby została ustawiona przypadkowo. Co najważniejsze, Duration nie można ustawić właściwości za pomocą powiązania danych.

Czas trwania filmu wideo nie jest dostępny natychmiast po Source ustawieniu właściwości kontrolki Video . Wideo musi zostać częściowo pobrane, zanim widok macierzysty może określić jego czas trwania.

Android

W systemie Android VideoView.Duration właściwość zgłasza prawidłowy czas trwania w milisekundach po wystąpieniu VideoView.Prepared zdarzenia. Klasa MauiVideoPlayer używa Prepared procedury obsługi zdarzeń, aby uzyskać Duration wartość właściwości:

using Android.Content;
using Android.Views;
using Android.Widget;
using AndroidX.CoordinatorLayout.Widget;
using VideoDemos.Controls;
using Color = Android.Graphics.Color;
using Uri = Android.Net.Uri;

namespace VideoDemos.Platforms.Android
{
    public class MauiVideoPlayer : CoordinatorLayout
    {
        VideoView _videoView;
        Video _video;
        ...

        void OnVideoViewPrepared(object sender, EventArgs args)
        {
            ...
            ((IVideoController)_video).Duration = TimeSpan.FromMilliseconds(_videoView.Duration);
        }
        ...
    }
}
Katalizator systemów iOS i Mac

W systemach iOS i Mac Catalyst czas trwania filmu wideo jest uzyskiwany z AVPlayerItem.Duration właściwości, ale nie natychmiast po utworzeniu AVPlayerItem . Istnieje możliwość ustawienia obserwatora systemu iOS dla Duration właściwości, ale MauiVideoPlayer klasa uzyskuje czas trwania w UpdateStatus metodzie, która jest wywoływana 10 razy na sekundę:

using AVFoundation;
using AVKit;
using CoreMedia;
using Foundation;
using System.Diagnostics;
using UIKit;
using VideoDemos.Controls;

namespace VideoDemos.Platforms.MaciOS
{
    public class MauiVideoPlayer : UIView
    {
        AVPlayerItem _playerItem;
        ...

        TimeSpan ConvertTime(CMTime cmTime)
        {
            return TimeSpan.FromSeconds(Double.IsNaN(cmTime.Seconds) ? 0 : cmTime.Seconds);
        }

        public void UpdateStatus()
        {
            ...
            if (_playerItem != null)
            {
                ((IVideoController)_video).Duration = ConvertTime(_playerItem.Duration);
                ...
            }
        }
        ...
    }
}

Metoda ConvertTime konwertuje CMTime obiekt na TimeSpan wartość.

Windows

W systemie Windows MediaPlayerElement.MediaPlayer.NaturalDuration właściwość jest wartością TimeSpan , która staje się prawidłowa, gdy MediaPlayerElement.MediaPlayer.MediaOpened zdarzenie zostało zgłoszone. Klasa MauiVideoPlayer używa MediaOpened procedury obsługi zdarzeń, aby uzyskać NaturalDuration wartość właściwości:

using Microsoft.UI.Xaml.Controls;
using VideoDemos.Controls;
using Windows.Media.Core;
using Windows.Media.Playback;
using Windows.Storage;
using Grid = Microsoft.UI.Xaml.Controls.Grid;

namespace VideoDemos.Platforms.Windows
{
    public class MauiVideoPlayer : Grid, IDisposable
    {
        MediaPlayerElement _mediaPlayerElement;
        Video _video;
        bool _isMediaPlayerAttached;
        ...

        void OnMediaPlayerMediaOpened(MediaPlayer sender, object args)
        {
            MainThread.BeginInvokeOnMainThread(() =>
            {
                ((IVideoController)_video).Duration = _mediaPlayerElement.MediaPlayer.NaturalDuration;
            });
        }
        ...
    }
}

Procedura OnMediaPlayer obsługi zdarzeń wywołuje następnie metodę MainThread.BeginInvokeOnMainThread , aby ustawić Duration właściwość obiektu Video przez rzutowanie jej na IVideoController, w głównym wątku. Jest to konieczne, ponieważ MediaPlayerElement.MediaPlayer.MediaOpened zdarzenie jest obsługiwane w wątku w tle. Aby uzyskać więcej informacji na temat uruchamiania kodu w wątku głównym, zobacz Tworzenie wątku w wątku interfejsu użytkownika programu .NET MAUI.

Position

Kontrolka Video wymaga również właściwości, która zwiększa się z zera do Duration w miarę Position odtwarzania wideo. Klasa Video implementuje tę właściwość jako powiązaną właściwość z publicznymi get i set metodami dostępu:

namespace VideoDemos.Controls
{
    public class Video : View, IVideoController
    {
        ...
        public static readonly BindableProperty PositionProperty =
            BindableProperty.Create(nameof(Position), typeof(TimeSpan), typeof(Video), new TimeSpan(),
                propertyChanged: (bindable, oldValue, newValue) => ((Video)bindable).SetTimeToEnd());

        public TimeSpan Position
        {
            get { return (TimeSpan)GetValue(PositionProperty); }
            set { SetValue(PositionProperty, value); }
        }
        ...
    }
}

Metoda get dostępu zwraca bieżącą pozycję filmu wideo podczas odtwarzania. Akcesorium set reaguje na manipulowanie paskiem pozycjonowania przez przesunięcie pozycji wideo do przodu lub do tyłu.

Uwaga

Procedura obsługi zdarzeń zmiany właściwości dla właściwości możliwej Position do powiązania wywołuje metodę o nazwie SetTimeToEnd, która została opisana w artykule Obliczanie czasu do końca.

W systemach Android, iOS i Mac Catalyst właściwość, która uzyskuje bieżącą get pozycję, ma tylko akcesorium. Seek Zamiast tego dostępna jest metoda ustawiania pozycji. Wydaje się to być bardziej rozsądne podejście niż użycie jednej Position właściwości, która ma nieodłączny problem. W miarę odtwarzania wideo właściwość musi być stale aktualizowana, Position aby odzwierciedlić nową pozycję. Nie chcesz jednak, aby większość zmian Position właściwości powodowała przejście odtwarzacza wideo na nową pozycję w filmie wideo. Jeśli tak się stanie, odtwarzacz wideo odpowie, szukając ostatniej wartości Position właściwości, a film nie zostanie wcześniejszy.

Pomimo trudności z implementacją Position właściwości za pomocą get metody i set metod dostępu, to podejście jest używane, ponieważ może korzystać z powiązania danych. Właściwość Position kontrolki Video może być powiązana z elementem Slider używanym zarówno do wyświetlania pozycji, jak i do wyszukiwania nowej pozycji. Jednak kilka środków ostrożności jest niezbędnych podczas implementowania Position właściwości, aby uniknąć pętli opinii.

Android

W systemie Android VideoView.CurrentPosition właściwość wskazuje bieżącą pozycję filmu wideo. Klasa MauiVideoPlayer ustawia Position właściwość w metodzie UpdateStatus w tym samym czasie, co ustawia Duration właściwość:

using Android.Content;
using Android.Views;
using Android.Widget;
using AndroidX.CoordinatorLayout.Widget;
using VideoDemos.Controls;
using Color = Android.Graphics.Color;
using Uri = Android.Net.Uri;

namespace VideoDemos.Platforms.Android
{
    public class MauiVideoPlayer : CoordinatorLayout
    {
        VideoView _videoView;
        Video _video;
        ...

        public void UpdateStatus()
        {
            ...
            TimeSpan timeSpan = TimeSpan.FromMilliseconds(_videoView.CurrentPosition);
            _video.Position = timeSpan;
        }

        public void UpdatePosition()
        {
            if (Math.Abs(_videoView.CurrentPosition - _video.Position.TotalMilliseconds) > 1000)
            {
                _videoView.SeekTo((int)_video.Position.TotalMilliseconds);
            }
        }
        ...
    }
}

Za każdym razem, gdy Position właściwość jest ustawiana przez UpdateStatus metodę, Position właściwość uruchamia PropertyChanged zdarzenie, co powoduje, że maper właściwości programu obsługi wywołuje metodę UpdatePosition . Metoda UpdatePosition nie powinna nic robić dla większości zmian właściwości. W przeciwnym razie każda zmiana pozycji filmu wideo zostanie przeniesiona do tej samej pozycji, która właśnie osiągnęła. Aby uniknąć tej pętli opinii, jedyną UpdatePosition metodą obiektu jest wywołanie Seek metody, VideoView gdy różnica między Position właściwością a bieżącą pozycją VideoView obiektu jest większa niż jedna sekunda.

Katalizator systemów iOS i Mac

W systemach iOS i Mac Catalyst AVPlayerItem.CurrentTime właściwość wskazuje bieżącą pozycję filmu wideo. Klasa MauiVideoPlayer ustawia Position właściwość w metodzie UpdateStatus w tym samym czasie, co ustawia Duration właściwość:

using AVFoundation;
using AVKit;
using CoreMedia;
using Foundation;
using System.Diagnostics;
using UIKit;
using VideoDemos.Controls;

namespace VideoDemos.Platforms.MaciOS
{
    public class MauiVideoPlayer : UIView
    {
        AVPlayer _player;
        AVPlayerItem _playerItem;
        Video _video;
        ...

        TimeSpan ConvertTime(CMTime cmTime)
        {
            return TimeSpan.FromSeconds(Double.IsNaN(cmTime.Seconds) ? 0 : cmTime.Seconds);
        }

        public void UpdateStatus()
        {
            ...
            if (_playerItem != null)
            {
                ...
                _video.Position = ConvertTime(_playerItem.CurrentTime);
            }
        }

        public void UpdatePosition()
        {
            TimeSpan controlPosition = ConvertTime(_player.CurrentTime);
            if (Math.Abs((controlPosition - _video.Position).TotalSeconds) > 1)
            {
                _player.Seek(CMTime.FromSeconds(_video.Position.TotalSeconds, 1));
            }
        }
        ...
    }
}

Za każdym razem, gdy Position właściwość jest ustawiana przez UpdateStatus metodę, Position właściwość uruchamia PropertyChanged zdarzenie, co powoduje, że maper właściwości programu obsługi wywołuje metodę UpdatePosition . Metoda UpdatePosition nie powinna nic robić dla większości zmian właściwości. W przeciwnym razie każda zmiana pozycji filmu wideo zostanie przeniesiona do tej samej pozycji, która właśnie osiągnęła. Aby uniknąć tej pętli opinii, jedyną UpdatePosition metodą obiektu jest wywołanie Seek metody, AVPlayer gdy różnica między Position właściwością a bieżącą pozycją AVPlayer obiektu jest większa niż jedna sekunda.

Windows

W systemie Windows MediaPlayerElement.MedaPlayer.Position właściwość wskazuje bieżącą pozycję filmu wideo. Klasa MauiVideoPlayer ustawia Position właściwość w metodzie UpdateStatus w tym samym czasie, co ustawia Duration właściwość:

using Microsoft.UI.Xaml.Controls;
using VideoDemos.Controls;
using Windows.Media.Core;
using Windows.Media.Playback;
using Windows.Storage;
using Grid = Microsoft.UI.Xaml.Controls.Grid;

namespace VideoDemos.Platforms.Windows
{
    public class MauiVideoPlayer : Grid, IDisposable
    {
        MediaPlayerElement _mediaPlayerElement;
        Video _video;
        bool _isMediaPlayerAttached;
        ...

        public void UpdateStatus()
        {
            if (_isMediaPlayerAttached)
            {
                ...
                _video.Position = _mediaPlayerElement.MediaPlayer.Position;
            }
        }

        public void UpdatePosition()
        {
            if (_isMediaPlayerAttached)
            {
                if (Math.Abs((_mediaPlayerElement.MediaPlayer.Position - _video.Position).TotalSeconds) > 1)
                {
                    _mediaPlayerElement.MediaPlayer.Position = _video.Position;
                }
            }
        }
        ...
    }
}

Za każdym razem, gdy Position właściwość jest ustawiana przez UpdateStatus metodę, Position właściwość uruchamia PropertyChanged zdarzenie, co powoduje, że maper właściwości programu obsługi wywołuje metodę UpdatePosition . Metoda UpdatePosition nie powinna nic robić dla większości zmian właściwości. W przeciwnym razie każda zmiana pozycji filmu wideo zostanie przeniesiona do tej samej pozycji, która właśnie osiągnęła. Aby uniknąć tej pętli opinii, UpdatePosition jedynym ustawieniem MediaPlayerElement.MediaPlayer.Position właściwości jest różnica między Position właściwością a bieżącą pozycją MediaPlayerElement obiektu jest większa niż jedna sekunda.

Obliczanie czasu do końca

Czasami odtwarzacze wideo pokazują czas pozostały w filmie. Ta wartość rozpoczyna się od czasu rozpoczęcia filmu wideo i zmniejsza się do zera po zakończeniu wideo.

Klasa Video zawiera właściwość tylko TimeToEnd do odczytu, która jest obliczana na podstawie zmian Duration właściwości i Position :

namespace VideoDemos.Controls
{
    public class Video : View, IVideoController
    {
        ...
        private static readonly BindablePropertyKey TimeToEndPropertyKey =
            BindableProperty.CreateReadOnly(nameof(TimeToEnd), typeof(TimeSpan), typeof(Video), new TimeSpan());

        public static readonly BindableProperty TimeToEndProperty = TimeToEndPropertyKey.BindableProperty;

        public TimeSpan TimeToEnd
        {
            get { return (TimeSpan)GetValue(TimeToEndProperty); }
            private set { SetValue(TimeToEndPropertyKey, value); }
        }

        void SetTimeToEnd()
        {
            TimeToEnd = Duration - Position;
        }
        ...
    }
}

Metoda jest wywoływana SetTimeToEnd z procedur obsługi zdarzeń zmienionych Duration właściwości i Position .

Niestandardowy pasek pozycjonowania

Niestandardowy pasek pozycjonowania można zaimplementować, tworząc klasę pochodzącą z Sliderklasy , która zawiera Duration właściwości typu TimeSpani Position :

namespace VideoDemos.Controls
{
    public class PositionSlider : Slider
    {
        public static readonly BindableProperty DurationProperty =
            BindableProperty.Create(nameof(Duration), typeof(TimeSpan), typeof(PositionSlider), new TimeSpan(1),
                propertyChanged: (bindable, oldValue, newValue) =>
                {
                    double seconds = ((TimeSpan)newValue).TotalSeconds;
                    ((Slider)bindable).Maximum = seconds <= 0 ? 1 : seconds;
                });

        public static readonly BindableProperty PositionProperty =
            BindableProperty.Create(nameof(Position), typeof(TimeSpan), typeof(PositionSlider), new TimeSpan(0),
                defaultBindingMode: BindingMode.TwoWay,
                propertyChanged: (bindable, oldValue, newValue) =>
                {
                    double seconds = ((TimeSpan)newValue).TotalSeconds;
                    ((Slider)bindable).Value = seconds;
                });

        public TimeSpan Duration
        {
            get { return (TimeSpan)GetValue(DurationProperty); }
            set { SetValue(DurationProperty, value); }
        }

        public TimeSpan Position
        {
            get { return (TimeSpan)GetValue(PositionProperty); }
            set { SetValue (PositionProperty, value); }
        }

        public PositionSlider()
        {
            PropertyChanged += (sender, args) =>
            {
                if (args.PropertyName == "Value")
                {
                    TimeSpan newPosition = TimeSpan.FromSeconds(Value);
                    if (Math.Abs(newPosition.TotalSeconds - Position.TotalSeconds) / Duration.TotalSeconds > 0.01)
                        Position = newPosition;
                }
            };
        }
    }
}

Procedura obsługi zdarzeń zmiany właściwości dla Duration właściwości ustawia Maximum właściwość na TotalSeconds właściwość Slider TimeSpan wartości. Podobnie procedura obsługi zdarzeń zmiany właściwości dla Position właściwości ustawia Value właściwość Slider. Jest to mechanizm, za pomocą którego Slider śledzi położenie PositionSliderelementu .

Element PositionSlider jest aktualizowany z bazowego Slider tylko w jednym scenariuszu, który polega na tym, że użytkownik manipuluje elementem Slider , aby wskazać, że film wideo powinien zostać zaawansowany lub odwrócony do nowej pozycji. Jest to wykrywane w procedurze PropertyChanged obsługi w konstruktorze PositionSlider . Ta procedura obsługi zdarzeń sprawdza zmianę Value właściwości, a jeśli różni się od Position właściwości, Position właściwość jest ustawiana z Value właściwości .

Rejestrowanie programu obsługi

Kontrolka niestandardowa i jej program obsługi muszą być zarejestrowane w aplikacji, zanim będzie można jej używać. Powinno się to zdarzyć w CreateMauiApp metodzie w MauiProgram klasie w projekcie aplikacji, czyli międzyplatformowym punkcie wejścia aplikacji:

using VideoDemos.Controls;
using VideoDemos.Handlers;

namespace VideoDemos;

public static class MauiProgram
{
    public static MauiApp CreateMauiApp()
    {
        var builder = MauiApp.CreateBuilder();
        builder
            .UseMauiApp<App>()
            .ConfigureFonts(fonts =>
            {
                fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
                fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
            })
            .ConfigureMauiHandlers(handlers =>
            {
                handlers.AddHandler(typeof(Video), typeof(VideoHandler));
            });

        return builder.Build();
    }
}

Procedura obsługi jest zarejestrowana w metodzie ConfigureMauiHandlers i AddHandler . Pierwszym argumentem AddHandler metody jest typ sterowania międzyplatformowego, a drugi argument jest jego typem obsługi.

Korzystanie z kontroli międzyplatformowej

Po zarejestrowaniu programu obsługi w aplikacji można użyć wieloplatformowej kontrolki.

Odtwarzanie wideo w sieci Web

Kontrolka Video może odtworzyć wideo z adresu URL, jak pokazano w poniższym przykładzie:

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:controls="clr-namespace:VideoDemos.Controls"
             x:Class="VideoDemos.Views.PlayWebVideoPage"
             Unloaded="OnContentPageUnloaded"
             Title="Play web video">
    <controls:Video x:Name="video"
                    Source="https://archive.org/download/BigBuckBunny_328/BigBuckBunny_512kb.mp4" />
</ContentPage>

W tym przykładzie VideoSourceConverter klasa konwertuje ciąg reprezentujący identyfikator URI na UriVideoSource. Następnie wideo rozpoczyna ładowanie i rozpoczyna odtwarzanie po pobraniu i buforowi wystarczającej ilości danych. Na każdej platformie kontrolki transportu zanikają, jeśli nie są używane, ale można je przywrócić, naciskając film wideo.

Odtwarzanie zasobu wideo

Pliki wideo osadzone w folderze Resources\Raw aplikacji z akcją kompilacji MauiAsset mogą być odtwarzane przez kontrolkę Video :

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:controls="clr-namespace:VideoDemos.Controls"
             x:Class="VideoDemos.Views.PlayVideoResourcePage"
             Unloaded="OnContentPageUnloaded"
             Title="Play video resource">
    <controls:Video x:Name="video"
                    Source="video.mp4" />
</ContentPage>

W tym przykładzie VideoSourceConverter klasa konwertuje ciąg reprezentujący nazwę pliku wideo na ResourceVideoSource. Dla każdej platformy wideo rozpoczyna odtwarzanie niemal natychmiast po ustawieniu źródła wideo, ponieważ plik znajduje się w pakiecie aplikacji i nie musi być pobierany. Na każdej platformie kontrolki transportu zanikają, jeśli nie są używane, ale można je przywrócić, naciskając film wideo.

Odtwarzanie pliku wideo z biblioteki urządzenia

Pliki wideo przechowywane na urządzeniu można pobrać, a następnie odtwarzane przez kontrolkę Video :

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:controls="clr-namespace:VideoDemos.Controls"
             x:Class="VideoDemos.Views.PlayLibraryVideoPage"
             Unloaded="OnContentPageUnloaded"
             Title="Play library video">
    <Grid RowDefinitions="*,Auto">
        <controls:Video x:Name="video" />
        <Button Grid.Row="1"
                Text="Show Video Library"
                Margin="10"
                HorizontalOptions="Center"
                Clicked="OnShowVideoLibraryClicked" />
    </Grid>
</ContentPage>

Po naciśnięciu Button programu Clicked obsługi zdarzeń jest wykonywana, co jest wyświetlane w poniższym przykładzie kodu:

async void OnShowVideoLibraryClicked(object sender, EventArgs e)
{
    Button button = sender as Button;
    button.IsEnabled = false;

    var pickedVideo = await MediaPicker.PickVideoAsync();
    if (!string.IsNullOrWhiteSpace(pickedVideo?.FileName))
    {
        video.Source = new FileVideoSource
        {
            File = pickedVideo.FullPath
        };
    }

    button.IsEnabled = true;
}

Program Clicked obsługi zdarzeń używa klasy .NET MAUI MediaPicker , aby umożliwić użytkownikowi wybranie pliku wideo z urządzenia. Wybrany plik wideo jest następnie hermetyzowany jako FileVideoSource obiekt i ustawiany jako Source właściwość kontrolki Video . Aby uzyskać więcej informacji na temat MediaPicker klasy, zobacz Selektor multimediów. Dla każdej platformy wideo rozpoczyna odtwarzanie niemal natychmiast po ustawieniu źródła wideo, ponieważ plik znajduje się na urządzeniu i nie musi być pobierany. Na każdej platformie kontrolki transportu zanikają, jeśli nie są używane, ale można je przywrócić, naciskając film wideo.

Konfigurowanie kontrolki Wideo

Możesz zapobiec automatycznemu uruchamianiu AutoPlay filmu wideo, ustawiając właściwość na false:

<controls:Video x:Name="video"
                Source="https://archive.org/download/BigBuckBunny_328/BigBuckBunny_512kb.mp4"
                AutoPlay="False" />

Kontrolki transportu można pominąć, ustawiając AreTransportControlsEnabled właściwość na false:

<controls:Video x:Name="video"
                Source="https://archive.org/download/BigBuckBunny_328/BigBuckBunny_512kb.mp4"
                AreTransportControlsEnabled="False" />

Jeśli ustawisz AutoPlay wartość i falseAreTransportControlsEnabled na , wideo nie rozpocznie się odtwarzania i nie będzie można rozpocząć odtwarzania. W tym scenariuszu należy wywołać metodę Play z pliku za pomocą kodu lub utworzyć własne kontrolki transportu.

Ponadto można ustawić pętlę wideo, ustawiając IsLooping właściwość na true:

<controls:Video x:Name="video"
                Source="https://archive.org/download/BigBuckBunny_328/BigBuckBunny_512kb.mp4"
                IsLooping="true" />

Jeśli ustawisz właściwość na true wartość , dzięki Video czemu kontrolka IsLooping automatycznie ustawi pozycję wideo na początek po osiągnięciu końca.

Używanie niestandardowych kontrolek transportu

W poniższym przykładzie XAML przedstawiono niestandardowe kontrolki transportu, które odtwarzają, wstrzymywać i zatrzymywać wideo:

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:controls="clr-namespace:VideoDemos.Controls"
             x:Class="VideoDemos.Views.CustomTransportPage"
             Unloaded="OnContentPageUnloaded"
             Title="Custom transport controls">
    <Grid RowDefinitions="*,Auto">
        <controls:Video x:Name="video"
                        AutoPlay="False"
                        AreTransportControlsEnabled="False"
                        Source="https://archive.org/download/BigBuckBunny_328/BigBuckBunny_512kb.mp4" />
        <ActivityIndicator Color="Gray"
                           IsVisible="False">
            <ActivityIndicator.Triggers>
                <DataTrigger TargetType="ActivityIndicator"
                             Binding="{Binding Source={x:Reference video},
                                               Path=Status}"
                             Value="{x:Static controls:VideoStatus.NotReady}">
                    <Setter Property="IsVisible"
                            Value="True" />
                    <Setter Property="IsRunning"
                            Value="True" />
                </DataTrigger>
            </ActivityIndicator.Triggers>
        </ActivityIndicator>
        <Grid Grid.Row="1"
              Margin="0,10"
              ColumnDefinitions="0.5*,0.5*"
              BindingContext="{x:Reference video}">
            <Button Text="&#x25B6;&#xFE0F; Play"
                    HorizontalOptions="Center"
                    Clicked="OnPlayPauseButtonClicked">
                <Button.Triggers>
                    <DataTrigger TargetType="Button"
                                 Binding="{Binding Status}"
                                 Value="{x:Static controls:VideoStatus.Playing}">
                        <Setter Property="Text"
                                Value="&#x23F8; Pause" />
                    </DataTrigger>
                    <DataTrigger TargetType="Button"
                                 Binding="{Binding Status}"
                                 Value="{x:Static controls:VideoStatus.NotReady}">
                        <Setter Property="IsEnabled"
                                Value="False" />
                    </DataTrigger>
                </Button.Triggers>
            </Button>
            <Button Grid.Column="1"
                    Text="&#x23F9; Stop"
                    HorizontalOptions="Center"
                    Clicked="OnStopButtonClicked">
                <Button.Triggers>
                    <DataTrigger TargetType="Button"
                                 Binding="{Binding Status}"
                                 Value="{x:Static controls:VideoStatus.NotReady}">
                        <Setter Property="IsEnabled"
                                Value="False" />
                    </DataTrigger>
                </Button.Triggers>
            </Button>
        </Grid>
    </Grid>
</ContentPage>

W tym przykładzie kontrolka Video ustawia AreTransportControlsEnabled właściwość na false wartość i definiuje Button element, który odtwarza i wstrzymuje klip wideo oraz Button zatrzymuje odtwarzanie wideo. Wygląd przycisku jest definiowany przy użyciu znaków Unicode i ich odpowiedników tekstu w celu utworzenia przycisków składających się z ikony i tekstu:

Zrzut ekranu przedstawiający przyciski odtwarzania i wstrzymywania.

Podczas odtwarzania wideo przycisk odtwarzania jest aktualizowany do przycisku wstrzymywania:

Zrzut ekranu przedstawiający przyciski wstrzymywania i zatrzymywania.

Interfejs użytkownika zawiera również element ActivityIndicator wyświetlany podczas ładowania wideo. Wyzwalacze danych służą do włączania i wyłączania ActivityIndicator przycisków oraz przełączania pierwszego przycisku między odtwarzaniem a wstrzymaniem. Aby uzyskać więcej informacji na temat wyzwalaczy danych, zobacz Wyzwalacze danych.

Plik kodu definiuje programy obsługi zdarzeń dla zdarzeń przycisku Clicked :

public partial class CustomTransportPage : ContentPage
{
    ...
    void OnPlayPauseButtonClicked(object sender, EventArgs args)
    {
        if (video.Status == VideoStatus.Playing)
        {
            video.Pause();
        }
        else if (video.Status == VideoStatus.Paused)
        {
            video.Play();
        }
    }

    void OnStopButtonClicked(object sender, EventArgs args)
    {
        video.Stop();
    }
    ...
}

Niestandardowy pasek pozycjonowania

W poniższym przykładzie pokazano niestandardowy pasek pozycjonowania, PositionSliderużywany w języku XAML:

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:controls="clr-namespace:VideoDemos.Controls"
             x:Class="VideoDemos.Views.CustomPositionBarPage"
             Unloaded="OnContentPageUnloaded"
             Title="Custom position bar">
    <Grid RowDefinitions="*,Auto,Auto">
        <controls:Video x:Name="video"
                        AreTransportControlsEnabled="False"
                        Source="{StaticResource ElephantsDream}" />
        ...
        <Grid Grid.Row="1"
              Margin="10,0"
              ColumnDefinitions="0.25*,0.25*,0.25*,0.25*"
              BindingContext="{x:Reference video}">
            <Label Text="{Binding Path=Position,
                                  StringFormat='{0:hh\\:mm\\:ss}'}"
                   HorizontalOptions="Center"
                   VerticalOptions="Center" />
            ...
            <Label Grid.Column="3"
                   Text="{Binding Path=TimeToEnd,
                                  StringFormat='{0:hh\\:mm\\:ss}'}"
                   HorizontalOptions="Center"
                   VerticalOptions="Center" />
        </Grid>
        <controls:PositionSlider Grid.Row="2"
                                 Margin="10,0,10,10"
                                 BindingContext="{x:Reference video}"
                                 Duration="{Binding Duration}"
                                 Position="{Binding Position}">
            <controls:PositionSlider.Triggers>
                <DataTrigger TargetType="controls:PositionSlider"
                             Binding="{Binding Status}"
                             Value="{x:Static controls:VideoStatus.NotReady}">
                    <Setter Property="IsEnabled"
                            Value="False" />
                </DataTrigger>
            </controls:PositionSlider.Triggers>
        </controls:PositionSlider>
    </Grid>
</ContentPage>

Position Właściwość Video obiektu jest powiązana z Position właściwością PositionSlider, bez problemów z wydajnością, ponieważ Video.Position właściwość jest zmieniana przez MauiVideoPlayer.UpdateStatus metodę na każdej platformie, która jest wywoływana tylko 10 razy na sekundę. Ponadto dwa Label obiekty wyświetlają Position wartości właściwości i TimeToEnd z Video obiektu .

Czyszczenie widoku natywnego

Implementacja programu obsługi każdej platformy zastępuje implementację DisconnectHandler , która służy do przeprowadzania oczyszczania widoku natywnego, takiego jak anulowanie subskrypcji z zdarzeń i usuwanie obiektów. Jednak to zastąpienie nie jest celowo wywoływane przez program .NET MAUI. Zamiast tego należy wywołać ją samodzielnie z odpowiedniej lokalizacji w cyklu życia aplikacji. Często będzie to miało miejsce, gdy strona zawierająca kontrolkę Video zostanie odchynięta, co powoduje, że zdarzenie strony Unloaded zostanie podniesione.

Program obsługi zdarzeń dla zdarzenia strony Unloaded można zarejestrować w języku XAML:

<ContentPage ...
             xmlns:controls="clr-namespace:VideoDemos.Controls"
             Unloaded="OnContentPageUnloaded">
    <controls:Video x:Name="video"
                    ... />
</ContentPage>

Program obsługi zdarzeń dla Unloaded zdarzenia może następnie wywołać metodę w jego Handler wystąpieniuDisconnectHandler:

void OnContentPageUnloaded(object sender, EventArgs e)
{
    video.Handler?.DisconnectHandler();
}

Oprócz czyszczenia zasobów widoku natywnego wywoływanie metody programu obsługi DisconnectHandler zapewnia również, że filmy wideo przestaną odtwarzać się w nawigacji wstecznej w systemie iOS.

Rozłączanie programu obsługi sterowania

Implementacja programu obsługi każdej platformy zastępuje implementację DisconnectHandler , która służy do przeprowadzania oczyszczania widoku natywnego, takiego jak anulowanie subskrypcji z zdarzeń i usuwanie obiektów. Domyślnie programy obsługi automatycznie odłączają się od kontrolek, gdy to możliwe, na przykład podczas przechodzenia do tyłu w aplikacji.

W niektórych scenariuszach możesz chcieć kontrolować, kiedy program obsługi rozłącza się z jego kontrolką, co można osiągnąć za pomocą dołączonej HandlerProperties.DisconnectPolicy właściwości. Ta właściwość wymaga argumentu z wyliczeniem HandlerDisconnectPolicy definiującym następujące wartości:

  • Automatic, który wskazuje, że program obsługi zostanie automatycznie odłączony. Jest to wartość domyślna dołączonej HandlerProperties.DisconnectPolicy właściwości.
  • Manual, który wskazuje, że program obsługi będzie musiał zostać odłączony ręcznie przez wywołanie implementacji DisconnectHandler() .

W poniższym przykładzie pokazano ustawienie dołączonej HandlerProperties.DisconnectPolicy właściwości:

<controls:Video x:Name="video"
                HandlerProperties.DisconnectPolicy="Manual"
                Source="video.mp4"
                AutoPlay="False" />

Równoważny kod języka C# to:

Video video = new Video
{
    Source = "video.mp4",
    AutoPlay = false
};
HandlerProperties.SetDisconnectPolicy(video, HandlerDisconnectPolicy.Manual);

Podczas ustawiania dołączonej HandlerProperties.DisconnectPolicy właściwości Manual należy wywołać implementację programu obsługi DisconnectHandler samodzielnie z odpowiedniej lokalizacji w cyklu życia aplikacji. Można to osiągnąć, wywołując video.Handler?.DisconnectHandler();.

Ponadto istnieje DisconnectHandlers metoda rozszerzenia, która rozłącza programy obsługi z danego IViewelementu :

video.DisconnectHandlers();

Podczas odłączania DisconnectHandlers metoda będzie propagowana w dół drzewa kontrolek do momentu zakończenia lub odebrania kontrolki, która ustawiła zasady ręczne.