【发布时间】:2018-06-23 02:26:23
【问题描述】:
我有一个自定义控件 (MediaPlayer),其中包含 2 个其他自定义控件、一个媒体播放器 (Host) 和一个控制栏 (UI)。
这个控件本身就很简单,只是将两者绑定在一起显示而已。
现在我遇到的第一个问题是我无法从 MediaPlayer 设置 Host 或 UI 属性,因此我在设计时复制了所有相关的属性并通过绑定将它们链接起来。这是实现这一目标的正确方法吗?这有点笨拙,但很有效。
<Style TargetType="{x:Type local:MediaPlayerWpf}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:MediaPlayerWpf}">
<Border Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<Grid>
<Grid x:Name="PART_HostGrid" Margin="0,0,0,46">
<!--Filled by SetPlayerHost -->
</Grid>
<local:PlayerControls x:Name="PART_MediaUI" Height="46" Width="Auto"
VerticalAlignment="Bottom" MouseFullscreen="{TemplateBinding MouseFullscreen}"
MousePause="{TemplateBinding MousePause}"
IsPlayPauseVisible="{Binding IsPlayPauseVisible, RelativeSource={RelativeSource TemplatedParent}}"
IsStopVisible="{TemplateBinding IsStopVisible}"
IsLoopVisible="{TemplateBinding IsLoopVisible}"
IsVolumeVisible="{TemplateBinding IsVolumeVisible}"
IsSpeedVisible="{TemplateBinding IsSpeedVisible}"
IsSeekBarVisible="{TemplateBinding IsSeekBarVisible}"
PositionDisplay="{TemplateBinding PositionDisplay}" />
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
这是一个通用媒体播放器的类。然后我有另一个从它派生的自定义控件,将其设置为使用特定的媒体播放器。 (有一个使用 MPV 视频播放器,另一个显示 VapourSynth 脚本输出)
派生类如下所示。
<Style TargetType="{x:Type local:VsMediaPlayer}" BasedOn="{StaticResource {x:Type ui:MediaPlayerWpf}}" />
现在的问题是我想将 Script 和 Path 属性公开为依赖属性,以便它们可以进行数据绑定。我不能采取与上述完全相同的方法,那我该怎么做呢?路径和脚本将绑定到的主机是在运行时在 OnApplyTemplate 中创建的。
我对如何使它工作有点困惑,我不确定上面的第一个代码是最好的解决方案。感谢您的指导。
我想一种选择是复制基本样式模板而不是从它继承,我可以在那里而不是在运行时启动 Host 类。还有其他选择吗?
编辑:在我的通用 MediaPlayer 类中这样声明主机属性,但我找不到从设计器设置其子属性 (Host.Source) 的方法。
public static DependencyProperty HostProperty = DependencyProperty.Register("Host", typeof(PlayerBase), typeof(MediaPlayerWpf),
new PropertyMetadata(null, OnHostChanged));
public PlayerBase Host { get => (PlayerBase)GetValue(HostProperty); set => SetValue(HostProperty, value); }
private static void OnHostChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) {
MediaPlayerWpf P = d as MediaPlayerWpf;
if (e.OldValue != null)
P.HostGrid.Children.Remove(e.OldValue as PlayerBase);
if (e.NewValue != null) {
P.HostGrid.Children.Add(e.NewValue as PlayerBase);
P.TemplateUI.PlayerHost = e.NewValue as PlayerBase;
}
}
编辑:这是 MediaPlayer 的 XAML 代码
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:EmergenceGuardian.MediaPlayerUI">
<Style TargetType="{x:Type local:MediaPlayerWpf}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:MediaPlayerWpf}">
<Border Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<Grid>
<ContentPresenter x:Name="PART_HostGrid" Margin="0,0,0,46"
Content="{TemplateBinding Content}" />
<local:PlayerControls x:Name="PART_MediaUI" Height="46" Width="Auto"
VerticalAlignment="Bottom" MouseFullscreen="{TemplateBinding MouseFullscreen}"
MousePause="{TemplateBinding MousePause}"
IsPlayPauseVisible="{Binding IsPlayPauseVisible, RelativeSource={RelativeSource TemplatedParent}}"
IsStopVisible="{TemplateBinding IsStopVisible}"
IsLoopVisible="{TemplateBinding IsLoopVisible}"
IsVolumeVisible="{TemplateBinding IsVolumeVisible}"
IsSpeedVisible="{TemplateBinding IsSpeedVisible}"
IsSeekBarVisible="{TemplateBinding IsSeekBarVisible}"
PositionDisplay="{TemplateBinding PositionDisplay}" />
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
将 x:FieldModifier="public" 添加到 PART_MediaUI 会引发“属性 FieldModifier 不存在于命名空间中”
解决方案!!!在处理了一些附加属性之后,我终于明白了它们是如何工作的,附加属性确实是正确的解决方案。这将允许我在父类上设置 UIProperties.IsVolumeVisible。我只需要为每个属性重复该代码。
public static class UIProperties {
// IsVolumeVisible
public static readonly DependencyProperty IsVolumeVisibleProperty = DependencyProperty.RegisterAttached("IsVolumeVisible", typeof(bool),
typeof(UIProperties), new UIPropertyMetadata(false, OnIsVolumeVisibleChanged));
public static bool GetIsVolumeVisible(DependencyObject obj) => (bool)obj.GetValue(IsVolumeVisibleProperty);
public static void SetIsVolumeVisible(DependencyObject obj, bool value) => obj.SetValue(IsVolumeVisibleProperty, value);
private static void OnIsVolumeVisibleChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) {
if (!(d is MediaPlayerWpf P))
return;
P.UI.IsVolumeVisible = (bool)e.NewValue;
}
}
【问题讨论】:
-
复制它很笨重,但它会起作用;但是您可以将该控件类型设置为本身的依赖属性并直接引用它。例如
local:PlayerControls.MediaPlayer和local:PlayerControls.ControlBar。另一种选择是将公共字段修饰符应用于控件。
标签: c# wpf wpf-controls