繁体   English   中英

WPF UserControl具有自己的数据上下文和外部依赖项属性

[英]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}" />

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

  • 创建ViewModel作为私有属性,而不是将其辅助到UserControl.DataContext
  • 将UserControl中顶级子级的DataContext绑定或设置为私有属性(在您的情况下为StackPanel)
  • 调整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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM