从 ContentPage xaml CS 类调用位于 ContentView 上的媒体元素事件

问题描述 投票:0回答:1

我已经为媒体元素设置了一个 BindableProperty,效果很好。但我想从我使用自定义类的页面访问事件。我有各种 bools 和 string 工作以及主要的方法。我只需要让活动正常进行。这是一些示例代码。

MediaControl.xaml:

<?xml version="1.0" encoding="utf-8" ?>
<ContentView
    x:Class="NerdNewsNavigator2.Controls.MediaControl"
    xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
    xmlns:item="clr-namespace:NerdNewsNavigator2.Controls"
    xmlns:page="clr-namespace:NerdNewsNavigator2.View"
    xmlns:toolkit="http://schemas.microsoft.com/dotnet/2022/maui/toolkit"
    xmlns:vm="clr-namespace:NerdNewsNavigator2.ViewModel"
    Unloaded="ContentView_Unloaded">
    <Grid>

        <Grid.GestureRecognizers>
            <TapGestureRecognizer Command="{Binding Source={RelativeSource AncestorType={x:Type vm:BaseViewModel}}, Path=MovedCommand}" />
            <SwipeGestureRecognizer Direction="Up" Swiped="SwipeGestureRecognizer_Swiped" />
            <SwipeGestureRecognizer Direction="Down" Swiped="SwipeGestureRecognizer_Swiped" />
        </Grid.GestureRecognizers>

        <toolkit:MediaElement
            x:Name="mediaElement"
            ShouldAutoPlay="True"
            ShouldKeepScreenOn="True"
            ShouldShowPlaybackControls="False" />

        <Frame
            BackgroundColor="Black"
            IsEnabled="{Binding Source={RelativeSource AncestorType={x:Type vm:BaseViewModel}}, Path=SetFullScreen}"
            IsVisible="{Binding Source={RelativeSource AncestorType={x:Type vm:BaseViewModel}}, Path=SetFullScreen}"
            Opacity="0.5" />

        <Grid IsEnabled="{Binding Source={RelativeSource AncestorType={x:Type vm:BaseViewModel}}, Path=SetFullScreen}" IsVisible="{Binding Source={RelativeSource AncestorType={x:Type vm:BaseViewModel}}, Path=SetFullScreen}">

            <ImageButton
                x:Name="btnFullScreen"
                Margin="10"
                BackgroundColor="Transparent"
                Clicked="BtnFullScreen_Clicked"
                HeightRequest="40"
                HorizontalOptions="End"
                Source="whitefs.png"
                VerticalOptions="Start"
                WidthRequest="40" />

            <VerticalStackLayout VerticalOptions="End">

                <HorizontalStackLayout HorizontalOptions="Center">

                    <ImageButton
                        x:Name="BtnRewind"
                        Margin="10"
                        BackgroundColor="Transparent"
                        BindingContext="{x:Reference mediaElement}"
                        Clicked="BtnRewind_Clicked"
                        HeightRequest="40"
                        HorizontalOptions="Center"
                        Source="rewind.png"
                        VerticalOptions="End"
                        WidthRequest="40" />

                    <ImageButton
                        x:Name="BtnPLay"
                        Margin="10"
                        BackgroundColor="Transparent"
                        BindingContext="{x:Reference mediaElement}"
                        Clicked="BtnPlay_Clicked"
                        HeightRequest="40"
                        HorizontalOptions="Center"
                        Source="pause.png"
                        VerticalOptions="End"
                        WidthRequest="40" />

                    <ImageButton
                        x:Name="BtnForward"
                        Margin="10"
                        BackgroundColor="Transparent"
                        BindingContext="{x:Reference mediaElement}"
                        Clicked="BtnForward_Clicked"
                        HeightRequest="40"
                        HorizontalOptions="Center"
                        Source="fastforward.png"
                        VerticalOptions="End"
                        WidthRequest="40" />

                    <ImageButton
                        x:Name="ImageButtonMute"
                        Margin="10"
                        BackgroundColor="Transparent"
                        Clicked="OnMuteClicked"
                        HeightRequest="40"
                        Source="muted.png"
                        WidthRequest="40">
                        <ImageButton.Triggers>
                            <DataTrigger
                                Binding="{Binding ShouldMute, Source={x:Reference mediaElement}}"
                                TargetType="ImageButton"
                                Value="True" />
                            <DataTrigger
                                Binding="{Binding ShouldMute, Source={x:Reference mediaElement}}"
                                TargetType="ImageButton"
                                Value="False" />
                        </ImageButton.Triggers>
                    </ImageButton>

                </HorizontalStackLayout>

                <HorizontalStackLayout HorizontalOptions="Start" VerticalOptions="End">

                    <Label
                        Margin="5"
                        FontSize="12"
                        HorizontalOptions="Center"
                        Text="{Binding Source={RelativeSource AncestorType={x:Type item:MediaControl}}, Path=PlayPosition}"
                        TextColor="White" />
                </HorizontalStackLayout>

                <Slider
                    x:Name="PositionSlider"
                    Margin="10"
                    DragCompleted="Slider_DragCompleted"
                    DragStarted="Slider_DragStarted"
                    MaximumTrackColor="LightGray"
                    MinimumTrackColor="Red" />

            </VerticalStackLayout>

        </Grid>
    </Grid>

