Aracılığıyla paylaş


İşleyicileri kullanarak özel denetim oluşturma

Browse sample. Örneğe göz atın

Uygulamalar için standart bir gereksinim, video oynatabilmektir. Bu makalede, platformlar arası denetim API'sini Video oynatan Android, iOS ve Mac Catalyst'daki yerel görünümlerle eşlemek için işleyici kullanan bir .NET Çok platformlu Uygulama Kullanıcı Arabirimi (.NET MAUI) Video platformlar arası denetimin nasıl oluşturulacağı incelenmektedir. Bu denetim üç kaynaktan video oynatabilir:

  • Uzak bir videosunu temsil eden URL.
  • Uygulamaya eklenmiş bir dosya olan kaynak.
  • Cihazın video kitaplığından bir dosya.

Video denetimleri, videoyu oynatmaya ve duraklatmaya yönelik düğmeler olan aktarım denetimlerini ve videodaki ilerlemeyi gösteren ve kullanıcının farklı bir konuma hızla geçiş yapmasını sağlayan bir konumlandırma çubuğu gerektirir. Denetim Video , platform tarafından sağlanan aktarım denetimlerini ve konumlandırma çubuğunu kullanabilir veya özel aktarım denetimleri ve bir konumlandırma çubuğu sağlayabilirsiniz. Aşağıdaki ekran görüntüleri, özel aktarım denetimleri olan ve olmayan iOS üzerindeki denetimi gösterir:

Screenshot of video playback on iOS.Screenshot of video playback using custom transport controls on iOS.

Daha gelişmiş bir video denetiminin ses denetimi, çağrı alındığında video kayıttan yürütmeyi kesintiye uğratma mekanizması ve kayıttan yürütme sırasında ekranı etkin tutma yöntemi gibi ek özellikleri olabilir.

Denetimin Video mimarisi aşağıdaki diyagramda gösterilmiştir:

Video handler architecture.

sınıfı, Video denetim için platformlar arası API sağlar. Platformlar arası API'nin yerel görünüm API'lerine eşlemesi, sınıfı sınıfa eşleyen Video her platformdaki sınıf MauiVideoPlayer tarafından VideoHandler gerçekleştirilir. iOS ve Mac Catalyst'te MauiVideoPlayer sınıfı, video kayıttan yürütme sağlamak için türünü kullanır AVPlayer . Android'de MauiVideoPlayer sınıfı, video kayıttan yürütme sağlamak için türünü kullanır VideoView . Windows'da MauiVideoPlayer sınıfı, video kayıttan yürütme sağlamak için türünü kullanır MediaPlayerElement .

Önemli

.NET MAUI, işleyicilerini arabirimler aracılığıyla platformlar arası denetimlerinden ayırır. Bu, .NET MAUI'nin işleyicilerini kullanmaya devam ederken arabirimleri uygulayan Comet ve Fabulous gibi deneysel çerçevelerin kendi platformlar arası denetimlerini sağlamasına olanak tanır. Platformlar arası denetiminiz için bir arabirim oluşturmak, yalnızca işleyicinizi benzer bir amaçla veya test amacıyla platformlar arası denetiminden ayırmanız gerekiyorsa gereklidir.

Platform uygulamaları işleyiciler tarafından sağlanan platformlar arası bir .NET MAUI özel denetimi oluşturma işlemi aşağıdaki gibidir:

  1. Denetimin genel API'sini sağlayan platformlar arası denetim için bir sınıf oluşturun. Daha fazla bilgi için bkz . Platformlar arası denetim oluşturma.
  2. Gerekli ek platformlar arası türleri oluşturun.
  3. İşleyici partial sınıfı oluşturun. Daha fazla bilgi için bkz . İşleyiciyi oluşturma.
  4. İşleyici sınıfında, platformlar arası özellik değişiklikleri gerçekleştiğinde gerçekleştirecek eylemleri tanımlayan bir PropertyMapper sözlük oluşturun. Daha fazla bilgi için bkz . Özellik eşleyicisini oluşturma.
  5. İsteğe bağlı olarak, işleyici sınıfınızda, platformlar arası denetim, platformlar arası denetimi uygulayan yerel görünümlere yönergeler gönderdiğinde gerçekleştirilecek eylemleri tanımlayan bir CommandMapper sözlük oluşturun. Daha fazla bilgi için bkz . Komut eşleyicisini oluşturma.
  6. Platformlar arası denetimi uygulayan yerel görünümleri oluşturan her platform için işleyici sınıfları oluşturun partial . Daha fazla bilgi için bkz . Platform denetimleri oluşturma.
  7. uygulamanızın MauiProgram sınıfında ve AddHandler yöntemlerini kullanarak ConfigureMauiHandlers işleyiciyi kaydedin. Daha fazla bilgi için bkz . İşleyiciyi kaydetme.

Daha sonra platformlar arası denetim kullanılabilir. Daha fazla bilgi için bkz . Platformlar arası denetimi kullanma.

Platformlar arası denetim oluşturma

Platformlar arası denetim oluşturmak için, öğesinden Viewtüretilen bir sınıf oluşturmanız gerekir:

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); }
        }        
        ...
    }
}

Denetim, işleyicisi tarafından erişilecek bir genel API sağlamalı ve tüketicileri denetlemelidir. Platformlar arası denetimler, düzenleri ve görünümleri ekrana yerleştirmek için kullanılan bir görsel öğeyi temsil eden öğesinden Viewtüretilmelidir.

İşleyiciyi oluşturma

Platformlar arası denetiminizi oluşturduktan sonra işleyiciniz için bir partial sınıf oluşturmanız gerekir:

#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
    {
    }
}

İşleyici sınıfı, uygulaması ek bir kısmi sınıfla her platformda tamamlanacak kısmi bir sınıftır.

Koşullu using deyimler, her platformda türü tanımlar PlatformView . Android, iOS, Mac Catalyst ve Windows'da yerel görünümler özel MauiVideoPlayer sınıf tarafından sağlanır. Son koşullu using deyim, değerine System.Objecteşit olduğunu tanımlarPlatformView. Bu, türün tüm platformlardaki PlatformView kullanım için işleyici içinde kullanılabilmesi için gereklidir. Alternatif olarak, koşullu derleme kullanarak özelliği platform başına bir kez tanımlamanız PlatformView gerekir.

Özellik eşleyicisi oluşturma

Her işleyici genellikle platformlar arası denetimde bir özellik değişikliği gerçekleştiğinde hangi Eylemlerin gerçekleştireceğini tanımlayan bir özellik eşleyicisi sağlar. Tür PropertyMapper , platformlar arası denetimin özelliklerini ilişkili Eylemleriyle eşleyen bir Dictionary türdür.

PropertyMapper .NET MAUI'nin genel ViewHandler sınıfında tanımlanır ve iki genel bağımsız değişkenin sağlanmasını gerektirir:

  • platformlar arası denetimin sınıfından Viewtüretilir.
  • İşleyici için sınıfı.

Aşağıdaki kod örneği, tanımıyla PropertyMapper genişletilmiş sınıfı gösterirVideoHandler:

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)
    {
    }
}

PropertyMapper, Dictionary anahtarı a string olan ve değeri genel Actionolan bir değeridir. , string platformlar arası denetimin özellik adını, Action işleyici ve platformlar arası denetimi bağımsız değişken olarak gerektiren bir static yöntemi temsil eder. Örneğin, yönteminin MapSource imzası şeklindedir public static void MapSource(VideoHandler handler, Video video).

Her platform işleyicisi, yerel görünüm API'lerini işleyen Eylemlerin uygulamalarını sağlamalıdır. Bu, bir özellik platformlar arası denetimde ayarlandığında temel alınan yerel görünümün gerektiği gibi güncelleştirilmesini sağlar. Bu yaklaşımın avantajı, özellik eşleyicisi alt sınıflama olmadan platformlar arası denetim tüketicileri tarafından değiştirilebildiği için kolay platformlar arası denetim özelleştirmesine olanak sağlamasıdır.

Komut eşleyicisi oluşturma

Her işleyici, platformlar arası denetim komutları yerel görünümlere gönderdiğinde hangi Eylemlerin gerçekleştirebileceğini tanımlayan bir komut eşleyici de sağlayabilir. Komut eşleyicileri özellik eşleyicilerine benzer, ancak ek verilerin geçirilmesine izin verir. Bu bağlamda, komut bir yönergedir ve isteğe bağlı olarak verileri yerel görünüme gönderilir. Tür CommandMapper , platformlar arası denetim üyelerini ilişkili Eylemleriyle eşleyen bir Dictionary türdür.

