我创建一个附加属性来统一管理页面上的按钮状态。在调试中,发现附加属性先于xaml页面的DataContext属性绑定,导致出现绑定错误
| 错误 |
|---|
| System.Windows.Data Error: 40 : BindingExpression path error: 'Text' property not found on 'object' ''CIconTexts' (HashCode=13213278)'. BindingExpression:Path=Text; DataItem='CIconTexts' (HashCode=13213278); target element is 'Button' (Name='btnMoveDown'); target property is 'ToolTip' (type 'Object') |
我简单写个测试程序,发现确实附加属性先于datacontext绑定执行。百度了许久,没有找到xaml调整各个属性执行顺序的方法。特来论坛请教大家。
测试类
Public Class Items
Property Text As String
Property Item As item
Public Overrides Function ToString() As String
Return Text
End Function
End Class
Public Class item
Property Text As String
Public Overrides Function ToString() As String
Return Text
End Function
End Class
附加属性管理类
Public Class manage
#Region "*** attached1 附加依赖属性 ****"
''' <summary>
''' 从指定元素获取 attached1 依赖项属性的值。
''' </summary>
''' <param name="obj">从中读取属性值的元素。</param>
''' <returns>从属性存储获取的属性值。</returns>
Public Shared Function Getattached1(obj As DependencyObject) As String
Return obj.GetValue(attached1Property)
End Function
''' <summary>
''' 将 attached1 依赖项属性的值设置为指定元素。
''' </summary>
''' <param name="obj">对其设置属性值的元素。</param>
''' <param name="value">要设置的值。</param>
Public Shared Sub Setattached1(obj As DependencyObject, value As String)
obj.SetValue(attached1Property, value)
End Sub
''' <summary>
''' 标识 attached1 依赖项属性。
''' </summary>
Public Shared ReadOnly attached1Property As DependencyProperty =
DependencyProperty.RegisterAttached("attached1", GetType(String), GetType(manage),
New PropertyMetadata(Nothing, New PropertyChangedCallback(AddressOf Onattached1PropertyChangedBackCall)))
Private Shared Sub Onattached1PropertyChangedBackCall(d As DependencyObject, args As DependencyPropertyChangedEventArgs)
Dim fe As FrameworkElement = TryCast(d, FrameworkElement)
MsgBox($" { fe.DataContext }")
Dim oldValue As String = args.OldValue
Dim newValue As String = args.NewValue
If oldValue = newValue Then Return
'Dim target As manage = TryCast(d, manage)
'target?.Onattached1Changed(newValue, oldValue)
End Sub
#End Region
End Class
创建一个自定义按钮并创建一个依赖属性
Imports System.Windows.Controls.Primitives
Public Class testButton
Inherits System.Windows.Controls.Button
Shared Sub New()
'此 OverrideMetadata 调用通知系统该元素希望提供不同于其基类的样式。
'此样式定义在 themes\generic.xaml 中
DefaultStyleKeyProperty.OverrideMetadata(GetType(testButton), new FrameworkPropertyMetadata(GetType(testButton)))
End Sub
#Region " ***** icon 依赖属性 *****"
''' <summary>
''' 获取或设置 属性描述 的值
''' </summary>
'''<remarks>
'''
'''</remarks>
Public Property icon As String
Get
Return GetValue(iconProperty)
End Get
Set(ByVal value As String)
SetValue(iconProperty, value)
End Set
End Property
''' <summary>
''' 标识 icon 依赖属性。
''' </summary>
Public Shared ReadOnly iconProperty As DependencyProperty =
DependencyProperty.Register("icon",
GetType(String), GetType(testButton),
New PropertyMetadata(Nothing, New PropertyChangedCallback(AddressOf iconPropertyChangedBackCall)))
''' <summary>
''' icon 属性更改时回调此方法。
'''</summary>
Private Shared Sub iconPropertyChangedBackCall(d As DependencyObject, dp As DependencyPropertyChangedEventArgs)
Dim newValue As String = TryCast(dp.NewValue, String)
Dim oldValue As String = TryCast(dp.OldValue, String)
If Object.Equals(newValue, oldValue) Then Return
Dim target As testButton = TryCast(d, testButton)
target?.OniconChanged(newValue, oldValue)
End Sub
''' <summary>
''' icon 属性更改时调用此方法。
'''</summary>
''' <param name="oldValue"> 属性的旧值。</param>
''' <param name="newValue"> 属性的新值。</param>
Protected Overridable Sub OniconChanged(newValue As String, oldValue As String)
MsgBox("icon被设置")
End Sub
#End Region
End Class
generic
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:依赖属性和附加属性同时设置的执行顺序">
<Style TargetType="{x:Type local:testButton}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:testButton}">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<ContentPresenter />
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
测试页面
<Window x:Class="MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:依赖属性和附加属性同时设置的执行顺序"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.Resources>
<local:Items Text ="items" x:Key="t1" >
<local:Items.Item >
<local:item Text="item" ></local:item>
</local:Items.Item>
</local:Items>
</Window.Resources>
<Grid DataContext="{StaticResource t1 }">
<Grid.RowDefinitions >
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<!-- 根据运行情况来看,哪个在前面,哪个先运行-->
<!--<local:testButton x:Name ="testbtn1" icon="a" local:manage.attached1="bb" />-->
<!-- 根据运行情况来看,下面代码附加属性先运行,datacontext慢运行-->
<local:testButton x:Name ="testbtn2" DataContext="{Binding Path=Item }" local:manage.attached1="bb" />
<Button Content="test" Click="Button_Click" Grid.Row="1"/>
</Grid>
</Window>
后台代码
Class MainWindow
Private Sub Button_Click(sender As Object, e As RoutedEventArgs)
MsgBox(testbtn2.DataContext.ToString)
End Sub
End Class