WPF TreeView 中的 TextBox 不更新源

TextBox in WPF TreeView not updating source

我不知道我是根本不理解这里的某些东西,还是刚刚做了一些愚蠢的事情。

我有一个包含两种类型模板的树视图。

  1. 具有 TextBlock 的 HierarchicalDataTemplate 节点
  2. 叶子是带有 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"/>