CommandMapper .NET MAUI'nin genel ViewHandler sınıfında tanımlanır ve iki genel bağımsız değişkenin sağlanmasını gerektirir:

  • platformlar arası denetimin sınıfından Viewtüretilir.
  • İşleyici için sınıfı.

Aşağıdaki kod örneği, tanımıyla CommandMapper genişletilmiş sınıfı gösterirVideoHandler:

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)
    {
    }
}

CommandMapper, Dictionary anahtarı a string olan ve değeri genel Actionolan bir değeridir. , string platformlar arası denetimin komut adını temsil eder ve Action işleyici, platformlar arası denetim ve isteğe bağlı verileri bağımsız değişken olarak gerektiren bir static yöntemi temsil eder. Örneğin, yönteminin MapPlayRequested imzası şeklindedir public static void MapPlayRequested(VideoHandler handler, Video video, object? args).

Her platform işleyicisi, yerel görünüm API'lerini işleyen Eylemlerin uygulamalarını sağlamalıdır. Bu, platformlar arası denetimden bir komut gönderildiğinde, temel alınan yerel görünümün gerektiği gibi işlendiğinden emin olur. Bu yaklaşımın avantajı, yerel görünümlerin platformlar arası denetim olaylarına abone olma ve abonelikten çıkma gereksinimini ortadan kaldırmasıdır. Ayrıca, komut eşleyicisi alt sınıflama olmadan platformlar arası denetim tüketicileri tarafından değiştirilebildiği için kolay özelleştirmeye olanak tanır.

Platform denetimlerini oluşturma

İşleyiciniz için eşleyicileri oluşturduktan sonra tüm platformlarda işleyici uygulamaları sağlamanız gerekir. Bu, Platforms klasörünün alt klasörlerine kısmi sınıf işleyicisi uygulamaları eklenerek gerçekleştirilebilir. Alternatif olarak projenizi dosya adı tabanlı çoklu hedeflemeyi veya klasör tabanlı çoklu hedeflemeyi ya da her ikisini de destekleyecek şekilde yapılandırabilirsiniz.

Örnek uygulama, dosya adı tabanlı çoklu hedeflemeyi destekleyecek şekilde yapılandırılır, böylece işleyici sınıflarının tümü tek bir klasörde bulunur:

Screenshot of the files in the Handlers folder of the project.

VideoHandler Eşleyicileri içeren sınıf VideoHandler.cs olarak adlandırılır. Platform uygulamaları VideoHandler.Android.cs, VideoHandler.MaciOS.cs ve VideoHandler.Windows.cs dosyalarındadır. Bu dosya adı tabanlı çoklu hedefleme, düğümün alt öğeleri <Project> olarak proje dosyasına aşağıdaki XML eklenerek yapılandırılır:

<!-- 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>

Çoklu hedeflemeyi yapılandırma hakkında daha fazla bilgi için bkz . Çoklu hedeflemeyi yapılandırma.

Her platform işleyici sınıfı kısmi bir sınıf olmalı ve iki tür bağımsız değişken gerektiren genel ViewHandler sınıftan türetilmelidir:

  • platformlar arası denetimin sınıfından Viewtüretilir.
  • Platformda platformlar arası denetimi uygulayan yerel görünümün türü. Bu, işleyicideki özelliğin PlatformView türüyle aynı olmalıdır.

Önemli

ViewHandler sınıfı ve PlatformView özellikleri sağlarVirtualView. VirtualView özelliği, işleyicisinden platformlar arası denetime erişmek için kullanılır. PlatformView özelliği, platformlar arası denetimi uygulayan her platformda yerel görünüme erişmek için kullanılır.

Platform işleyicisi uygulamalarının her biri aşağıdaki yöntemleri geçersiz kılmalıdır:

  • CreatePlatformView, platformlar arası denetimi uygulayan yerel görünümü oluşturup döndürmelidir.
  • ConnectHandler, yerel görünümü başlatma ve olay aboneliklerini gerçekleştirme gibi herhangi bir yerel görünüm kurulumu gerçekleştirmelidir.
  • DisconnectHandler, olaylardan aboneliği kaldırma ve nesneleri yok etme gibi herhangi bir yerel görünüm temizlemesi gerçekleştirmelidir.

Önemli

DisconnectHandler yöntemi kasıtlı olarak .NET MAUI tarafından çağrılmıyor. Bunun yerine, uygulamanızın yaşam döngüsünde uygun bir konumdan kendiniz çağırmanız gerekir. Daha fazla bilgi için bkz . Yerel görünüm temizleme.

Her platform işleyicisi, eşleyici sözlüklerinde tanımlanan Eylemleri de uygulamalıdır.

Ayrıca her platform işleyicisi, platform üzerinde platformlar arası denetimin işlevselliğini uygulamak için gerektiği gibi kod sağlamalıdır. Alternatif olarak, bu, burada benimsenen yaklaşım olan ek bir tür tarafından sağlanabilir.

Android

Android'de bir VideoViewile video oynatılır. Ancak burada, VideoView yerel görünümü işleyicisinden ayrı tutmak için türü MauiVideoPlayer kapsüllenmiştir. Aşağıdaki örnekte, android için kısmi sınıfı üç geçersiz kılmayla gösterilmiştir VideoHandler :

#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 sınıfından ViewHandler türetilir ve Video genel bağımsız değişken platformlar arası denetim türünü belirtir ve MauiVideoPlayer bağımsız değişken yerel görünümü kapsülleyen VideoView türü belirtir.

Geçersiz CreatePlatformView kılma bir MauiVideoPlayer nesne oluşturur ve döndürür. Geçersiz ConnectHandler kılma, gerekli yerel görünüm ayarlarını gerçekleştirmek için konumdur. Geçersiz DisconnectHandler kılma, herhangi bir yerel görünüm temizleme işlemini gerçekleştirmek için konumdur ve bu nedenle örnekte yöntemini MauiVideoPlayer çağırırDispose.

Platform işleyicisinin ayrıca özellik eşleyici sözlüğünde tanımlanan Eylemleri uygulaması gerekir:

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();
    }
    ...
}

Her Eylem, platformlar arası denetimde değişen bir özelliğe yanıt olarak yürütülür ve bağımsız değişken olarak işleyici ve platformlar arası denetim örnekleri gerektiren bir static yöntemdir. Her durumda, Eylem türünde tanımlanan MauiVideoPlayer bir yöntemi çağırır.

Platform işleyicisinin komut eşleyici sözlüğünde tanımlanan Eylemleri de uygulaması gerekir:

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);
    }
    ...
}

Her Eylem, platformlar arası denetimden gönderilen bir komuta yanıt olarak yürütülür ve işleyici ve platformlar arası denetim örnekleri ile isteğe bağlı verileri bağımsız değişken olarak gerektiren bir static yöntemdir. Her durumda, Eylem isteğe bağlı verileri ayıkladıktan sonra sınıfında tanımlanan MauiVideoPlayer bir yöntemi çağırır.

Android'de MauiVideoPlayer sınıfı, yerel görünümü işleyicisinden ayrı tutmak için öğesini kapsüller VideoView :

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 ' den CoordinatorLayouttüretilir çünkü Android'deki bir .NET MAUI uygulamasındaki kök yerel görünümü şeklindedir CoordinatorLayout. MauiVideoPlayer sınıfı diğer yerel Android türlerinden türetilebilir ancak bazı senaryolarda yerel görünüm konumlandırmasını denetlemek zor olabilir.

doğrudan VideoView öğesine CoordinatorLayouteklenebilir ve gerektiğinde düzende konumlandırılabilir. Ancak, burada, öğesine CoordinatorLayoutbir Android RelativeLayout eklenir ve öğesine VideoView eklenirRelativeLayout. Düzen parametreleri hem de RelativeLayoutVideoView üzerinde ayarlanır, böylece VideoView sayfa ortalanır ve en boy oranını korurken kullanılabilir alanı dolduracak şekilde genişletilir.

Oluşturucu ayrıca olaya abonedir VideoView.Prepared . Bu olay, video kayıttan yürütme için hazır olduğunda oluşturulur ve geçersiz kılmada Dispose aboneliği kaldırılır:

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);
    }
    ...
}

Geçersiz kılma, Dispose olaydan aboneliği kaldırmaya Prepared ek olarak yerel görünüm temizleme de gerçekleştirir.

Dekont

Geçersiz Dispose kılma, işleyicinin DisconnectHandler geçersiz kılma tarafından çağrılır.

