我有一个自定义控件(MediaPlayer),它包含另外两个自定义控件,一个媒体播放器(主机)和一个控制条(UI)。
这个控件本身非常简单,它只是将这两个控件绑定在一起以供显示。
现在我遇到的第一个问题是我无法从MediaPlayer中设置主机或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}}" />现在的问题是,我希望将脚本和路径属性公开为依赖属性,这样它们就可以被数据库化了。我不能采取与上面完全相同的方法,那么我如何才能做到呢?主机、路径和脚本将绑定在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;
}
}发布于 2018-06-23 20:14:55
我找到了一个部分的解决方案。我不是从Control继承MediaPlayer,而是从ContentControl继承。
在MediaPlayer Generic.xaml中,我在UI控件的上方显示类似于此的内容
<ContentPresenter x:Name="PART_HostGrid" Margin="0,0,0,46" Content="{TemplateBinding Content}" />覆盖属性元数据,以确保内容类型为PlayerBase,并将内容引用传递给UI控件
static MediaPlayerWpf() {
ContentProperty.OverrideMetadata(typeof(MediaPlayerWpf), new FrameworkPropertyMetadata(ContentChanged, CoerceContent));
}
public override void OnApplyTemplate() {
base.OnApplyTemplate();
UI = TemplateUI;
UI.PlayerHost = Content as PlayerBase;
}
private static void ContentChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) {
MediaPlayerWpf P = d as MediaPlayerWpf;
if (P.TemplateUI != null)
P.TemplateUI.PlayerHost = e.NewValue as PlayerBase;
}
private static object CoerceContent(DependencyObject d, object baseValue) {
return baseValue as PlayerBase;
}然后我可以像这样用它
<MediaPlayerUI:MediaPlayerWpf x:Name="Player" IsVolumeVisible="False" IsSpeedVisible="False" IsLoopVisible="False" PositionDisplay="Seconds">
<VapourSynthUI:VsMediaPlayerHost x:Name="PlayerHost" />
</MediaPlayerUI:MediaPlayerWpf>优点是我不再需要从MediaPlayerWpf继承,因此需要管理的控件更少。
但是,我仍然需要复制UI属性才能将它们公开给设计人员,还没有找到以任何其他方式访问它们的方法。
在x:FieldModifier=中设置Generic.xaml“public”将导致“XML命名空间中不存在属性'FieldModifier‘”
UI被公开为如下所示的依赖项属性。设计者允许设置UI=“.”但UI.IsVolumeVisible="false“也不识别< local:UI>。是否有方法从自定义控件中公开它?
public static DependencyPropertyKey UIPropertyKey = DependencyProperty.RegisterReadOnly("UI", typeof(PlayerControls), typeof(MediaPlayerWpf), new PropertyMetadata());
public static DependencyProperty UIProperty = UIPropertyKey.DependencyProperty;
public PlayerControls UI { get => (PlayerControls)GetValue(UIProperty); private set => SetValue(UIPropertyKey, value); }发布于 2018-06-23 04:33:59
我在上面给出了一个关于如何使用DependencyProperty并将其设置为其他类型的评论。这一切都很好,但可能对您所需的东西来说太过分了。只要使用x:FieldModifier="public"就可以得到你想要的东西。
下面是一个例子:
我制作了3个用户控件并拥有了我的MainWindow。用户控件为MainControl、SubControlA、SubControlB。
在MainControl中,我首先给控件一个逻辑名称,然后将FieldModifier给公共。
<UserControl x:Class="Question_Answer_WPF_App.MainControl"
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"
xmlns:local="clr-namespace:Question_Answer_WPF_App"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<StackPanel>
<local:SubControlA x:Name="SubControlA" x:FieldModifier="public"/>
<local:SubControlB x:Name="SubControlB" x:FieldModifier="public"/>
</StackPanel>
</UserControl>然后,我将该MainControl放在我的MainWindow中,并按如下方式使用:
<Window x:Class="Question_Answer_WPF_App.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Question_Answer_WPF_App"
mc:Ignorable="d"
Title="MainWindow"
Height="450"
Width="800">
<Grid>
<local:MainControl>
<local:SubControlA>
<TextBlock Text="I'm in SubControlA" />
</local:SubControlA>
</local:MainControl>
</Grid>
</Window>希望这能有所帮助。这个想法是,您也可以从这些控件引用DependencyProperty,比如Visibility等(或者您在问题中使用的任何东西)。
这只是一个例子,因为我不建议这么便宜。
好的,为了跟进你下面的评论/问题的答案,让我解释一下它是如何工作的。首先,SubControlA和SubControlB只是我为使示例工作而创建的两个空UserControls。
在xaml中,括号之间的任何内容都会在这一点上被初始化。我们使用名称空间/类型名称来锁定属性,括号之间的任何内容都会传递给该属性的setter。
想想这个MainWindow..。我所做的就是在其中放置一个自定义UserControl,它在xaml中看起来就像这样
<Window x:Class="Question_Answer_WPF_App.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Question_Answer_WPF_App"
mc:Ignorable="d"
Title="MainWindow"
Height="450"
Width="800">
<local:ExampleControl />
</Window>…看起来就像在跑步的时候

