在Style中设置自定义依赖属性时绑定自身属性的问题
我定义了名为PropertyPanel的自定义依赖属性,通过Style为infiniteReservoir:PressureSource类型设置该属性的值。我希望将PressureSource自身的属性(如Name、ToolTip)绑定到PropertyPanel内的对应控件,尝试使用RelativeSource={RelativeSource TemplatedParent}进行绑定,但无显示效果。请问是否需要使用ControlTemplate声明目标类型,或有其他解决方法?
相关代码如下:
ResourceDictionary 代码
<Style TargetType="{x:Type infiniteReservoir:PressureSource}"> <Setter Property="PropertyPanel"> <Setter.Value> <ScrollViewer > <Grid DataContext="{Binding RelativeSource={RelativeSource TemplatedParent}}"> <Grid.ColumnDefinitions> <ColumnDefinition/> <ColumnDefinition/> </Grid.ColumnDefinitions> <Grid.RowDefinitions> <RowDefinition Height="30"/> <RowDefinition Height="30"/> <RowDefinition Height="30"/> <RowDefinition Height="30"/> <RowDefinition Height="30"/> <RowDefinition Height="30"/> </Grid.RowDefinitions> <TextBlock Text="Pressure:" Grid.Row="0"/> <Grid Grid.Column="1"> <Grid.ColumnDefinitions> <ColumnDefinition Width="2*"/> <ColumnDefinition Width="*"/> </Grid.ColumnDefinitions> <TextBox/> <ComboBox Grid.Column="1"/> </Grid> <TextBlock Text="Temperature:" Grid.Row="1"/> <Grid Grid.Row="1" Grid.Column="1"> <Grid.ColumnDefinitions> <ColumnDefinition Width="2*"/> <ColumnDefinition Width="*"/> </Grid.ColumnDefinitions> <TextBox/> <ComboBox Grid.Column="1"/> </Grid> <TextBlock Text="Label:" Grid.Row="2"/> <TextBox Grid.Row="2" Grid.Column="1" Text="{Binding Name}"/> <TextBlock Text="Media:" Grid.Row="3"/> <TextBlock Grid.Row="3" Grid.Column="1" Text="{Binding ToolTip}"/> <TextBlock Text="Fluid Zone ID:" Grid.Row="4"/> <TextBlock Grid.Row="4" Grid.Column="1"/> <TextBlock Text="Port Name:" Grid.Row="5"/> <TextBox Grid.Row="5" Grid.Column="1"/> </Grid> </ScrollViewer> </Setter.Value> </Setter> </Style>
Model 代码
#region PropertyPanel public ScrollViewer PropertyPanel { get { return (ScrollViewer)GetValue(PropertyPanelProperty); } set { SetValue(PropertyPanelProperty, value); } } public static readonly DependencyProperty PropertyPanelProperty = DependencyProperty.Register("PropertyPanel", typeof(ScrollViewer), typeof(DesignerItem)); #endregion
你遇到的问题根源很明确:RelativeSource TemplatedParent只在ControlTemplate的内部有效,而你现在是直接把ScrollViewer作为PropertyPanel属性的Setter值,它并不是ControlTemplate的一部分,所以这个绑定根本找不到对应的父元素。
这里有两种可行的解决思路:
方法1:使用ControlTemplate(推荐)
如果PressureSource是Control的子类,这是最规范的解决方案。通过ControlTemplate定义它的可视化结构后,TemplatedParent就能正确指向PressureSource实例。修改方式如下:
- 把原来的
ScrollViewer及内部结构移到ControlTemplate中 - 在Style中设置
Template属性,若仍需保留PropertyPanel属性,可以通过绑定关联到内部的ScrollViewer
示例代码片段:
<Style TargetType="{x:Type infiniteReservoir:PressureSource}"> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type infiniteReservoir:PressureSource}"> <ScrollViewer x:Name="PropertyPanelHost"> <Grid DataContext="{Binding RelativeSource={RelativeSource TemplatedParent}}"> <!-- 原有Grid内容,此时Name、ToolTip的绑定会正常生效 --> <TextBox Grid.Row="2" Grid.Column="1" Text="{Binding Name}"/> <TextBlock Grid.Row="3" Grid.Column="1" Text="{Binding ToolTip}"/> </Grid> </ScrollViewer> </ControlTemplate> </Setter.Value> </Setter> <!-- 绑定PropertyPanel属性到内部的ScrollViewer --> <Setter Property="PropertyPanel" Value="{Binding ElementName=PropertyPanelHost, RelativeSource={RelativeSource TemplatedParent}}"/> </Style>
方法2:直接绑定到PressureSource实例(无需ControlTemplate)
如果不想重构为ControlTemplate,可以在设置PropertyPanel内容时,显式把DataContext绑定到当前的PressureSource对象。因为Style的Setter作用于每个PressureSource实例,所以用RelativeSource FindAncestor就能定位到目标:
修改ResourceDictionary中的Grid DataContext:
<ScrollViewer> <Grid DataContext="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type infiniteReservoir:PressureSource}}}"> <!-- 这里直接绑定Name、ToolTip即可,DataContext已指向PressureSource --> <TextBox Grid.Row="2" Grid.Column="1" Text="{Binding Name}"/> <TextBlock Grid.Row="3" Grid.Column="1" Text="{Binding ToolTip}"/> <!-- 其余原有控件保持不变 --> </Grid> </ScrollViewer>
另外要注意:你的PropertyPanel依赖属性定义在DesignerItem类上,需要确保PressureSource是DesignerItem的子类,否则这个Setter会无法生效;如果不是子类,建议把PropertyPanelProperty移到PressureSource类中。
内容的提问来源于stack exchange,提问作者Yekta Mirkan