</ContentView>

显示属性和可绑定属性的缩写代码。 MediaControl.xaml.cs:


using Application = Microsoft.Maui.Controls.Application;
using Platform = Microsoft.Maui.ApplicationModel.Platform;

#if ANDROID
using Views = AndroidX.Core.View;
#endif

#if WINDOWS
using Microsoft.UI;
using Microsoft.UI.Windowing;
using WinRT;
using Microsoft.Maui.Controls;
using CommunityToolkit.Maui.Core.Primitives;
#endif

namespace NerdNewsNavigator2.Controls;

public partial class MediaControl : ContentView
{
    #region Properties and Bindable Properties
    /// <summary>
    /// Initilizes a new instance of the <see cref="Position"/> class
    /// </summary>
    private Position Pos { get; set; } = new();
    public string PlayPosition { get; set; }
    public Page CurrentPage { get; set; }

    private bool _fullScreen = false;
#if WINDOWS
    private static MauiWinUIWindow CurrentWindow { get; set; }
#endif

    public static readonly BindableProperty TitleProperty = BindableProperty.Create(nameof(Name), typeof(MediaElement), typeof(MediaControl), propertyChanged: (bindable, oldValue, newValue) =>
        {
            var control = (MediaControl)bindable;
            control.mediaElement.ShouldAutoPlay = (bool)newValue;
            control.mediaElement.ShouldKeepScreenOn = (bool)newValue;
            control.mediaElement.Source = newValue as MediaSource;
            control.mediaElement.ShouldShowPlaybackControls = (bool)newValue;
            control.mediaElement.StateChanged += (System.EventHandler<MediaStateChangedEventArgs>)newValue;
            control.mediaElement.MediaOpened += (System.EventHandler)newValue;
        });

    public static readonly BindableProperty SourceProperty = BindableProperty.Create(nameof(Source), typeof(MediaSource), typeof(MediaControl), propertyChanged: (bindableProperty, oldValue, newValue) =>
    {
        var control = (MediaControl)bindableProperty;
        control.mediaElement.Source = newValue as MediaSource;
    });
    public TimeSpan Position
    {
        get => (TimeSpan)GetValue(TitleProperty);
    }
    public MediaSource Source
    {
        get => GetValue(SourceProperty) as MediaSource;
        set => SetValue(SourceProperty, value);
    }
    public MediaElement Name
    {
        get => GetValue(TitleProperty) as MediaElement;
        set => SetValue(TitleProperty, value);
    }
    public bool ShouldShowPlaybackControls
    {
        get => (bool)GetValue(TitleProperty);
        set => SetValue(TitleProperty, value);
    }
    public bool ShouldAutoPlay
    {
        get => (bool)GetValue(TitleProperty);
        set => SetValue(TitleProperty, value);
    }
    public bool ShouldKeepScreenOn
    {
        get => (bool)GetValue(TitleProperty);
        set => SetValue(TitleProperty, value);
    }
    #endregion
    public MediaControl()
    {
        InitializeComponent();
        PlayPosition = string.Empty;
        mediaElement.PropertyChanged += MediaElement_PropertyChanged;
        mediaElement.PositionChanged += ChangedPosition;
        CurrentPage = Shell.Current.CurrentPage;
    }
    public void SeekTo(TimeSpan position)
    {
        mediaElement.SeekTo(position);
    }
    public void Stop()
    {
        mediaElement.Stop();
    }
    #region Events
#nullable enable
    private void MediaElement_PropertyChanged(object? sender, PropertyChangedEventArgs e)
    {
        if (e.PropertyName == MediaElement.DurationProperty.PropertyName)
        {
            PositionSlider.Maximum = mediaElement.Duration.TotalSeconds;
        }
    }
    private void OnPositionChanged(object? sender, MediaPositionChangedEventArgs e)
    {
        PositionSlider.Value = e.Position.TotalSeconds;
    }
    private void SwipeGestureRecognizer_Swiped(object sender, SwipedEventArgs e)
    {
#if WINDOWS
        CurrentWindow = BaseViewModel.CurrentWindow;
#endif
        if (e.Direction == SwipeDirection.Up)
        {
            SetFullScreen();
        }
        if (e.Direction == SwipeDirection.Down)
        {
            RestoreScreen();
        }
    }
    private void Slider_DragCompleted(object? sender, EventArgs e)
    {
        ArgumentNullException.ThrowIfNull(sender);

        var newValue = ((Slider)sender).Value;
        mediaElement.SeekTo(TimeSpan.FromSeconds(newValue));
        mediaElement.Play();
    }
#nullable disable
    private void Slider_DragStarted(object sender, EventArgs e)
    {
        mediaElement.Pause();
    }
    private void ChangedPosition(object sender, EventArgs e)
    {
        var playDuration = BaseViewModel.TimeConverter(mediaElement.Duration);
        var position = BaseViewModel.TimeConverter(mediaElement.Position);
        PlayPosition = $"{position}/{playDuration}";
        OnPropertyChanged(nameof(PlayPosition));
    }
    #endregion