Platform taşıma denetimleri videoyu yürüten, duraklatan ve durduran düğmeler içerir ve Android'in MediaController türüne göre sağlanır. Video.AreTransportControlsEnabled özelliği olarak trueayarlanırsa, a MediaController öğesinin medya yürütücüsü VideoViewolarak ayarlanır. Bunun nedeniAreTransportControlsEnabled, özellik ayarlandığında işleyicinin özellik eşleyicisinin yönteminin MapAreTransportControlsEnabled çağrılmasını sağlaması ve bunun da içinde MauiVideoPlayeryöntemini çağırmasıdırUpdateTransportControlsEnabled:

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;
            }
        }
    }
    ...
}

Kullanılmayan ancak videoya dokunarak geri yüklenebilen aktarım denetimleri kaybolur.

Video.AreTransportControlsEnabled özelliği olarak falseayarlanırsa, MediaController öğesinin medya yürütücüsü VideoViewolarak kaldırılır. Bu senaryoda video kayıttan yürütmeyi program aracılığıyla denetleyebilir veya kendi aktarım denetimlerinizi sağlayabilirsiniz. Daha fazla bilgi için bkz . Özel aktarım denetimleri oluşturma.

iOS ve Mac Catalyst

Video, ve ile AVPlayerAVPlayerViewControlleriOS ve Mac Catalyst üzerinde oynatılır. Ancak burada, bu türler yerel görünümleri işleyicilerinden ayrı tutmak için bir MauiVideoPlayer tür içinde kapsüllenir. Aşağıdaki örnek, iOS için kısmi sınıfı ve üç geçersiz kılmasını gösterir VideoHandler :

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);
        }
        ...
    }
}

VideoHandlersınıfından ViewHandler türetilir ve Video genel bağımsız değişken platformlar arası denetim türünü, MauiVideoPlayer bağımsız değişken ise ve AVPlayerViewController yerel görünümlerini AVPlayer kapsülleyen türü belirtir.

Geçersiz CreatePlatformView kılma bir MauiVideoPlayer nesne oluşturur ve döndürür. Geçersiz ConnectHandler kılma, gerekli yerel görünüm ayarlarını gerçekleştirmek için konumdur. Geçersiz DisconnectHandler kılma, herhangi bir yerel görünüm temizleme işlemini gerçekleştirmek için konumdur ve bu nedenle örnekte yöntemini MauiVideoPlayer çağırırDispose.

Platform işleyicisinin ayrıca özellik eşleyici sözlüğünde tanımlanan Eylemleri uygulaması gerekir:

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();
    }
    ...
}

Her Eylem, platformlar arası denetimde değişen bir özelliğe yanıt olarak yürütülür ve bağımsız değişken olarak işleyici ve platformlar arası denetim örnekleri gerektiren bir static yöntemdir. Her durumda, Eylem türünde tanımlanan MauiVideoPlayer bir yöntemi çağırır.

Platform işleyicisinin komut eşleyici sözlüğünde tanımlanan Eylemleri de uygulaması gerekir:

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);
    }
    ...
}

Her Eylem, platformlar arası denetimden gönderilen bir komuta yanıt olarak yürütülür ve işleyici ve platformlar arası denetim örnekleri ile isteğe bağlı verileri bağımsız değişken olarak gerektiren bir static yöntemdir. Her durumda, Eylem isteğe bağlı verileri ayıkladıktan sonra sınıfında tanımlanan MauiVideoPlayer bir yöntemi çağırır.

iOS ve Mac Catalyst'te sınıfı, MauiVideoPlayer yerel görünümleri işleyicilerinden ayrı tutmak için ve AVPlayerViewController türlerini kapsüllerAVPlayer:

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 , içeriği görüntüleyen ve bu içerikle kullanıcı etkileşimini işleyen nesneler için iOS ve Mac Catalyst'te temel sınıf olan öğesinden UIViewtüretilir. Oluşturucu, bir AVPlayer medya dosyasının kayıttan yürütmesini ve zamanlamasını yöneten bir nesnesi oluşturur ve bunu bir AVPlayerViewControlleröğesinin Player özellik değeri olarak ayarlar. öğesinin AVPlayerViewControllerAVPlayer içeriğini görüntüler ve aktarım denetimlerini ve diğer özellikleri sunar. Ardından denetimin boyutu ve konumu ayarlanır ve videonun sayfada ortalanmasını ve en boy oranını korurken kullanılabilir alanı dolduracak şekilde genişletilmesini sağlar. iOS 16 ve Mac Catalyst 16'da, AVPlayerViewController Kabuk tabanlı uygulamalar için üst ViewController öğeye eklenmesi gerekir, aksi takdirde aktarım denetimleri görüntülenmez. Yerel görünüm, 'den görünümdür AVPlayerViewControllerve ardından sayfaya eklenir.

Dispose yöntemi, yerel görünüm temizleme işlemini gerçekleştirmekle sorumludur:

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);
    }
    ...
}

Bazı senaryolarda, bir video kayıttan yürütme sayfası kaldırıldıktan sonra videolar oynatılır. Videoyu durdurmak için, ReplaceCurrentItemWithPlayerItem geçersiz kılmada Dispose olarak ayarlanır null ve diğer yerel görünüm temizleme gerçekleştirilir.

Dekont

Geçersiz Dispose kılma, işleyicinin DisconnectHandler geçersiz kılma tarafından çağrılır.

Platform taşıma denetimleri, videoyu yürüten, duraklatan ve durduran düğmeler içerir ve tür tarafından AVPlayerViewController sağlanır. Video.AreTransportControlsEnabled özelliği olarak trueAVPlayerViewController ayarlanırsa, kayıttan yürütme denetimlerini görüntüler. Bunun nedeniAreTransportControlsEnabled, özellik ayarlandığında işleyicinin özellik eşleyicisinin yönteminin MapAreTransportControlsEnabled çağrılmasını sağlaması ve bunun da içinde MauiVideoPlayeryöntemini çağırmasıdırUpdateTransportControlsEnabled:

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

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

Kullanılmayan ancak videoya dokunarak geri yüklenebilen aktarım denetimleri kaybolur.

Video.AreTransportControlsEnabled özelliği olarak ayarlanırsafalseAVPlayerViewController, kayıttan yürütme denetimlerini göstermez. Bu senaryoda video kayıttan yürütmeyi program aracılığıyla denetleyebilir veya kendi aktarım denetimlerinizi sağlayabilirsiniz. Daha fazla bilgi için bkz . Özel aktarım denetimleri oluşturma.

Windows

Ile Windows'ta MediaPlayerElementvideo oynatılır. Ancak burada, MediaPlayerElement yerel görünümü işleyicisinden ayrı tutmak için türü MauiVideoPlayer kapsüllenmiştir. Aşağıdaki örnekte, windows'un VideoHandler kısmi sınıfı üç geçersiz kılmayla gösterilmiştir:

#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 sınıfından ViewHandler türetilir ve Video genel bağımsız değişken platformlar arası denetim türünü belirtir ve MauiVideoPlayer bağımsız değişken yerel görünümü kapsülleyen MediaPlayerElement türü belirtir.

Geçersiz CreatePlatformView kılma bir MauiVideoPlayer nesne oluşturur ve döndürür. Geçersiz ConnectHandler kılma, gerekli yerel görünüm ayarlarını gerçekleştirmek için konumdur. Geçersiz DisconnectHandler kılma, herhangi bir yerel görünüm temizleme işlemini gerçekleştirmek için konumdur ve bu nedenle örnekte yöntemini MauiVideoPlayer çağırırDispose.

Platform işleyicisinin ayrıca özellik eşleyici sözlüğünde tanımlanan Eylemleri uygulaması gerekir:

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();
    }
    ...
}

Her Eylem, platformlar arası denetimde değişen bir özelliğe yanıt olarak yürütülür ve bağımsız değişken olarak işleyici ve platformlar arası denetim örnekleri gerektiren bir static yöntemdir. Her durumda, Eylem türünde tanımlanan MauiVideoPlayer bir yöntemi çağırır.

Platform işleyicisinin komut eşleyici sözlüğünde tanımlanan Eylemleri de uygulaması gerekir:

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);
    }
    ...
}

Her Eylem, platformlar arası denetimden gönderilen bir komuta yanıt olarak yürütülür ve işleyici ve platformlar arası denetim örnekleri ile isteğe bağlı verileri bağımsız değişken olarak gerektiren bir static yöntemdir. Her durumda, Eylem isteğe bağlı verileri ayıkladıktan sonra sınıfında tanımlanan MauiVideoPlayer bir yöntemi çağırır.

Windows'da sınıfı, MauiVideoPlayer yerel görünümü işleyicisinden ayrı tutmak için öğesini kapsüller MediaPlayerElement :

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 öğesinden Gridtüretilir ve MediaPlayerElement öğesinin Gridalt öğesi olarak eklenir. Bu, tüm kullanılabilir alanı doldurmak için öğesinin otomatik olarak boyutlandırılabilmesini sağlar MediaPlayerElement .

Dispose yöntemi, yerel görünüm temizleme işlemini gerçekleştirmekle sorumludur:

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;
    }
    ...
}

Geçersiz kılma, Dispose olaydan aboneliği kaldırmaya MediaOpened ek olarak yerel görünüm temizleme de gerçekleştirir.

Dekont

Geçersiz Dispose kılma, işleyicinin DisconnectHandler geçersiz kılma tarafından çağrılır.

Platform taşıma denetimleri, videoyu yürüten, duraklatan ve durduran düğmeler içerir ve tür tarafından MediaPlayerElement sağlanır. Video.AreTransportControlsEnabled özelliği olarak trueMediaPlayerElement ayarlanırsa, kayıttan yürütme denetimlerini görüntüler. Bunun nedeniAreTransportControlsEnabled, özellik ayarlandığında işleyicinin özellik eşleyicisinin yönteminin MapAreTransportControlsEnabled çağrılmasını sağlaması ve bunun da içinde MauiVideoPlayeryöntemini çağırmasıdırUpdateTransportControlsEnabled:

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

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

}

Video.AreTransportControlsEnabled özelliği olarak ayarlanırsafalseMediaPlayerElement, kayıttan yürütme denetimlerini göstermez. Bu senaryoda video kayıttan yürütmeyi program aracılığıyla denetleyebilir veya kendi aktarım denetimlerinizi sağlayabilirsiniz. Daha fazla bilgi için bkz . Özel aktarım denetimleri oluşturma.

Platformlar arası denetimi platform denetimine dönüştürme

'den Elementtüretilen herhangi bir .NET MAUI platformlar arası denetimi, uzantı yöntemiyle temel alınan platform denetimine ToPlatform dönüştürülebilir:

  • Android'de bir ToPlatform .NET MAUI denetimini Android View nesnesine dönüştürür.
  • iOS ve Mac Catalyst'te . ToPlatform NET MAUI denetimini bir UIView nesneye dönüştürür.
  • Windows'da bir ToPlatform .NET MAUI denetimini nesneye FrameworkElement dönüştürür.

Dekont

ToPlatform yöntemi ad alanındadırMicrosoft.Maui.Platform.

Tüm platformlarda ToPlatform yöntemi bir MauiContext bağımsız değişken gerektirir.

ToPlatform yöntemi, platformlar arası denetimi platform kodundan temel alınan platform denetimine dönüştürebilir; örneğin, bir platform için kısmi işleyici sınıfında:

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);
            ...
        }
        ...
    }
}

Bu örnekte, Android'in VideoHandlerMapSource kısmi sınıfında yöntemi örneği bir MauiVideoPlayer nesneye dönüştürürVideo.

Yöntemi ayrıca ToPlatform platformlar arası bir denetimi, platformlar arası koddan temel alınan platform denetimine dönüştürebilir:

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
        ...
    }
    ...
}

Bu örnekte, adlı video bir platformlar Video arası denetim, geçersiz kılmadaki her platformda temel alınan yerel görünümüne OnHandlerChanged() dönüştürülür. Bu geçersiz kılma, platformlar arası denetimi uygulayan yerel görünüm kullanılabilir ve başlatıldığında çağrılır. yöntemi tarafından ToPlatform döndürülen nesne, burada bir MauiVideoPlayerolan tam yerel türüne yayınlanabilir.

Video oynatma

Video sınıfı, video dosyasının kaynağını ve bir özelliği belirtmek için kullanılan bir AutoPlay özelliği tanımlarSource. AutoPlaytruevarsayılan olarak ayarlanır. Bu, videonun ayarlandıktan sonra Source otomatik olarak oynatılması gerektiği anlamına gelir. Bu özelliklerin tanımı için bkz . Platformlar arası denetim oluşturma.

Source özelliği türündedir VideoSourceve öğesinden türetilen üç sınıfın örneğini oluşturan üç statik yöntemden VideoSourceoluşan soyut bir sınıftır:

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 };
        }
    }
}

sınıfına VideoSource başvuran VideoSourceConverterbir TypeConverter öznitelik içerir:

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.");
        }
    }
}

Özelliği XAML'de bir dizeye ayarlandığında tür dönüştürücüsü çağrılır Source . yöntemi, ConvertFromInvariantString dizeyi bir Uri nesneye dönüştürmeyi dener. Başarılı olursa ve düzen değilse fileyöntemi bir UriVideoSourcedöndürür. Aksi takdirde bir ResourceVideoSourcedöndürür.

Web videosu oynatma

UriVideoSource sınıfı, URI ile uzak bir video belirtmek için kullanılır. türünde stringbir Uri özellik tanımlar:

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 özelliği olarak UriVideoSourceayarlandığında işleyicinin özellik eşleyicisi yöntemin MapSource çağrılmasını sağlar:

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

MapSource içindeki yöntemi, işleyicinin UpdateSourcePlatformView özelliğinde yöntemini çağırır. PlatformView türünde MauiVideoPlayerolan özelliği, her platformda video oynatıcı uygulamasını sağlayan yerel görünümü temsil eder.

Android

Android'de bir VideoViewile video oynatılır. Aşağıdaki kod örneği, türü UriVideoSourceolduğunda yönteminin Source özelliğini nasıl UpdateSource işlediğini gösterir:

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();
            }
        }
        ...
    }
}

türündeki UriVideoSourceSetVideoUri nesneler işlenirken, yöntemiVideoView, dize URI'sinden oluşturulan bir Android Uri nesnesiyle oynatılacak videoyu belirtmek için kullanılır.

özelliğinin AutoPlay üzerinde VideoVieweşdeğeri yoktur, bu nedenle Start yeni bir video ayarlandıysa yöntemi çağrılır.

iOS ve Mac Catalyst

iOS ve Mac Catalyst'te bir video oynatmak için, videoyu kapsüllemek için türünde AVAsset bir nesne oluşturulur ve bu nesne, nesnesine devredilen bir AVPlayerItemoluşturmak için AVPlayer kullanılır. Aşağıdaki kod örneği, türü UriVideoSourceolduğunda yönteminin Source özelliğini nasıl UpdateSource işlediğini gösterir:

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();
            }
        }
        ...
    }
}

türündeki UriVideoSourcenesneler işlenirken, statik AVAsset.FromUrl yöntem, dize URI'sinden oluşturulan bir iOS NSUrl nesnesiyle oynatılacak videoyu belirtmek için kullanılır.

Özelliğin AutoPlay iOS video sınıflarında eşdeğeri yoktur, bu nedenle özelliği nesnesinde AVPlayer yöntemini çağırmak için yöntemin UpdateSourcePlay sonunda inceler.

iOS'ta bazı durumlarda, video kayıttan yürütme sayfası kaldırıldıktan sonra videolar oynatılır. Videoyu durdurmak için geçersiz ReplaceCurrentItemWithPlayerItem kılmada Dispose olarak ayarlanırnull:

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

Windows

Windows'ta bir MediaPlayerElementile video oynatılır. Aşağıdaki kod örneği, türü UriVideoSourceolduğunda yönteminin Source özelliğini nasıl UpdateSource işlediğini gösterir:

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;
            }
        }
        ...
    }
}

türündeki UriVideoSourcenesneler işlenirken özelliği, MediaPlayerElement.Source oynatılacak videonun URI'siyle bir Uri başlatan bir MediaSource nesneye ayarlanır. MediaPlayerElement.Source ayarlandığında, OnMediaPlayerMediaOpened olay işleyicisi yöntemi olaya göre MediaPlayerElement.MediaPlayer.MediaOpened kaydedilir. Bu olay işleyicisi denetimin Duration özelliğini Video ayarlamak için kullanılır.

yönteminin UpdateSource sonunda özelliği Video.AutoPlay incelenmiş ve doğruysa MediaPlayerElement.AutoPlay özellik video kayıttan yürütmeyi başlatacak şekilde true ayarlanmıştır.

Video kaynağı oynatma

