[英]Wpf UserControl with its own data context and external dependency property
I'm trying to create a simple AudioPlayer control multiple reuse in a solution I'm working on. 我正在尝试创建一个简单的AudioPlayer控件,以便在我正在使用的解决方案中进行多次重用。 I have seen numerous example in various posts and blogs around the net and from those have created a small control with four buttons. 我在网上的各种帖子和博客中看到了无数示例,并从中创建了带有四个按钮的小控件。
The xaml is defined thus: xaml的定义如下:
<UserControl x:Class="AudioPlayer"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="30" d:DesignWidth="150">
<StackPanel Orientation="Horizontal">
<StackPanel.Resources>
<Style TargetType="{x:Type Button}">
<Setter Property="Margin" Value="10,0,0,0" />
</Style>
</StackPanel.Resources>
<MediaElement Name="media" Source="{Binding Source}" LoadedBehavior="{Binding LoadedBehavior}"/>
<Button Width="24" Height="24" x:Name="Repeat" Background="Transparent" BorderBrush="Transparent">
<Image Source="Images/button_blue_repeat.png" ToolTip="Repeat"/>
</Button>
<Button Width="24" Height="24" x:Name="Play" Background="Transparent" BorderBrush="Transparent">
<Image Source="Images/button_blue_play.png" ToolTip="Play"/>
</Button>
<Button Width="24" Height="24" x:Name="Pause" Background="Transparent" BorderBrush="Transparent">
<Image Source="Images/button_blue_pause.png" ToolTip="Pause"/>
</Button>
<Button Width="24" Height="24" x:Name="Stop" Background="Transparent" BorderBrush="Transparent">
<Image Source="Images/button_blue_stop.png" ToolTip="Stop"/>
</Button>
</StackPanel>
With fairly simple code in the background; 在后台有相当简单的代码;
Public Class AudioPlayer
Public Sub New()
InitializeComponent()
DataContext = New AudioPlayerViewModel With {.MediaElement = media, .Source = "bag1.mp3", .LoadedBehavior = MediaState.Manual, .CanCommandExecute = True}
End Sub
End Class
Public Class AudioPlayerViewModel
Inherits DependencyObject
Public Sub New()
Me.MediaCommand = New MediaElementCommand(Me)
End Sub
Public Property MediaElement() As MediaElement
Public Property Source() As String
Public Property LoadedBehavior() As MediaState
Public Property CanCommandExecute() As Boolean
Public Property MediaCommand() As ICommand
End Class
Public Class MediaElementCommand
Implements ICommand
Private vm As AudioPlayerViewModel
Public Sub New(ByVal vm As AudioPlayerViewModel)
Me.vm = vm
End Sub
Public Function CanExecute(ByVal parameter As Object) As Boolean Implements ICommand.CanExecute
Return vm.CanCommandExecute
End Function
Public Custom Event CanExecuteChanged As EventHandler Implements ICommand.CanExecuteChanged
AddHandler(ByVal value As EventHandler)
AddHandler CommandManager.RequerySuggested, value
End AddHandler
RemoveHandler(ByVal value As EventHandler)
RemoveHandler CommandManager.RequerySuggested, value
End RemoveHandler
RaiseEvent(ByVal sender As System.Object, ByVal e As System.EventArgs)
End RaiseEvent
End Event
Public Sub Execute(ByVal parameter As Object) Implements ICommand.Execute
Dim action As String = DirectCast(parameter, String)
Select Case action.ToLower()
Case "play"
vm.MediaElement.Position = TimeSpan.Zero
vm.MediaElement.Play()
Case "stop"
vm.MediaElement.Stop()
Case "pause"
vm.MediaElement.Pause()
Case "resume"
vm.MediaElement.Play()
Case Else
Throw New NotSupportedException(String.Format("Unknown media action {0}", action))
End Select
End Sub
End Class
My question quite simply is this. 我的问题很简单是这个。 From the code you can see that at present the sound that is being played is hard coded. 从代码中可以看到,目前正在播放的声音已经过硬编码。 What I would like to know is wheteher it would be possible to create a dependency property for this control (I presume it would be of type string to represent a path to a sound file but I'm not sure) so that when the control is created in other controls or windows their viewmodels can pass a sound property to it (if that makes sense!). 我想知道的是,有可能为此控件创建一个依赖项属性(我想它应该是字符串类型,代表声音文件的路径,但我不确定),以便在控件处于在其他控件或窗口中创建的它们的视图模型可以向其传递声音属性(如果有道理!)。 If it is possible where should I create it in respect of the code snippets shown? 如果有可能,我应该根据所示的代码片段在何处创建它?
Many thanks 非常感谢
You could create a DP, but it would not work the way users would expect. 您可以创建一个DP,但是它不能像用户期望的那样工作。
For example, if the user were to write 例如,如果用户要写
<local:AudioPlayer Media="{Binding SomeString}" />
Then WPF tries to set Media = DataContext.SomeString
然后WPF尝试设置Media = DataContext.SomeString
But since you have hardcoded DataContext = New AudioPlayerViewModel
in the constructor, then the binding will most likely fail because users will be expecting their inherited DataContext to be used by the UserControl, but the hardcoded DataContext will be used instead. 但是,由于在构造函数中已对DataContext = New AudioPlayerViewModel
进行了硬编码,因此绑定很可能会失败,因为用户希望其继承的DataContext被UserControl使用,但是将使用硬编码的DataContext。
It is always my advice to never hardcode the DataContext
property inside of a UserControl. 始终建议不要在UserControl内部对DataContext
属性进行硬编码。 It breaks the entire WPF design pattern of having separate layers for UI and Data. 它打破了将UI和数据具有单独的图层的整个WPF设计模式。
Either build a UserControl specifically for use with a specific Model or ViewModel being used as the DataContext
, such as this : 构建专门用于与用作DataContext
的特定Model或ViewModel一起使用的UserControl,例如:
<!-- Draw anything of type AudioPlayerViewModel with control AudioPlayer -->
<!-- DataContext will automatically set to the AudioPlayerViewModel -->
<DataTemplate DataType="{x:Type local:AudioPlayerViewModel}}">
<local:AudioPlayer />
</DataTemplate>
Or build it with the expectation that the DataContext
can be absolutely anything, and DependencyProperites will be used to give the control the data it needs : 或在期望DataContext
绝对可以是任何东西的情况下进行构建,并且DependencyProperites将用于为控件提供所需的数据:
<!-- DataContext property can be anything, as long as it as the property MyString -->
<local:AudioPlayer Media="{Binding MyString}" />
The easiest way to get your code to work would probably be 使代码正常工作的最简单方法可能是
MediaElement
to read from a custom DependencyProperty instead of from StackPanel.DataContext
调整MediaElement
的绑定以从自定义DependencyProperty而不是从StackPanel.DataContext
读取 Something like this : 像这样的东西:
<UserControl x:Name="MyAudioPlayer" ...>
<StackPanel x:Name="AudioPlayerRoot">
...
<MediaElement Source="{Binding ElementName=MyAudioPlayer, Path=MediaDependecyProperty}" ... />
...
</StackPanel>
</UserControl>
Public Sub New()
InitializeComponent()
AudioPlayerRoot.DataContext = New AudioPlayerViewModel ...
End Sub
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.