Search code examples
wpfvb.netdata-bindingtextboxtreeview

TextBox in WPF TreeView not updating source


I cannot tell if I fundamentally don't understand something here, or have just done something silly.

I have a tree view with two types of templates.

  1. Nodes that are HierarchicalDataTemplate with a TextBlock
  2. Leaves that are DataTemplate with a TextBlock and a TextBox3.

The binding from source is working fine in all cases and reading the Name or Name & Value properties from the underlying classes, and updating if from any changes from the code behind.

Both the TreeNode and TreeLeaf class implement INotifyPropertyChanged

However the binding of the TextBox.text property back to the TreeLeaf.Value property (with its getter) does not seem to work.

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>

And then code behind for adding the items to the tree

ItemsTree.Items.Add(treeRoot)

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

I have seen mentions of setting the DataContext but I don't understand how that would apply to this situation.


Solution

  • Well, unfortunately figured it out.

    The style template for the text box was overriding the binding.

    The line with TemplateBinding seems to default to a one way from source

    <TextBox Margin="1" Text="{TemplateBinding Text}" BorderThickness="0"/>
    

    and it now works if you change it too

    <TextBox Margin="1" Text="{Binding Value, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, NotifyOnSourceUpdated=True}" BorderThickness="0"/>