sınıfı ResourceVideoSource , uygulamaya eklenmiş video dosyalarına erişmek için kullanılır. türünde stringbir Path özellik tanımlar:

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 özelliği olarak ResourceVideoSourceayarlandığında işleyicinin özellik eşleyicisi yöntemin MapSource çağrılmasını sağlar:

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

MapSource içindeki yöntemi, işleyicinin UpdateSourcePlatformView özelliğinde yöntemini çağırır. PlatformView türünde MauiVideoPlayerolan özelliği, her platformda video oynatıcı uygulamasını sağlayan yerel görünümü temsil eder.

Android

Aşağıdaki kod örneği, türü ResourceVideoSourceolduğunda yönteminin Source özelliğini nasıl UpdateSource işlediğini gösterir:

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;
                }
            }
            ...
        }
        ...
    }
}

türündeki ResourceVideoSourceSetVideoPathVideoView nesneler işlenirken yöntemi, uygulamanın paket adını videonun dosya adıyla birleştiren bir dize bağımsız değişkeniyle birlikte oynatılacak videoyu belirtmek için kullanılır.

Kaynak video dosyası paketin varlıklar klasöründe depolanır ve buna erişmek için bir içerik sağlayıcısı gerekir. İçerik sağlayıcısı, video dosyasına erişim sağlayan bir AssetFileDescriptor nesne oluşturan sınıfı tarafından VideoProvider sağlanır:

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;
        }
        ...
    }
}

iOS ve Mac Catalyst

Aşağıdaki kod örneği, türü ResourceVideoSourceolduğunda yönteminin Source özelliğini nasıl UpdateSource işlediğini gösterir:

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);
                }
            }
            ...
        }
        ...
    }
}

türündeki ResourceVideoSourceGetUrlForResource nesneler işlenirken yöntemiNSBundle, dosyayı uygulama paketinden almak için kullanılır. Tam yol bir dosya adı, uzantı ve dizine ayrılmalıdır.

iOS'ta bazı durumlarda, video kayıttan yürütme sayfası kaldırıldıktan sonra videolar oynatılır. Videoyu durdurmak için geçersiz ReplaceCurrentItemWithPlayerItem kılmada Dispose olarak ayarlanırnull:

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

Windows

Aşağıdaki kod örneği, türü ResourceVideoSourceolduğunda yönteminin Source özelliğini nasıl UpdateSource işlediğini gösterir:

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;
                }
            }
            ...
        }
        ...
    }
}

türündeki ResourceVideoSourcenesneler işlenirken özelliği, ön ekli MediaPlayerElement.Source video kaynağının yolu ile başlatan ms-appx:///bir MediaSourceUri nesneye ayarlanır.

Cihazın kitaplığından video dosyası yürütme

FileVideoSource sınıfı, cihazın video kitaplığındaki videolara erişmek için kullanılır. türünde stringbir File özellik tanımlar:

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 özelliği olarak FileVideoSourceayarlandığında işleyicinin özellik eşleyicisi yöntemin MapSource çağrılmasını sağlar:

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

MapSource içindeki yöntemi, işleyicinin UpdateSourcePlatformView özelliğinde yöntemini çağırır. PlatformView türünde MauiVideoPlayerolan özelliği, her platformda video oynatıcı uygulamasını sağlayan yerel görünümü temsil eder.

Android

Aşağıdaki kod örneği, türü FileVideoSourceolduğunda yönteminin Source özelliğini nasıl UpdateSource işlediğini gösterir:

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;
                }
            }
            ...
        }
        ...
    }
}

türündeki FileVideoSourcenesneler işlenirken, SetVideoPath oynatılacak video dosyasını belirtmek için yöntemi VideoView kullanılır.

iOS ve Mac Catalyst

Aşağıdaki kod örneği, türü FileVideoSourceolduğunda yönteminin Source özelliğini nasıl UpdateSource işlediğini gösterir:

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 }));
            }
            ...
        }
        ...
    }
}

türündeki FileVideoSourcenesneler işlenirken, statik AVAsset.FromUrl yöntem, dize URI'sinden bir iOS NSUrl nesnesi oluşturma yöntemiyle NSUrl.CreateFileUrl birlikte oynatılacak video dosyasını belirtmek için kullanılır.

Windows

Aşağıdaki kod örneği, türü FileVideoSourceolduğunda yönteminin Source özelliğini nasıl UpdateSource işlediğini gösterir:

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;
                }
            }
            ...
        }
        ...
    }
}

türünde FileVideoSourcenesneler işlenirken, video dosya adı bir StorageFile nesneye dönüştürülür. Ardından yöntemi, MediaSource.CreateFromStorageFile özelliğin değeri MediaPlayerElement.Source olarak ayarlanmış bir MediaSource nesne döndürür.

Video döngüleri oluşturma

Video sınıfı, denetimin sonuna ulaştıktan sonra video konumunu otomatik olarak başlangıç konumuna ayarlamasını sağlayan bir IsLooping özellik tanımlar. Varsayılan olarak olur ve videoların falseotomatik olarak döngüye alındığını gösterir.

IsLooping Özellik ayarlandığında, işleyicinin özellik eşleyicisi yöntemin MapIsLooping çağrılmasını sağlar:

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

yöntemi MapIsLooping de işleyicinin UpdateIsLoopingPlatformView özelliğinde yöntemini çağırır. PlatformView türünde MauiVideoPlayerolan özelliği, her platformda video oynatıcı uygulamasını sağlayan yerel görünümü temsil eder.

Android

Aşağıdaki kod örneği, Android'de yönteminin UpdateIsLooping video döngüsüne nasıl olanak sağladığını gösterir:

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;
        }
        ...
    }
}

Video döngüsünü MauiVideoPlayer etkinleştirmek için sınıfı arabirimini MediaPlayer.IOnPreparedListener uygular. Bu arabirim, medya kaynağı kayıttan yürütme için hazır olduğunda çağrılan bir OnPrepared geri çağırma tanımlar. Video.IsLooping özelliği olduğunda trueUpdateIsLooping yöntemi, geri çağırmayı sağlayan OnPrepared nesne olarak ayarlarMauiVideoPlayer. Geri çağırma özelliği özelliğinin Video.IsLooping değerine ayarlarMediaPlayer.IsLooping.

iOS ve Mac Catalyst

Aşağıdaki kod örneği, iOS ve Mac Catalyst'te yönteminin UpdateIsLooping video döngüye nasıl olanak sağladığını gösterir:

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);
        }
        ...
    }
}

iOS ve Mac Catalyst'te, video sonuna kadar oynatıldığında geri çağırma yürütmek için bir bildirim kullanılır. Video.IsLooping özelliği olduğunda trueUpdateIsLooping yöntemi bildirim için AVPlayerItem.DidPlayToEndTimeNotification bir gözlemci ekler ve bildirim alındığında yöntemini yürütürPlayedToEnd. Buna karşılık, bu yöntem videonun başından itibaren kayıttan yürütmeyi sürdürür. Video.IsLooping özelliği isefalse, video kayıttan yürütmenin sonunda duraklatılır.

Bildirim MauiVideoPlayer için bir gözlemci eklendiğinden, yerel görünüm temizlemesi gerçekleştirirken gözlemciyi de kaldırması gerekir. Bu, geçersiz kılmada Dispose gerçekleştirilir:

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;
    }
    ...
}

Geçersiz Dispose kılma, bildirim için AVPlayerItem.DidPlayToEndTimeNotification gözlemciyi kaldıran ve üzerinde yöntemini de çağıran yöntemini NSObjectçağırır DestroyPlayedToEndObserverDispose.

Windows

Aşağıdaki kod örneği, Windows'taki yönteminin UpdateIsLooping video döngüye nasıl olanak sağladığını gösterir:

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

Yöntemi, UpdateIsLooping video döngüsünü etkinleştirmek için özelliğini özelliğinin Video.IsLooping değerine ayarlarMediaPlayerElement.MediaPlayer.IsLoopingEnabled.

Özel aktarım denetimleri oluşturma

Video oynatıcının aktarım denetimleri, videoyu yürüten, duraklatan ve durduran düğmeler içerir. Bu düğmeler genellikle metin yerine tanıdık simgelerle tanımlanır ve yürütme ve duraklat düğmeleri genellikle tek bir düğmede birleştirilir.

Varsayılan olarak, Video denetim her platform tarafından desteklenen aktarım denetimlerini görüntüler. Ancak özelliğini falseolarak ayarladığınızda AreTransportControlsEnabled bu denetimler gizleniyor. Daha sonra video kayıttan yürütmeyi program aracılığıyla denetleyebilir veya kendi aktarım denetimlerinizi sağlayabilirsiniz.

