具有自己的数据上下文和外部依赖项属性的 WPF 用户控件



我正在尝试在我正在处理的解决方案中创建一个简单的 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,但将改用硬编码的 DataContext。


我的建议始终是永远不要对用户控件中的DataContext属性进行硬编码。它打破了为 UI 和数据具有单独层的整个 WPF 设计模式。

要么构建

一个专门用于特定模型的用户控件,要么构建用作DataContext的视图模型,例如:

<!-- 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}" />

使代码工作的最简单方法可能是

  • 将 ViewModel 创建为私有属性,而不是将其辅助到 UserControl.DataContext
  • 将用户控件中顶级子项的数据上下文绑定或设置为私有属性(在本例中为 StackPanel)
  • 调整MediaElement的绑定,使其从自定义依赖项属性读取,而不是从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

相关内容

  • 没有找到相关文章

最新更新