    #region Buttons
    private void BtnRewind_Clicked(object sender, EventArgs e)
    {
        var time = mediaElement.Position - TimeSpan.FromSeconds(15);
        mediaElement.Pause();
        mediaElement.SeekTo(time);
        mediaElement.Play();
    }

    private void BtnForward_Clicked(object sender, EventArgs e)
    {
        var time = mediaElement.Position + TimeSpan.FromSeconds(15);
        mediaElement.Pause();
        mediaElement.SeekTo(time);
        mediaElement.Play();
    }

这是我想从内容页面调用的事件触发方法的示例 TabletPlayPodcastPage.xaml:

   /// <summary>
    /// Manages IOS seeking for <see cref="mediaElement"/> with <see cref="Pos"/> at start of playback.
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    public async void SeekIOS(object? sender, MediaStateChangedEventArgs e)
    {
        if (sender == null)
        {
            return;
        }
        Pos.Title = Preferences.Default.Get("New_Url", string.Empty);
        Pos.SavedPosition = TimeSpan.Zero;
        var positionList = await App.PositionData.GetAllPositions();
        foreach (var item in positionList)
        {
            if (Pos.Title == item.Title)
            {
                Pos.SavedPosition = item.SavedPosition;
                Debug.WriteLine($"Retrieved Saved position from database is: {item.Title} - {item.SavedPosition}");
            }
        }
        if (e.NewState == MediaElementState.Playing)
        {
            mediaElement.SeekTo(Pos.SavedPosition);
            mediaElement.ShouldKeepScreenOn = true;
            Debug.WriteLine("Media playback started. ShouldKeepScreenOn is set to true.");
        }
    }

这里是我想使用的示例调用函数:

   /// <summary>
    /// A method that starts <see cref="MediaElement"/> event for <see cref="TabletPlayPodcastPage"/>
    /// </summary>
    public void Load()
    {
#if WINDOWS || ANDROID
        mediaElement.MediaOpened += Seek;
#endif

#if IOS || MACCATALYST
        mediaElement.StateChanged += SeekIOS;
#endif 
    }

尝试调用 mediaElement.StateChanged += SeekIOS 给出错误 CS1061 Error Message Link

如果我使用 Visual studio 快速操作,我会得到这个消除错误但不允许我访问事件的功能。

public Action<object, EventArgs> MediaOpened { get; internal set; }

我不再收到错误消息,但事件也没有触发。我正在寻找一种从另一个使用 MediaControl 的页面调用事件的方法。我创建所有这些是为了减少代码以及我想要工作的所有功能和行为,这是关于减少代码并使其更整洁。

events maui mediaelement
1个回答
0
投票

好吧,结果我自己弄明白了。我查看了媒体元素代码本身并通过反复试验实现了这个解决方案

 public static readonly BindableProperty TitleProperty = BindableProperty.Create(nameof(Name), typeof(MediaElement), typeof(MediaControl), propertyChanged: (bindable, oldValue, newValue) =>
        {
            var control = (MediaControl)bindable;
            control.mediaElement.ShouldAutoPlay = (bool)newValue;
            control.mediaElement.ShouldKeepScreenOn = (bool)newValue;
            control.mediaElement.Source = newValue as MediaSource;
            control.mediaElement.ShouldShowPlaybackControls = (bool)newValue;
            control.mediaElement.PositionChanged += (EventHandler<MediaPositionChangedEventArgs>)newValue;
            control.mediaElement.StateChanged += (EventHandler<MediaStateChangedEventArgs>)newValue;
            control.mediaElement.MediaOpened += (EventHandler)newValue;
        });

