Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/wpf/14.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Wpf MVVM模式冲突:MediaElement.Play()_Wpf_Mvvm_Visibility_Mediaelement - Fatal编程技术网

Wpf MVVM模式冲突:MediaElement.Play()

Wpf MVVM模式冲突:MediaElement.Play(),wpf,mvvm,visibility,mediaelement,Wpf,Mvvm,Visibility,Mediaelement,我知道ViewModel不应该了解View,但是除了在ViewModel中引用View(或直接引用MediaElement)之外,我如何从ViewModel调用MediaElement.Play()方法呢? 其他(链接)问题:如何在不违反MVVM模式的情况下从ViewModel管理视图控件的可见性?1)不要从视图模型调用Play()。改为在视图模型中引发事件(例如playdrequest),并在视图中侦听此事件: 视图模型: public event EventHandler PlayReque

我知道ViewModel不应该了解View,但是除了在ViewModel中引用View(或直接引用MediaElement)之外,我如何从ViewModel调用MediaElement.Play()方法呢?
其他(链接)问题:如何在不违反MVVM模式的情况下从ViewModel管理视图控件的可见性?

1)不要从视图模型调用
Play()
。改为在视图模型中引发事件(例如
playdrequest
),并在视图中侦听此事件:

视图模型:

public event EventHandler PlayRequested;
...
if (this.PlayRequested != null)
{
    this.PlayRequested(this, EventArgs.Empty);
}
视图:

2) 您可以在视图模型中公开一个公共布尔属性,并将控件的
可见性
属性绑定到此属性。由于
Visibility
属于
Visibility
类型,而不是
bool
,因此您必须使用转换器

您可以找到这种转换器的基本实现。
这可能也会对您有所帮助。

每当应用程序中发生事件时,我都会使用媒体元素在UI中播放声音。处理此问题的视图模型是使用Uri类型的源属性创建的(notify属性已更改,但您已经知道需要它来通知UI)

每当源代码发生更改时(这取决于您),您所要做的就是将源属性设置为null(这就是为什么源属性应该是Uri而不是字符串,MediaElement自然会抛出exception,我认为是NotSupportedException),然后将其设置为您想要的任何Uri

可能,本技巧最重要的方面是您必须设置MediaElement的property LoadedBehavior以在视图的XAML中播放。希望您想要实现的目标不需要代码隐藏

这个技巧非常简单,所以我不会发布一个完整的例子。视图模型的播放功能应如下所示:

    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);
    }
这是源属性,没有什么特别之处:

    #region Source property

    /// <summary>
    /// Stores Source value.
    /// </summary>
    private Uri _Source = null;

    /// <summary>
    /// Gets or sets file URI to play.
    /// </summary>
    public Uri Source
    {
        get { return this._Source; }
        private set
        {
            if (this._Source != value)
            {
                this._Source = value;
                this.RaisePropertyChanged("Source");
            }
        }
    }

    #endregion Source property
#区域源属性
/// 
///存储源值。
/// 
私有Uri _Source=null;
/// 
///获取或设置要播放的文件URI。
/// 
公共Uri源
{
获取{返回此。_Source;}
专用设备
{
如果(此._源!=值)
{
这个。_Source=值;
本.RaisePropertyChanged(“来源”);
}
}
}
#端域源属性
至于可见性,以及类似的东西,您可以使用转换器(例如从bool到Visibility,您可以在CodePlex for WPF、SL、WP7、8上找到),并将控件的属性绑定到视图模型的属性(例如IsVisible)。这样,您就可以控制视图的部分外观。或者,您可以在视图模型上键入可见性属性System.Windows.Visibility(我在这里没有看到任何模式冲突)。真的,这并不罕见

祝你好运

安德烈


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

对于所有后来的人

有很多方法可以达到相同的结果,这实际上取决于您希望如何实现您的代码,只要您的代码不难维护,我相信在某些情况下打破MVVM模式是可以的

但话虽如此,我也相信在模式中总是有办法做到这一点,下面就是其中之一,以防有人想知道还有哪些其他替代方案可用

任务包括:

  • 我们不希望从ViewModel直接引用任何UI元素,即MediaElement和视图本身
  • 我们想在这里用命令来变魔术
  • 解决方案:

    简言之,我们将在视图和ViewModel之间引入一个接口,以打破依赖关系,视图将实现该接口,并负责直接控制MediaElement,同时让ViewModel只与接口对话,如果需要,可以与其他实现交换以进行测试,下面是长版本:

  • 引入一个名为IMediaService的接口,如下所示:

    public interface IMediaService
    {
        void Play();
        void Pause();
        void Stop();
        void Rewind();
        void FastForward();
    }
    
  • 在视图中实现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();
        }
    }
    
  • 然后,我们在DemoView.XAML中执行一些操作:

    • 为MediaElement指定一个名称,以便隐藏的代码可以像上面那样访问它:
    
    
    • 为视图指定一个名称,以便我们可以将其作为参数传递,然后
    • 导入交互性命名空间供以后使用(出于简单原因,省略了一些默认命名空间):
    
    
    • 通过触发器连接加载的事件,通过命令将视图本身传递给视图模型
    
    
    • 最后但并非最不重要的一点是,我们需要通过命令连接媒体控件:
    
    
  • 现在我们可以捕获ViewModel中的所有内容(我在这里使用prism的DelegateCommand):

    关于ViewModel的公共类:SkinTalkViewModelBase,IConfirmNavigationRequest { 公共IMediaService{get;private set;} 私有DelegateCommand loadedCommand; 公共DelegateCommand LoadedCommand { 得到 { if(this.loadedCommand==null) { this.loadedCommand=newdelegatecommand((mediaService)=> { this.MediaService=MediaService; }); } 返回loaded命令; } } 私有DelegateCommand和playCommand; 公共委托人命令 { 得到 { if(this.playCommand==null) { this.playCommand=新的DelegateCommand(()=> { 这个.MediaService.Play(); }); } 返回播放命令; } } . .//未列出其他命令,但您得到了t
    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();
        }
    }
    
       <MediaElement Source="{Binding CurrentMedia}" x:Name="MediaPlayer"/>
    
       <UserControl x:Class="Test.DemoView"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:ia="http://schemas.microsoft.com/expression/2010/interactivity"
         x:Name="MediaService">
    
       <ia:Interaction.Triggers>
             <ia:EventTrigger EventName="Loaded">
                 <ia:InvokeCommandAction Command="{Binding LoadedCommand}" CommandParameter="{Binding ElementName=MediaService}"></ia:InvokeCommandAction>
             </ia:EventTrigger>
         </ia:Interaction.Triggers>
    
       <Button Command="{Binding PlayCommand}" Content="Play"></Button> 
       <Button Command="{Binding PauseCommand}" Content="Pause"></Button> 
       <Button Command="{Binding StopCommand}" Content="Stop"></Button> 
       <Button Command="{Binding RewindCommand}" Content="Rewind"></Button> 
       <Button Command="{Binding FastForwardCommand}" Content="FastForward"></Button> 
    
    public class AboutUsViewModel : SkinTalkViewModelBase, IConfirmNavigationRequest
    {
        public IMediaService {get; private set;}
    
        private DelegateCommand<IMediaService> loadedCommand;
        public DelegateCommand<IMediaService> LoadedCommand
        {
            get
            {
                if (this.loadedCommand == null)
                {
                    this.loadedCommand = new DelegateCommand<IMediaService>((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;
            }
        }
    
        .
        . // other commands are not listed, but you get the idea
        .
    }