我明白,视图模型不应该查看的任何知识,但我怎么能叫MediaElement.Play()方法从视图模型,除了具有视图模型的引用视图(或者直接到MediaElement的)?
其他(链接)问题:我怎么能管理View的控件从视图模型知名度不违反MVVM模式?
Answer 1:
1)不要调用Play()
从视图模型。 提高在视图模型的事件,而不是(例如PlayRequested
),并听取这一事件的看法:
视图模型:
public event EventHandler PlayRequested;
...
if (this.PlayRequested != null)
{
this.PlayRequested(this, EventArgs.Empty);
}
视图:
ViewModel vm = new ViewModel();
this.DataContext = vm;
vm.PlayRequested += (sender, e) =>
{
this.myMediaElement.Play();
};
2)您可以在视图模型公开公共布尔属性,以及绑定Visibility
您的控件的属性,这个属性。 由于Visibility
是类型的Visibility
,而不是bool
,你必须使用转换器。
你可以找到这样的转换器的基本实现在这里 。 此相关的问题可能会帮助你。
Answer 2:
对于所有的后来者,
有许多方法来达到同样的效果,它真的取决于你想如何实现你的,只要你的代码是不是很难维持,我相信它的确定打破在某些情况下MVVM模式。
不过话说回来,我也相信总会有办法的格局内做到这一点,下面是其中的一个以防万一,如果有人想知道还有什么其他替代品可供选择。
任务:
- 我们不希望有从视图模型到任何UI元素直接引用,即中的MediaElement和视图本身。
- 我们要使用命令在这里做魔术
解决方案:
总之,我们要介绍的视图和视图模型之间的界面,打破的关系是不,和视图将实现该接口并同时使视图模型谈论了这个接口负责MediaElement的直接控制,这如果需要的话,和来这里的长版可以与其他实施互换用于测试目的:
介绍如下称为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的名称,以便后面的代码可以像上面的访问:
<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">
- 通过联播触发Loaded事件通过命令视图本身传递给视图模型
<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>
现在,我们可以赶上在视图模型的一切(我在这里使用棱镜的DelegateCommand):
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 . }
附注:我用的棱镜的自动布线功能连接起来,查看和视图模型。 因此,在查看的隐藏文件的代码是没有的DataContext分配代码,我宁愿保持这种方式,因此我选择使用纯粹的命令来实现这一结果。
Answer 3:
我使用的媒体元素,每当一个事件在应用程序发生在UI播放声音。 处理这个视图模型,用类型的来源属性URI创建(含通知属性更改,但你已经知道你需要通知UI)。
所有你需要做的,每当源的变化(这是由你),是设置源属性设置为null(这就是为什么来源属性应该是开放的,而不是字符串的MediaElement自然会抛出异常,引发NotSupportedException我认为),那么它设置为任何你想要的URI。
也许,这种尖端的最重要的方面是,你要设置的MediaElement的属性LoadedBehaviour在视图的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
至于知名度,这样的东西,你可以使用转换器(例如,从布尔能见度,你可以找到在CodePlex上的WPF,SL,WP7,8)和控件的属性绑定到该视图模型(如ISVISIBLE)的。 通过这种方式,您控制部件视图的方面。 或者,你可以有Visibility属性类型System.Windows.Visibility您的视图模型(我在这里看不到任何图案违约)。 真的,它不是少见。
祝好运,
安德烈
PS我不得不提的是.NET 4.5是我测试过的版本,但我认为它应该在其他版本上正常工作。