WPF非依赖属性绑定的问题

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了WPF非依赖属性绑定的问题相关的知识,希望对你有一定的参考价值。

初学WPF,如图所示:
我想把MediaElement的Position属性绑定在MediaCurrentTs上面。
但是出现了一个Position不是依赖属性的异常。
我该怎么解决才能正常绑定呢?

wpf里可以用来错绑定的属性都是依赖属性。你可以通过后台打MediaElement. 来获取到它所有的
依赖属性。Position不是它的依赖属性,不能用来做绑定。我的建议是,你可以继承MediaElement做个自定义控件,给自定义控件加个依赖属性,属性的回调函数中设置Positon。
参考技术A 使用附加属性,就像Cavas.Top一样 ,可以附加到其他控件上

WPF如何将附加属性绑定到依赖属性

【中文标题】WPF如何将附加属性绑定到依赖属性【英文标题】:WPF how to bind attached property to dependency property 【发布时间】:2020-12-22 10:45:24 【问题描述】:

我正在自定义 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 test”,因此没有隐藏三角形,但是当我将光标放在工具提示上时为空:

提示值:

工具提示为空:

另一方面,如果我直接绑定到样式中 ItemsSource 的属性,例如 Employee Name,ToolTip="Binding EmployeeName" 效果很好:

如何将属性的值和附加属性绑定到属性?这可能吗?也许我应该换个方向?

感谢您的关注

【问题讨论】:

你的设置器 【参考方案1】:

转换器是多余的,您只需要更正您的绑定以使其工作。

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"/>

在您的触发器中,相对来源是正确的,因为在这种情况下SelfDataGridCell,但如上所述,您需要访问附加了HintColumn

<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>

更新您的 cmets。在当前项目的数据上下文或数据网格数据上下文上绑定属性不起作用,因为列不是可视化树的一部分,因为列只是单元格如何定义在此列中应构造。

我将向您展示当前项目的数据上下文的解决方法。绑定到父 DataGrid 的数据上下文的工作方式不同。您可以通过StyleHint 属性附加到实际显示在单元格内的元素。在样式中,您可以访问当前项目的数据上下文。

如果您有一个只读列,您只需要创建一个ElementStyle。如果您有一个要在其中显示多边形和工具提示的可编辑列,则还需要一个 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 属性附加到的单元格模板中的***元素,例如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 的方法是创建DataGridTemplateColumns,在其中为每个列创建自定义DataTemplate,其中包含箭头多边形和工具提示。

然后您可以轻松附加和绑定您的Hint 属性,因为您可以访问所有控件和当前项目的数据上下文。此解决方案的缺点是您必须为每一列创建自定义数据模板,并将箭头和工具提示 XAML 复制到任何地方。

【讨论】:

感谢您的回复!这在直接设置附加属性的值时确实有效,但如果我将它绑定到一个属性,它不会 local:HintAttachedProperty.Hint="Binding EmployeeName" @mmas 我为您的方案添加了两个解决方法。 谢谢!它确实有效,我一直在寻找一种更通用的方法,不必为每列创建自定义数据模板,但我会这样做

以上是关于WPF非依赖属性绑定的问题的主要内容,如果未能解决你的问题,请参考以下文章

依赖属性

2019-11-29-WPF-依赖属性绑定不上调试方法

WPF:绑定到依赖属性

为啥WPF里面的PasswordBox的Password属性不是依赖属性?

WPF入门教程系列十三——依赖属性

在依赖属性 WPF 上使用绑定时出现问题 [重复]