[英]Wpf UserControl with its own data context and external dependency property
我正在尝试创建一个简单的AudioPlayer控件,以便在我正在使用的解决方案中进行多次重用。 我在网上的各种帖子和博客中看到了无数示例,并从中创建了带有四个按钮的小控件。
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>
在后台有相当简单的代码;
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
我的问题很简单是这个。 从代码中可以看到,目前正在播放的声音已经过硬编码。 我想知道的是,有可能为此控件创建一个依赖项属性(我想它应该是字符串类型,代表声音文件的路径,但我不确定),以便在控件处于在其他控件或窗口中创建的它们的视图模型可以向其传递声音属性(如果有道理!)。 如果有可能,我应该根据所示的代码片段在何处创建它?
非常感谢
您可以创建一个DP,但是它不能像用户期望的那样工作。
例如,如果用户要写
<local:AudioPlayer Media="{Binding SomeString}" />
然后WPF尝试设置Media = DataContext.SomeString
但是,由于在构造函数中已对DataContext = New AudioPlayerViewModel
进行了硬编码,因此绑定很可能会失败,因为用户希望其继承的DataContext被UserControl使用,但是将使用硬编码的DataContext。
始终建议不要在UserControl内部对DataContext
属性进行硬编码。 它打破了将UI和数据具有单独的图层的整个WPF设计模式。
构建专门用于与用作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>
或在期望DataContext
绝对可以是任何东西的情况下进行构建,并且DependencyProperites将用于为控件提供所需的数据:
<!-- DataContext property can be anything, as long as it as the property MyString -->
<local:AudioPlayer Media="{Binding MyString}" />
使代码正常工作的最简单方法可能是
MediaElement
的绑定以从自定义DependencyProperty而不是从StackPanel.DataContext
读取 像这样的东西:
<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.