我發現了一個可行的解決方案,我會保留一個答案,直到有更好的解決方案出現。
解決方案的基礎是定製的Behavior Generic Class。
重要的是要注意,問題和答案在全球範圍內,而不是MediaElement
的範圍。因此,該解決方案與支持ViewModel優先方法的每個FrameworkElement
衍生控件完全相關。
我沒有刪除任何代碼,目的明確。
ViewTemplates.xaml
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication2"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity">
<DataTemplate DataType="{x:Type local:MediaElementViewModel}">
<MediaElement Source="{Binding Source}" Volume="{Binding Volume}"
LoadedBehavior="Manual" UnloadedBehavior="Manual">
<i:Interaction.Behaviors>
<local:MediaElementBehavior/>
</i:Interaction.Behaviors>
</MediaElement>
</DataTemplate>
</ResourceDictionary>
MediaElementViewModel.cs
public MediaElementViewModel()
{
Volume = 0.5;
}
private Uri _source;
public Uri Source
{
get { return _source; }
set
{
_source = value;
RaisePropertyChanged("Source");
}
}
private double _volume;
public double Volume
{
get { return _volume; }
set
{
_volume = value;
RaisePropertyChanged("Volume");
}
}
public Action Play { get; set; }
public Action Stop { get; set; }
public Func<bool> Focus { get; set; }
MediaElementBehavior.cs
public MediaElementBehavior()
{
}
protected override void OnAttached()
{
base.OnAttached();
MediaElement player = (MediaElement)this.AssociatedObject;
MediaElementViewModel viewModel = (MediaElementViewModel)this.AssociatedObject.DataContext;
player.Dispatcher.Invoke(() =>
{
// backing up the player methods inside its view-model.
if (viewModel.Play == null)
viewModel.Play = player.Play;
if (viewModel.Stop == null)
viewModel.Stop = player.Stop;
if (viewModel.Focus == null)
viewModel.Focus = player.Focus;
});
}
protected override void OnDetaching()
{
base.OnDetaching();
}
以下是上述溶液的使用的一個示例:
的App.xaml
<Application x:Class="WpfApplication2.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
StartupUri="MainWindow.xaml">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="ViewTemplates.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
</Application>
主窗口。XAML
<Window x:Class="WpfApplication2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication2"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<local:MainViewModel/>
</Window.DataContext>
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<ContentControl Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="3" Content="{Binding CurrentMediaElement}"/>
<Button Grid.Row="1" Grid.Column="0" Content="Set Source" Command="{Binding SetSourceCommand}"/>
<WrapPanel Grid.Row="1" Grid.Column="2">
<Button Grid.Row="1" Grid.Column="2" Content="Stop" Command="{Binding StopCommand}"/>
<Button Content="Focus" Command="{Binding FocusCommand}"/>
</WrapPanel>
<Button Grid.Row="1" Grid.Column="1" Content="Play" Command="{Binding PlayCommand}"/>
<TextBlock Grid.Row="2" Grid.Column="0" Text="{Binding CurrentMediaElement.Source}" TextWrapping="Wrap"/>
<Label Grid.Row="2" Grid.Column="1" Content="{Binding ElementName=SliderVolume, Path=Value}"/>
<Slider x:Name="SliderVolume" Value="{Binding CurrentMediaElement.Volume}" Grid.Row="2" Grid.Column="2" Minimum="0" Maximum="1" Orientation="Horizontal"/>
</Grid>
</Window>
MainViewModel.cs
public MainViewModel()
{
CurrentMediaElement = new MediaElementViewModel();
}
private MediaElementViewModel _currentMediaElement;
public MediaElementViewModel CurrentMediaElement
{
get { return _currentMediaElement; }
set
{
_currentMediaElement = value;
RaisePropertyChanged("CurrentMediaElement");
}
}
private RelayCommand _setSourceCommand;
public ICommand SetSourceCommand
{
get
{
return _setSourceCommand ??
(_setSourceCommand = new RelayCommand(SetSourceExecute));
}
}
private RelayCommand _playCommand;
public ICommand PlayCommand
{
get
{
return _playCommand ??
(_playCommand = new RelayCommand(PlayExecute));
}
}
private RelayCommand _stopCommand;
public ICommand StopCommand
{
get
{
return _stopCommand ??
(_stopCommand = new RelayCommand(StopExecute));
}
}
private RelayCommand _focusCommand;
public ICommand FocusCommand
{
get
{
return _focusCommand ??
(_focusCommand = new RelayCommand(FocusExecute));
}
}
/// <summary>
/// Invoked whenever focusing media element;
/// </summary>
private void FocusExecute()
{
bool isFocused = this.CurrentMediaElement.Focus();
}
/// <summary>
/// Invoked whenever setting a media source.
/// </summary>
private void SetSourceExecute()
{
// Assume the media file location is Debug/bin/Resources/
this.CurrentMediaElement.Source = new Uri(AppDomain.CurrentDomain.BaseDirectory + "Resources\\media.mp3");
}
/// <summary>
/// Invoked whenever playing media.
/// </summary>
private void PlayExecute()
{
this.CurrentMediaElement.Play();
}
/// <summary>
/// Invoked whenerver stopping media.
/// </summary>
private void StopExecute()
{
this.CurrentMediaElement.Stop();
}
正如你可以看到(在MainViewModel.cs),我從調用視圖模型視圖模型使用優先方法 「純」 查看方式。 (PlayExecute
,StopExecute
和FocusExecute
)。
如果你絕對需要這樣做,我建議使用MVVM Light Messenger類從虛擬機發送消息,在View代碼隱藏中接收消息並採取適當的操作。 *需要的情況非常少見*如果您只是想在視圖之間切換,那麼這種方法* *更好:https://rachel53461.wordpress.com/2011/05/28/switching- between-viewsusercontrols-using-mvvm。 – goobering
你想從你的Viewmodel調用視圖中的.close()方法嗎?如果是的話爲什麼不在你的虛擬機中創建一個事件並在你的視圖中訂閱這個事件 – blindmeis
@goobering感謝你的回答,MVVM Light Messenger確實是一個推薦的工具,不幸的是,這個工具以靜態方式發送消息_,這意味着如果我具有相同ViewModel的許多實例,這是我使用ViewModel-First_的原因之一,消息傳遞將導致重複,跨實例和錯誤結果。不小心,[我已經問過關於切換視圖](http://stackoverflow.com/questions/30232697/how-to-cache-dynamically-switched-views-by-viewmodel-first-approach-using-datate),並找到它爲我提供了比您提供的鏈接更好的解決方案。 – Eido95