Kendi aktarım denetimlerinizi uygulamak için sınıfın videoyu Video oynatmak, duraklatmak veya durdurmak için yerel görünümlerini bilgilendirebilmesi ve video kayıttan yürütmenin geçerli durumunu bilebilmesi gerekir. Video sınıfı, Pausekarşılık gelen bir olayı tetikleyen , ve Stop adlı Playyöntemleri tanımlar ve öğesine VideoHandlerbir komut gönderir:

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);
        }
    }
}

sınıfı, VideoPositionEventArgs oluşturucu aracılığıyla ayarlanabilen bir Position özellik tanımlar. Bu özellik, video kayıttan yürütmenin başlatıldığı, duraklatıldığı veya durdurulduğu konumu temsil eder.

, ve yöntemlerindeki Playson satır komutu ve ilişkili verileri adresine VideoHandlergönderir.StopPause CommandMapper for VideoHandler komutu, komut adlarını bir komut alındığında yürütülen Eylemlerle eşler. Örneğin, komutunu aldığında VideoHandlerPlayRequested yöntemini yürütür MapPlayRequested . Bu yaklaşımın avantajı, yerel görünümlerin platformlar arası denetim olaylarına abone olma ve abonelikten çıkma gereksinimini ortadan kaldırmasıdır. Ayrıca, komut eşleyicisi alt sınıflama olmadan platformlar arası denetim tüketicileri tarafından değiştirilebildiği için kolay özelleştirmeye olanak tanır. hakkında CommandMapperdaha fazla bilgi için bkz . Komut eşleyicisini oluşturma.

MauiVideoPlayer Android, iOS ve Mac Catalyst'teki uygulama, , PauseRequestedve StopRequested komutlarını gönderen PlayRequesteddenetime Video yanıt olarak yürütülen , PauseRequestedve StopRequested yöntemlerine sahiptirPlayRequested. Her yöntem, videoyu oynatmak, duraklatmak veya durdurmak için yerel görünümünde bir yöntem çağırır. Örneğin, aşağıdaki kod iOS ve StopRequested Mac Catalyst'te , PauseRequestedve yöntemlerini gösterirPlayRequested:

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}.");
        }
    }
}

Üç yöntemin her biri, komutuyla gönderilen verileri kullanarak videonun oynatıldığı, duraklatıldığı veya durdurulduğu konumu günlüğe kaydeder.

Bu mekanizma, denetimde Video , Pauseveya Stop yöntemi çağrıldığındaPlay, yerel görünümünün videoyu oynatması, duraklatması veya durdurması ve videonun oynatıldığı, duraklatıldığı veya durdurulduğu konumu günlüğe kaydetmesi talimatını sağlar. Tüm bunlar, yerel görünümlerin platformlar arası olaylara abone olması gerekmeden ayrılmış bir yaklaşım kullanılarak gerçekleşir.

Video durumu

Yürütme, duraklatma ve durdurma işlevlerinin uygulanması, özel aktarım denetimlerini desteklemek için yeterli değildir. Oynatma ve duraklatma işlevi genellikle videonun oynatıldığını veya duraklatıldığını göstermek için görünümünü değiştiren aynı düğmeyle uygulanmalıdır. Ayrıca, video henüz yüklenmemişse düğmenin etkinleştirilmemesi gerekir.

Bu gereksinimler, video oynatıcının oynatıldığını veya duraklatıldığını ya da henüz video oynatmaya hazır olmadığını belirten geçerli bir durumu kullanıma sunması gerektiği anlamına geliyor. Bu durum bir sabit listesiyle gösterilebilir:

public enum VideoStatus
{
    NotReady,
    Playing,
    Paused
}

Video sınıfı, türünde VideoStatusadlı Status salt okunur bağlanabilir bir özellik tanımlar. Yalnızca denetimin işleyicisinden ayarlanması gerektiğinden bu özellik salt okunur olarak tanımlanır:

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); }
        }
        ...
    }
}

Genellikle, salt okunur bir bağlanabilir özelliğin sınıfı içinden ayarlanmasına izin vermek için özelliğinde Status özel set bir erişimci olur. Ancak, işleyiciler tarafından desteklenen bir View türev için özelliği sınıfın dışından ayarlanmalıdır, ancak yalnızca denetimin işleyicisi tarafından ayarlanmalıdır.

Bu nedenle, adıyla IVideoController.Statusbaşka bir özellik tanımlanır. Bu açık bir arabirim uygulamasıdır ve sınıfın IVideoController uyguladığı arabirim Video tarafından mümkün hale getirilir:

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

Bu arabirim, bir sınıfın arabirimine Video başvurarak özelliğini ayarlamasını StatusIVideoController mümkün kılar. özelliği diğer sınıflardan ve işleyiciden ayarlanabilir, ancak yanlışlıkla ayarlanması pek olası değildir. En önemlisi, Status özelliği bir veri bağlama aracılığıyla ayarlanamaz.

İşleyici uygulamalarının özelliği güncel Video tutmasına Status yardımcı olmak için sınıfı bir UpdateStatus olay ve komut tanımlar:

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));
        }
        ...
    }
}

Olay OnTimerTick işleyicisi saniyenin onda birinde yürütülür ve bu da olayı tetikler UpdateStatus ve komutunu çağırır UpdateStatus .

UpdateStatus Komut denetimden Video işleyicisine gönderildiğinde, işleyicinin komut eşleyicisi yöntemin MapUpdateStatus çağrılmasını sağlar:

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

MapUpdateStatus içindeki yöntemi, işleyicinin UpdateStatusPlatformView özelliğinde yöntemini çağırır. PlatformView türünde MauiVideoPlayerolan özelliği, her platformda video oynatıcı uygulamasını sağlayan yerel görünümleri kapsüller.

Android

Aşağıdaki kod örneği, Android'de yönteminin UpdateStatus özelliğini ayarlamasını Status gösterir:

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;
            ...
        }
        ...
    }
}

VideoView.IsPlaying özelliği, videonun oynatıldığını veya duraklatıldığını gösteren bir Boole değeridir. videonun VideoView oynatılmadığını veya duraklatılmadığını belirlemek için olayın Prepared işlenmesi gerekir. Medya kaynağı kayıttan yürütme için hazır olduğunda bu olay tetikleniyor. Olay oluşturucuda MauiVideoPlayer abone yapılır ve geçersiz kılmada Dispose aboneliği kaldırılır. UpdateStatus Yöntemi daha sonra nesnesini değerine dönüştürerek IVideoControllerözelliğini ayarlamak Status için alanını ve VideoView.IsPlaying özelliğini Video kullanırisPrepared.

iOS ve Mac Catalyst

Aşağıdaki kod örneği, iOS ve Mac Catalyst'te özelliğini ayarlarken Status yöntemini gösterirUpdateStatus:

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;
            ...
        }
        ...
    }
}

özelliğini ayarlamak Status için iki özelliğine AVPlayer erişilmelidir: Status türün AVPlayerStatus özelliği ve TimeControlStatus türünün AVPlayerTimeControlStatusözelliği. Daha Status sonra özelliği nesnesine Video yayınlanarak IVideoControllernesne üzerinde ayarlanabilir.

Windows

Aşağıdaki kod örneği, Windows'da özelliğini ayarlarken Status yöntemini gösterirUpdateStatus:

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;
            }
        }
        ...
    }
}

yöntemi, UpdateStatus özelliğin MediaPlayerElement.MediaPlayer.CurrentState değerini belirlemek için özelliğinin Status değerini kullanır. Daha Status sonra özelliği nesnesine Video yayınlanarak IVideoControllernesne üzerinde ayarlanabilir.

Konumlandırma çubuğu

Her platform tarafından uygulanan taşıma denetimleri bir konumlandırma çubuğu içerir. Bu çubuk kaydırıcı veya kaydırma çubuğuna benzer ve videonun toplam süresi içindeki geçerli konumunu gösterir. Kullanıcılar, videoda yeni bir konuma ileri veya geri gitmek için konumlandırma çubuğunu değiştirebilir.

Kendi konumlandırma çubuğunuzu uygulamak için sınıfın Video videonun süresini ve bu süre içindeki geçerli konumunu bilmesi gerekir.

Süre

Denetimin özel bir konumlandırma çubuğunu desteklemesi Video için gereken bilgilerden biri videonun süresidir. Video sınıfı, türünde TimeSpanadlı Durationsalt okunur bir bağlanabilir özellik tanımlar. Yalnızca denetimin işleyicisinden ayarlanması gerektiğinden bu özellik salt okunur olarak tanımlanır:

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); }
        }
        ...
    }
}

