如何在ViewModel中访问MediaElement的方法

12 浏览
0 Comments

如何在ViewModel中访问MediaElement的方法

我明白ViewModel不应该了解View,但是我如何在ViewModel中调用MediaElement.Play()方法,而不是在ViewModel中引用View(或直接引用MediaElement)?

另一个(相关的)问题:如何在不违反MVVM模式的情况下,通过ViewModel来管理View的控件可见性?

0
0 Comments

问题的出现原因是ViewModel中无法直接访问MediaElement的方法。解决方法是在ViewModel中引发一个事件(例如PlayRequested),并在View中监听此事件并调用MediaElement的Play方法。

在解决方法中,还提到了另一种解决方案,即在ViewModel中公开一个公共布尔属性,并将控件的Visibility属性绑定到该属性上。由于Visibility是Visibility类型而不是bool类型,所以需要使用转换器。文章中提供了一个转换器的基本实现,并提供了一个相关问题的链接,该问题可能对读者有所帮助。

此外,文章中还提到了不应该在ViewModel中公开Visibility类型的属性,因为Visibility类型是与视图相关的,不应该存在于ViewModel中。建议使用bool类型的属性,并使用转换器进行转换。

最后,还提到了不应该从ViewModel继承DependencyObject以及不应该在ViewModel中使用DependencyProperty的观点,并给出了相关的Stack Overflow链接。

总结起来,文章介绍了如何在ViewModel中访问MediaElement的方法以及解决这个问题的方法,同时还提到了关于使用Visibility类型属性和使用DependencyProperty的一些建议。

0
0 Comments

在这篇文章中,作者分享了如何在ViewModel中访问MediaElement的方法。作者使用MediaElement来在应用程序中的UI中播放声音。处理此功能的ViewModel被创建为具有Uri类型的Source属性(具有通知属性更改,这是为了通知UI所需的)。

每当source更改时,您需要做的就是将source属性设置为null(这就是为什么Source属性应该是Uri而不是字符串,因为MediaElement自然会抛出异常,我想是NotSupportedException),然后将其设置为您想要的任何URI。

这个技巧最重要的方面可能是你必须在XAML中将MediaElement的LoadedBehavior属性设置为Play。希望你想要实现的内容不需要代码后台。

这个技巧非常简单,所以我不会发布一个完整的示例。ViewModel的播放函数应该像这样:

private void PlaySomething(string fileUri)
{
    if (string.IsNullOrWhiteSpace(fileUri))
        return;
    // HACK for MediaElement: to force it to play a new source, set source to null then put the real source URI. 
    this.Source = null;
    this.Source = new Uri(fileUri);
}

这是Source属性,没有什么特别的:

#region Source property
/// 
/// Stores Source value.
/// 
private Uri _Source = null;
/// 
/// Gets or sets file URI to play.
/// 
public Uri Source
{
    get { return this._Source; }
    private set
    {
        if (this._Source != value)
        {
            this._Source = value;
            this.RaisePropertyChanged("Source");
        }
    }
}
#endregion Source property

至于Visibility之类的内容,您可以使用转换器(例如从bool到visibility,您可以在CodePlex上找到WPF,SL,WP7,8的转换器),并将控件的属性绑定到ViewModel的属性(例如IsVisible)。这样,您就可以控制视图的一部分外观。或者,您可以在ViewModel中使用类型为System.Windows.Visibility的Visibility属性(我在这里没有看到任何模式违规)。实际上,这并不那么不常见。

祝你好运,

Andrei

P.S.我必须提到我测试过的是.NET 4.5版本,但我认为它也应该适用于其他版本。

0
0 Comments

如何在ViewModel中访问MediaElement的方法

问题出现的原因:

我们不希望ViewModel直接引用任何UI元素,即MediaElement和View本身。

解决方法:

我们将在View和ViewModel之间引入一个接口来打破依赖关系,View将实现该接口并负责直接控制MediaElement,而ViewModel只与接口进行交互,如果需要的话,可以通过其他实现进行测试。具体步骤如下:

1. 引入一个名为IMediaService的接口:

public interface IMediaService
{
    void Play();
    void Pause();
    void Stop();
    void Rewind();
    void FastForward();
}

2. 在View中实现IMediaService接口:

public partial class DemoView : UserControl, IMediaService
{
    public DemoView()
    {
        InitializeComponent();
    }
    void IMediaService.FastForward()
    {
        this.MediaPlayer.Position += TimeSpan.FromSeconds(10);
    }
    void IMediaService.Pause()
    {
        this.MediaPlayer.Pause();
    }
    void IMediaService.Play()
    {
        this.MediaPlayer.Play();
    }
    void IMediaService.Rewind()
    {
        this.MediaPlayer.Position -= TimeSpan.FromSeconds(10);
    }
    void IMediaService.Stop()
    {
        this.MediaPlayer.Stop();
    }
}

3. 在DemoView.XAML中进行以下操作:

- 为MediaElement指定一个名称,以便后续的代码可以访问它:


- 为View指定一个名称,以便将其作为参数传递,并导入交互命名空间以供后续使用:


- 通过触发器将Loaded事件与命令绑定,将View本身通过命令参数传递给ViewModel:


    
        
    

- 最后,通过命令将媒体控件与ViewModel关联起来:

 
 
 
 

4. 在ViewModel中捕获所有内容(这里使用Prism的DelegateCommand):

public class AboutUsViewModel : SkinTalkViewModelBase, IConfirmNavigationRequest
{
    public IMediaService {get; private set;}
    
    private DelegateCommand loadedCommand;
    public DelegateCommand LoadedCommand
    {
        get
        {
            if (this.loadedCommand == null)
            {
                this.loadedCommand = new DelegateCommand((mediaService) =>
                {
                    this.MediaService = mediaService;
                });
            }
            return loadedCommand;
        }
    }
    
    private DelegateCommand playCommand;
    public DelegateCommand PlayCommand
    {
        get
        {
            if (this.playCommand == null)
            {
                this.playCommand = new DelegateCommand(() =>
                {
                    this.MediaService.Play();
                });
            }
            return playCommand;
        }
    }
    
    // 其他命令未列出,但你可以理解这个思路
}

附注:我使用Prism的自动连接功能来链接View和ViewModel。因此,在View的代码后台文件中没有DataContext分配代码,我更喜欢保持这种状态,因此我选择 purely Commands来实现这个结果。

public IMediaService {get; private set;}更改为public IMediaService MediaService {get; private set;},并添加stackoverflow.com/a/16819598/4675770以获取MediaState,你就得到了完美的解决方案!

0