    public static readonly BindableProperty SourceProperty = BindableProperty.Create(nameof(Source), typeof(MediaSource), typeof(MediaControl), propertyChanged: (bindableProperty, oldValue, newValue) =>
    {
        var control = (MediaControl)bindableProperty;
        control.mediaElement.Source = newValue as MediaSource;
    });
    public static readonly BindableProperty StateChangedProperty = BindableProperty.Create(nameof(StateChanged), typeof(EventHandler<MediaStateChangedEventArgs>), typeof(MediaControl), propertyChanged: (bindableProperty, oldValue, newValue) =>
    {
        var control = (MediaControl)bindableProperty;
        control.mediaElement.StateChanged += (EventHandler<MediaStateChangedEventArgs>)newValue;
    });
    public static readonly BindableProperty MediaOpenedProperty = BindableProperty.Create(nameof(MediaOpened), typeof(EventHandler), typeof(MediaControl), propertyChanged: (bindableProperty, oldValue, newValue) =>
    {
        var control = (MediaControl)bindableProperty;
        control.mediaElement.MediaOpened += (EventHandler)newValue;
    });
    public static readonly BindableProperty ShouldKeepScreenOnProperty = BindableProperty.Create(nameof(ShouldKeepScreenOn), typeof(bool), typeof(MediaControl), propertyChanged: (bindableProperty, oldValue, newValue) =>
    {
        var control = (MediaControl)bindableProperty;
        control.mediaElement.ShouldKeepScreenOn = (bool)newValue;
    });
    public static readonly BindableProperty PositionProperty = BindableProperty.Create(nameof(Position), typeof(TimeSpan), typeof(MediaElement), TimeSpan.Zero);
    public static readonly BindableProperty ShouldAutoPlayProperty = BindableProperty.Create(nameof(ShouldAutoPlay), typeof(bool), typeof(MediaControl), propertyChanged: (bindableProperty, oldValue, newValue) =>
    {
        var control = (MediaControl)bindableProperty;
        control.mediaElement.ShouldAutoPlay = (bool)newValue;
    });
    public static readonly BindableProperty ShouldShowPlaybackControlsProperty = BindableProperty.Create(nameof(ShouldShowPlaybackControls), typeof(bool), typeof(MediaControl), propertyChanged: (bindableProperty, oldValue, newValue) =>
    {
        var control = (MediaControl)bindableProperty;
        control.mediaElement.ShouldShowPlaybackControls = (bool)newValue;
    });
    public static readonly BindableProperty PositionChangedProperty = BindableProperty.Create(nameof(PositionChanged), typeof(EventHandler<MediaPositionChangedEventArgs>), typeof(MediaControl), propertyChanged: (bindableProperty, oldValue, newValue) =>
    {
        var control = (MediaControl)bindableProperty;
        control.mediaElement.PositionChanged += (EventHandler<MediaPositionChangedEventArgs>)newValue;
    });
    public EventHandler MediaOpened
    {
        get => GetValue(MediaOpenedProperty) as EventHandler;
        set => SetValue(MediaOpenedProperty, value);
    }
    public EventHandler<MediaStateChangedEventArgs> StateChanged
    {
        get => GetValue(StateChangedProperty) as EventHandler<MediaStateChangedEventArgs>;
        set => SetValue(StateChangedProperty, value);
    }
    public EventHandler<MediaPositionChangedEventArgs> PositionChanged
    {
        get => GetValue(PositionChangedProperty) as EventHandler<MediaPositionChangedEventArgs>;
        set => SetValue(PositionChangedProperty, value);
    }
    public MediaSource Source
    {
        get => GetValue(SourceProperty) as MediaSource;
        set => SetValue(SourceProperty, value);
    }
    public TimeSpan Position => mediaElement.Position;
    public MediaElement Name
    {
        get => GetValue(TitleProperty) as MediaElement;
        set => SetValue(TitleProperty, value);
    }
    public bool ShouldShowPlaybackControls
    {
        get => (bool)GetValue(ShouldShowPlaybackControlsProperty);
        set => SetValue(ShouldShowPlaybackControlsProperty, value);
    }
    public bool ShouldAutoPlay
    {
        get => (bool)GetValue(ShouldAutoPlayProperty);
        set => SetValue(ShouldAutoPlayProperty, value);
    }
    public bool ShouldKeepScreenOn
    {
        get => (bool)GetValue(ShouldKeepScreenOnProperty);
        set => SetValue(ShouldKeepScreenOnProperty, value);
    }

这让我可以使用我需要的媒体元素的所有类型的属性。这不是他们的全部。但是我能够将所有页面特定代码移回实际页面,并且我的 ContentView 类不再与其他页面的代码混在一起。

© www.soinside.com 2019 - 2024. All rights reserved.