现在来看一下定制的ExampleControl,因为到目前为止没有什么大不了的。
<UserControl x:Class="Question_Answer_WPF_App.ExampleControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:System="clr-namespace:System;assembly=mscorlib"
xmlns:Media="clr-namespace:System.Windows.Media;assembly=PresentationCore"
xmlns:Windows="clr-namespace:System.Windows;assembly=PresentationCore"
mc:Ignorable="d"
d:DesignHeight="450"
d:DesignWidth="800">
<StackPanel>
<Button Visibility="Visible"
Height="50"
Background="Blue"
Content="Button A"/>
<Button>
<Button.Visibility>
<Windows:Visibility> Visible </Windows:Visibility>
</Button.Visibility>
<Button.Height>
<System:Double> 50 </System:Double>
</Button.Height>
<Button.Background>
<Media:SolidColorBrush>
<Media:SolidColorBrush.Color>
<Media:Color>
<Media:Color.R> 0 </Media:Color.R>
<Media:Color.G> 0 </Media:Color.G>
<Media:Color.B> 255 </Media:Color.B>
<Media:Color.A> 255 </Media:Color.A>
</Media:Color>
</Media:SolidColorBrush.Color>
</Media:SolidColorBrush>
</Button.Background>
<Button.Content> Button B </Button.Content>
</Button>
</StackPanel>
</UserControl>在这个ExampleControl中,我有两个相同的按钮,一个是按钮A,另一个是按钮B。
请注意,我是如何通过属性名称直接引用第一个按钮中的属性的(这主要是使用的),但是我通过名称空间/类型引用了第二个按钮。他们的结果是一样的。
还请注意,对于某些类型,我必须包含对命名空间的引用,如:
xmlns:System="clr-namespace:System;assembly=mscorlib"
xmlns:Media="clr-namespace:System.Windows.Media;assembly=PresentationCore"
xmlns:Windows="clr-namespace:System.Windows;assembly=PresentationCore"XAML有一个内置的解析器,它接受您发送的字符串,并试图将其指定为属性所需的类型。请参阅枚举(可见性: System.Windows.Visibility)、原语(高度: System.Double)和惟一对象(背景: System.Windows.Media.Brush)的工作原理。
还请注意,Background是Brush类型,可以是从Brush派生的任何类型。在这个例子中,我使用了一个SolidColorBrush,它的基础是Brush。
然而,我也在Background中更进一步。注意,我不仅分配SolidColorBrush,还分配SolidColorBrush的Color属性。
花点时间来理解xaml是如何解析和使用这些特性的,我相信它会回答您的问题:在这个答案的开头,我是如何从SubControlA和SubControlB中引用SubControlA和SubControlB的。
https://stackoverflow.com/questions/50997442
复制相似问题