WPF 如何将附加 属性 绑定到依赖项 属性
WPF how to bind attached property to dependency property
我正在自定义 DataGridCells,使其在角落有一个带有工具提示的小三角形。此三角形应仅在工具提示有内容时显示,并且工具提示的内容将来自 DataGrid 的 ItemsSource。
DataGrid 的角上有三角形:
现在我创建了一个名为 Hint 的附加 属性,它将绑定到具有工具提示内容的 ItemsSource 的 属性。
public class HintAttachedProperty
{
public static void SetHint(DependencyObject obj, string text)
{
obj.SetValue(HintProperty, text);
}
public static string GetHint(DependencyObject obj)
{
return obj.GetValue(HintProperty)?.ToString() ?? string.Empty;
}
public static readonly DependencyProperty HintProperty =
DependencyProperty.RegisterAttached("Hint", typeof(string), typeof(HintAttachedProperty), new FrameworkPropertyMetadata(null));
}
DataGrid 的 xaml,现在附加的 属性 提示设置为值“A test”:
<local:CustomDataGrid x:Name="datagrid"
AutoGenerateColumns="False"
ItemsSource="{Binding ItemsCollection}"
ClipboardCopyMode="IncludeHeader"
EnableRowsMove="True"
AlternatingRowBackground="LightBlue"
AlternationCount="2">
<DataGrid.Columns>
<DataGridCheckBoxColumn Header="IsChecked" Binding="{Binding IsChecked}"/>
<DataGridTextColumn x:Name="Test"
local:IsHighLightedAttachedProperty.IsHighLighted="True"
local:HintAttachedProperty.Hint="A test"
Header="ExperienceInMonth"
Binding="{Binding ExperienceInMonth}">
</DataGridTextColumn>
<DataGridTemplateColumn Header="References">
...
</DataGridTemplateColumn>
<DataGridTextColumn Header="EmployeeName" Binding="{Binding EmployeeName}"/>
<DataGridTextColumn Header="EmployeeAge" Binding="{Binding EmployeeAge}" />
</DataGrid.Columns>
</local:CustomDataGrid>
这里是 DataGridCell 的样式,它控制三角形的可见性并且应该将附加的 属性 绑定到工具提示的内容:
<Style TargetType="{x:Type DataGridCell}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type DataGridCell}">
<Border x:Name="borderSides" BorderThickness="2,0,2,0" Background="Transparent" BorderBrush="Transparent" SnapsToDevicePixels="True">
<Border x:Name="borderTopBottom" BorderThickness="0,2,0,2" Background="Transparent" BorderBrush="Transparent" SnapsToDevicePixels="True">
<Grid>
<Grid x:Name="grid">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<ContentPresenter Grid.Column="0" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
<Polygon x:Name="pol" ToolTip="{Binding RelativeSource={RelativeSource Self}, Converter={StaticResource HintConverter}}" Grid.Column="1" HorizontalAlignment="Right" Points="5,0 10,0, 10,5" Stroke="Black" Fill="Black" >
</Polygon>
</Grid>
</Grid>
</Border>
</Border>
<ControlTemplate.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Converter={StaticResource HintConverter}}" Value="{x:Static sys:String.Empty}">
<Setter TargetName="pol" Property="Visibility" Value="Collapsed"/>
</DataTrigger>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Converter={StaticResource HintConverter}}" Value="{x:Null}">
<Setter TargetName="pol" Property="Visibility" Value="Collapsed"/>
</DataTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
在绑定中 {Binding RelativeSource={RelativeSource Self}, Converter={StaticResource HintConverter}} 我正在使用 DataGridCell 内容的转换器来获取值附加的 属性,因为当我做 {Binding path=(local:HintAttachedProperty.Hint)} 它总是返回 null。
转换器代码:
public class HintConverters : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
DataGridCell dgc = value as DataGridCell;
if (dgc != null)
{
DataGridColumn col = dgc.Column;
if (col != null)
{
var hint = HintAttachedProperty.GetHint(col);
return hint;
}
}
return "";
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
调试时,我可以在转换器中看到值是“A 测试”,因此没有隐藏三角形,但是当我将光标放在工具提示上时,工具提示是空的:
提示值:
工具提示为空:
另一方面,如果我直接绑定到样式中 ItemsSource 的 属性,例如 Employee Name,ToolTip="{Binding EmployeeName}" 完美运行:
如何将 属性 的值绑定并附加到 属性?这可能吗?也许我应该换个方向?
感谢您的关注
转换器是多余的,您只需更正绑定即可使其正常工作。
在 Polygon
中您需要找到 DataGridCell
祖先,而不是 Self
,因为那是多边形。从那里您需要访问 Column
,因为那是 Hint
所附加的内容。
<Polygon x:Name="pol" ToolTip="{Binding Column.(local:HintAttachedProperty.Hint), RelativeSource={RelativeSource AncestorType={x:Type DataGridCell}}}" Grid.Column="1" HorizontalAlignment="Right" Points="5,0 10,0, 10,5" Stroke="Black" Fill="Black"/>
在你的触发器中,相对来源是正确的,因为在这种情况下 Self
是 DataGridCell
,但如上所述你需要访问 Column
其中 Hint
附上。
<DataTrigger Binding="{Binding Column.(local:HintAttachedProperty.Hint), RelativeSource={RelativeSource Self}}" Value="{x:Static system:String.Empty}">
<Setter TargetName="pol" Property="Visibility" Value="Collapsed"/>
</DataTrigger>
<DataTrigger Binding="{Binding Column.(local:HintAttachedProperty.Hint), RelativeSource={RelativeSource Self}}" Value="{x:Null}">
<Setter TargetName="pol" Property="Visibility" Value="Collapsed"/>
</DataTrigger>
更新您的意见。在 当前项的数据上下文 或数据网格数据上下文上绑定 属性 不会以这种方式工作,因为列不是可视化树的一部分,因为列只是定义应如何构建此列中的单元格。
我将向您展示当前项目的数据上下文的解决方法。绑定到父 DataGrid
的数据上下文的工作方式不同。您可以通过 Style
将 Hint
属性 附加到实际显示在单元格内的元素。在样式中,您可以访问当前项目的数据上下文。
如果您有一个 read-only 列,您只需创建一个 ElementStyle
。如果你有一个 editbale 列,你想在其中显示多边形和工具提示,你还需要一个 EditingElementStyle
.
<DataGridTextColumn Header="EmployeeName" Binding="{Binding EmployeeName}">
<DataGridTextColumn.ElementStyle>
<Style TargetType="{x:Type TextBlock}" BasedOn="{StaticResource {x:Type TextBlock}}">
<Setter Property="local:HintAttachedProperty.Hint" Value="{Binding EmployeeName}"/>
</Style>
</DataGridTextColumn.ElementStyle>
<DataGridTextColumn.EditingElementStyle>
<Style TargetType="{x:Type TextBox}" BasedOn="{StaticResource {x:Type TextBox}}">
<Setter Property="local:HintAttachedProperty.Hint" Value="{Binding EmployeeName}"/>
</Style>
</DataGridTextColumn.EditingElementStyle>
</DataGridTextColumn>
调整绑定以使用 Content
属性 而不是 Column
来访问已附加 Hint
[= 的单元格模板中的顶级元素62=] 到,例如TextBlock
.
<Polygon x:Name="pol" ToolTip="{Binding Content.(local:HintAttachedProperty.Hint), RelativeSource={RelativeSource AncestorType={x:Type DataGridCell}}}" Grid.Column="1" HorizontalAlignment="Right" Points="5,0 10,0, 10,5" Stroke="Black" Fill="Black" >
<DataTrigger Binding="{Binding Content.(local:HintAttachedProperty.Hint), RelativeSource={RelativeSource Self}}" Value="{x:Static system:String.Empty}">
<Setter TargetName="pol" Property="Visibility" Value="Collapsed"/>
</DataTrigger>
<DataTrigger Binding="{Binding Content.(local:HintAttachedProperty.Hint), RelativeSource={RelativeSource Self}}" Value="{x:Null}">
<Setter TargetName="pol" Property="Visibility" Value="Collapsed"/>
</DataTrigger>
回顾一下,这里的主要问题是您无法从列访问数据上下文。
覆盖单元格 ControlTemplate
的另一种方法是创建 DataGridTemplateColumn
s,在其中为每一列创建自定义 DataTemplate
,其中包含箭头多边形和工具提示.
然后您可以轻松附加和绑定您的 Hint
属性,因为您可以访问当前项目的所有控件和数据上下文。此解决方案的缺点是您必须为每一列创建自定义数据模板并在各处复制箭头和工具提示 XAML。
我正在自定义 DataGridCells,使其在角落有一个带有工具提示的小三角形。此三角形应仅在工具提示有内容时显示,并且工具提示的内容将来自 DataGrid 的 ItemsSource。
DataGrid 的角上有三角形:
现在我创建了一个名为 Hint 的附加 属性,它将绑定到具有工具提示内容的 ItemsSource 的 属性。
public class HintAttachedProperty
{
public static void SetHint(DependencyObject obj, string text)
{
obj.SetValue(HintProperty, text);
}
public static string GetHint(DependencyObject obj)
{
return obj.GetValue(HintProperty)?.ToString() ?? string.Empty;
}
public static readonly DependencyProperty HintProperty =
DependencyProperty.RegisterAttached("Hint", typeof(string), typeof(HintAttachedProperty), new FrameworkPropertyMetadata(null));
}
DataGrid 的 xaml,现在附加的 属性 提示设置为值“A test”:
<local:CustomDataGrid x:Name="datagrid"
AutoGenerateColumns="False"
ItemsSource="{Binding ItemsCollection}"
ClipboardCopyMode="IncludeHeader"
EnableRowsMove="True"
AlternatingRowBackground="LightBlue"
AlternationCount="2">
<DataGrid.Columns>
<DataGridCheckBoxColumn Header="IsChecked" Binding="{Binding IsChecked}"/>
<DataGridTextColumn x:Name="Test"
local:IsHighLightedAttachedProperty.IsHighLighted="True"
local:HintAttachedProperty.Hint="A test"
Header="ExperienceInMonth"
Binding="{Binding ExperienceInMonth}">
</DataGridTextColumn>
<DataGridTemplateColumn Header="References">
...
</DataGridTemplateColumn>
<DataGridTextColumn Header="EmployeeName" Binding="{Binding EmployeeName}"/>
<DataGridTextColumn Header="EmployeeAge" Binding="{Binding EmployeeAge}" />
</DataGrid.Columns>
</local:CustomDataGrid>
这里是 DataGridCell 的样式,它控制三角形的可见性并且应该将附加的 属性 绑定到工具提示的内容:
<Style TargetType="{x:Type DataGridCell}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type DataGridCell}">
<Border x:Name="borderSides" BorderThickness="2,0,2,0" Background="Transparent" BorderBrush="Transparent" SnapsToDevicePixels="True">
<Border x:Name="borderTopBottom" BorderThickness="0,2,0,2" Background="Transparent" BorderBrush="Transparent" SnapsToDevicePixels="True">
<Grid>
<Grid x:Name="grid">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<ContentPresenter Grid.Column="0" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
<Polygon x:Name="pol" ToolTip="{Binding RelativeSource={RelativeSource Self}, Converter={StaticResource HintConverter}}" Grid.Column="1" HorizontalAlignment="Right" Points="5,0 10,0, 10,5" Stroke="Black" Fill="Black" >
</Polygon>
</Grid>
</Grid>
</Border>
</Border>
<ControlTemplate.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Converter={StaticResource HintConverter}}" Value="{x:Static sys:String.Empty}">
<Setter TargetName="pol" Property="Visibility" Value="Collapsed"/>
</DataTrigger>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Converter={StaticResource HintConverter}}" Value="{x:Null}">
<Setter TargetName="pol" Property="Visibility" Value="Collapsed"/>
</DataTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
在绑定中 {Binding RelativeSource={RelativeSource Self}, Converter={StaticResource HintConverter}} 我正在使用 DataGridCell 内容的转换器来获取值附加的 属性,因为当我做 {Binding path=(local:HintAttachedProperty.Hint)} 它总是返回 null。
转换器代码:
public class HintConverters : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
DataGridCell dgc = value as DataGridCell;
if (dgc != null)
{
DataGridColumn col = dgc.Column;
if (col != null)
{
var hint = HintAttachedProperty.GetHint(col);
return hint;
}
}
return "";
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
调试时,我可以在转换器中看到值是“A 测试”,因此没有隐藏三角形,但是当我将光标放在工具提示上时,工具提示是空的:
提示值:
工具提示为空:
另一方面,如果我直接绑定到样式中 ItemsSource 的 属性,例如 Employee Name,ToolTip="{Binding EmployeeName}" 完美运行:
如何将 属性 的值绑定并附加到 属性?这可能吗?也许我应该换个方向?
感谢您的关注
转换器是多余的,您只需更正绑定即可使其正常工作。
在 Polygon
中您需要找到 DataGridCell
祖先,而不是 Self
,因为那是多边形。从那里您需要访问 Column
,因为那是 Hint
所附加的内容。
<Polygon x:Name="pol" ToolTip="{Binding Column.(local:HintAttachedProperty.Hint), RelativeSource={RelativeSource AncestorType={x:Type DataGridCell}}}" Grid.Column="1" HorizontalAlignment="Right" Points="5,0 10,0, 10,5" Stroke="Black" Fill="Black"/>
在你的触发器中,相对来源是正确的,因为在这种情况下 Self
是 DataGridCell
,但如上所述你需要访问 Column
其中 Hint
附上。
<DataTrigger Binding="{Binding Column.(local:HintAttachedProperty.Hint), RelativeSource={RelativeSource Self}}" Value="{x:Static system:String.Empty}">
<Setter TargetName="pol" Property="Visibility" Value="Collapsed"/>
</DataTrigger>
<DataTrigger Binding="{Binding Column.(local:HintAttachedProperty.Hint), RelativeSource={RelativeSource Self}}" Value="{x:Null}">
<Setter TargetName="pol" Property="Visibility" Value="Collapsed"/>
</DataTrigger>
更新您的意见。在 当前项的数据上下文 或数据网格数据上下文上绑定 属性 不会以这种方式工作,因为列不是可视化树的一部分,因为列只是定义应如何构建此列中的单元格。
我将向您展示当前项目的数据上下文的解决方法。绑定到父 DataGrid
的数据上下文的工作方式不同。您可以通过 Style
将 Hint
属性 附加到实际显示在单元格内的元素。在样式中,您可以访问当前项目的数据上下文。
如果您有一个 read-only 列,您只需创建一个 ElementStyle
。如果你有一个 editbale 列,你想在其中显示多边形和工具提示,你还需要一个 EditingElementStyle
.
<DataGridTextColumn Header="EmployeeName" Binding="{Binding EmployeeName}">
<DataGridTextColumn.ElementStyle>
<Style TargetType="{x:Type TextBlock}" BasedOn="{StaticResource {x:Type TextBlock}}">
<Setter Property="local:HintAttachedProperty.Hint" Value="{Binding EmployeeName}"/>
</Style>
</DataGridTextColumn.ElementStyle>
<DataGridTextColumn.EditingElementStyle>
<Style TargetType="{x:Type TextBox}" BasedOn="{StaticResource {x:Type TextBox}}">
<Setter Property="local:HintAttachedProperty.Hint" Value="{Binding EmployeeName}"/>
</Style>
</DataGridTextColumn.EditingElementStyle>
</DataGridTextColumn>
调整绑定以使用 Content
属性 而不是 Column
来访问已附加 Hint
[= 的单元格模板中的顶级元素62=] 到,例如TextBlock
.
<Polygon x:Name="pol" ToolTip="{Binding Content.(local:HintAttachedProperty.Hint), RelativeSource={RelativeSource AncestorType={x:Type DataGridCell}}}" Grid.Column="1" HorizontalAlignment="Right" Points="5,0 10,0, 10,5" Stroke="Black" Fill="Black" >
<DataTrigger Binding="{Binding Content.(local:HintAttachedProperty.Hint), RelativeSource={RelativeSource Self}}" Value="{x:Static system:String.Empty}">
<Setter TargetName="pol" Property="Visibility" Value="Collapsed"/>
</DataTrigger>
<DataTrigger Binding="{Binding Content.(local:HintAttachedProperty.Hint), RelativeSource={RelativeSource Self}}" Value="{x:Null}">
<Setter TargetName="pol" Property="Visibility" Value="Collapsed"/>
</DataTrigger>
回顾一下,这里的主要问题是您无法从列访问数据上下文。
覆盖单元格 ControlTemplate
的另一种方法是创建 DataGridTemplateColumn
s,在其中为每一列创建自定义 DataTemplate
,其中包含箭头多边形和工具提示.
然后您可以轻松附加和绑定您的 Hint
属性,因为您可以访问当前项目的所有控件和数据上下文。此解决方案的缺点是您必须为每一列创建自定义数据模板并在各处复制箭头和工具提示 XAML。