Genellikle, salt okunur bir bağlanabilir özelliğin sınıfı içinden ayarlanmasına izin vermek için özelliğinde Duration özel set bir erişimci olur. Ancak, işleyiciler tarafından desteklenen bir View türev için özelliği sınıfın dışından ayarlanmalıdır, ancak yalnızca denetimin işleyicisi tarafından ayarlanmalıdır.

Dekont

Bağlanabilir özelliğin Duration özellik tarafından değiştirilen olay işleyicisi, Bitiş saatini hesaplama bölümünde açıklanan adlı SetTimeToEndbir yöntemi çağırır.

Bu nedenle, adıyla IVideoController.Durationbaşka bir özellik tanımlanır. Bu açık bir arabirim uygulamasıdır ve sınıfın IVideoController uyguladığı arabirim Video tarafından mümkün hale getirilir:

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

Bu arabirim, bir sınıfın arabirimine Video başvurarak özelliğini ayarlamasını DurationIVideoController mümkün kılar. özelliği diğer sınıflardan ve işleyiciden ayarlanabilir, ancak yanlışlıkla ayarlanması pek olası değildir. En önemlisi, Duration özelliği bir veri bağlama aracılığıyla ayarlanamaz.

Denetimin özelliği ayarlandıktan hemen sonra Source videonun Video süresi kullanılamaz. Yerel görünümün süresini belirleyebilmesi için önce videonun kısmen indirilmesi gerekir.

Android

Android'de VideoView.Duration özellik, etkinlik oluşturulduktan sonra VideoView.Prepared milisaniye cinsinden geçerli bir süre bildirir. sınıfı, MauiVideoPlayer özellik değerini almak için olay işleyicisini Duration kullanırPrepared:

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);
        }
        ...
    }
}
iOS ve Mac Catalyst

iOS ve Mac Catalyst'te, videonun süresi özelliğinden AVPlayerItem.Duration alınır, ancak oluşturulduktan hemen sonra AVPlayerItem alınmaz. Özelliği için bir iOS gözlemcisi Duration ayarlamak mümkündür, ancak MauiVideoPlayer sınıfı saniyede 10 kez çağrılan yöntemde UpdateStatus süreyi alır:

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);
                ...
            }
        }
        ...
    }
}

yöntemi bir ConvertTimeCMTime nesneyi bir TimeSpan değere dönüştürür.

Windows

Windows'da özelliği, MediaPlayerElement.MediaPlayer.NaturalDuration olay tetiklendiğinde MediaPlayerElement.MediaPlayer.MediaOpened geçerli hale gelen bir TimeSpan değerdir. sınıfı, MauiVideoPlayer özellik değerini almak için olay işleyicisini NaturalDuration kullanırMediaOpened:

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;
            });
        }
        ...
    }
}

Daha OnMediaPlayer sonra olay işleyicisi, ana iş parçacığında Video öğesine dönüştürerek nesnesinin özelliğini ayarlamak Duration için IVideoControlleryöntemini çağırırMainThread.BeginInvokeOnMainThread. Olay bir arka plan iş parçacığında işlendiğinden MediaPlayerElement.MediaPlayer.MediaOpened bu gereklidir. Kodu ana iş parçacığında çalıştırma hakkında daha fazla bilgi için bkz . .NET MAUI kullanıcı arabirimi iş parçacığında iş parçacığı oluşturma.

Position

Denetim ayrıca Video video oynattıkça sıfırdan bir Duration özelliğe de ihtiyaç duyarPosition. sınıfı bu Video özelliği public get ve set accessors ile bağlanabilir bir özellik olarak uygular:

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); }
        }
        ...
    }
}

Erişimci get videonun geçerli konumunu oynatırken döndürür. Erişimci, set video konumunu ileri veya geri hareket ettirerek konumlandırma çubuğunun kullanıcı düzenlemesine yanıt verir.

Dekont

Bağlanabilir özelliğin Position özellik tarafından değiştirilen olay işleyicisi, Bitiş saatini hesaplama bölümünde açıklanan adlı SetTimeToEndbir yöntemi çağırır.

Android, iOS ve Mac Catalyst'te, geçerli konumu alan özelliğin yalnızca bir get aksesuarı vardır. Bunun yerine, konumu ayarlamak için bir Seek yöntem kullanılabilir. Bu, doğal bir sorunu olan tek Position bir özelliği kullanmaktan daha mantıklı bir yaklaşım gibi görünüyor. Video oynattıkça özelliğin Position yeni konumu yansıtacak şekilde sürekli güncelleştirilmiş olması gerekir. Ancak özellikteki çoğu değişikliğin video oynatıcının Position videoda yeni bir konuma geçmesine neden olmasını istemezsiniz. Böyle bir durumda video oynatıcı özelliğin son değerini Position arayarak yanıt verir ve video ilerlemez.

ve set erişimcileriyle get özellik Position uygulama güçlüklerine rağmen, veri bağlamayı kullanabildiği için bu yaklaşım kullanılır. Denetimin PositionVideo özelliği, hem konumu görüntülemek hem de yeni bir Slider konum aramak için kullanılan bir öğesine bağlanabilir. Ancak, geri bildirim döngülerini önlemek için özelliği uygularken Position çeşitli önlemler gereklidir.

Android

Android'de VideoView.CurrentPosition özelliği videonun geçerli konumunu gösterir. MauiVideoPlayer sınıfı, yönteminin Position özelliğini UpdateStatus ayarlarken özelliğini ayarlarDuration:

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);
            }
        }
        ...
    }
}

Position özelliği yöntemi tarafından UpdateStatus her ayarlandığında, Position özelliği bir PropertyChanged olay tetikler ve bu da işleyici için özellik eşleyicisinin yöntemini çağırmasına UpdatePosition neden olur. yöntemi, UpdatePosition özellik değişikliklerinin çoğu için hiçbir şey yapmamalıdır. Aksi takdirde, videonun konumundaki her değişiklikle aynı konuma taşınırdı. Bu geri bildirim döngüsünü önlemek için, UpdatePosition özelliği ile geçerli konumu VideoView arasındaki Position fark bir saniyeden büyük olduğunda yalnızca nesnede yöntemini VideoView çağırırSeek.

iOS ve Mac Catalyst

iOS ve Mac Catalyst'te özelliği videonun AVPlayerItem.CurrentTime geçerli konumunu gösterir. MauiVideoPlayer sınıfı, yönteminin Position özelliğini UpdateStatus ayarlarken özelliğini ayarlarDuration:

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));
            }
        }
        ...
    }
}

Position özelliği yöntemi tarafından UpdateStatus her ayarlandığında, Position özelliği bir PropertyChanged olay tetikler ve bu da işleyici için özellik eşleyicisinin yöntemini çağırmasına UpdatePosition neden olur. yöntemi, UpdatePosition özellik değişikliklerinin çoğu için hiçbir şey yapmamalıdır. Aksi takdirde, videonun konumundaki her değişiklikle aynı konuma taşınırdı. Bu geri bildirim döngüsünü önlemek için, UpdatePosition özelliği ile geçerli konumu AVPlayer arasındaki Position fark bir saniyeden büyük olduğunda yalnızca nesnede yöntemini AVPlayer çağırırSeek.

Windows

Windows'da özelliği videonun MediaPlayerElement.MedaPlayer.Position geçerli konumunu gösterir. MauiVideoPlayer sınıfı, yönteminin Position özelliğini UpdateStatus ayarlarken özelliğini ayarlarDuration:

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;
                }
            }
        }
        ...
    }
}

Position özelliği yöntemi tarafından UpdateStatus her ayarlandığında, Position özelliği bir PropertyChanged olay tetikler ve bu da işleyici için özellik eşleyicisinin yöntemini çağırmasına UpdatePosition neden olur. yöntemi, UpdatePosition özellik değişikliklerinin çoğu için hiçbir şey yapmamalıdır. Aksi takdirde, videonun konumundaki her değişiklikle aynı konuma taşınırdı. Bu geri bildirim döngüsünü önlemek için özelliği UpdatePosition ile geçerli konumu arasındaki fark bir saniyeden PositionMediaPlayerElement büyük olduğunda yalnızca özelliği ayarlarMediaPlayerElement.MediaPlayer.Position.

Bitiş süresini hesaplama

Bazen video oynatıcılar videoda kalan süreyi gösterir. Bu değer, video başladığında videonun süresiyle başlar ve video sona erdiğinde sıfıra düşer.

