WPF TreeView 中的 TextBox 不更新源
TextBox in WPF TreeView not updating source
我不知道我是根本不理解这里的某些东西,还是刚刚做了一些愚蠢的事情。
我有一个包含两种类型模板的树视图。
- 具有 TextBlock 的 HierarchicalDataTemplate 节点
- 叶子是带有 TextBlock 和 TextBox3 的 DataTemplate。
来自源的绑定在所有情况下都工作正常,并从底层 classes 读取 Name 或 Name & Value 属性,并根据后面代码的任何更改进行更新。
TreeNode 和 TreeLeaf class 实现 INotifyPropertyChanged
然而,TextBox.text 属性 绑定回 TreeLeaf.Value 属性(及其 getter)似乎不起作用。
XAML
<TreeView Name="ItemsTree" Grid.Row="1" Grid.Column="1" ItemsSource="{Binding}">
<TreeView.Resources>
<Color x:Key="detailMark">#FFA1A9B3</Color>
<SolidColorBrush x:Key="detailMarkBrush" Color="{StaticResource ResourceKey=detailMark}" />
<Style x:Key="flatTextBox" TargetType="{x:Type TextBox}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TextBox}">
<Grid>
<Rectangle Stroke="{StaticResource ResourceKey=detailMarkBrush}" StrokeThickness="0"/>
<TextBox Margin="1" Text="{TemplateBinding Text}" BorderThickness="0"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<HierarchicalDataTemplate DataType="{x:Type local:TreeNode}" ItemsSource="{Binding Children, NotifyOnSourceUpdated=True, UpdateSourceTrigger=PropertyChanged}">
<TextBlock Text="{Binding Name, NotifyOnSourceUpdated=True}">
</TextBlock>
</HierarchicalDataTemplate>
<DataTemplate DataType="{x:Type local:TreeLeaf}" >
<StackPanel Orientation="Horizontal">
<TextBlock Width="150" Text="{Binding Name, NotifyOnSourceUpdated=True}"/>
<TextBox Width="150" Text="{Binding Value, NotifyOnSourceUpdated=True, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" Style="{StaticResource ResourceKey=flatTextBox}"/>
</StackPanel>
</DataTemplate>
</TreeView.Resources>
</TreeView>
然后是将项目添加到树中的代码
ItemsTree.Items.Add(treeRoot)
树叶class
Public Class TreeLeaf
Implements INotifyPropertyChanged, IEditableObject
Private m_Key As String
Private m_Value As String
Private m_Parent As TreeNode
Private temp_Item As TreeLeaf = Nothing
Private m_Editing As Boolean = False
Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
Public Sub New()
m_Key = ""
m_Value = ""
End Sub
Public Sub NotifyPropertyChanged(ByVal propertyName As String)
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName))
End Sub
Public Sub BeginEdit() Implements IEditableObject.BeginEdit
If Not m_Editing Then
temp_Item = MemberwiseClone()
m_Editing = True
End If
End Sub
Public Sub EndEdit() Implements IEditableObject.EndEdit
If m_Editing = True Then
temp_Item = Nothing
m_Editing = False
End If
End Sub
Public Sub CancelEdit() Implements IEditableObject.CancelEdit
If m_Editing = True Then
m_Key = temp_Item.m_Key
m_Value = temp_Item.m_Value
m_Editing = False
End If
End Sub
Public Property Parent As TreeNode
Get
Return m_Parent
End Get
Set(value As TreeNode)
m_Parent = value
NotifyPropertyChanged("Parent")
End Set
End Property
Public ReadOnly Property Name As String
Get
Return m_Key & " : "
End Get
End Property
Public Property Key As String
Get
Return m_Key
End Get
Set(ByVal value As String)
If Not value = m_Key Then
m_Key = value
NotifyPropertyChanged("Key")
End If
End Set
End Property
Public Property Value As String
Get
Return m_Value
End Get
Set(ByVal value As String)
If Not value = m_Value Then
m_Value = value
NotifyPropertyChanged("Value")
End If
End Set
End Property
End Class
我看到有人提到设置 DataContext,但我不明白这将如何适用于这种情况。
不可能从您的代码中判断绑定是否有效,因为 ViewModel 代码已经足够复杂并且还取决于 treeview viewmodel 代码等...
调试此类问题的一种方法是创建一个转换器并使其成为应调试的绑定的一部分。在转换器中设置断点将显示当源或目标中的值发生更改时是否按预期调用绑定。转换器的代码非常简单:
Imports System
Imports System.Globalization
Imports System.Windows.Data
Namespace MyApp.Converters
Public Class DebugConverter
Inherits IValueConverter
Public Function Convert(ByVal value As Object, ByVal targetType As Type, ByVal parameter As Object, ByVal culture As CultureInfo) As Object
Return value
End Function
Public Function ConvertBack(ByVal value As Object, ByVal targetType As Type, ByVal parameter As Object, ByVal culture As CultureInfo) As Object
Return value
End Function
End Class
End Namespace
将其集成到有问题的绑定中也很简单。通过其名称空间(例如 MyApp.Converters)将其添加到 XAML:
xmlns:conv="clr-namespace:MyApp.Converters"
...并在 TreeView 代码清单的资源部分通过此语句创建一个实例:
<conv:DebugConverter x:Key="DebugConverter"/>
...最后但同样重要的是将转换器集成到您要调试的绑定中:
<TextBox
Width="150"
Style="{StaticResource ResourceKey=flatTextBox}"
Text="{Binding Value, NotifyOnSourceUpdated=True, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay, Converter={StaticResource DebugConverter}}" />
现在,当您在 Convert 和 ConvertBack 方法中设置断点时,您应该会在启动应用程序时看到这些方法被调用。如果您在任一方向都看不到它,则表明绑定无效或值未更新,然后您可以进一步搜索原因。
希望对您有所帮助。
好吧,不幸的是想通了。
文本框的样式模板覆盖了绑定。
带有 TemplateBinding 的行似乎默认为来自源的一种方式
<TextBox Margin="1" Text="{TemplateBinding Text}" BorderThickness="0"/>
如果您也更改它,它现在可以正常工作了
<TextBox Margin="1" Text="{Binding Value, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, NotifyOnSourceUpdated=True}" BorderThickness="0"/>
我不知道我是根本不理解这里的某些东西,还是刚刚做了一些愚蠢的事情。
我有一个包含两种类型模板的树视图。
- 具有 TextBlock 的 HierarchicalDataTemplate 节点
- 叶子是带有 TextBlock 和 TextBox3 的 DataTemplate。
来自源的绑定在所有情况下都工作正常,并从底层 classes 读取 Name 或 Name & Value 属性,并根据后面代码的任何更改进行更新。
TreeNode 和 TreeLeaf class 实现 INotifyPropertyChanged
然而,TextBox.text 属性 绑定回 TreeLeaf.Value 属性(及其 getter)似乎不起作用。
XAML
<TreeView Name="ItemsTree" Grid.Row="1" Grid.Column="1" ItemsSource="{Binding}">
<TreeView.Resources>
<Color x:Key="detailMark">#FFA1A9B3</Color>
<SolidColorBrush x:Key="detailMarkBrush" Color="{StaticResource ResourceKey=detailMark}" />
<Style x:Key="flatTextBox" TargetType="{x:Type TextBox}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TextBox}">
<Grid>
<Rectangle Stroke="{StaticResource ResourceKey=detailMarkBrush}" StrokeThickness="0"/>
<TextBox Margin="1" Text="{TemplateBinding Text}" BorderThickness="0"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<HierarchicalDataTemplate DataType="{x:Type local:TreeNode}" ItemsSource="{Binding Children, NotifyOnSourceUpdated=True, UpdateSourceTrigger=PropertyChanged}">
<TextBlock Text="{Binding Name, NotifyOnSourceUpdated=True}">
</TextBlock>
</HierarchicalDataTemplate>
<DataTemplate DataType="{x:Type local:TreeLeaf}" >
<StackPanel Orientation="Horizontal">
<TextBlock Width="150" Text="{Binding Name, NotifyOnSourceUpdated=True}"/>
<TextBox Width="150" Text="{Binding Value, NotifyOnSourceUpdated=True, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" Style="{StaticResource ResourceKey=flatTextBox}"/>
</StackPanel>
</DataTemplate>
</TreeView.Resources>
</TreeView>
然后是将项目添加到树中的代码
ItemsTree.Items.Add(treeRoot)
树叶class
Public Class TreeLeaf
Implements INotifyPropertyChanged, IEditableObject
Private m_Key As String
Private m_Value As String
Private m_Parent As TreeNode
Private temp_Item As TreeLeaf = Nothing
Private m_Editing As Boolean = False
Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
Public Sub New()
m_Key = ""
m_Value = ""
End Sub
Public Sub NotifyPropertyChanged(ByVal propertyName As String)
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName))
End Sub
Public Sub BeginEdit() Implements IEditableObject.BeginEdit
If Not m_Editing Then
temp_Item = MemberwiseClone()
m_Editing = True
End If
End Sub
Public Sub EndEdit() Implements IEditableObject.EndEdit
If m_Editing = True Then
temp_Item = Nothing
m_Editing = False
End If
End Sub
Public Sub CancelEdit() Implements IEditableObject.CancelEdit
If m_Editing = True Then
m_Key = temp_Item.m_Key
m_Value = temp_Item.m_Value
m_Editing = False
End If
End Sub
Public Property Parent As TreeNode
Get
Return m_Parent
End Get
Set(value As TreeNode)
m_Parent = value
NotifyPropertyChanged("Parent")
End Set
End Property
Public ReadOnly Property Name As String
Get
Return m_Key & " : "
End Get
End Property
Public Property Key As String
Get
Return m_Key
End Get
Set(ByVal value As String)
If Not value = m_Key Then
m_Key = value
NotifyPropertyChanged("Key")
End If
End Set
End Property
Public Property Value As String
Get
Return m_Value
End Get
Set(ByVal value As String)
If Not value = m_Value Then
m_Value = value
NotifyPropertyChanged("Value")
End If
End Set
End Property
End Class
我看到有人提到设置 DataContext,但我不明白这将如何适用于这种情况。
不可能从您的代码中判断绑定是否有效,因为 ViewModel 代码已经足够复杂并且还取决于 treeview viewmodel 代码等...
调试此类问题的一种方法是创建一个转换器并使其成为应调试的绑定的一部分。在转换器中设置断点将显示当源或目标中的值发生更改时是否按预期调用绑定。转换器的代码非常简单:
Imports System
Imports System.Globalization
Imports System.Windows.Data
Namespace MyApp.Converters
Public Class DebugConverter
Inherits IValueConverter
Public Function Convert(ByVal value As Object, ByVal targetType As Type, ByVal parameter As Object, ByVal culture As CultureInfo) As Object
Return value
End Function
Public Function ConvertBack(ByVal value As Object, ByVal targetType As Type, ByVal parameter As Object, ByVal culture As CultureInfo) As Object
Return value
End Function
End Class
End Namespace
将其集成到有问题的绑定中也很简单。通过其名称空间(例如 MyApp.Converters)将其添加到 XAML:
xmlns:conv="clr-namespace:MyApp.Converters"
...并在 TreeView 代码清单的资源部分通过此语句创建一个实例:
<conv:DebugConverter x:Key="DebugConverter"/>
...最后但同样重要的是将转换器集成到您要调试的绑定中:
<TextBox
Width="150"
Style="{StaticResource ResourceKey=flatTextBox}"
Text="{Binding Value, NotifyOnSourceUpdated=True, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay, Converter={StaticResource DebugConverter}}" />
现在,当您在 Convert 和 ConvertBack 方法中设置断点时,您应该会在启动应用程序时看到这些方法被调用。如果您在任一方向都看不到它,则表明绑定无效或值未更新,然后您可以进一步搜索原因。
希望对您有所帮助。
好吧,不幸的是想通了。
文本框的样式模板覆盖了绑定。
带有 TemplateBinding 的行似乎默认为来自源的一种方式
<TextBox Margin="1" Text="{TemplateBinding Text}" BorderThickness="0"/>
如果您也更改它,它现在可以正常工作了
<TextBox Margin="1" Text="{Binding Value, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, NotifyOnSourceUpdated=True}" BorderThickness="0"/>