自定义控件和依赖项 属性 继承
Custom Control and Dependency Property Inheritance
我有一个自定义控件 (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>
这是一个 class 通用媒体播放器。然后我有另一个派生自它的自定义控件,将其设置为使用特定的媒体播放器。 (有一个使用 MPV 视频播放器,另一个显示 VapourSynth 脚本输出)
派生的 class 看起来像这样。
<Style TargetType="{x:Type local:VsMediaPlayer}" BasedOn="{StaticResource {x:Type ui:MediaPlayerWpf}}" />
现在的问题是我想将 Script 和 Path 属性公开为依赖属性,以便它们可以进行数据绑定。我不能采取与上面完全相同的方法,那我该怎么做呢?路径和脚本将绑定到的主机是在 运行 时在 OnApplyTemplate 中创建的。
我对如何让它工作有点困惑,我不确定上面的第一个代码是最好的解决方案。感谢您的指导。
我想一个选择是复制基本样式模板而不是从它继承,并且我可以在那里而不是在 运行 时启动主机 class。还有其他选择吗?
编辑:主机 属性 在我的通用 MediaPlayer class 中是这样声明的,但我无法从设计师。
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 会引发 "The attribute FieldModifier does not exist in namespace"
解决方案!!!在使用了一些附加属性之后,我终于明白了它们是如何工作的,附加属性确实是正确的解决方案。这将允许我在父 class 上设置 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;
}
}
我在上面发表了关于如何使用 DependencyProperty
并将其设置为该类型等的评论。这一切都很好,但对于您的需要可能有点矫枉过正。只需使用 x:FieldModifier="public"
即可获得您要查找的内容。
这是一个例子:
我制作了 3 个用户控件并拥有我的 MainWindow
。用户控件是 MainControl
、SubControlA
、SubControlB
.
在MainControl
中我首先给控件一个逻辑名称然后FieldModifier
到public.
<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
s 也像 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
中,我有两个相同的按钮,除了一个是 Button A 和另一个 Button 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)。
另请注意,作为 Brush
类型的 Background
可以是派生自 Brush
的任何类型。在示例中,我使用了 SolidColorBrush
,其基数为 Brush
。
但是;我也在Background
中更进一步。请注意,我不仅分配了 SolidColorBrush
,而且还分配了 SolidColorBrush
的 Color
属性。
花点时间了解 xaml
如何解析和使用这些功能,我相信它会回答你关于我如何引用 SubControlA
和 SubControlB
的问题我的 MainControl
在这个答案的开头。
我找到了部分解决方案。我没有从 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 属性以将它们公开给设计者,还没有找到以任何其他方式访问它们的方法。
在 Generic.xaml 中设置 x:FieldModifier="public" 会导致 "The attribute 'FieldModifier' does not exist in XML namespace"
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); }
我有一个自定义控件 (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>
这是一个 class 通用媒体播放器。然后我有另一个派生自它的自定义控件,将其设置为使用特定的媒体播放器。 (有一个使用 MPV 视频播放器,另一个显示 VapourSynth 脚本输出)
派生的 class 看起来像这样。
<Style TargetType="{x:Type local:VsMediaPlayer}" BasedOn="{StaticResource {x:Type ui:MediaPlayerWpf}}" />
现在的问题是我想将 Script 和 Path 属性公开为依赖属性,以便它们可以进行数据绑定。我不能采取与上面完全相同的方法,那我该怎么做呢?路径和脚本将绑定到的主机是在 运行 时在 OnApplyTemplate 中创建的。
我对如何让它工作有点困惑,我不确定上面的第一个代码是最好的解决方案。感谢您的指导。
我想一个选择是复制基本样式模板而不是从它继承,并且我可以在那里而不是在 运行 时启动主机 class。还有其他选择吗?
编辑:主机 属性 在我的通用 MediaPlayer class 中是这样声明的,但我无法从设计师。
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 会引发 "The attribute FieldModifier does not exist in namespace"
解决方案!!!在使用了一些附加属性之后,我终于明白了它们是如何工作的,附加属性确实是正确的解决方案。这将允许我在父 class 上设置 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;
}
}
我在上面发表了关于如何使用 DependencyProperty
并将其设置为该类型等的评论。这一切都很好,但对于您的需要可能有点矫枉过正。只需使用 x:FieldModifier="public"
即可获得您要查找的内容。
这是一个例子:
我制作了 3 个用户控件并拥有我的 MainWindow
。用户控件是 MainControl
、SubControlA
、SubControlB
.
在MainControl
中我首先给控件一个逻辑名称然后FieldModifier
到public.
<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
s 也像 Visibility
等(或者你在问题中使用的任何东西。)
这只是一个例子,因为我不建议以这么便宜的价格来做。
好的,为了跟进下面您的评论/问题的答案,让我更深入地解释一下它是如何工作的。首先,SubControlA
和 SubControlB
只是两个空的 UserControls
,我为了让示例工作而制作的。
在 xaml
中,括号之间的任何内容都会在该点进行初始化。我们使用命名空间/类型名称来定位 属性,括号之间的任何内容都指向 属性.
考虑这个 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
中,我有两个相同的按钮,除了一个是 Button A 和另一个 Button 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)。
另请注意,作为 Brush
类型的 Background
可以是派生自 Brush
的任何类型。在示例中,我使用了 SolidColorBrush
,其基数为 Brush
。
但是;我也在Background
中更进一步。请注意,我不仅分配了 SolidColorBrush
,而且还分配了 SolidColorBrush
的 Color
属性。
花点时间了解 xaml
如何解析和使用这些功能,我相信它会回答你关于我如何引用 SubControlA
和 SubControlB
的问题我的 MainControl
在这个答案的开头。
我找到了部分解决方案。我没有从 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 属性以将它们公开给设计者,还没有找到以任何其他方式访问它们的方法。
在 Generic.xaml 中设置 x:FieldModifier="public" 会导致 "The attribute 'FieldModifier' does not exist in XML namespace"
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); }