sınıfı, Video ve Position özelliklerindeki değişikliklere Duration göre hesaplanan salt okunur TimeToEnd bir özellik içerir:

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;
        }
        ...
    }
}

SetTimeToEnd yöntemi ve Position özelliklerinin özellik tarafından değiştirilen olay işleyicilerinden çağrılırDuration.

Özel konumlandırma çubuğu

özel konumlandırma çubuğu, türünde TimeSpanve Position özelliklerini içeren Duration öğesinden Slidertüretilen bir sınıf oluşturularak uygulanabilir:

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;
                }
            };
        }
    }
}

özelliği için Duration özellik tarafından değiştirilen olay işleyicisi Slider özelliğini değerinin TotalSeconds özelliğine TimeSpan ayarlarMaximum. Benzer şekilde, özelliği için Position özellik tarafından değiştirilen olay işleyicisi Sliderözelliğini ayarlarValue. Bu, konumunun izlendiği Slider mekanizmadır PositionSlider.

PositionSlider, yalnızca bir senaryoda temel alınandan Slider güncelleştirilir. Bu, kullanıcının videonun gelişmiş veya yeni bir konuma ters çevrilmesi gerektiğini belirtmek için öğesini işlemesidirSlider. Bu, oluşturucudaki PropertyChanged işleyicide PositionSlider algılanır. Bu olay işleyici özelliğinde Value bir değişiklik olup olmadığını denetler ve özelliğinden Position farklıysa özelliği Position özelliğinden Value ayarlanır.

İşleyiciyi kaydetme

Bir özel denetimin ve işleyicinin, bir uygulamanın tüketilmesi için önce bir uygulamaya kaydedilmesi gerekir. Bu, uygulamanın platformlar arası giriş noktası olan uygulama projenizdeki sınıfındaki yönteminde MauiProgram gerçekleşmelidirCreateMauiApp:

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();
    }
}

İşleyici ve AddHandler yöntemiyle ConfigureMauiHandlers kaydedilir. Yöntemin AddHandler ilk bağımsız değişkeni platformlar arası denetim türüdür ve ikinci bağımsız değişken de işleyici türüdür.

Platformlar arası denetimi kullanma

İşleyiciyi uygulamanıza kaydettikten sonra platformlar arası denetim kullanılabilir.

Web videosu oynatma

Denetim Video , aşağıdaki örnekte gösterildiği gibi BIR URL'den video oynatabilir:

<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>

Bu örnekte sınıfı, VideoSourceConverter URI'yi temsil eden dizeyi öğesine UriVideoSourcedönüştürür. Daha sonra video yüklenmeye başlar ve yeterli miktarda veri indirildikten ve arabelleğe alındıktan sonra oynatılmaya başlar. Her platformda, kullanılmayan ancak videoya dokunarak geri yüklenebilen aktarım denetimleri kaybolur.

Video kaynağı oynatma

MauiAsset derleme eylemiyle uygulamanın Resources\Raw klasörüne eklenmiş video dosyaları denetim tarafından Video oynatılabilir:

<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>

Bu örnekte sınıfı, VideoSourceConverter videonun dosya adını temsil eden dizeyi öğesine ResourceVideoSourcedönüştürür. Her platform için, dosya uygulama paketinde olduğundan ve indirilmesi gerekmeyen video kaynağı ayarlandıktan hemen sonra video oynatılmaya başlar. Her platformda, kullanılmayan ancak videoya dokunarak geri yüklenebilen aktarım denetimleri kaybolur.

Cihazın kitaplığından video dosyası yürütme

Cihazda depolanan video dosyaları alınabilir ve ardından denetim tarafından Video oynatılabilir:

<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>

öğesine dokunulduğunda ButtonClicked , aşağıdaki kod örneğinde gösterilen olay işleyicisi yürütülür:

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;
}

Olay işleyicisi Clicked , kullanıcının cihazdan bir video dosyası seçmesine izin vermek için .NET MAUI'nin MediaPicker sınıfını kullanır. Seçilen video dosyası daha sonra bir FileVideoSource nesne olarak kapsüllenip denetimin SourceVideo özelliği olarak ayarlanır. Sınıf hakkında MediaPicker daha fazla bilgi için bkz . Medya seçici. Her platform için, dosya cihazda olduğundan ve indirilmesi gerekmeyen video kaynağı ayarlandıktan hemen sonra video oynatılmaya başlar. Her platformda, kullanılmayan ancak videoya dokunarak geri yüklenebilen aktarım denetimleri kaybolur.

Video denetimini yapılandırma

özelliğini falseolarak ayarlayarak videonun otomatik olarak başlatılmasını AutoPlay engelleyebilirsiniz:

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

özelliğini falseolarak ayarlayarak AreTransportControlsEnabled aktarım denetimlerini gizleyebilirsiniz:

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

ve AreTransportControlsEnabledfalseolarak ayarlarsanızAutoPlay, video oynatılmaya başlamaz ve oynatılmaya başlamanın hiçbir yolu yoktur. Bu senaryoda arka planda kod dosyasından Play yöntemini çağırmanız veya kendi aktarım denetimlerinizi oluşturmanız gerekir.

Ayrıca, özelliğini olarak ayarlayarak bir videoyu döngüye IsLooping ayarlayabilirsiniz true:

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

özelliğini bu şekilde true ayarlarsanızIsLooping, denetimin Video sonuna ulaştıktan sonra video konumunu otomatik olarak başlangıç konumuna ayarlamasını sağlar.

Özel aktarım denetimlerini kullanma

Aşağıdaki XAML örneği videoyu yürüten, duraklatan ve durduran özel aktarım denetimlerini gösterir:

<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>

Bu örnekte, Video denetim özelliğini false olarak ayarlar ve videoyu AreTransportControlsEnabled yürüten ve duraklatan ve video kayıttan yürütmeyi durduran bir Button öğesini tanımlarButton. Düğme görünümü, simge ve metinden oluşan düğmeler oluşturmak için unicode karakterler ve bunların metin eşdeğerleri kullanılarak tanımlanır:

Screenshot of play and pause buttons.

Video oynatılırken oynat düğmesi duraklat düğmesine güncelleştirilir:

Screenshot of pause and stop buttons.

Kullanıcı arabirimi, video yüklenirken görüntülenen bir ActivityIndicator de içerir. Veri tetikleyicileri, ve düğmelerini etkinleştirmek ve devre dışı bırakmak ActivityIndicator ve ilk düğmeyi oynatma ve duraklatma arasında değiştirmek için kullanılır. Veri tetikleyicileri hakkında daha fazla bilgi için bkz . Veri tetikleyicileri.

Arka planda kod dosyası, düğme Clicked olayları için olay işleyicilerini tanımlar:

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();
    }
    ...
}

Özel konumlandırma çubuğu

Aşağıdaki örnekte, PositionSliderXAML'de kullanılan özel bir konumlandırma çubuğu gösterilmektedir:

<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 ÖzelliğiVideo, her platformda yöntemi tarafından MauiVideoPlayer.UpdateStatus değiştirildiğinden, nesnenin PositionSliderözelliği performans sorunları Video.Position olmadan öğesinin özelliğine bağlıdır Position ve bu da saniyede yalnızca 10 kez çağrılır. Buna ek olarak, iki Label nesne nesneden PositionVideo ve TimeToEnd özellikleri değerlerini görüntüler.

Yerel görünüm temizleme

Her platformun işleyici uygulaması, olaylardan abonelikten çıkarma ve nesneleri yok etme gibi yerel görünüm temizleme gerçekleştirmek için kullanılan uygulamayı geçersiz kılar DisconnectHandler . Ancak, bu geçersiz kılma kasıtlı olarak .NET MAUI tarafından çağrılmıyor. Bunun yerine, uygulamanızın yaşam döngüsünde uygun bir konumdan kendiniz çağırmanız gerekir. Bu durum genellikle denetimi içeren sayfanın bulunduğu sayfadan Video uzaklaştığında olur ve bu da sayfanın Unloaded olayının tetiklenmesine neden olur.

Sayfanın Unloaded olayı için bir olay işleyicisi XAML'ye kaydedilebilir:

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

Daha sonra olayın olay işleyicisi Unloaded , örneğinde DisconnectHandlerHandler yöntemini çağırabilir:

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

Yerel görünüm kaynaklarını temizlemeye ek olarak, işleyicinin DisconnectHandler yöntemini çağırmak, iOS'ta videoların geriye dönük gezintide oynatılmasını da durdurur.