MediaElement.play() from within ViewModel
Asked Answered
S

3

6

I'm struggling with the following issue:

I am building a WP8 application using the MVVM patern. I have a media element on my view.xaml and the logic to control this media element (for example, play, stop, pause and volume) in my viewmodel.cs.

How do I play a sound on this media element from my viewmodel using binding. Without destroying the purpose and structure of MvvM.

(PS: i've seen the following post, but i'm not sure in how to implement it? Link to post)

Sinter answered 24/3, 2014 at 9:59 Comment(6)
What did not you understand from the above link?Davin
How to implement the event handler.. Seems like part of the code is missing? the .... what should be placed there?Sinter
No, can you try the same code and post if there is an error!Davin
I'm a bloody idiot. I missed the ";" after the "public event EventHandler PlayRequested;" and thought it was some sort of eventhandler method. Therefor the rest of the code gave me errors. Thats clear now. What i don't understand is, when does the event fire?Sinter
this is the place, vm.PlayRequested += (sender, e) => { this.myMediaElement.Play(); };Davin
So, if i want to replace MediaElement.play in my viewmodel, how do i replace that call so the Playrequested in my view fires?Sinter
S
12

You can bind Media Element directly from the view model

in xaml:

<ContentControl Content="{Binding MediaElementObject}"/>

in ViewModel:

private MediaElement _mediaElementObject;

public MediaElement MediaElementObject
{
   get { return _mediaElementObject; }
   set { _mediaElementObject = value;RaisePropertyChanged(); }
}

And on OnNavigatedTo Override method you can create it's new object & can register it's events.

MediaElementObject=new MediaElement();

So that you can do all thing from the viewmodel itself.

Sorcerer answered 24/3, 2014 at 13:1 Comment(4)
Great Answer Asitis .. :)Bronchoscope
I can verify this works 100% and I strongly encourage to use this solution. it is an excellent thinking line for all those nastiness regarding few other controls with same problem, it will require some plumbing between the inner props of the mediaElement and those your VM's designed to expose to the view which are very easy to implement. Thank you asitis.Aromaticity
I agree this will work, BUT this is definitely not rcommended. You should never have UI elemnets on your ViewModel. What if you want to use the same ViewModel on Xamarin (we dont have MediaElement there), Also what if you use Unit testing?Admiral
The ViewModel should not know about MediaElement. This breaks the principles of MVVM. Wrong answer.Express
E
1

The answers above make use of MediaElement in the ViewModel. This element is a View-side component the VM should remain agnostic of.

One way to implement this would be exposing an event in your ViewModel via an interface that exposes that event, then have the view respond to that event by running its MediaElement, via code-behind, or perhaps using interactivity triggers.

ViewModel:

public interface ISoundPlayer
{
  event Action Play();
}    

public class MyViewModel : ViewModelBase, ISoundPlayer
{
  public event Action Play;
}

View:

public class MyView : UserControl
{
  ISoundPlayer _SoundPlayer;

  public MyView()
  {
    DataContextChanged += OnDataContextChanged;
    Unloaded += OnUnloaded;
  }  

  void OnDataContextChanged(DependencyObject sender, DataContextChangedEventArgs args)
  {
    if (DataContext is ISoundPlayer player && _SoundPlayer != player)
    {
      if (_SoundPlayer != null)
        _SoundPlayer.Play -= OnPlay;

      _SoundPlayer = player;
      _SoundPlayer.Play += OnPlay;
    }    
  }

  void OnUnloaded(object sender, RoutedEventArgs e)
  {
    if (_SoundPlayer != null)
      _Metronome.Play -= OnPlay;
  }

  void OnPlay() => myMediaElement.Play();
}
Express answered 9/6, 2019 at 21:38 Comment(0)
T
0

The answer of asitis is great.And I show you my detail code:(use caliburn.micro so don't need to bind name)

View

    <Grid>
         <ContentControl Content="{Binding MediaElementObject}"/>
    </Grid>

    <StackPanel Orientation="Horizontal">
         <Button x:Name="ButtonPlay" Content="Play" Width="220px" Margin="20,5"/>
         <Button x:Name="ButtonStop" Content="Stop" Width="220px" Margin="20,5"/>
         <Button x:Name="ButtonForward" Content="Forward(30s)" Width="220px" Margin="20,5"/>
         <Button x:Name="ButtonBack" Content="Back(30s)"Width="220px" Margin="20,5"/>
     </StackPanel>

ViewModel

    private MediaElement _mediaElementObject = new MediaElement();
    public MediaElement MediaElementObject
    {
        get { return _mediaElementObject; }
        set
        {
            _mediaElementObject = value;
            NotifyOfPropertyChange(() => MediaElementObject);
        }
    }

    public void ButtonPlay()
    {
        MediaElementObject.Source =new Uri( @"C:\Users\admin\Videos\XXXXXX.wmv");
        MediaElementObject.LoadedBehavior = MediaState.Manual;
        MediaElementObject.UnloadedBehavior = MediaState.Manual;
        MediaElementObject.Play();
    }     

    public void ButtonStop()
    {
        MediaElementObject.Stop();
    }
    public void ButtonForward()
    {
        MediaElementObject.Position = _mediaElementObject.Position + TimeSpan.FromSeconds(30);
    }
    public void ButtonBack()
    {
        MediaElementObject.Position = _mediaElementObject.Position - TimeSpan.FromSeconds(30);
    }

May can help someone:)

Tremor answered 17/5, 2019 at 7:28 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.